Skip to content

Commit 85d20d1

Browse files
fix(feishu): pass drive list pagination params
1 parent d86bb60 commit 85d20d1

3 files changed

Lines changed: 93 additions & 4 deletions

File tree

extensions/feishu/src/drive-schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const FeishuDriveSchema = Type.Union([
2626
folder_token: Type.Optional(
2727
Type.String({ description: "Folder token (optional, omit for root directory)" }),
2828
),
29+
page_size: Type.Optional(Type.Integer({ minimum: 1, maximum: 200, description: "Page size" })),
30+
page_token: Type.Optional(Type.String({ description: "Folder list page token" })),
2931
}),
3032
Type.Object({
3133
action: Type.Literal("info"),

extensions/feishu/src/drive.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,25 @@ function expectRequestCall(
107107
}
108108
}
109109

110+
function schemaForAction(
111+
schema: unknown,
112+
action: string,
113+
): { properties?: Record<string, unknown> } {
114+
const variants =
115+
(schema as { anyOf?: unknown[]; oneOf?: unknown[] }).anyOf ??
116+
(schema as { anyOf?: unknown[]; oneOf?: unknown[] }).oneOf ??
117+
[];
118+
const variant = variants.find((entry) => {
119+
const actionSchema = (entry as { properties?: { action?: { const?: unknown } } }).properties
120+
?.action;
121+
return actionSchema?.const === action;
122+
});
123+
if (!variant) {
124+
throw new Error(`Missing schema variant for action ${action}`);
125+
}
126+
return variant as { properties?: Record<string, unknown> };
127+
}
128+
110129
describe("registerFeishuDriveTools", () => {
111130
const requestMock = vi.fn();
112131

@@ -360,6 +379,60 @@ describe("registerFeishuDriveTools", () => {
360379
expect((replyCommentResult.details as { reply_id?: string }).reply_id).toBe("r4");
361380
});
362381

382+
it("lists a folder continuation page when page_token is provided", async () => {
383+
const registerTool = vi.fn();
384+
const listFiles = vi.fn().mockResolvedValue({
385+
code: 0,
386+
data: {
387+
files: [{ token: "file_1", name: "File 1", type: "docx", url: "https://example.test/doc" }],
388+
next_page_token: "page-3",
389+
},
390+
});
391+
createFeishuToolClientMock.mockReturnValue({
392+
drive: { file: { list: listFiles } },
393+
});
394+
registerFeishuDriveTools(
395+
createDriveToolApi({
396+
config: {
397+
channels: {
398+
feishu: {
399+
enabled: true,
400+
appId: "app_id",
401+
appSecret: "app_secret", // pragma: allowlist secret
402+
tools: { drive: true },
403+
},
404+
},
405+
},
406+
registerTool,
407+
}),
408+
);
409+
410+
const toolFactory = firstToolFactory(registerTool);
411+
const tool = toolFactory({ agentAccountId: undefined });
412+
const listSchema = schemaForAction((tool as { parameters?: unknown }).parameters, "list");
413+
expect(listSchema.properties?.page_token).toMatchObject({ type: "string" });
414+
expect(listSchema.properties?.page_size).toMatchObject({
415+
type: "integer",
416+
minimum: 1,
417+
maximum: 200,
418+
});
419+
420+
const result = await tool.execute("call-list-page-2", {
421+
action: "list",
422+
folder_token: "folder_1",
423+
page_size: 25,
424+
page_token: "page-2",
425+
});
426+
427+
expect(listFiles).toHaveBeenCalledWith({
428+
params: { folder_token: "folder_1", page_size: 25, page_token: "page-2" },
429+
});
430+
expect(result.details).toMatchObject({
431+
files: [{ token: "file_1", name: "File 1", type: "docx", url: "https://example.test/doc" }],
432+
next_page_token: "page-3",
433+
});
434+
});
435+
363436
it("defaults add_comment file_type to docx when omitted", async () => {
364437
const registerTool = vi.fn();
365438
const infoSpy = vi.spyOn(console, "info").mockImplementation(() => {});

extensions/feishu/src/drive.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,19 @@ async function getRootFolderToken(client: Lark.Client): Promise<string> {
325325
return token;
326326
}
327327

328-
async function listFolder(client: Lark.Client, folderToken?: string) {
328+
async function listFolder(
329+
client: Lark.Client,
330+
params: { folder_token?: string; page_size?: number; page_token?: string } = {},
331+
) {
329332
// Filter out invalid folder_token values (empty, "0", etc.)
330-
const validFolderToken = folderToken && folderToken !== "0" ? folderToken : undefined;
333+
const validFolderToken =
334+
params.folder_token && params.folder_token !== "0" ? params.folder_token : undefined;
331335
const res = await client.drive.file.list({
332-
params: validFolderToken ? { folder_token: validFolderToken } : {},
336+
params: {
337+
...(validFolderToken ? { folder_token: validFolderToken } : {}),
338+
...(params.page_size ? { page_size: params.page_size } : {}),
339+
...(params.page_token ? { page_token: params.page_token } : {}),
340+
},
333341
});
334342
if (res.code !== 0) {
335343
throw new Error(res.msg);
@@ -766,7 +774,13 @@ export function registerFeishuDriveTools(api: OpenClawPluginApi) {
766774
});
767775
switch (p.action) {
768776
case "list":
769-
return jsonResult(await listFolder(client, p.folder_token));
777+
return jsonResult(
778+
await listFolder(client, {
779+
folder_token: p.folder_token,
780+
page_size: p.page_size,
781+
page_token: p.page_token,
782+
}),
783+
);
770784
case "info":
771785
return jsonResult(await getFileInfo(client, p.file_token));
772786
case "create_folder":

0 commit comments

Comments
 (0)