@superbenxxxh/feishu 2.0.0 → 3.0.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/README.md +423 -322
- package/index.ts +21 -13
- package/openclaw.plugin.json +10 -9
- package/package.json +2 -1
- package/src/bot.ts +35 -21
- package/src/channel.ts +13 -2
- package/src/config-schema.ts +31 -11
- package/src/doc-schema.ts +47 -0
- package/src/docx.ts +506 -0
- package/src/drive-schema.ts +46 -0
- package/src/drive.ts +201 -0
- package/src/perm-schema.ts +52 -0
- package/src/perm.ts +160 -0
- package/src/tools-config.ts +21 -0
- package/src/types.ts +13 -5
- package/src/wiki-schema.ts +55 -0
- package/src/wiki.ts +218 -0
package/src/drive.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { createFeishuClient } from "./client.js";
|
|
3
|
+
import type { FeishuConfig } from "./types.js";
|
|
4
|
+
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
5
|
+
import { FeishuDriveSchema, type FeishuDriveParams } from "./drive-schema.js";
|
|
6
|
+
import { resolveToolsConfig } from "./tools-config.js";
|
|
7
|
+
|
|
8
|
+
// ============ Helpers ============
|
|
9
|
+
|
|
10
|
+
function json(data: unknown) {
|
|
11
|
+
return {
|
|
12
|
+
content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
|
|
13
|
+
details: data,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ============ Actions ============
|
|
18
|
+
|
|
19
|
+
async function getRootFolderToken(client: Lark.Client): Promise<string> {
|
|
20
|
+
// Use generic HTTP client to call the root folder meta API
|
|
21
|
+
// as it's not directly exposed in the SDK
|
|
22
|
+
const domain = (client as any).domain ?? "https://open.feishu.cn";
|
|
23
|
+
const res = (await (client as any).httpInstance.get(
|
|
24
|
+
`${domain}/open-apis/drive/explorer/v2/root_folder/meta`,
|
|
25
|
+
)) as { code: number; msg?: string; data?: { token?: string } };
|
|
26
|
+
if (res.code !== 0) throw new Error(res.msg ?? "Failed to get root folder");
|
|
27
|
+
const token = res.data?.token;
|
|
28
|
+
if (!token) throw new Error("Root folder token not found");
|
|
29
|
+
return token;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function listFolder(client: Lark.Client, folderToken?: string) {
|
|
33
|
+
// Filter out invalid folder_token values (empty, "0", etc.)
|
|
34
|
+
const validFolderToken = folderToken && folderToken !== "0" ? folderToken : undefined;
|
|
35
|
+
const res = await client.drive.file.list({
|
|
36
|
+
params: validFolderToken ? { folder_token: validFolderToken } : {},
|
|
37
|
+
});
|
|
38
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
files:
|
|
42
|
+
res.data?.files?.map((f) => ({
|
|
43
|
+
token: f.token,
|
|
44
|
+
name: f.name,
|
|
45
|
+
type: f.type,
|
|
46
|
+
url: f.url,
|
|
47
|
+
created_time: f.created_time,
|
|
48
|
+
modified_time: f.modified_time,
|
|
49
|
+
owner_id: f.owner_id,
|
|
50
|
+
})) ?? [],
|
|
51
|
+
next_page_token: res.data?.next_page_token,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function getFileInfo(client: Lark.Client, fileToken: string, folderToken?: string) {
|
|
56
|
+
// Use list with folder_token to find file info
|
|
57
|
+
const res = await client.drive.file.list({
|
|
58
|
+
params: folderToken ? { folder_token: folderToken } : {},
|
|
59
|
+
});
|
|
60
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
61
|
+
|
|
62
|
+
const file = res.data?.files?.find((f) => f.token === fileToken);
|
|
63
|
+
if (!file) {
|
|
64
|
+
throw new Error(`File not found: ${fileToken}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
token: file.token,
|
|
69
|
+
name: file.name,
|
|
70
|
+
type: file.type,
|
|
71
|
+
url: file.url,
|
|
72
|
+
created_time: file.created_time,
|
|
73
|
+
modified_time: file.modified_time,
|
|
74
|
+
owner_id: file.owner_id,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function createFolder(client: Lark.Client, name: string, folderToken?: string) {
|
|
79
|
+
// Feishu supports using folder_token="0" as the root folder.
|
|
80
|
+
// We *try* to resolve the real root token (explorer API), but fall back to "0"
|
|
81
|
+
// because some tenants/apps return 400 for that explorer endpoint.
|
|
82
|
+
let effectiveToken = folderToken && folderToken !== "0" ? folderToken : "0";
|
|
83
|
+
if (effectiveToken === "0") {
|
|
84
|
+
try {
|
|
85
|
+
effectiveToken = await getRootFolderToken(client);
|
|
86
|
+
} catch {
|
|
87
|
+
// ignore and keep "0"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const res = await client.drive.file.createFolder({
|
|
92
|
+
data: {
|
|
93
|
+
name,
|
|
94
|
+
folder_token: effectiveToken,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
token: res.data?.token,
|
|
101
|
+
url: res.data?.url,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function moveFile(
|
|
106
|
+
client: Lark.Client,
|
|
107
|
+
fileToken: string,
|
|
108
|
+
type: string,
|
|
109
|
+
folderToken: string,
|
|
110
|
+
) {
|
|
111
|
+
const res = await client.drive.file.move({
|
|
112
|
+
path: { file_token: fileToken },
|
|
113
|
+
data: {
|
|
114
|
+
type: type as "doc" | "docx" | "sheet" | "bitable" | "folder" | "file" | "mindnote" | "slides",
|
|
115
|
+
folder_token: folderToken,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
task_id: res.data?.task_id,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function deleteFile(client: Lark.Client, fileToken: string, type: string) {
|
|
127
|
+
const res = await client.drive.file.delete({
|
|
128
|
+
path: { file_token: fileToken },
|
|
129
|
+
params: {
|
|
130
|
+
type: type as
|
|
131
|
+
| "doc"
|
|
132
|
+
| "docx"
|
|
133
|
+
| "sheet"
|
|
134
|
+
| "bitable"
|
|
135
|
+
| "folder"
|
|
136
|
+
| "file"
|
|
137
|
+
| "mindnote"
|
|
138
|
+
| "slides"
|
|
139
|
+
| "shortcut",
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
success: true,
|
|
146
|
+
task_id: res.data?.task_id,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ============ Tool Registration ============
|
|
151
|
+
|
|
152
|
+
export function registerFeishuDriveTools(api: OpenClawPluginApi) {
|
|
153
|
+
const feishuCfg = api.config?.channels?.feishu as FeishuConfig | undefined;
|
|
154
|
+
if (!feishuCfg?.appId || !feishuCfg?.appSecret) {
|
|
155
|
+
api.logger.debug?.("feishu_drive: Feishu credentials not configured, skipping drive tools");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const toolsCfg = resolveToolsConfig(feishuCfg.tools);
|
|
160
|
+
if (!toolsCfg.drive) {
|
|
161
|
+
api.logger.debug?.("feishu_drive: drive tool disabled in config");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const getClient = () => createFeishuClient(feishuCfg);
|
|
166
|
+
|
|
167
|
+
api.registerTool(
|
|
168
|
+
{
|
|
169
|
+
name: "feishu_drive",
|
|
170
|
+
label: "Feishu Drive",
|
|
171
|
+
description:
|
|
172
|
+
"Feishu cloud storage operations. Actions: list, info, create_folder, move, delete",
|
|
173
|
+
parameters: FeishuDriveSchema,
|
|
174
|
+
async execute(_toolCallId, params) {
|
|
175
|
+
const p = params as FeishuDriveParams;
|
|
176
|
+
try {
|
|
177
|
+
const client = getClient();
|
|
178
|
+
switch (p.action) {
|
|
179
|
+
case "list":
|
|
180
|
+
return json(await listFolder(client, p.folder_token));
|
|
181
|
+
case "info":
|
|
182
|
+
return json(await getFileInfo(client, p.file_token));
|
|
183
|
+
case "create_folder":
|
|
184
|
+
return json(await createFolder(client, p.name, p.folder_token));
|
|
185
|
+
case "move":
|
|
186
|
+
return json(await moveFile(client, p.file_token, p.type, p.folder_token));
|
|
187
|
+
case "delete":
|
|
188
|
+
return json(await deleteFile(client, p.file_token, p.type));
|
|
189
|
+
default:
|
|
190
|
+
return json({ error: `Unknown action: ${(p as any).action}` });
|
|
191
|
+
}
|
|
192
|
+
} catch (err) {
|
|
193
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{ name: "feishu_drive" },
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
api.logger.info?.(`feishu_drive: Registered feishu_drive tool`);
|
|
201
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
2
|
+
|
|
3
|
+
const TokenType = Type.Union([
|
|
4
|
+
Type.Literal("doc"),
|
|
5
|
+
Type.Literal("docx"),
|
|
6
|
+
Type.Literal("sheet"),
|
|
7
|
+
Type.Literal("bitable"),
|
|
8
|
+
Type.Literal("folder"),
|
|
9
|
+
Type.Literal("file"),
|
|
10
|
+
Type.Literal("wiki"),
|
|
11
|
+
Type.Literal("mindnote"),
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
const MemberType = Type.Union([
|
|
15
|
+
Type.Literal("email"),
|
|
16
|
+
Type.Literal("openid"),
|
|
17
|
+
Type.Literal("userid"),
|
|
18
|
+
Type.Literal("unionid"),
|
|
19
|
+
Type.Literal("openchat"),
|
|
20
|
+
Type.Literal("opendepartmentid"),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const Permission = Type.Union([
|
|
24
|
+
Type.Literal("view"),
|
|
25
|
+
Type.Literal("edit"),
|
|
26
|
+
Type.Literal("full_access"),
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
export const FeishuPermSchema = Type.Union([
|
|
30
|
+
Type.Object({
|
|
31
|
+
action: Type.Literal("list"),
|
|
32
|
+
token: Type.String({ description: "File token" }),
|
|
33
|
+
type: TokenType,
|
|
34
|
+
}),
|
|
35
|
+
Type.Object({
|
|
36
|
+
action: Type.Literal("add"),
|
|
37
|
+
token: Type.String({ description: "File token" }),
|
|
38
|
+
type: TokenType,
|
|
39
|
+
member_type: MemberType,
|
|
40
|
+
member_id: Type.String({ description: "Member ID (email, open_id, user_id, etc.)" }),
|
|
41
|
+
perm: Permission,
|
|
42
|
+
}),
|
|
43
|
+
Type.Object({
|
|
44
|
+
action: Type.Literal("remove"),
|
|
45
|
+
token: Type.String({ description: "File token" }),
|
|
46
|
+
type: TokenType,
|
|
47
|
+
member_type: MemberType,
|
|
48
|
+
member_id: Type.String({ description: "Member ID to remove" }),
|
|
49
|
+
}),
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
export type FeishuPermParams = Static<typeof FeishuPermSchema>;
|
package/src/perm.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { createFeishuClient } from "./client.js";
|
|
3
|
+
import type { FeishuConfig } from "./types.js";
|
|
4
|
+
import type * as Lark from "@larksuiteoapi/node-sdk";
|
|
5
|
+
import { FeishuPermSchema, type FeishuPermParams } from "./perm-schema.js";
|
|
6
|
+
import { resolveToolsConfig } from "./tools-config.js";
|
|
7
|
+
|
|
8
|
+
// ============ Helpers ============
|
|
9
|
+
|
|
10
|
+
function json(data: unknown) {
|
|
11
|
+
return {
|
|
12
|
+
content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
|
|
13
|
+
details: data,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ListTokenType =
|
|
18
|
+
| "doc"
|
|
19
|
+
| "sheet"
|
|
20
|
+
| "file"
|
|
21
|
+
| "wiki"
|
|
22
|
+
| "bitable"
|
|
23
|
+
| "docx"
|
|
24
|
+
| "mindnote"
|
|
25
|
+
| "minutes"
|
|
26
|
+
| "slides";
|
|
27
|
+
type CreateTokenType =
|
|
28
|
+
| "doc"
|
|
29
|
+
| "sheet"
|
|
30
|
+
| "file"
|
|
31
|
+
| "wiki"
|
|
32
|
+
| "bitable"
|
|
33
|
+
| "docx"
|
|
34
|
+
| "folder"
|
|
35
|
+
| "mindnote"
|
|
36
|
+
| "minutes"
|
|
37
|
+
| "slides";
|
|
38
|
+
type MemberType =
|
|
39
|
+
| "email"
|
|
40
|
+
| "openid"
|
|
41
|
+
| "unionid"
|
|
42
|
+
| "openchat"
|
|
43
|
+
| "opendepartmentid"
|
|
44
|
+
| "userid"
|
|
45
|
+
| "groupid"
|
|
46
|
+
| "wikispaceid";
|
|
47
|
+
type PermType = "view" | "edit" | "full_access";
|
|
48
|
+
|
|
49
|
+
// ============ Actions ============
|
|
50
|
+
|
|
51
|
+
async function listMembers(client: Lark.Client, token: string, type: string) {
|
|
52
|
+
const res = await client.drive.permissionMember.list({
|
|
53
|
+
path: { token },
|
|
54
|
+
params: { type: type as ListTokenType },
|
|
55
|
+
});
|
|
56
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
members:
|
|
60
|
+
res.data?.items?.map((m) => ({
|
|
61
|
+
member_type: m.member_type,
|
|
62
|
+
member_id: m.member_id,
|
|
63
|
+
perm: m.perm,
|
|
64
|
+
name: m.name,
|
|
65
|
+
})) ?? [],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function addMember(
|
|
70
|
+
client: Lark.Client,
|
|
71
|
+
token: string,
|
|
72
|
+
type: string,
|
|
73
|
+
memberType: string,
|
|
74
|
+
memberId: string,
|
|
75
|
+
perm: string,
|
|
76
|
+
) {
|
|
77
|
+
const res = await client.drive.permissionMember.create({
|
|
78
|
+
path: { token },
|
|
79
|
+
params: { type: type as CreateTokenType, need_notification: false },
|
|
80
|
+
data: {
|
|
81
|
+
member_type: memberType as MemberType,
|
|
82
|
+
member_id: memberId,
|
|
83
|
+
perm: perm as PermType,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
success: true,
|
|
90
|
+
member: res.data?.member,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function removeMember(
|
|
95
|
+
client: Lark.Client,
|
|
96
|
+
token: string,
|
|
97
|
+
type: string,
|
|
98
|
+
memberType: string,
|
|
99
|
+
memberId: string,
|
|
100
|
+
) {
|
|
101
|
+
const res = await client.drive.permissionMember.delete({
|
|
102
|
+
path: { token, member_id: memberId },
|
|
103
|
+
params: { type: type as CreateTokenType, member_type: memberType as MemberType },
|
|
104
|
+
});
|
|
105
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============ Tool Registration ============
|
|
113
|
+
|
|
114
|
+
export function registerFeishuPermTools(api: OpenClawPluginApi) {
|
|
115
|
+
const feishuCfg = api.config?.channels?.feishu as FeishuConfig | undefined;
|
|
116
|
+
if (!feishuCfg?.appId || !feishuCfg?.appSecret) {
|
|
117
|
+
api.logger.debug?.("feishu_perm: Feishu credentials not configured, skipping perm tools");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const toolsCfg = resolveToolsConfig(feishuCfg.tools);
|
|
122
|
+
if (!toolsCfg.perm) {
|
|
123
|
+
api.logger.debug?.("feishu_perm: perm tool disabled in config (default: false)");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const getClient = () => createFeishuClient(feishuCfg);
|
|
128
|
+
|
|
129
|
+
api.registerTool(
|
|
130
|
+
{
|
|
131
|
+
name: "feishu_perm",
|
|
132
|
+
label: "Feishu Perm",
|
|
133
|
+
description: "Feishu permission management. Actions: list, add, remove",
|
|
134
|
+
parameters: FeishuPermSchema,
|
|
135
|
+
async execute(_toolCallId, params) {
|
|
136
|
+
const p = params as FeishuPermParams;
|
|
137
|
+
try {
|
|
138
|
+
const client = getClient();
|
|
139
|
+
switch (p.action) {
|
|
140
|
+
case "list":
|
|
141
|
+
return json(await listMembers(client, p.token, p.type));
|
|
142
|
+
case "add":
|
|
143
|
+
return json(
|
|
144
|
+
await addMember(client, p.token, p.type, p.member_type, p.member_id, p.perm),
|
|
145
|
+
);
|
|
146
|
+
case "remove":
|
|
147
|
+
return json(await removeMember(client, p.token, p.type, p.member_type, p.member_id));
|
|
148
|
+
default:
|
|
149
|
+
return json({ error: `Unknown action: ${(p as any).action}` });
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
return json({ error: err instanceof Error ? err.message : String(err) });
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{ name: "feishu_perm" },
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
api.logger.info?.(`feishu_perm: Registered feishu_perm tool`);
|
|
160
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FeishuToolsConfig } from "./types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default tool configuration.
|
|
5
|
+
* - doc, wiki, drive, scopes: enabled by default
|
|
6
|
+
* - perm: disabled by default (sensitive operation)
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_TOOLS_CONFIG: Required<FeishuToolsConfig> = {
|
|
9
|
+
doc: true,
|
|
10
|
+
wiki: true,
|
|
11
|
+
drive: true,
|
|
12
|
+
perm: false,
|
|
13
|
+
scopes: true,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve tools config with defaults.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveToolsConfig(cfg?: FeishuToolsConfig): Required<FeishuToolsConfig> {
|
|
20
|
+
return { ...DEFAULT_TOOLS_CONFIG, ...cfg };
|
|
21
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -48,8 +48,16 @@ export type FeishuProbeResult = {
|
|
|
48
48
|
botOpenId?: string;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
export type FeishuMediaInfo = {
|
|
52
|
-
path: string;
|
|
53
|
-
contentType?: string;
|
|
54
|
-
placeholder: string;
|
|
55
|
-
};
|
|
51
|
+
export type FeishuMediaInfo = {
|
|
52
|
+
path: string;
|
|
53
|
+
contentType?: string;
|
|
54
|
+
placeholder: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type FeishuToolsConfig = {
|
|
58
|
+
doc?: boolean;
|
|
59
|
+
wiki?: boolean;
|
|
60
|
+
drive?: boolean;
|
|
61
|
+
perm?: boolean;
|
|
62
|
+
scopes?: boolean;
|
|
63
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Type, type Static } from "@sinclair/typebox";
|
|
2
|
+
|
|
3
|
+
export const FeishuWikiSchema = Type.Union([
|
|
4
|
+
Type.Object({
|
|
5
|
+
action: Type.Literal("spaces"),
|
|
6
|
+
}),
|
|
7
|
+
Type.Object({
|
|
8
|
+
action: Type.Literal("nodes"),
|
|
9
|
+
space_id: Type.String({ description: "Knowledge space ID" }),
|
|
10
|
+
parent_node_token: Type.Optional(
|
|
11
|
+
Type.String({ description: "Parent node token (optional, omit for root)" }),
|
|
12
|
+
),
|
|
13
|
+
}),
|
|
14
|
+
Type.Object({
|
|
15
|
+
action: Type.Literal("get"),
|
|
16
|
+
token: Type.String({ description: "Wiki node token (from URL /wiki/XXX)" }),
|
|
17
|
+
}),
|
|
18
|
+
Type.Object({
|
|
19
|
+
action: Type.Literal("search"),
|
|
20
|
+
query: Type.String({ description: "Search query" }),
|
|
21
|
+
space_id: Type.Optional(Type.String({ description: "Limit search to this space (optional)" })),
|
|
22
|
+
}),
|
|
23
|
+
Type.Object({
|
|
24
|
+
action: Type.Literal("create"),
|
|
25
|
+
space_id: Type.String({ description: "Knowledge space ID" }),
|
|
26
|
+
title: Type.String({ description: "Node title" }),
|
|
27
|
+
obj_type: Type.Optional(
|
|
28
|
+
Type.Union([Type.Literal("docx"), Type.Literal("sheet"), Type.Literal("bitable")], {
|
|
29
|
+
description: "Object type (default: docx)",
|
|
30
|
+
}),
|
|
31
|
+
),
|
|
32
|
+
parent_node_token: Type.Optional(
|
|
33
|
+
Type.String({ description: "Parent node token (optional, omit for root)" }),
|
|
34
|
+
),
|
|
35
|
+
}),
|
|
36
|
+
Type.Object({
|
|
37
|
+
action: Type.Literal("move"),
|
|
38
|
+
space_id: Type.String({ description: "Source knowledge space ID" }),
|
|
39
|
+
node_token: Type.String({ description: "Node token to move" }),
|
|
40
|
+
target_space_id: Type.Optional(
|
|
41
|
+
Type.String({ description: "Target space ID (optional, same space if omitted)" }),
|
|
42
|
+
),
|
|
43
|
+
target_parent_token: Type.Optional(
|
|
44
|
+
Type.String({ description: "Target parent node token (optional, root if omitted)" }),
|
|
45
|
+
),
|
|
46
|
+
}),
|
|
47
|
+
Type.Object({
|
|
48
|
+
action: Type.Literal("rename"),
|
|
49
|
+
space_id: Type.String({ description: "Knowledge space ID" }),
|
|
50
|
+
node_token: Type.String({ description: "Node token to rename" }),
|
|
51
|
+
title: Type.String({ description: "New title" }),
|
|
52
|
+
}),
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
export type FeishuWikiParams = Static<typeof FeishuWikiSchema>;
|