fix(hub): download Feishu resources via bot-owned lark-cli

Agent tool downloads and trigger attachment staging both used the SDK
messageResource path, which fails closed for multi-MB teacher files and
did not share the bot-identity transport contract. Route every download
through Hub-owned createFeishuBotCli (secret via stdin, disposable HOME,
HUB_FEISHU_CLI_BIN), keep workspace containment on write, and inject the
adapter in trigger tests.
This commit is contained in:
2026-07-30 11:27:53 +08:00
parent 97c7054529
commit 88386fb943
11 changed files with 456 additions and 56 deletions
+11 -3
View File
@@ -54,6 +54,7 @@ import {
type MessageResourceStageRequest,
type StagedMessageResourceBatch,
} from "./resourceStaging.js";
import { createFeishuBotCli, type FeishuBotCli } from "./botCli.js";
import { TriggerQueue, triggerQueue as defaultTriggerQueue, type QueuedTrigger } from "./triggerQueue.js";
import { createSlashCommandRegistry, parseSlashInvocation } from "./slashCommands.js";
import { cphHubMcpToolsForRole, roleToolsAllow } from "../agent/roleTools.js";
@@ -115,6 +116,8 @@ interface TriggerDeps {
readonly allowLegacyFeishuIdentity?: boolean | undefined;
/** Alpha Silo aggregate ingress ceiling across message and card events. */
readonly maxFeishuEventsPerMinute?: number | undefined;
/** Test/injection seam for bot-identity Feishu resource downloads. */
readonly feishuBotCli?: FeishuBotCli | undefined;
}
interface TriggerActor {
@@ -303,8 +306,13 @@ export function makeTriggerHandler(deps: TriggerDeps): TriggerHandler {
senderOpenId,
});
const senderMetadata = await senderAuditMetadata(rt, senderOpenId);
const botCli = deps.feishuBotCli ?? createFeishuBotCli({
organizationId: deps.siloOrganizationId,
prisma: deps.prisma,
secretEnvelope: deps.secretEnvelope,
});
const stagedResources = await stageTriggerMessageResources(
rt,
botCli,
msg,
projectWorkspaceRoot,
deps.resourceLimits,
@@ -1952,7 +1960,7 @@ function isPrismaUniqueConstraintError(error: unknown): boolean {
}
async function stageTriggerMessageResources(
rt: FeishuRuntime,
botCli: FeishuBotCli,
msg: MessageReceiveEvent["message"],
workspaceRoot: string,
limits?: TriggerDeps["resourceLimits"],
@@ -1986,7 +1994,7 @@ async function stageTriggerMessageResources(
}
}
return stageMessageResources(
rt,
botCli,
msg.message_id,
requests,
workspaceRoot,