@larksuite/openclaw-lark 2026.3.15 → 2026.3.17-beta.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.
- package/bin/openclaw-lark.js +48 -0
- package/index.d.ts +1 -1
- package/package.json +4 -1
- package/src/commands/auth.js +3 -2
- package/src/core/auth-errors.d.ts +12 -10
- package/src/core/auth-errors.js +13 -14
- package/src/core/config-schema.d.ts +429 -4
- package/src/core/tool-scopes.d.ts +3 -3
- package/src/core/tool-scopes.js +3 -2
- package/src/core/uat-client.js +31 -16
- package/src/messaging/inbound/reaction-handler.js +5 -4
- package/src/tools/oapi/helpers.d.ts +1 -1
- package/src/tools/oapi/im/message-read.js +2 -2
- package/src/tools/oapi/im/user-name-uat.d.ts +3 -0
- package/src/tools/oapi/im/user-name-uat.js +25 -12
- package/src/tools/oauth.js +8 -10
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
// --tools-version <ver> lets the user pin a specific version
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
let version = "latest";
|
|
9
|
+
|
|
10
|
+
const vIdx = args.indexOf("--tools-version");
|
|
11
|
+
if (vIdx !== -1) {
|
|
12
|
+
version = args[vIdx + 1];
|
|
13
|
+
// Remove --tools-version <ver> from forwarded args
|
|
14
|
+
args.splice(vIdx, 2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const allArgs = ["--yes", `@larksuite/openclaw-lark-tools@${version}`, ...args];
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
if (process.platform === "win32") {
|
|
21
|
+
// On Windows, npx is a .cmd shim that can be broken or trigger
|
|
22
|
+
// DEP0190. Bypass it entirely: run node with the npx-cli.js
|
|
23
|
+
// script located next to the running node binary.
|
|
24
|
+
const npxCli = join(
|
|
25
|
+
dirname(process.execPath),
|
|
26
|
+
"node_modules",
|
|
27
|
+
"npm",
|
|
28
|
+
"bin",
|
|
29
|
+
"npx-cli.js",
|
|
30
|
+
);
|
|
31
|
+
execFileSync(process.execPath, [npxCli, ...allArgs], {
|
|
32
|
+
stdio: "inherit",
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
NODE_OPTIONS: [
|
|
36
|
+
process.env.NODE_OPTIONS,
|
|
37
|
+
"--disable-warning=DEP0190",
|
|
38
|
+
]
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.join(" "),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
execFileSync("npx", allArgs, { stdio: "inherit" });
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
process.exit(error.status ?? 1);
|
|
48
|
+
}
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larksuite/openclaw-lark",
|
|
3
|
-
"version": "2026.3.
|
|
3
|
+
"version": "2026.3.17-beta.0",
|
|
4
4
|
"description": "OpenClaw Lark/Feishu channel plugin",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"openclaw-lark": "bin/openclaw-lark.js"
|
|
8
|
+
},
|
|
6
9
|
"files": [
|
|
7
10
|
"**/*"
|
|
8
11
|
],
|
package/src/commands/auth.js
CHANGED
|
@@ -13,7 +13,7 @@ import { getTicket } from '../core/lark-ticket';
|
|
|
13
13
|
import { getLarkAccount } from '../core/accounts';
|
|
14
14
|
import { LarkClient } from '../core/lark-client';
|
|
15
15
|
import { getAppInfo, getAppGrantedScopes } from '../core/app-scope-checker';
|
|
16
|
-
import { getStoredToken } from '../core/token-store';
|
|
16
|
+
import { getStoredToken, tokenStatus } from '../core/token-store';
|
|
17
17
|
import { filterSensitiveScopes } from '../core/tool-scopes';
|
|
18
18
|
import { assertOwnerAccessStrict, OwnerAccessDeniedError } from '../core/owner-policy';
|
|
19
19
|
import { openPlatformDomain } from '../core/domains';
|
|
@@ -124,7 +124,8 @@ async function executeFeishuAuth(config) {
|
|
|
124
124
|
return { kind: 'no_user_scopes' };
|
|
125
125
|
}
|
|
126
126
|
const existing = await getStoredToken(appId, senderOpenId);
|
|
127
|
-
const
|
|
127
|
+
const tokenValid = existing && tokenStatus(existing) !== 'expired';
|
|
128
|
+
const grantedScopes = new Set(tokenValid ? (existing.scope?.split(/\s+/).filter(Boolean) ?? []) : []);
|
|
128
129
|
const missingScopes = appScopes.filter((s) => !grantedScopes.has(s));
|
|
129
130
|
if (missingScopes.length === 0) {
|
|
130
131
|
return { kind: 'all_authorized', count: appScopes.length };
|
|
@@ -18,22 +18,24 @@ export declare const LARK_ERROR: {
|
|
|
18
18
|
/** access_token 无效 */
|
|
19
19
|
readonly TOKEN_INVALID: 99991668;
|
|
20
20
|
/** access_token 已过期 */
|
|
21
|
-
readonly TOKEN_EXPIRED:
|
|
22
|
-
/** refresh_token
|
|
23
|
-
readonly REFRESH_TOKEN_INVALID:
|
|
24
|
-
/** refresh_token
|
|
25
|
-
readonly REFRESH_TOKEN_EXPIRED:
|
|
26
|
-
/** refresh_token 缺失 */
|
|
27
|
-
readonly REFRESH_TOKEN_MISSING: 20024;
|
|
21
|
+
readonly TOKEN_EXPIRED: 99991677;
|
|
22
|
+
/** refresh_token 本身无效(格式非法或来自 v1 API) */
|
|
23
|
+
readonly REFRESH_TOKEN_INVALID: 20026;
|
|
24
|
+
/** refresh_token 已过期(超过 365 天) */
|
|
25
|
+
readonly REFRESH_TOKEN_EXPIRED: 20037;
|
|
28
26
|
/** refresh_token 已被吊销 */
|
|
29
|
-
readonly REFRESH_TOKEN_REVOKED:
|
|
27
|
+
readonly REFRESH_TOKEN_REVOKED: 20064;
|
|
28
|
+
/** refresh_token 已被使用(单次消费,rotation 场景) */
|
|
29
|
+
readonly REFRESH_TOKEN_ALREADY_USED: 20073;
|
|
30
|
+
/** refresh token 端点服务端内部错误,可重试 */
|
|
31
|
+
readonly REFRESH_SERVER_ERROR: 20050;
|
|
30
32
|
/** 消息已被撤回 */
|
|
31
33
|
readonly MESSAGE_RECALLED: 230011;
|
|
32
34
|
/** 消息已被删除 */
|
|
33
35
|
readonly MESSAGE_DELETED: 231003;
|
|
34
36
|
};
|
|
35
|
-
/**
|
|
36
|
-
export declare const
|
|
37
|
+
/** refresh token 端点可重试的错误码集合(服务端瞬时故障)。遇到后重试一次,仍失败则清 token。 */
|
|
38
|
+
export declare const REFRESH_TOKEN_RETRYABLE: ReadonlySet<number>;
|
|
37
39
|
/** 消息终止错误码集合(撤回/删除),遇到后应停止对该消息的后续操作。 */
|
|
38
40
|
export declare const MESSAGE_TERMINAL_CODES: ReadonlySet<number>;
|
|
39
41
|
/** access_token 失效相关的错误码集合,遇到后可尝试刷新重试。 */
|
package/src/core/auth-errors.js
CHANGED
|
@@ -22,26 +22,25 @@ export const LARK_ERROR = {
|
|
|
22
22
|
/** access_token 无效 */
|
|
23
23
|
TOKEN_INVALID: 99991668,
|
|
24
24
|
/** access_token 已过期 */
|
|
25
|
-
TOKEN_EXPIRED:
|
|
26
|
-
/** refresh_token
|
|
27
|
-
REFRESH_TOKEN_INVALID:
|
|
28
|
-
/** refresh_token
|
|
29
|
-
REFRESH_TOKEN_EXPIRED:
|
|
30
|
-
/** refresh_token 缺失 */
|
|
31
|
-
REFRESH_TOKEN_MISSING: 20024,
|
|
25
|
+
TOKEN_EXPIRED: 99991677,
|
|
26
|
+
/** refresh_token 本身无效(格式非法或来自 v1 API) */
|
|
27
|
+
REFRESH_TOKEN_INVALID: 20026,
|
|
28
|
+
/** refresh_token 已过期(超过 365 天) */
|
|
29
|
+
REFRESH_TOKEN_EXPIRED: 20037,
|
|
32
30
|
/** refresh_token 已被吊销 */
|
|
33
|
-
REFRESH_TOKEN_REVOKED:
|
|
31
|
+
REFRESH_TOKEN_REVOKED: 20064,
|
|
32
|
+
/** refresh_token 已被使用(单次消费,rotation 场景) */
|
|
33
|
+
REFRESH_TOKEN_ALREADY_USED: 20073,
|
|
34
|
+
/** refresh token 端点服务端内部错误,可重试 */
|
|
35
|
+
REFRESH_SERVER_ERROR: 20050,
|
|
34
36
|
/** 消息已被撤回 */
|
|
35
37
|
MESSAGE_RECALLED: 230011,
|
|
36
38
|
/** 消息已被删除 */
|
|
37
39
|
MESSAGE_DELETED: 231003,
|
|
38
40
|
};
|
|
39
|
-
/**
|
|
40
|
-
export const
|
|
41
|
-
LARK_ERROR.
|
|
42
|
-
LARK_ERROR.REFRESH_TOKEN_EXPIRED,
|
|
43
|
-
LARK_ERROR.REFRESH_TOKEN_MISSING,
|
|
44
|
-
LARK_ERROR.REFRESH_TOKEN_REVOKED,
|
|
41
|
+
/** refresh token 端点可重试的错误码集合(服务端瞬时故障)。遇到后重试一次,仍失败则清 token。 */
|
|
42
|
+
export const REFRESH_TOKEN_RETRYABLE = new Set([
|
|
43
|
+
LARK_ERROR.REFRESH_SERVER_ERROR,
|
|
45
44
|
]);
|
|
46
45
|
/** 消息终止错误码集合(撤回/删除),遇到后应停止对该消息的后续操作。 */
|
|
47
46
|
export const MESSAGE_TERMINAL_CODES = new Set([
|
|
@@ -9,10 +9,435 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
export { z };
|
|
12
|
-
export declare const UATConfigSchema:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export declare const UATConfigSchema: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
allowedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
15
|
+
blockedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
16
|
+
}, z.core.$strip>>;
|
|
17
|
+
export declare const FeishuGroupSchema: z.ZodObject<{
|
|
18
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
19
|
+
allowlist: "allowlist";
|
|
20
|
+
open: "open";
|
|
21
|
+
disabled: "disabled";
|
|
22
|
+
}>>;
|
|
23
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
|
+
}, z.core.$strip>>;
|
|
28
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
29
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
30
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
31
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
export declare const FeishuAccountConfigSchema: z.ZodObject<{
|
|
34
|
+
appId: z.ZodOptional<z.ZodString>;
|
|
35
|
+
appSecret: z.ZodOptional<z.ZodString>;
|
|
36
|
+
encryptKey: z.ZodOptional<z.ZodString>;
|
|
37
|
+
verificationToken: z.ZodOptional<z.ZodString>;
|
|
38
|
+
name: z.ZodOptional<z.ZodString>;
|
|
39
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
40
|
+
domain: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"feishu">, z.ZodLiteral<"lark">, z.ZodString]>>;
|
|
41
|
+
connectionMode: z.ZodOptional<z.ZodEnum<{
|
|
42
|
+
websocket: "websocket";
|
|
43
|
+
webhook: "webhook";
|
|
44
|
+
}>>;
|
|
45
|
+
webhookPath: z.ZodOptional<z.ZodString>;
|
|
46
|
+
webhookPort: z.ZodOptional<z.ZodNumber>;
|
|
47
|
+
dmPolicy: z.ZodOptional<z.ZodEnum<{
|
|
48
|
+
allowlist: "allowlist";
|
|
49
|
+
open: "open";
|
|
50
|
+
pairing: "pairing";
|
|
51
|
+
disabled: "disabled";
|
|
52
|
+
}>>;
|
|
53
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
54
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
55
|
+
allowlist: "allowlist";
|
|
56
|
+
open: "open";
|
|
57
|
+
disabled: "disabled";
|
|
58
|
+
}>>;
|
|
59
|
+
groupAllowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
60
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
61
|
+
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
62
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
63
|
+
allowlist: "allowlist";
|
|
64
|
+
open: "open";
|
|
65
|
+
disabled: "disabled";
|
|
66
|
+
}>>;
|
|
67
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
68
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
69
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
71
|
+
}, z.core.$strip>>;
|
|
72
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
73
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
74
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
75
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
76
|
+
}, z.core.$strip>>>;
|
|
77
|
+
historyLimit: z.ZodOptional<z.ZodNumber>;
|
|
78
|
+
dmHistoryLimit: z.ZodOptional<z.ZodNumber>;
|
|
79
|
+
dms: z.ZodOptional<z.ZodObject<{
|
|
80
|
+
historyLimit: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
}, z.core.$strip>>;
|
|
82
|
+
textChunkLimit: z.ZodOptional<z.ZodNumber>;
|
|
83
|
+
chunkMode: z.ZodOptional<z.ZodEnum<{
|
|
84
|
+
newline: "newline";
|
|
85
|
+
paragraph: "paragraph";
|
|
86
|
+
none: "none";
|
|
87
|
+
}>>;
|
|
88
|
+
blockStreamingCoalesce: z.ZodOptional<z.ZodObject<{
|
|
89
|
+
minChars: z.ZodOptional<z.ZodNumber>;
|
|
90
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
91
|
+
idleMs: z.ZodOptional<z.ZodNumber>;
|
|
92
|
+
}, z.core.$strip>>;
|
|
93
|
+
mediaMaxMb: z.ZodOptional<z.ZodNumber>;
|
|
94
|
+
heartbeat: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
every: z.ZodOptional<z.ZodString>;
|
|
96
|
+
activeHours: z.ZodOptional<z.ZodObject<{
|
|
97
|
+
start: z.ZodOptional<z.ZodString>;
|
|
98
|
+
end: z.ZodOptional<z.ZodString>;
|
|
99
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
100
|
+
}, z.core.$strip>>;
|
|
101
|
+
target: z.ZodOptional<z.ZodString>;
|
|
102
|
+
to: z.ZodOptional<z.ZodString>;
|
|
103
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
104
|
+
accountId: z.ZodOptional<z.ZodString>;
|
|
105
|
+
}, z.core.$strip>>;
|
|
106
|
+
replyMode: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
107
|
+
streaming: "streaming";
|
|
108
|
+
auto: "auto";
|
|
109
|
+
static: "static";
|
|
110
|
+
}>, z.ZodObject<{
|
|
111
|
+
default: z.ZodOptional<z.ZodEnum<{
|
|
112
|
+
streaming: "streaming";
|
|
113
|
+
auto: "auto";
|
|
114
|
+
static: "static";
|
|
115
|
+
}>>;
|
|
116
|
+
group: z.ZodOptional<z.ZodEnum<{
|
|
117
|
+
streaming: "streaming";
|
|
118
|
+
auto: "auto";
|
|
119
|
+
static: "static";
|
|
120
|
+
}>>;
|
|
121
|
+
direct: z.ZodOptional<z.ZodEnum<{
|
|
122
|
+
streaming: "streaming";
|
|
123
|
+
auto: "auto";
|
|
124
|
+
static: "static";
|
|
125
|
+
}>>;
|
|
126
|
+
}, z.core.$strip>]>>;
|
|
127
|
+
streaming: z.ZodOptional<z.ZodBoolean>;
|
|
128
|
+
blockStreaming: z.ZodOptional<z.ZodBoolean>;
|
|
129
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
130
|
+
doc: z.ZodOptional<z.ZodBoolean>;
|
|
131
|
+
wiki: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
+
drive: z.ZodOptional<z.ZodBoolean>;
|
|
133
|
+
perm: z.ZodOptional<z.ZodBoolean>;
|
|
134
|
+
scopes: z.ZodOptional<z.ZodBoolean>;
|
|
135
|
+
}, z.core.$strip>>;
|
|
136
|
+
footer: z.ZodOptional<z.ZodObject<{
|
|
137
|
+
status: z.ZodOptional<z.ZodBoolean>;
|
|
138
|
+
elapsed: z.ZodOptional<z.ZodBoolean>;
|
|
139
|
+
}, z.core.$strip>>;
|
|
140
|
+
markdown: z.ZodOptional<z.ZodObject<{
|
|
141
|
+
tables: z.ZodOptional<z.ZodEnum<{
|
|
142
|
+
code: "code";
|
|
143
|
+
off: "off";
|
|
144
|
+
bullets: "bullets";
|
|
145
|
+
}>>;
|
|
146
|
+
}, z.core.$strip>>;
|
|
147
|
+
configWrites: z.ZodOptional<z.ZodBoolean>;
|
|
148
|
+
capabilities: z.ZodOptional<z.ZodObject<{
|
|
149
|
+
image: z.ZodOptional<z.ZodBoolean>;
|
|
150
|
+
audio: z.ZodOptional<z.ZodBoolean>;
|
|
151
|
+
video: z.ZodOptional<z.ZodBoolean>;
|
|
152
|
+
}, z.core.$strip>>;
|
|
153
|
+
dedup: z.ZodOptional<z.ZodObject<{
|
|
154
|
+
ttlMs: z.ZodOptional<z.ZodNumber>;
|
|
155
|
+
maxEntries: z.ZodOptional<z.ZodNumber>;
|
|
156
|
+
}, z.core.$strip>>;
|
|
157
|
+
reactionNotifications: z.ZodOptional<z.ZodEnum<{
|
|
158
|
+
all: "all";
|
|
159
|
+
off: "off";
|
|
160
|
+
own: "own";
|
|
161
|
+
}>>;
|
|
162
|
+
threadSession: z.ZodOptional<z.ZodBoolean>;
|
|
163
|
+
uat: z.ZodOptional<z.ZodObject<{
|
|
164
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
165
|
+
allowedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
166
|
+
blockedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
167
|
+
}, z.core.$strip>>;
|
|
168
|
+
}, z.core.$strip>;
|
|
169
|
+
export declare const FeishuConfigSchema: z.ZodObject<{
|
|
170
|
+
appId: z.ZodOptional<z.ZodString>;
|
|
171
|
+
appSecret: z.ZodOptional<z.ZodString>;
|
|
172
|
+
encryptKey: z.ZodOptional<z.ZodString>;
|
|
173
|
+
verificationToken: z.ZodOptional<z.ZodString>;
|
|
174
|
+
name: z.ZodOptional<z.ZodString>;
|
|
175
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
176
|
+
domain: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"feishu">, z.ZodLiteral<"lark">, z.ZodString]>>;
|
|
177
|
+
connectionMode: z.ZodOptional<z.ZodEnum<{
|
|
178
|
+
websocket: "websocket";
|
|
179
|
+
webhook: "webhook";
|
|
180
|
+
}>>;
|
|
181
|
+
webhookPath: z.ZodOptional<z.ZodString>;
|
|
182
|
+
webhookPort: z.ZodOptional<z.ZodNumber>;
|
|
183
|
+
dmPolicy: z.ZodOptional<z.ZodEnum<{
|
|
184
|
+
allowlist: "allowlist";
|
|
185
|
+
open: "open";
|
|
186
|
+
pairing: "pairing";
|
|
187
|
+
disabled: "disabled";
|
|
188
|
+
}>>;
|
|
189
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
190
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
191
|
+
allowlist: "allowlist";
|
|
192
|
+
open: "open";
|
|
193
|
+
disabled: "disabled";
|
|
194
|
+
}>>;
|
|
195
|
+
groupAllowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
196
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
197
|
+
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
198
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
199
|
+
allowlist: "allowlist";
|
|
200
|
+
open: "open";
|
|
201
|
+
disabled: "disabled";
|
|
202
|
+
}>>;
|
|
203
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
204
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
205
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
206
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
207
|
+
}, z.core.$strip>>;
|
|
208
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
209
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
210
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
211
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
212
|
+
}, z.core.$strip>>>;
|
|
213
|
+
historyLimit: z.ZodOptional<z.ZodNumber>;
|
|
214
|
+
dmHistoryLimit: z.ZodOptional<z.ZodNumber>;
|
|
215
|
+
dms: z.ZodOptional<z.ZodObject<{
|
|
216
|
+
historyLimit: z.ZodOptional<z.ZodNumber>;
|
|
217
|
+
}, z.core.$strip>>;
|
|
218
|
+
textChunkLimit: z.ZodOptional<z.ZodNumber>;
|
|
219
|
+
chunkMode: z.ZodOptional<z.ZodEnum<{
|
|
220
|
+
newline: "newline";
|
|
221
|
+
paragraph: "paragraph";
|
|
222
|
+
none: "none";
|
|
223
|
+
}>>;
|
|
224
|
+
blockStreamingCoalesce: z.ZodOptional<z.ZodObject<{
|
|
225
|
+
minChars: z.ZodOptional<z.ZodNumber>;
|
|
226
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
227
|
+
idleMs: z.ZodOptional<z.ZodNumber>;
|
|
228
|
+
}, z.core.$strip>>;
|
|
229
|
+
mediaMaxMb: z.ZodOptional<z.ZodNumber>;
|
|
230
|
+
heartbeat: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
every: z.ZodOptional<z.ZodString>;
|
|
232
|
+
activeHours: z.ZodOptional<z.ZodObject<{
|
|
233
|
+
start: z.ZodOptional<z.ZodString>;
|
|
234
|
+
end: z.ZodOptional<z.ZodString>;
|
|
235
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
236
|
+
}, z.core.$strip>>;
|
|
237
|
+
target: z.ZodOptional<z.ZodString>;
|
|
238
|
+
to: z.ZodOptional<z.ZodString>;
|
|
239
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
240
|
+
accountId: z.ZodOptional<z.ZodString>;
|
|
241
|
+
}, z.core.$strip>>;
|
|
242
|
+
replyMode: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
243
|
+
streaming: "streaming";
|
|
244
|
+
auto: "auto";
|
|
245
|
+
static: "static";
|
|
246
|
+
}>, z.ZodObject<{
|
|
247
|
+
default: z.ZodOptional<z.ZodEnum<{
|
|
248
|
+
streaming: "streaming";
|
|
249
|
+
auto: "auto";
|
|
250
|
+
static: "static";
|
|
251
|
+
}>>;
|
|
252
|
+
group: z.ZodOptional<z.ZodEnum<{
|
|
253
|
+
streaming: "streaming";
|
|
254
|
+
auto: "auto";
|
|
255
|
+
static: "static";
|
|
256
|
+
}>>;
|
|
257
|
+
direct: z.ZodOptional<z.ZodEnum<{
|
|
258
|
+
streaming: "streaming";
|
|
259
|
+
auto: "auto";
|
|
260
|
+
static: "static";
|
|
261
|
+
}>>;
|
|
262
|
+
}, z.core.$strip>]>>;
|
|
263
|
+
streaming: z.ZodOptional<z.ZodBoolean>;
|
|
264
|
+
blockStreaming: z.ZodOptional<z.ZodBoolean>;
|
|
265
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
266
|
+
doc: z.ZodOptional<z.ZodBoolean>;
|
|
267
|
+
wiki: z.ZodOptional<z.ZodBoolean>;
|
|
268
|
+
drive: z.ZodOptional<z.ZodBoolean>;
|
|
269
|
+
perm: z.ZodOptional<z.ZodBoolean>;
|
|
270
|
+
scopes: z.ZodOptional<z.ZodBoolean>;
|
|
271
|
+
}, z.core.$strip>>;
|
|
272
|
+
footer: z.ZodOptional<z.ZodObject<{
|
|
273
|
+
status: z.ZodOptional<z.ZodBoolean>;
|
|
274
|
+
elapsed: z.ZodOptional<z.ZodBoolean>;
|
|
275
|
+
}, z.core.$strip>>;
|
|
276
|
+
markdown: z.ZodOptional<z.ZodObject<{
|
|
277
|
+
tables: z.ZodOptional<z.ZodEnum<{
|
|
278
|
+
code: "code";
|
|
279
|
+
off: "off";
|
|
280
|
+
bullets: "bullets";
|
|
281
|
+
}>>;
|
|
282
|
+
}, z.core.$strip>>;
|
|
283
|
+
configWrites: z.ZodOptional<z.ZodBoolean>;
|
|
284
|
+
capabilities: z.ZodOptional<z.ZodObject<{
|
|
285
|
+
image: z.ZodOptional<z.ZodBoolean>;
|
|
286
|
+
audio: z.ZodOptional<z.ZodBoolean>;
|
|
287
|
+
video: z.ZodOptional<z.ZodBoolean>;
|
|
288
|
+
}, z.core.$strip>>;
|
|
289
|
+
dedup: z.ZodOptional<z.ZodObject<{
|
|
290
|
+
ttlMs: z.ZodOptional<z.ZodNumber>;
|
|
291
|
+
maxEntries: z.ZodOptional<z.ZodNumber>;
|
|
292
|
+
}, z.core.$strip>>;
|
|
293
|
+
reactionNotifications: z.ZodOptional<z.ZodEnum<{
|
|
294
|
+
all: "all";
|
|
295
|
+
off: "off";
|
|
296
|
+
own: "own";
|
|
297
|
+
}>>;
|
|
298
|
+
threadSession: z.ZodOptional<z.ZodBoolean>;
|
|
299
|
+
uat: z.ZodOptional<z.ZodObject<{
|
|
300
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
301
|
+
allowedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
302
|
+
blockedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
303
|
+
}, z.core.$strip>>;
|
|
304
|
+
accounts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
305
|
+
appId: z.ZodOptional<z.ZodString>;
|
|
306
|
+
appSecret: z.ZodOptional<z.ZodString>;
|
|
307
|
+
encryptKey: z.ZodOptional<z.ZodString>;
|
|
308
|
+
verificationToken: z.ZodOptional<z.ZodString>;
|
|
309
|
+
name: z.ZodOptional<z.ZodString>;
|
|
310
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
311
|
+
domain: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"feishu">, z.ZodLiteral<"lark">, z.ZodString]>>;
|
|
312
|
+
connectionMode: z.ZodOptional<z.ZodEnum<{
|
|
313
|
+
websocket: "websocket";
|
|
314
|
+
webhook: "webhook";
|
|
315
|
+
}>>;
|
|
316
|
+
webhookPath: z.ZodOptional<z.ZodString>;
|
|
317
|
+
webhookPort: z.ZodOptional<z.ZodNumber>;
|
|
318
|
+
dmPolicy: z.ZodOptional<z.ZodEnum<{
|
|
319
|
+
allowlist: "allowlist";
|
|
320
|
+
open: "open";
|
|
321
|
+
pairing: "pairing";
|
|
322
|
+
disabled: "disabled";
|
|
323
|
+
}>>;
|
|
324
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
325
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
326
|
+
allowlist: "allowlist";
|
|
327
|
+
open: "open";
|
|
328
|
+
disabled: "disabled";
|
|
329
|
+
}>>;
|
|
330
|
+
groupAllowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
331
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
332
|
+
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
333
|
+
groupPolicy: z.ZodOptional<z.ZodEnum<{
|
|
334
|
+
allowlist: "allowlist";
|
|
335
|
+
open: "open";
|
|
336
|
+
disabled: "disabled";
|
|
337
|
+
}>>;
|
|
338
|
+
requireMention: z.ZodOptional<z.ZodBoolean>;
|
|
339
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
340
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
341
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
342
|
+
}, z.core.$strip>>;
|
|
343
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
344
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
345
|
+
allowFrom: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>, z.ZodTransform<string[] | undefined, string | string[] | undefined>>;
|
|
346
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
347
|
+
}, z.core.$strip>>>;
|
|
348
|
+
historyLimit: z.ZodOptional<z.ZodNumber>;
|
|
349
|
+
dmHistoryLimit: z.ZodOptional<z.ZodNumber>;
|
|
350
|
+
dms: z.ZodOptional<z.ZodObject<{
|
|
351
|
+
historyLimit: z.ZodOptional<z.ZodNumber>;
|
|
352
|
+
}, z.core.$strip>>;
|
|
353
|
+
textChunkLimit: z.ZodOptional<z.ZodNumber>;
|
|
354
|
+
chunkMode: z.ZodOptional<z.ZodEnum<{
|
|
355
|
+
newline: "newline";
|
|
356
|
+
paragraph: "paragraph";
|
|
357
|
+
none: "none";
|
|
358
|
+
}>>;
|
|
359
|
+
blockStreamingCoalesce: z.ZodOptional<z.ZodObject<{
|
|
360
|
+
minChars: z.ZodOptional<z.ZodNumber>;
|
|
361
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
362
|
+
idleMs: z.ZodOptional<z.ZodNumber>;
|
|
363
|
+
}, z.core.$strip>>;
|
|
364
|
+
mediaMaxMb: z.ZodOptional<z.ZodNumber>;
|
|
365
|
+
heartbeat: z.ZodOptional<z.ZodObject<{
|
|
366
|
+
every: z.ZodOptional<z.ZodString>;
|
|
367
|
+
activeHours: z.ZodOptional<z.ZodObject<{
|
|
368
|
+
start: z.ZodOptional<z.ZodString>;
|
|
369
|
+
end: z.ZodOptional<z.ZodString>;
|
|
370
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
371
|
+
}, z.core.$strip>>;
|
|
372
|
+
target: z.ZodOptional<z.ZodString>;
|
|
373
|
+
to: z.ZodOptional<z.ZodString>;
|
|
374
|
+
prompt: z.ZodOptional<z.ZodString>;
|
|
375
|
+
accountId: z.ZodOptional<z.ZodString>;
|
|
376
|
+
}, z.core.$strip>>;
|
|
377
|
+
replyMode: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
378
|
+
streaming: "streaming";
|
|
379
|
+
auto: "auto";
|
|
380
|
+
static: "static";
|
|
381
|
+
}>, z.ZodObject<{
|
|
382
|
+
default: z.ZodOptional<z.ZodEnum<{
|
|
383
|
+
streaming: "streaming";
|
|
384
|
+
auto: "auto";
|
|
385
|
+
static: "static";
|
|
386
|
+
}>>;
|
|
387
|
+
group: z.ZodOptional<z.ZodEnum<{
|
|
388
|
+
streaming: "streaming";
|
|
389
|
+
auto: "auto";
|
|
390
|
+
static: "static";
|
|
391
|
+
}>>;
|
|
392
|
+
direct: z.ZodOptional<z.ZodEnum<{
|
|
393
|
+
streaming: "streaming";
|
|
394
|
+
auto: "auto";
|
|
395
|
+
static: "static";
|
|
396
|
+
}>>;
|
|
397
|
+
}, z.core.$strip>]>>;
|
|
398
|
+
streaming: z.ZodOptional<z.ZodBoolean>;
|
|
399
|
+
blockStreaming: z.ZodOptional<z.ZodBoolean>;
|
|
400
|
+
tools: z.ZodOptional<z.ZodObject<{
|
|
401
|
+
doc: z.ZodOptional<z.ZodBoolean>;
|
|
402
|
+
wiki: z.ZodOptional<z.ZodBoolean>;
|
|
403
|
+
drive: z.ZodOptional<z.ZodBoolean>;
|
|
404
|
+
perm: z.ZodOptional<z.ZodBoolean>;
|
|
405
|
+
scopes: z.ZodOptional<z.ZodBoolean>;
|
|
406
|
+
}, z.core.$strip>>;
|
|
407
|
+
footer: z.ZodOptional<z.ZodObject<{
|
|
408
|
+
status: z.ZodOptional<z.ZodBoolean>;
|
|
409
|
+
elapsed: z.ZodOptional<z.ZodBoolean>;
|
|
410
|
+
}, z.core.$strip>>;
|
|
411
|
+
markdown: z.ZodOptional<z.ZodObject<{
|
|
412
|
+
tables: z.ZodOptional<z.ZodEnum<{
|
|
413
|
+
code: "code";
|
|
414
|
+
off: "off";
|
|
415
|
+
bullets: "bullets";
|
|
416
|
+
}>>;
|
|
417
|
+
}, z.core.$strip>>;
|
|
418
|
+
configWrites: z.ZodOptional<z.ZodBoolean>;
|
|
419
|
+
capabilities: z.ZodOptional<z.ZodObject<{
|
|
420
|
+
image: z.ZodOptional<z.ZodBoolean>;
|
|
421
|
+
audio: z.ZodOptional<z.ZodBoolean>;
|
|
422
|
+
video: z.ZodOptional<z.ZodBoolean>;
|
|
423
|
+
}, z.core.$strip>>;
|
|
424
|
+
dedup: z.ZodOptional<z.ZodObject<{
|
|
425
|
+
ttlMs: z.ZodOptional<z.ZodNumber>;
|
|
426
|
+
maxEntries: z.ZodOptional<z.ZodNumber>;
|
|
427
|
+
}, z.core.$strip>>;
|
|
428
|
+
reactionNotifications: z.ZodOptional<z.ZodEnum<{
|
|
429
|
+
all: "all";
|
|
430
|
+
off: "off";
|
|
431
|
+
own: "own";
|
|
432
|
+
}>>;
|
|
433
|
+
threadSession: z.ZodOptional<z.ZodBoolean>;
|
|
434
|
+
uat: z.ZodOptional<z.ZodObject<{
|
|
435
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
436
|
+
allowedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
437
|
+
blockedScopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
438
|
+
}, z.core.$strip>>;
|
|
439
|
+
}, z.core.$strip>>>;
|
|
440
|
+
}, z.core.$strip>;
|
|
16
441
|
/**
|
|
17
442
|
* JSON Schema derived from FeishuConfigSchema.
|
|
18
443
|
*
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
*
|
|
54
54
|
* 总计:98 个工具动作
|
|
55
55
|
*/
|
|
56
|
-
export type ToolActionKey = 'feishu_bitable_app.copy' | 'feishu_bitable_app.create' | 'feishu_bitable_app.get' | 'feishu_bitable_app.list' | 'feishu_bitable_app.patch' | 'feishu_bitable_app_table.batch_create' | 'feishu_bitable_app_table.batch_delete' | 'feishu_bitable_app_table.create' | 'feishu_bitable_app_table.delete' | 'feishu_bitable_app_table.list' | 'feishu_bitable_app_table.patch' | 'feishu_bitable_app_table_field.create' | 'feishu_bitable_app_table_field.delete' | 'feishu_bitable_app_table_field.list' | 'feishu_bitable_app_table_field.update' | 'feishu_bitable_app_table_record.batch_create' | 'feishu_bitable_app_table_record.batch_delete' | 'feishu_bitable_app_table_record.batch_update' | 'feishu_bitable_app_table_record.create' | 'feishu_bitable_app_table_record.delete' | 'feishu_bitable_app_table_record.list' | 'feishu_bitable_app_table_record.update' | 'feishu_bitable_app_table_view.create' | 'feishu_bitable_app_table_view.delete' | 'feishu_bitable_app_table_view.get' | 'feishu_bitable_app_table_view.list' | 'feishu_bitable_app_table_view.patch' | 'feishu_calendar_calendar.get' | 'feishu_calendar_calendar.list' | 'feishu_calendar_calendar.primary' | 'feishu_calendar_event.create' | 'feishu_calendar_event.delete' | 'feishu_calendar_event.get' | 'feishu_calendar_event.instance_view' | 'feishu_calendar_event.instances' | 'feishu_calendar_event.list' | 'feishu_calendar_event.patch' | 'feishu_calendar_event.reply' | 'feishu_calendar_event.search' | 'feishu_calendar_event_attendee.batch_delete' | 'feishu_calendar_event_attendee.create' | 'feishu_calendar_event_attendee.list' | 'feishu_calendar_freebusy.list' | 'feishu_chat.get' | 'feishu_chat.search' | 'feishu_chat_members.default' | 'feishu_create_doc.default' | 'feishu_doc_comments.create' | 'feishu_doc_comments.list' | 'feishu_doc_comments.patch' | 'feishu_doc_media.download' | 'feishu_doc_media.insert' | 'feishu_drive_file.copy' | 'feishu_drive_file.delete' | 'feishu_drive_file.download' | 'feishu_drive_file.get_meta' | 'feishu_drive_file.list' | 'feishu_drive_file.move' | 'feishu_drive_file.upload' | 'feishu_fetch_doc.default' | 'feishu_get_user.default' | 'feishu_im_user_fetch_resource.default' | 'feishu_im_user_get_messages.default' | 'feishu_im_user_message.reply' | 'feishu_im_user_message.send' | 'feishu_im_user_search_messages.default' | 'feishu_search_doc_wiki.search' | 'feishu_search_user.default' | 'feishu_task_comment.create' | 'feishu_task_comment.get' | 'feishu_task_comment.list' | 'feishu_task_subtask.create' | 'feishu_task_subtask.list' | 'feishu_task_task.create' | 'feishu_task_task.get' | 'feishu_task_task.list' | 'feishu_task_task.patch' | 'feishu_task_tasklist.add_members' | 'feishu_task_tasklist.create' | 'feishu_task_tasklist.delete' | 'feishu_task_tasklist.get' | 'feishu_task_tasklist.list' | 'feishu_task_tasklist.patch' | 'feishu_task_tasklist.remove_members' | 'feishu_task_tasklist.tasks' | 'feishu_update_doc.default' | 'feishu_wiki_space.create' | 'feishu_wiki_space.get' | 'feishu_wiki_space.list' | 'feishu_wiki_space_node.copy' | 'feishu_wiki_space_node.create' | 'feishu_wiki_space_node.get' | 'feishu_wiki_space_node.list' | 'feishu_wiki_space_node.move' | 'feishu_sheet.info' | 'feishu_sheet.read' | 'feishu_sheet.write' | 'feishu_sheet.append' | 'feishu_sheet.find' | 'feishu_sheet.create' | 'feishu_sheet.export';
|
|
56
|
+
export type ToolActionKey = 'feishu_bitable_app.copy' | 'feishu_bitable_app.create' | 'feishu_bitable_app.get' | 'feishu_bitable_app.list' | 'feishu_bitable_app.patch' | 'feishu_bitable_app_table.batch_create' | 'feishu_bitable_app_table.batch_delete' | 'feishu_bitable_app_table.create' | 'feishu_bitable_app_table.delete' | 'feishu_bitable_app_table.list' | 'feishu_bitable_app_table.patch' | 'feishu_bitable_app_table_field.create' | 'feishu_bitable_app_table_field.delete' | 'feishu_bitable_app_table_field.list' | 'feishu_bitable_app_table_field.update' | 'feishu_bitable_app_table_record.batch_create' | 'feishu_bitable_app_table_record.batch_delete' | 'feishu_bitable_app_table_record.batch_update' | 'feishu_bitable_app_table_record.create' | 'feishu_bitable_app_table_record.delete' | 'feishu_bitable_app_table_record.list' | 'feishu_bitable_app_table_record.update' | 'feishu_bitable_app_table_view.create' | 'feishu_bitable_app_table_view.delete' | 'feishu_bitable_app_table_view.get' | 'feishu_bitable_app_table_view.list' | 'feishu_bitable_app_table_view.patch' | 'feishu_calendar_calendar.get' | 'feishu_calendar_calendar.list' | 'feishu_calendar_calendar.primary' | 'feishu_calendar_event.create' | 'feishu_calendar_event.delete' | 'feishu_calendar_event.get' | 'feishu_calendar_event.instance_view' | 'feishu_calendar_event.instances' | 'feishu_calendar_event.list' | 'feishu_calendar_event.patch' | 'feishu_calendar_event.reply' | 'feishu_calendar_event.search' | 'feishu_calendar_event_attendee.batch_delete' | 'feishu_calendar_event_attendee.create' | 'feishu_calendar_event_attendee.list' | 'feishu_calendar_freebusy.list' | 'feishu_chat.get' | 'feishu_chat.search' | 'feishu_chat_members.default' | 'feishu_create_doc.default' | 'feishu_doc_comments.create' | 'feishu_doc_comments.list' | 'feishu_doc_comments.patch' | 'feishu_doc_media.download' | 'feishu_doc_media.insert' | 'feishu_drive_file.copy' | 'feishu_drive_file.delete' | 'feishu_drive_file.download' | 'feishu_drive_file.get_meta' | 'feishu_drive_file.list' | 'feishu_drive_file.move' | 'feishu_drive_file.upload' | 'feishu_fetch_doc.default' | 'feishu_get_user.basic_batch' | 'feishu_get_user.default' | 'feishu_im_user_fetch_resource.default' | 'feishu_im_user_get_messages.default' | 'feishu_im_user_message.reply' | 'feishu_im_user_message.send' | 'feishu_im_user_search_messages.default' | 'feishu_search_doc_wiki.search' | 'feishu_search_user.default' | 'feishu_task_comment.create' | 'feishu_task_comment.get' | 'feishu_task_comment.list' | 'feishu_task_subtask.create' | 'feishu_task_subtask.list' | 'feishu_task_task.create' | 'feishu_task_task.get' | 'feishu_task_task.list' | 'feishu_task_task.patch' | 'feishu_task_tasklist.add_members' | 'feishu_task_tasklist.create' | 'feishu_task_tasklist.delete' | 'feishu_task_tasklist.get' | 'feishu_task_tasklist.list' | 'feishu_task_tasklist.patch' | 'feishu_task_tasklist.remove_members' | 'feishu_task_tasklist.tasks' | 'feishu_update_doc.default' | 'feishu_wiki_space.create' | 'feishu_wiki_space.get' | 'feishu_wiki_space.list' | 'feishu_wiki_space_node.copy' | 'feishu_wiki_space_node.create' | 'feishu_wiki_space_node.get' | 'feishu_wiki_space_node.list' | 'feishu_wiki_space_node.move' | 'feishu_sheet.info' | 'feishu_sheet.read' | 'feishu_sheet.write' | 'feishu_sheet.append' | 'feishu_sheet.find' | 'feishu_sheet.create' | 'feishu_sheet.export';
|
|
57
57
|
/**
|
|
58
58
|
* Tool Scope 映射类型
|
|
59
59
|
*
|
|
@@ -146,8 +146,8 @@ export type SensitiveScope = (typeof SENSITIVE_SCOPES)[number];
|
|
|
146
146
|
*/
|
|
147
147
|
export declare function filterSensitiveScopes(scopes: string[]): string[];
|
|
148
148
|
/**
|
|
149
|
-
* 工具动作总数:
|
|
150
|
-
* 唯一 scope 总数:
|
|
149
|
+
* 工具动作总数: 99
|
|
150
|
+
* 唯一 scope 总数: 67
|
|
151
151
|
* 必需应用权限总数: 20
|
|
152
152
|
* 高敏感权限总数: 1
|
|
153
153
|
*/
|
package/src/core/tool-scopes.js
CHANGED
|
@@ -173,6 +173,7 @@ export const TOOL_SCOPES = {
|
|
|
173
173
|
'search:message',
|
|
174
174
|
],
|
|
175
175
|
'feishu_search_doc_wiki.search': ['search:docs:read'],
|
|
176
|
+
'feishu_get_user.basic_batch': ['contact:user.basic_profile:readonly'],
|
|
176
177
|
'feishu_get_user.default': ['contact:contact.base:readonly', 'contact:user.base:readonly'],
|
|
177
178
|
'feishu_search_user.default': ['contact:user:search'],
|
|
178
179
|
'feishu_create_doc.default': [
|
|
@@ -319,8 +320,8 @@ export function filterSensitiveScopes(scopes) {
|
|
|
319
320
|
}
|
|
320
321
|
// ===== 统计信息 =====
|
|
321
322
|
/**
|
|
322
|
-
* 工具动作总数:
|
|
323
|
-
* 唯一 scope 总数:
|
|
323
|
+
* 工具动作总数: 99
|
|
324
|
+
* 唯一 scope 总数: 67
|
|
324
325
|
* 必需应用权限总数: 20
|
|
325
326
|
* 高敏感权限总数: 1
|
|
326
327
|
*/
|
package/src/core/uat-client.js
CHANGED
|
@@ -14,7 +14,7 @@ import { resolveOAuthEndpoints } from './device-flow';
|
|
|
14
14
|
import { larkLogger } from './lark-logger';
|
|
15
15
|
const log = larkLogger('core/uat-client');
|
|
16
16
|
import { feishuFetch } from './feishu-fetch';
|
|
17
|
-
import {
|
|
17
|
+
import { REFRESH_TOKEN_RETRYABLE, TOKEN_RETRY_CODES, NeedAuthorizationError } from './auth-errors';
|
|
18
18
|
// Re-export for backward compatibility
|
|
19
19
|
export { NeedAuthorizationError };
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
@@ -39,31 +39,46 @@ async function doRefreshToken(opts, stored) {
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
const endpoints = resolveOAuthEndpoints(opts.domain);
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
const requestBody = new URLSearchParams({
|
|
43
|
+
grant_type: 'refresh_token',
|
|
44
|
+
refresh_token: stored.refreshToken,
|
|
45
|
+
client_id: opts.appId,
|
|
46
|
+
client_secret: opts.appSecret,
|
|
47
|
+
}).toString();
|
|
48
|
+
const callEndpoint = async () => {
|
|
49
|
+
const resp = await feishuFetch(endpoints.token, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
52
|
+
body: requestBody,
|
|
53
|
+
});
|
|
54
|
+
return (await resp.json());
|
|
55
|
+
};
|
|
56
|
+
let data = await callEndpoint();
|
|
53
57
|
// Feishu v2 token endpoint returns `code: 0` on success.
|
|
54
58
|
// Some responses use `error` field instead (standard OAuth).
|
|
55
59
|
const code = data.code;
|
|
56
60
|
const error = data.error;
|
|
57
61
|
if ((code !== undefined && code !== 0) || error) {
|
|
58
62
|
const errCode = code ?? error;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
// Transient server error: retry once, then clear.
|
|
64
|
+
if (REFRESH_TOKEN_RETRYABLE.has(code)) {
|
|
65
|
+
log.warn(`refresh transient error (code=${errCode}) for ${opts.userOpenId}, retrying once`);
|
|
66
|
+
data = await callEndpoint();
|
|
67
|
+
const retryCode = data.code;
|
|
68
|
+
const retryError = data.error;
|
|
69
|
+
if ((retryCode !== undefined && retryCode !== 0) || retryError) {
|
|
70
|
+
const retryErrCode = retryCode ?? retryError;
|
|
71
|
+
log.warn(`refresh failed after retry (code=${retryErrCode}), clearing token for ${opts.userOpenId}`);
|
|
72
|
+
await removeStoredToken(opts.appId, opts.userOpenId);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Any other error (invalid/expired/revoked token, or unknown): clear and force re-auth.
|
|
62
78
|
log.warn(`refresh failed (code=${errCode}), clearing token for ${opts.userOpenId}`);
|
|
63
79
|
await removeStoredToken(opts.appId, opts.userOpenId);
|
|
64
80
|
return null;
|
|
65
81
|
}
|
|
66
|
-
throw new Error(`Token refresh failed (code=${errCode}): ${errMsg}`);
|
|
67
82
|
}
|
|
68
83
|
if (!data.access_token) {
|
|
69
84
|
throw new Error('Token refresh returned no access_token');
|
|
@@ -75,11 +75,12 @@ export async function resolveReactionContext(params) {
|
|
|
75
75
|
log(`feishu[${accountId}]: reacted message ${messageId} not found or timed out, skipping`);
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
|
-
//
|
|
79
|
-
// not the bot's open_id (ou_xxx). Match against the account's appId.
|
|
78
|
+
// mget API returns app_id (cli_xxx) as sender.id for bot messages.
|
|
80
79
|
const isBotMessage = msg.senderType === 'app' && msg.senderId === account.appId;
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const isOtherBotMessage = msg.senderType === 'app' && account.appId && msg.senderId !== account.appId;
|
|
81
|
+
// 'own': only react to this bot's messages; 'all': also skip other bots' messages.
|
|
82
|
+
if ((reactionMode === 'own' && !isBotMessage) || (reactionMode === 'all' && isOtherBotMessage)) {
|
|
83
|
+
log(`feishu[${accountId}]: reaction on ${isOtherBotMessage ? 'other bot' : 'non-bot'} message ${messageId}, skipping`);
|
|
83
84
|
return null;
|
|
84
85
|
}
|
|
85
86
|
// ---- Resolve effective chatId ----
|
|
@@ -179,4 +179,4 @@ import type { SchemaOptions } from '@sinclair/typebox';
|
|
|
179
179
|
* 本函数生成 `{ type: 'string', enum: ['a', 'b'] }` 格式,
|
|
180
180
|
* 兼容性更好。
|
|
181
181
|
*/
|
|
182
|
-
export declare function StringEnum<T extends string>(values: T[], options?: SchemaOptions):
|
|
182
|
+
export declare function StringEnum<T extends string>(values: T[], options?: SchemaOptions): import("@sinclair/typebox").TUnsafe<T>;
|
|
@@ -140,7 +140,7 @@ function registerGetMessages(api) {
|
|
|
140
140
|
as: 'user',
|
|
141
141
|
});
|
|
142
142
|
assertLarkOk(res);
|
|
143
|
-
return formatAndReturn(res, config, log, client);
|
|
143
|
+
return await formatAndReturn(res, config, log, client);
|
|
144
144
|
}
|
|
145
145
|
catch (err) {
|
|
146
146
|
return await handleInvokeErrorWithAutoAuth(err, config);
|
|
@@ -192,7 +192,7 @@ function registerGetThreadMessages(api) {
|
|
|
192
192
|
as: 'user',
|
|
193
193
|
});
|
|
194
194
|
assertLarkOk(res);
|
|
195
|
-
return formatAndReturn(res, config, log, client);
|
|
195
|
+
return await formatAndReturn(res, config, log, client);
|
|
196
196
|
}
|
|
197
197
|
catch (err) {
|
|
198
198
|
return await handleInvokeErrorWithAutoAuth(err, config);
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* 设计动机:TAT 调用 contact/v3/users/batch 缺少权限导致返回的用户
|
|
11
11
|
* 条目不含 name 字段,而工具层搜索消息等场景运行在 UAT 上下文中,
|
|
12
12
|
* 用户 token 可以读取其他用户的名称。
|
|
13
|
+
*
|
|
14
|
+
* 底层使用 contact/v3/users/basic_batch 接口(scope: contact:user.basic_profile:readonly),
|
|
15
|
+
* 每次最多查询 10 个用户。
|
|
13
16
|
*/
|
|
14
17
|
import type { ToolClient } from '../../../core/tool-client';
|
|
15
18
|
/** 从 UAT 缓存中获取用户名 */
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* 设计动机:TAT 调用 contact/v3/users/batch 缺少权限导致返回的用户
|
|
12
12
|
* 条目不含 name 字段,而工具层搜索消息等场景运行在 UAT 上下文中,
|
|
13
13
|
* 用户 token 可以读取其他用户的名称。
|
|
14
|
+
*
|
|
15
|
+
* 底层使用 contact/v3/users/basic_batch 接口(scope: contact:user.basic_profile:readonly),
|
|
16
|
+
* 每次最多查询 10 个用户。
|
|
14
17
|
*/
|
|
15
18
|
import { isInvokeError } from '../helpers';
|
|
16
19
|
// ---------------------------------------------------------------------------
|
|
@@ -64,7 +67,7 @@ export function setUATUserNames(accountId, entries) {
|
|
|
64
67
|
// ---------------------------------------------------------------------------
|
|
65
68
|
// 以 UAT 身份批量解析用户名
|
|
66
69
|
// ---------------------------------------------------------------------------
|
|
67
|
-
const BATCH_SIZE =
|
|
70
|
+
const BATCH_SIZE = 10; // basic_batch API 限制每次最多 10 个
|
|
68
71
|
export async function batchResolveUserNamesAsUser(params) {
|
|
69
72
|
const { client, openIds, log } = params;
|
|
70
73
|
if (openIds.length === 0)
|
|
@@ -90,31 +93,41 @@ export async function batchResolveUserNamesAsUser(params) {
|
|
|
90
93
|
const uniqueMissing = [...new Set(missing)];
|
|
91
94
|
if (uniqueMissing.length === 0)
|
|
92
95
|
return result;
|
|
93
|
-
// 2. 分批通过 SDK 调用 contact/v3/users/
|
|
96
|
+
// 2. 分批通过 SDK 调用 contact/v3/users/basic_batch(UAT)
|
|
97
|
+
const totalBatches = Math.ceil(uniqueMissing.length / BATCH_SIZE);
|
|
98
|
+
log(`batchResolveUserNamesAsUser: resolving ${uniqueMissing.length} user(s) in ${totalBatches} batch(es), ${result.size} cache hit(s)`);
|
|
94
99
|
for (let i = 0; i < uniqueMissing.length; i += BATCH_SIZE) {
|
|
95
100
|
const chunk = uniqueMissing.slice(i, i + BATCH_SIZE);
|
|
101
|
+
const batchIndex = Math.floor(i / BATCH_SIZE) + 1;
|
|
96
102
|
try {
|
|
97
103
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
-
const res = await client.invoke('feishu_get_user.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
},
|
|
104
|
+
const res = await client.invoke('feishu_get_user.basic_batch', (sdk, opts) => sdk.request({
|
|
105
|
+
method: 'POST',
|
|
106
|
+
url: '/open-apis/contact/v3/users/basic_batch',
|
|
107
|
+
data: { user_ids: chunk },
|
|
108
|
+
params: { user_id_type: 'open_id' },
|
|
104
109
|
}, opts), {
|
|
105
110
|
as: 'user',
|
|
106
111
|
});
|
|
107
112
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
113
|
+
const users = res?.data?.users ?? [];
|
|
114
|
+
let resolved = 0;
|
|
115
|
+
for (const user of users) {
|
|
116
|
+
const openId = user.user_id;
|
|
117
|
+
// 实际返回 name 为字符串,兼容文档中 name.value 的对象结构
|
|
118
|
+
const rawName = user.name;
|
|
119
|
+
const name = typeof rawName === 'string' ? rawName : rawName?.value;
|
|
112
120
|
if (openId && name) {
|
|
113
121
|
cache.delete(openId);
|
|
114
122
|
cache.set(openId, { name, expireAt: Date.now() + UAT_TTL_MS });
|
|
115
123
|
result.set(openId, name);
|
|
124
|
+
resolved++;
|
|
116
125
|
}
|
|
117
126
|
}
|
|
127
|
+
const unresolvedCount = chunk.length - resolved;
|
|
128
|
+
if (unresolvedCount > 0) {
|
|
129
|
+
log(`batchResolveUserNamesAsUser: batch ${batchIndex}/${totalBatches}: ${resolved} resolved, ${unresolvedCount} missing name`);
|
|
130
|
+
}
|
|
118
131
|
}
|
|
119
132
|
catch (err) {
|
|
120
133
|
// 授权/权限错误向上冒泡,由上层 handleInvokeErrorWithAutoAuth 处理自动授权
|
package/src/tools/oauth.js
CHANGED
|
@@ -41,13 +41,12 @@ const FeishuOAuthSchema = Type.Object({
|
|
|
41
41
|
// Type.Literal("authorize"), // 已由 auto-auth 自动处理,不再对外暴露
|
|
42
42
|
Type.Literal('revoke'),
|
|
43
43
|
], {
|
|
44
|
-
description: 'revoke:
|
|
44
|
+
description: 'revoke: 撤销当前用户已保存的授权凭据',
|
|
45
45
|
}),
|
|
46
46
|
}, {
|
|
47
|
-
description: '
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'不需要传入 user_open_id,系统自动识别当前用户。',
|
|
47
|
+
description: '飞书用户撤销授权工具。' +
|
|
48
|
+
'仅在用户明确说"撤销授权"、"取消授权"、"退出登录"、"清除授权"时调用。' +
|
|
49
|
+
'【严禁调用场景】用户说"重新授权"、"发起授权"、"重新发起"、"授权失败"、"授权过期"时,绝对不要调用此工具,授权流程由系统自动处理。',
|
|
51
50
|
});
|
|
52
51
|
const pendingFlows = new Map();
|
|
53
52
|
// ---------------------------------------------------------------------------
|
|
@@ -96,11 +95,10 @@ export function registerFeishuOAuthTool(api) {
|
|
|
96
95
|
registerTool(api, {
|
|
97
96
|
name: 'feishu_oauth',
|
|
98
97
|
label: 'Feishu OAuth',
|
|
99
|
-
description: '
|
|
100
|
-
'
|
|
101
|
-
'
|
|
102
|
-
'不需要传入 user_open_id,系统自动从消息上下文获取当前用户。'
|
|
103
|
-
'【Token 过期处理】当返回 token_expired 错误时,调用 revoke 撤销后,系统会自动重新发起授权流程。',
|
|
98
|
+
description: '飞书用户撤销授权工具。' +
|
|
99
|
+
'仅在用户明确说"撤销授权"、"取消授权"、"退出登录"、"清除授权"时调用 revoke。' +
|
|
100
|
+
'【严禁调用场景】用户说"重新授权"、"发起授权"、"重新发起"、"授权失败"、"授权过期"时,绝对不要调用此工具,授权流程由系统自动处理,无需人工干预。' +
|
|
101
|
+
'不需要传入 user_open_id,系统自动从消息上下文获取当前用户。',
|
|
104
102
|
parameters: FeishuOAuthSchema,
|
|
105
103
|
async execute(_toolCallId, params) {
|
|
106
104
|
const p = params;
|