@trydying/opencode-feishu-notifier 0.3.1 → 0.3.2

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/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config.ts","../src/feishu/client.ts","../src/feishu/messages.ts","../src/hooks.ts","../src/index.ts"],"sourcesContent":["import fs from \"fs\"\nimport os from \"os\"\nimport path from \"path\"\n\nexport type ReceiverType = \"user_id\" | \"open_id\" | \"chat_id\"\n\nexport interface FeishuConfig {\n appId: string\n appSecret: string\n receiverType: ReceiverType\n receiverId: string\n}\n\nexport interface LoadConfigOptions {\n directory?: string\n configPath?: string\n}\n\nexport type ConfigSource =\n | { type: \"file\"; detail: string }\n | { type: \"env\"; detail: string }\n\nconst receiverTypes: ReceiverType[] = [\"user_id\", \"open_id\", \"chat_id\"]\n\nfunction getConfigPaths(options: LoadConfigOptions): string[] {\n if (options.configPath) {\n return [options.configPath]\n }\n\n const paths: string[] = []\n const directory = options.directory ?? process.cwd()\n const xdgConfig = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\")\n\n paths.push(path.join(xdgConfig, \"opencode\", \"feishu-notifier.json\"))\n paths.push(path.join(directory, \".opencode\", \"feishu-notifier.json\"))\n\n return paths\n}\n\nfunction readConfigFile(filePath: string): Partial<FeishuConfig> | null {\n if (!fs.existsSync(filePath)) {\n return null\n }\n\n const raw = fs.readFileSync(filePath, \"utf8\").trim()\n if (!raw) {\n return null\n }\n\n try {\n return JSON.parse(raw) as Partial<FeishuConfig>\n } catch (error) {\n throw new Error(`Invalid JSON in ${filePath}: ${String(error)}`)\n }\n}\n\nfunction readEnvConfig(): Partial<FeishuConfig> {\n return {\n appId: process.env.FEISHU_APP_ID,\n appSecret: process.env.FEISHU_APP_SECRET,\n receiverType: process.env.FEISHU_RECEIVER_TYPE as ReceiverType | undefined,\n receiverId: process.env.FEISHU_RECEIVER_ID\n }\n}\n\nfunction resolveConfig(options: LoadConfigOptions): {\n mergedConfig: Partial<FeishuConfig>\n sources: ConfigSource[]\n} {\n const configPaths = getConfigPaths(options)\n let mergedConfig: Partial<FeishuConfig> = {}\n const sources: ConfigSource[] = []\n\n for (const configPath of configPaths) {\n const config = readConfigFile(configPath)\n if (config) {\n mergedConfig = {\n ...mergedConfig,\n ...config\n }\n sources.push({ type: \"file\", detail: configPath })\n }\n }\n\n const envConfig = readEnvConfig()\n if (envConfig.appId || envConfig.appSecret || envConfig.receiverType || envConfig.receiverId) {\n mergedConfig = {\n ...mergedConfig,\n ...envConfig\n }\n sources.push({ type: \"env\", detail: \"FEISHU_*\" })\n }\n\n return { mergedConfig, sources }\n}\n\nfunction finalizeConfig(mergedConfig: Partial<FeishuConfig>, sources: ConfigSource[]): FeishuConfig {\n if (sources.length === 0) {\n throw new Error(\n \"Missing Feishu configuration. Use FEISHU_* environment variables or create feishu-notifier.json.\"\n )\n }\n\n const missing: string[] = []\n if (!mergedConfig.appId) missing.push(\"appId\")\n if (!mergedConfig.appSecret) missing.push(\"appSecret\")\n if (!mergedConfig.receiverType) missing.push(\"receiverType\")\n if (!mergedConfig.receiverId) missing.push(\"receiverId\")\n\n if (missing.length > 0) {\n throw new Error(`Missing config fields: ${missing.join(\", \")}`)\n }\n\n const receiverType = mergedConfig.receiverType as ReceiverType\n if (!receiverTypes.includes(receiverType)) {\n throw new Error(\n `Invalid receiverType: ${mergedConfig.receiverType}. Expected one of ${receiverTypes.join(\", \")}`\n )\n }\n\n return {\n appId: mergedConfig.appId!,\n appSecret: mergedConfig.appSecret!,\n receiverType,\n receiverId: mergedConfig.receiverId!\n }\n}\n\nexport function loadConfig(options: LoadConfigOptions = {}): FeishuConfig {\n return loadConfigWithSource(options).config\n}\n\nexport function loadConfigWithSource(\n options: LoadConfigOptions = {}\n): { config: FeishuConfig; sources: ConfigSource[] } {\n const { mergedConfig, sources } = resolveConfig(options)\n return {\n config: finalizeConfig(mergedConfig, sources),\n sources\n }\n}\n","import type { FeishuConfig } from \"../config\"\n\ntype TenantTokenResponse = {\n code: number\n msg: string\n tenant_access_token?: string\n expire?: number\n}\n\ninterface TokenCacheEntry {\n token: string\n expiresAt: number // timestamp in milliseconds\n}\n\nconst tokenCache = new Map<string, TokenCacheEntry>()\n\nfunction clearTokenCache(config: FeishuConfig) {\n const cacheKey = `${config.appId}:${config.appSecret}`\n tokenCache.delete(cacheKey)\n}\n\nfunction isTokenExpiredError(code: number): boolean {\n // Common Feishu token expiration error codes\n return code === 99991663 || code === 99991664 || code === 99991668\n}\n\ntype MessageResponse = {\n code: number\n msg: string\n data?: {\n message_id?: string\n }\n}\n\ntype FeishuPostContent = {\n post: {\n zh_cn: {\n title: string\n content: Array<Array<{\n tag: string\n text?: string\n href?: string\n un_escape?: boolean\n }>>\n }\n }\n}\n\nfunction ensureFetch(): typeof fetch {\n if (typeof fetch === \"undefined\") {\n throw new Error(\"Global fetch is not available. Use Node.js 18+.\")\n }\n\n return fetch\n}\n\nasync function readJson<T>(response: Response): Promise<T> {\n const text = await response.text()\n if (!text) {\n throw new Error(`Empty response from Feishu API (${response.status}).`)\n }\n\n return JSON.parse(text) as T\n}\n\nexport async function getTenantAccessToken(config: FeishuConfig): Promise<string> {\n const cacheKey = `${config.appId}:${config.appSecret}`\n const now = Date.now()\n \n // Check cache\n const cached = tokenCache.get(cacheKey)\n if (cached && cached.expiresAt > now + 60000) { // 60 second buffer\n return cached.token\n }\n \n const fetchImpl = ensureFetch()\n const response = await fetchImpl(\n \"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal\",\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n app_id: config.appId,\n app_secret: config.appSecret\n })\n }\n )\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => `Failed to read error response`)\n throw new Error(`Feishu auth request failed: ${response.status} - ${errorText}`)\n }\n\n const payload = await readJson<TenantTokenResponse>(response)\n if (payload.code !== 0 || !payload.tenant_access_token) {\n throw new Error(`Feishu auth failed: ${payload.msg} (${payload.code})`)\n }\n\n // Cache token with expiration (default 2 hours if not provided)\n const expiresIn = payload.expire ? payload.expire * 1000 : 2 * 60 * 60 * 1000\n const expiresAt = now + expiresIn - 60000 // 60 second buffer\n \n tokenCache.set(cacheKey, {\n token: payload.tenant_access_token,\n expiresAt\n })\n \n return payload.tenant_access_token\n}\n\nasync function sendMessage(\n config: FeishuConfig,\n msgType: \"text\" | \"post\",\n content: unknown\n): Promise<MessageResponse> {\n const fetchImpl = ensureFetch()\n \n const sendWithToken = async (retryOnTokenExpired = true): Promise<MessageResponse> => {\n const token = await getTenantAccessToken(config)\n const response = await fetchImpl(\n `https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=${config.receiverType}`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n receive_id: config.receiverId,\n msg_type: msgType,\n content: JSON.stringify(content)\n })\n }\n )\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => `Failed to read error response`)\n throw new Error(`Feishu message request failed: ${response.status} - ${errorText}`)\n }\n\n const payload = await readJson<MessageResponse>(response)\n if (payload.code !== 0) {\n // Check if token expired\n if (retryOnTokenExpired && isTokenExpiredError(payload.code)) {\n clearTokenCache(config)\n return sendWithToken(false) // Retry once\n }\n throw new Error(`Feishu message failed: ${payload.msg} (${payload.code}) - Response: ${JSON.stringify(payload)}`)\n }\n\n return payload\n }\n \n return sendWithToken()\n}\n\nexport async function sendTextMessage(\n config: FeishuConfig,\n text: string\n): Promise<MessageResponse> {\n return sendMessage(config, \"text\", { text })\n}\n\n/**\n * 将纯文本转换为飞书富文本(post)格式\n * 简化实现:所有文本作为一个段落\n */\nfunction textToPostContent(text: string, title: string = \"OpenCode 通知\"): FeishuPostContent {\n // 移除空行,但保留换行符\n const cleanedText = text.split('\\n').filter(line => line.trim().length > 0).join('\\n')\n \n const content = [\n [\n {\n tag: 'text',\n text: cleanedText,\n un_escape: true\n }\n ]\n ]\n \n return {\n post: {\n zh_cn: {\n title,\n content\n }\n }\n }\n}\n\nexport async function sendRichTextMessage(\n config: FeishuConfig,\n text: string,\n title?: string,\n richContent?: FeishuPostContent\n): Promise<MessageResponse> {\n const postContent = richContent || textToPostContent(text, title)\n return sendMessage(config, \"post\", postContent)\n}\n","import os from \"os\";\n\nexport type FeishuPostContent = {\n post: {\n zh_cn: {\n title: string\n content: Array<Array<{\n tag: string\n text?: string\n href?: string\n un_escape?: boolean\n }>>\n }\n }\n}\n\nexport type NotificationResult = {\n title: string\n text: string\n richContent?: FeishuPostContent\n}\n\nexport type NotificationType =\n | \"interaction_required\"\n | \"permission_required\"\n | \"command_args_required\"\n | \"confirmation_required\"\n | \"session_idle\"\n | \"question_asked\"\n | \"setup_test\"\n\ntype EventPayload = {\n type?: string;\n payload?: unknown;\n properties?: Record<string, unknown>;\n}\n\ntype SessionContext = {\n sessionID?: string;\n sessionTitle?: string;\n agentName?: string;\n};\n\ntype SessionClient = {\n session?: {\n get?: (options: { path: { id: string } }) => Promise<{\n data?: {\n title?: string;\n };\n }>;\n };\n};\n\nconst sessionTitleCache = new Map<string, string>();\nconst sessionAgentCache = new Map<string, string>();\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n if (value && typeof value === \"object\") {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction extractEventPayload(event?: EventPayload): unknown {\n if (!event) {\n return undefined;\n }\n if (event.payload !== undefined) {\n return event.payload;\n }\n if (event.properties !== undefined) {\n return event.properties;\n }\n return undefined;\n}\n\nfunction extractEventProperties(event?: EventPayload): Record<string, unknown> | undefined {\n if (!event) {\n return undefined;\n }\n if (event.properties) {\n return asRecord(event.properties);\n }\n if (event.payload) {\n return asRecord(event.payload);\n }\n return undefined;\n}\n\nfunction extractSessionContext(event?: EventPayload): SessionContext {\n const properties = extractEventProperties(event);\n const info = asRecord(properties?.info);\n const part = asRecord(properties?.part);\n\n const sessionID =\n readString(properties?.sessionID) ??\n readString(info?.sessionID) ??\n readString(info?.id) ??\n readString(part?.sessionID);\n\n const sessionTitle = readString(info?.title);\n\n const agentName =\n readString(properties?.agent) ??\n readString(info?.agent) ??\n readString(part?.agent) ??\n readString(part?.name);\n\n return {\n sessionID,\n sessionTitle,\n agentName,\n };\n}\n\nasync function resolveSessionContext(\n event?: EventPayload,\n client?: SessionClient\n): Promise<SessionContext> {\n const baseContext = extractSessionContext(event);\n if (!baseContext.sessionID) {\n return baseContext;\n }\n\n const cachedTitle = sessionTitleCache.get(baseContext.sessionID);\n const cachedAgent = sessionAgentCache.get(baseContext.sessionID);\n const mergedContext = {\n ...baseContext,\n sessionTitle: baseContext.sessionTitle ?? cachedTitle,\n agentName: baseContext.agentName ?? cachedAgent,\n };\n\n if (mergedContext.sessionTitle) {\n return mergedContext;\n }\n\n if (client?.session?.get) {\n try {\n const response = await client.session.get({\n path: { id: baseContext.sessionID },\n });\n const title = response?.data?.title;\n if (title) {\n sessionTitleCache.set(baseContext.sessionID, title);\n return {\n ...mergedContext,\n sessionTitle: title,\n };\n }\n } catch {\n // 忽略会话信息获取失败\n }\n }\n\n return mergedContext;\n}\n\nexport function recordEventContext(event?: EventPayload): void {\n const context = extractSessionContext(event);\n if (!context.sessionID) {\n return;\n }\n\n if (context.sessionTitle) {\n sessionTitleCache.set(context.sessionID, context.sessionTitle);\n }\n\n if (context.agentName) {\n sessionAgentCache.set(context.sessionID, context.agentName);\n }\n}\n\n// 保持向后兼容的标题映射\nconst titles: Record<NotificationType, string> = {\n interaction_required: \"需要交互\",\n permission_required: \"需要权限确认\",\n command_args_required: \"需要补充参数\",\n confirmation_required: \"需要确认\",\n session_idle: \"OpenCode 闲暇\",\n question_asked: \"需要选择方案\",\n setup_test: \"Feishu 通知测试\"\n}\n\n/**\n * 构建结构化通知消息(新版本)\n * @param type 通知类型\n * @param event 事件数据\n * @param directory 工作目录(可选,默认当前目录)\n * @returns 包含标题和文本的消息对象\n */\nexport async function buildStructuredNotification(\n type: NotificationType,\n event?: EventPayload,\n directory?: string,\n client?: SessionClient\n): Promise<NotificationResult> {\n // 导入模板系统\n const { buildStructuredMessage } = await import(\"./templates\")\n\n const eventPayload = extractEventPayload(event);\n const sessionContext = await resolveSessionContext(event, client);\n \n try {\n const text = await buildStructuredMessage(\n type,\n eventPayload,\n event?.type,\n directory,\n sessionContext\n )\n \n // 返回标题(保持向后兼容)\n return {\n title: titles[type],\n text,\n richContent: textToPostContent(text, titles[type])\n }\n } catch (error) {\n // 如果模板系统失败,回退到原始实现\n return buildLegacyNotification(type, event)\n }\n}\n\n/**\n * 构建传统格式的通知消息(向后兼容)\n */\nexport function buildLegacyNotification(\n type: NotificationType,\n event?: EventPayload\n): NotificationResult {\n const title = titles[type]\n if (type === \"setup_test\") {\n const text = `${title}\\nFeishu 通知已启用。`\n return {\n title,\n text,\n richContent: textToPostContent(text, title)\n }\n }\n\n const payloadText = formatPayload(extractEventPayload(event))\n const sessionContext = extractSessionContext(event)\n const lines = [\n `[OpenCode] ${title}`,\n event?.type ? `事件类型: ${event.type}` : \"\",\n sessionContext.sessionTitle || sessionContext.sessionID\n ? `会话: ${sessionContext.sessionTitle ?? sessionContext.sessionID}`\n : \"\",\n sessionContext.agentName ? `Agent: ${sessionContext.agentName}` : \"\",\n `主机: ${os.hostname()}`,\n payloadText ? `详情: ${payloadText}` : \"\"\n ].filter(Boolean)\n\n const text = lines.join(\"\\n\")\n return {\n title,\n text,\n richContent: textToPostContent(text, title)\n }\n}\n\n/**\n * 格式化负载文本\n */\nfunction formatPayload(payload: unknown): string {\n if (!payload) {\n return \"\"\n }\n\n const text = JSON.stringify(payload, null, 2)\n if (text.length > 1200) {\n return `${text.slice(0, 1200)}…`\n }\n\n return text\n}\n\n/**\n * 将纯文本转换为飞书富文本(post)格式\n * 简化实现:所有文本作为一个段落\n */\nfunction textToPostContent(text: string, title: string = \"OpenCode 通知\"): FeishuPostContent {\n // 移除空行,但保留换行符\n const cleanedText = text.split('\\n').filter(line => line.trim().length > 0).join('\\n')\n \n const content = [\n [\n {\n tag: 'text',\n text: cleanedText,\n un_escape: true\n }\n ]\n ]\n \n return {\n post: {\n zh_cn: {\n title,\n content\n }\n }\n }\n}\n\n/**\n * 构建通知消息(主入口,保持向后兼容)\n * 默认使用结构化消息,失败时回退\n */\nexport async function buildNotification(\n type: NotificationType,\n event?: EventPayload,\n directory?: string,\n client?: SessionClient\n): Promise<NotificationResult> {\n // 默认使用结构化消息\n return buildStructuredNotification(type, event, directory, client)\n}\n","import type { NotificationType } from \"./feishu/messages\"\n\nexport function mapEventToNotification(eventType: string): NotificationType | null {\n switch (eventType) {\n case \"permission.asked\":\n return \"permission_required\"\n case \"question.asked\":\n return \"question_asked\"\n default:\n return null\n }\n}\n\ntype CompletionCandidateEvent = {\n type: string\n properties?: Record<string, unknown>\n}\n\n/**\n * 消息完成事件(包含错误后终止)统一映射为完成通知。\n */\nexport function mapCompletionEventToNotification(\n event: CompletionCandidateEvent\n): NotificationType | null {\n const status = event.properties?.status as { type?: string } | undefined\n if (event.type === \"session.status\" && status?.type === \"idle\") {\n return \"session_idle\"\n }\n\n // 兼容不同版本 OpenCode 可能出现的完成/终止事件名\n if (\n event.type === \"message.completed\" ||\n event.type === \"message.failed\" ||\n event.type === \"message.errored\"\n ) {\n return \"session_idle\"\n }\n\n return null\n}\n","import type { Plugin } from \"@opencode-ai/plugin\";\nimport { loadConfigWithSource } from \"./config\";\nimport { sendTextMessage, sendRichTextMessage } from \"./feishu/client\";\nimport { buildNotification, recordEventContext } from \"./feishu/messages\";\nimport { mapCompletionEventToNotification, mapEventToNotification } from \"./hooks\";\n\nconst serviceName = \"opencode-feishu-notifier\";\n\nconst FeishuNotifierPlugin: Plugin = async ({ client, directory }) => {\n let configCache: ReturnType<typeof loadConfigWithSource> | null = null;\n let configError: Error | null = null;\n\n const log = (\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n extra?: Record<string, unknown>\n ) => {\n const payload = {\n body: {\n service: serviceName,\n level,\n message,\n extra,\n },\n };\n void client.app.log(payload).catch(() => undefined);\n };\n\n const logDebug = (message: string, extra?: Record<string, unknown>) => {\n log(\"debug\", message, extra);\n };\n\n const logInfo = (message: string, extra?: Record<string, unknown>) => {\n log(\"info\", message, extra);\n };\n\n const logError = (message: string, extra?: Record<string, unknown>) => {\n log(\"error\", message, extra);\n };\n\n logInfo(\"Feishu notifier plugin loading\", { directory });\n\n const ensureConfig = () => {\n if (configCache || configError) {\n return;\n }\n\n try {\n configCache = loadConfigWithSource({ directory });\n logInfo(\"Feishu notifier plugin initialized\", { sources: configCache.sources.map(s => s.type) });\n logDebug(\"Loaded Feishu config\", { sources: configCache.sources });\n } catch (error) {\n configError = error instanceof Error ? error : new Error(String(error));\n logError(\"Feishu config error\", { error: configError.message });\n }\n };\n\n ensureConfig();\n\n return {\n event: async ({ event }) => {\n recordEventContext(event);\n logDebug(\"Event received\", { eventType: event.type });\n\n let notificationType = mapEventToNotification(event.type);\n notificationType = notificationType ?? mapCompletionEventToNotification(event);\n\n if (!notificationType) {\n logDebug(\"Event ignored\", { eventType: event.type });\n return;\n }\n logDebug(\"Event mapped to notification\", {\n eventType: event.type,\n notificationType,\n });\n\n ensureConfig();\n if (configError) {\n logError(\"Feishu config error (cached)\", { error: configError.message });\n return;\n }\n if (!configCache) {\n logError(\"Feishu config not loaded\");\n return;\n }\n\n const { text, title, richContent } = await buildNotification(\n notificationType,\n event,\n directory,\n { session: client.session }\n );\n logDebug(\"Sending Feishu notification\", {\n eventType: event.type,\n notificationType,\n directory,\n hasRichContent: !!richContent,\n });\n\n try {\n let response;\n if (richContent) {\n // 尝试发送富文本消息\n try {\n logDebug(\"Attempting to send rich text message\", {\n richContentType: typeof richContent,\n hasPost: !!richContent.post,\n hasZhCn: !!(richContent.post?.zh_cn),\n titleLength: richContent.post?.zh_cn?.title?.length ?? 0,\n contentLength: richContent.post?.zh_cn?.content?.length ?? 0,\n });\n response = await sendRichTextMessage(configCache.config, text, title, richContent);\n logDebug(\"Feishu rich notification sent\", {\n messageId: response.data?.message_id ?? null,\n });\n } catch (richError) {\n // 富文本消息失败,回退到纯文本\n logDebug(\"Rich text message failed, falling back to text\", {\n error: richError instanceof Error ? richError.message : String(richError),\n stack: richError instanceof Error ? richError.stack : undefined,\n name: richError instanceof Error ? richError.name : undefined,\n });\n response = await sendTextMessage(configCache.config, text);\n logDebug(\"Feishu text notification sent (fallback)\", {\n messageId: response.data?.message_id ?? null,\n });\n }\n } else {\n // 回退到纯文本\n response = await sendTextMessage(configCache.config, text);\n logDebug(\"Feishu text notification sent\", {\n messageId: response.data?.message_id ?? null,\n });\n }\n } catch (error) {\n logError(\"Failed to send Feishu notification\", {\n error: String(error),\n });\n }\n },\n };\n};\n\nexport default FeishuNotifierPlugin;\n"],"mappings":"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","names":["os","textToPostContent","text"]}
@@ -1,564 +0,0 @@
1
- import {
2
- __require
3
- } from "./chunk-DGUM43GV.js";
4
-
5
- // src/context/progress.ts
6
- function extractProgressInfo(eventPayload) {
7
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
8
- let lastAction;
9
- let currentTask;
10
- if (eventPayload && typeof eventPayload === "object") {
11
- const payload = eventPayload;
12
- if (typeof payload.message === "string") {
13
- lastAction = payload.message;
14
- } else if (typeof payload.description === "string") {
15
- lastAction = payload.description;
16
- } else if (typeof payload.action === "string") {
17
- lastAction = payload.action;
18
- }
19
- if (typeof payload.task === "string") {
20
- currentTask = payload.task;
21
- } else if (typeof payload.currentTask === "string") {
22
- currentTask = payload.currentTask;
23
- }
24
- }
25
- if (!lastAction) {
26
- lastAction = "OpenCode \u6B63\u5728\u5904\u7406\u4EFB\u52A1";
27
- }
28
- return {
29
- lastAction,
30
- currentTask,
31
- timestamp
32
- // 文件变更信息需要从 Git 或其他来源获取,这里暂时留空
33
- };
34
- }
35
- function extractFileChanges(directory) {
36
- try {
37
- const { execSync: execSync2 } = __require("child_process");
38
- const statusOutput = execSync2("git status --porcelain", {
39
- cwd: directory,
40
- encoding: "utf-8"
41
- });
42
- let added = 0;
43
- let modified = 0;
44
- let deleted = 0;
45
- const lines = statusOutput.trim().split("\n");
46
- for (const line of lines) {
47
- if (!line.trim()) continue;
48
- const status = line.substring(0, 2).trim();
49
- if (status === "A" || status.startsWith("A")) {
50
- added++;
51
- } else if (status === "M" || status.startsWith("M")) {
52
- modified++;
53
- } else if (status === "D" || status.startsWith("D")) {
54
- deleted++;
55
- } else if (status === "??") {
56
- added++;
57
- }
58
- }
59
- if (added > 0 || modified > 0 || deleted > 0) {
60
- return { added, modified, deleted };
61
- }
62
- } catch (error) {
63
- }
64
- return void 0;
65
- }
66
- function createProgressInfo(eventPayload, directory) {
67
- const baseInfo = extractProgressInfo(eventPayload);
68
- if (directory) {
69
- const fileChanges = extractFileChanges(directory);
70
- if (fileChanges) {
71
- return {
72
- ...baseInfo,
73
- fileChanges
74
- };
75
- }
76
- }
77
- return baseInfo;
78
- }
79
- function formatProgressInfo(progress) {
80
- const lines = [];
81
- const time = new Date(progress.timestamp);
82
- const timeStr = time.toLocaleString("zh-CN", {
83
- year: "numeric",
84
- month: "2-digit",
85
- day: "2-digit",
86
- hour: "2-digit",
87
- minute: "2-digit",
88
- second: "2-digit"
89
- });
90
- lines.push(`\u2022 \u65F6\u95F4\uFF1A${timeStr}`);
91
- if (progress.lastAction) {
92
- lines.push(`\u2022 \u6700\u8FD1\u64CD\u4F5C\uFF1A${progress.lastAction}`);
93
- }
94
- if (progress.currentTask) {
95
- lines.push(`\u2022 \u5F53\u524D\u4EFB\u52A1\uFF1A${progress.currentTask}`);
96
- }
97
- if (progress.fileChanges) {
98
- const changes = progress.fileChanges;
99
- const changeParts = [];
100
- if (changes.added && changes.added > 0) {
101
- changeParts.push(`\u65B0\u589E ${changes.added} \u4E2A\u6587\u4EF6`);
102
- }
103
- if (changes.modified && changes.modified > 0) {
104
- changeParts.push(`\u4FEE\u6539 ${changes.modified} \u4E2A\u6587\u4EF6`);
105
- }
106
- if (changes.deleted && changes.deleted > 0) {
107
- changeParts.push(`\u5220\u9664 ${changes.deleted} \u4E2A\u6587\u4EF6`);
108
- }
109
- if (changeParts.length > 0) {
110
- lines.push(`\u2022 \u6587\u4EF6\u53D8\u66F4\uFF1A${changeParts.join("\uFF0C")}`);
111
- }
112
- }
113
- return lines.join("\n");
114
- }
115
-
116
- // src/context/project.ts
117
- import { execSync } from "child_process";
118
- import fs from "fs";
119
- import path from "path";
120
- import os from "os";
121
- async function extractProjectContext(directory) {
122
- const workingDir = path.resolve(directory);
123
- const projectName = await extractProjectName(workingDir);
124
- const gitInfo = await extractGitInfo(workingDir);
125
- return {
126
- projectName,
127
- branch: gitInfo.branch,
128
- workingDir,
129
- repoUrl: gitInfo.repoUrl,
130
- isGitRepo: gitInfo.isGitRepo,
131
- hostname: os.hostname()
132
- };
133
- }
134
- async function extractProjectName(directory) {
135
- const packageJsonPath = path.join(directory, "package.json");
136
- if (fs.existsSync(packageJsonPath)) {
137
- try {
138
- const content = await fs.promises.readFile(packageJsonPath, "utf-8");
139
- const packageJson = JSON.parse(content);
140
- if (packageJson.name) {
141
- return packageJson.name;
142
- }
143
- } catch {
144
- }
145
- }
146
- return path.basename(directory);
147
- }
148
- async function extractGitInfo(directory) {
149
- const gitDir = path.join(directory, ".git");
150
- if (!fs.existsSync(gitDir)) {
151
- return { isGitRepo: false };
152
- }
153
- try {
154
- const branch = execSync("git branch --show-current", {
155
- cwd: directory,
156
- encoding: "utf-8"
157
- }).trim();
158
- let repoUrl;
159
- try {
160
- const remoteUrl = execSync("git config --get remote.origin.url", {
161
- cwd: directory,
162
- encoding: "utf-8"
163
- }).trim();
164
- if (remoteUrl) {
165
- repoUrl = convertGitUrlToWeb(remoteUrl);
166
- }
167
- } catch {
168
- }
169
- return {
170
- isGitRepo: true,
171
- branch: branch || void 0,
172
- repoUrl
173
- };
174
- } catch (error) {
175
- return { isGitRepo: true };
176
- }
177
- }
178
- function convertGitUrlToWeb(gitUrl) {
179
- let url = gitUrl.replace(/\.git$/, "");
180
- if (url.startsWith("git@")) {
181
- url = url.replace(":", "/").replace("git@", "https://");
182
- }
183
- return url;
184
- }
185
-
186
- // src/feishu/templates.ts
187
- var REASON_CONFIGS = {
188
- session_idle: {
189
- category: "\u95F2\u6687\u7B49\u5F85",
190
- description: "\u5DF2\u5B8C\u6210\u5F53\u524D\u4EFB\u52A1\uFF0C\u7B49\u5F85\u4E0B\u4E00\u6B65\u6307\u793A",
191
- requiresAction: true,
192
- emoji: "\u{1F4A4}"
193
- },
194
- permission_required: {
195
- category: "\u9700\u8981\u6743\u9650",
196
- description: "\u9700\u8981\u8BBF\u95EE\u6587\u4EF6\u6743\u9650\u624D\u80FD\u7EE7\u7EED",
197
- requiresAction: true,
198
- emoji: "\u{1F510}"
199
- },
200
- question_asked: {
201
- category: "\u9700\u8981\u9009\u62E9",
202
- description: "\u63D0\u4F9B\u4E86\u591A\u4E2A\u65B9\u6848\uFF0C\u9700\u8981\u4F60\u9009\u62E9",
203
- requiresAction: true,
204
- emoji: "\u2753"
205
- },
206
- interaction_required: {
207
- category: "\u9700\u8981\u8F93\u5165",
208
- description: "\u9700\u8981\u4F60\u63D0\u4F9B\u989D\u5916\u4FE1\u606F",
209
- requiresAction: true,
210
- emoji: "\u270F\uFE0F"
211
- },
212
- command_args_required: {
213
- category: "\u53C2\u6570\u7F3A\u5931",
214
- description: "\u547D\u4EE4\u9700\u8981\u989D\u5916\u53C2\u6570\u624D\u80FD\u6267\u884C",
215
- requiresAction: true,
216
- emoji: "\u2699\uFE0F"
217
- },
218
- confirmation_required: {
219
- category: "\u9700\u8981\u786E\u8BA4",
220
- description: "\u9700\u8981\u4F60\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u64CD\u4F5C",
221
- requiresAction: true,
222
- emoji: "\u2705"
223
- },
224
- setup_test: {
225
- category: "\u6D4B\u8BD5\u901A\u77E5",
226
- description: "\u98DE\u4E66\u901A\u77E5\u529F\u80FD\u6D4B\u8BD5",
227
- requiresAction: false,
228
- emoji: "\u{1F9EA}"
229
- }
230
- };
231
- function getEventTitle(eventType) {
232
- const titles = {
233
- interaction_required: "\u9700\u8981\u4EA4\u4E92",
234
- permission_required: "\u9700\u8981\u6743\u9650",
235
- command_args_required: "\u7F3A\u5C11\u53C2\u6570",
236
- confirmation_required: "\u9700\u8981\u786E\u8BA4",
237
- session_idle: "\u4EFB\u52A1\u5B8C\u6210",
238
- question_asked: "\u8BF7\u505A\u9009\u62E9",
239
- setup_test: "\u6D4B\u8BD5\u901A\u77E5"
240
- };
241
- return titles[eventType];
242
- }
243
- var PERMISSION_TYPE_LABELS = {
244
- read: "\u8BFB\u53D6",
245
- write: "\u5199\u5165",
246
- execute: "\u6267\u884C",
247
- edit: "\u7F16\u8F91",
248
- bash: "\u547D\u4EE4\u884C",
249
- webfetch: "\u7F51\u7EDC\u8BF7\u6C42",
250
- external_directory: "\u5916\u90E8\u76EE\u5F55"
251
- };
252
- function extractActionDetails(eventPayload, originalEventType) {
253
- const details = [];
254
- if (!eventPayload) {
255
- return details;
256
- }
257
- if (typeof eventPayload !== "object" || eventPayload === null) {
258
- return details;
259
- }
260
- const payload = eventPayload;
261
- if (originalEventType === "permission.updated" || originalEventType === "permission.asked") {
262
- const permType = payload.type;
263
- const pattern = payload.pattern;
264
- const title = payload.title;
265
- if (title) {
266
- details.push(`- ${title}`);
267
- }
268
- if (permType) {
269
- const typeLabel = PERMISSION_TYPE_LABELS[permType] || permType;
270
- details.push(`- \u6743\u9650\u7C7B\u578B: ${typeLabel}`);
271
- }
272
- if (pattern) {
273
- if (Array.isArray(pattern)) {
274
- details.push(`- \u6D89\u53CA\u8DEF\u5F84:`);
275
- pattern.forEach((p) => {
276
- details.push(` - ${p}`);
277
- });
278
- } else {
279
- details.push(`- \u6D89\u53CA\u8DEF\u5F84: ${pattern}`);
280
- }
281
- }
282
- return details;
283
- }
284
- if (originalEventType === "tui.prompt.append") {
285
- const text = payload.text;
286
- if (text) {
287
- details.push(`- \u63D0\u793A\u5185\u5BB9: ${text}`);
288
- }
289
- return details;
290
- }
291
- if (originalEventType === "tui.command.execute") {
292
- const command = payload.command;
293
- if (command) {
294
- details.push(`- \u547D\u4EE4: ${command}`);
295
- }
296
- return details;
297
- }
298
- if (originalEventType === "tui.toast.show") {
299
- const title = payload.title;
300
- const message = payload.message;
301
- if (title) {
302
- details.push(`- \u6807\u9898: ${title}`);
303
- }
304
- if (message) {
305
- details.push(`- \u5185\u5BB9: ${message}`);
306
- }
307
- return details;
308
- }
309
- if (originalEventType === "session.status") {
310
- const status = payload.status;
311
- if (status) {
312
- const statusType = status.type;
313
- if (statusType === "idle") {
314
- details.push(`- \u72B6\u6001: \u7A7A\u95F2\uFF0C\u7B49\u5F85\u6307\u4EE4`);
315
- } else if (statusType === "busy") {
316
- details.push(`- \u72B6\u6001: \u5FD9\u788C\u4E2D`);
317
- } else if (statusType === "retry") {
318
- const attempt = status.attempt;
319
- const message = status.message;
320
- details.push(`- \u72B6\u6001: \u91CD\u8BD5\u4E2D (\u7B2C ${attempt || "?"} \u6B21)`);
321
- if (message) {
322
- details.push(`- \u539F\u56E0: ${message}`);
323
- }
324
- }
325
- }
326
- return details;
327
- }
328
- if (payload.options && Array.isArray(payload.options)) {
329
- const options = payload.options;
330
- if (options.length > 0) {
331
- details.push(`\u53EF\u9009\u65B9\u6848:`);
332
- options.forEach((option, index) => {
333
- const label = option.label || `\u9009\u9879 ${index + 1}`;
334
- const desc = option.description ? ` - ${option.description}` : "";
335
- details.push(` ${index + 1}. ${label}${desc}`);
336
- });
337
- }
338
- return details;
339
- }
340
- if (payload.prompt && typeof payload.prompt === "string") {
341
- details.push(`- \u63D0\u793A: ${payload.prompt}`);
342
- }
343
- if (payload.message && typeof payload.message === "string") {
344
- details.push(`- \u6D88\u606F: ${payload.message}`);
345
- }
346
- if (payload.title && typeof payload.title === "string" && details.length === 0) {
347
- details.push(`- ${payload.title}`);
348
- }
349
- if (payload.action && typeof payload.action === "string") {
350
- details.push(`- \u64CD\u4F5C: ${payload.action}`);
351
- }
352
- if (payload.args && Array.isArray(payload.args)) {
353
- const args = payload.args;
354
- if (args.length > 0) {
355
- details.push(`- \u53C2\u6570:`);
356
- args.forEach((arg) => {
357
- details.push(` - --${arg}`);
358
- });
359
- }
360
- }
361
- return details;
362
- }
363
- function buildHeader(context) {
364
- const { eventType } = context;
365
- const config = REASON_CONFIGS[eventType];
366
- const title = getEventTitle(eventType);
367
- return `${config.emoji} **${title}**`;
368
- }
369
- function buildEnvironment(context) {
370
- const { project, sessionTitle, sessionID, agentName } = context;
371
- const lines = [];
372
- lines.push("**\u{1F5A5}\uFE0F \u73AF\u5883**");
373
- if (project.hostname) {
374
- lines.push(`- \u4E3B\u673A: ${project.hostname}`);
375
- }
376
- lines.push(`- \u9879\u76EE: ${project.projectName}`);
377
- if (project.branch) {
378
- lines.push(`- \u5206\u652F: ${project.branch}`);
379
- }
380
- if (sessionTitle || sessionID) {
381
- const sessionLabel = sessionTitle || sessionID || "";
382
- lines.push(`- \u4F1A\u8BDD: ${sessionLabel}`);
383
- }
384
- if (agentName) {
385
- lines.push(`- Agent: ${agentName}`);
386
- }
387
- return lines.join("\n");
388
- }
389
- function buildReason(context) {
390
- const { eventType, eventPayload, originalEventType } = context;
391
- const config = REASON_CONFIGS[eventType];
392
- const lines = [];
393
- lines.push("**\u{1F4A1} \u8BF4\u660E**");
394
- lines.push(config.description);
395
- const actionDetails = extractActionDetails(eventPayload, originalEventType);
396
- if (actionDetails.length > 0) {
397
- lines.push("");
398
- actionDetails.forEach((detail) => lines.push(detail));
399
- }
400
- if (eventType === "confirmation_required") {
401
- lines.push("");
402
- lines.push("\u26A0\uFE0F \u8BF7\u8C28\u614E\u786E\u8BA4\u6B64\u64CD\u4F5C");
403
- }
404
- return lines.join("\n");
405
- }
406
- function buildWorkdir(context) {
407
- const { project } = context;
408
- const lines = [];
409
- lines.push("**\u{1F4C2} \u8DEF\u5F84**");
410
- lines.push(`- \u76EE\u5F55: ${project.workingDir}`);
411
- if (project.isGitRepo && project.repoUrl) {
412
- lines.push(`- \u4ED3\u5E93: ${project.repoUrl}`);
413
- }
414
- return lines.join("\n");
415
- }
416
- function buildTimestamp() {
417
- const now = /* @__PURE__ */ new Date();
418
- const timeStr = now.toLocaleString("zh-CN", {
419
- month: "2-digit",
420
- day: "2-digit",
421
- hour: "2-digit",
422
- minute: "2-digit"
423
- });
424
- return `\u23F0 ${timeStr}`;
425
- }
426
- var BeautifulMessageTemplate = class {
427
- buildTitle(context) {
428
- return buildHeader(context);
429
- }
430
- buildReason(context) {
431
- return buildReason(context);
432
- }
433
- buildProgress(context) {
434
- return buildEnvironment(context);
435
- }
436
- buildFullMessage(context) {
437
- const header = buildHeader(context);
438
- const environment = buildEnvironment(context);
439
- const reason = buildReason(context);
440
- const workdir = buildWorkdir(context);
441
- const timestamp = buildTimestamp();
442
- const sections = [
443
- header,
444
- "",
445
- environment,
446
- "",
447
- reason,
448
- "",
449
- workdir,
450
- "",
451
- timestamp
452
- ];
453
- return sections.join("\n");
454
- }
455
- };
456
- var DefaultMessageTemplate = class {
457
- buildTitle(context) {
458
- const { project, eventType } = context;
459
- const eventTitle = getEventTitle(eventType);
460
- let title = `\u{1F4E6} [${project.projectName}]`;
461
- if (project.branch) {
462
- title += ` ${project.branch}`;
463
- }
464
- if (project.hostname) {
465
- title += ` @${project.hostname}`;
466
- }
467
- title += ` | ${eventTitle}`;
468
- return title;
469
- }
470
- buildReason(context) {
471
- const { eventType, eventPayload, originalEventType } = context;
472
- const config = REASON_CONFIGS[eventType];
473
- const lines = [];
474
- lines.push(`\u{1F514} \u539F\u56E0\uFF1A${config.category}`);
475
- lines.push(config.description);
476
- const actionDetails = extractActionDetails(eventPayload, originalEventType);
477
- if (actionDetails.length > 0) {
478
- lines.push("");
479
- actionDetails.forEach((detail) => lines.push(detail));
480
- }
481
- if (eventType === "confirmation_required") {
482
- lines.push("");
483
- lines.push("\u26A0\uFE0F \u6B64\u64CD\u4F5C\u53EF\u80FD\u9700\u8981\u8C28\u614E\u786E\u8BA4\u3002");
484
- }
485
- return lines.join("\n");
486
- }
487
- buildProgress(context) {
488
- const { project, progress, sessionID, sessionTitle, agentName } = context;
489
- const lines = [];
490
- lines.push("\u{1F4CA} \u8FDB\u5EA6\u6458\u8981");
491
- lines.push(`\u2022 \u5DE5\u4F5C\u76EE\u5F55\uFF1A${project.workingDir}`);
492
- if (sessionTitle || sessionID) {
493
- const label = sessionTitle ?? sessionID ?? "";
494
- const suffix = sessionTitle && sessionID ? ` (${sessionID})` : "";
495
- lines.push(`\u2022 \u4F1A\u8BDD\uFF1A${label}${suffix}`);
496
- }
497
- if (agentName) {
498
- lines.push(`\u2022 Agent\uFF1A${agentName}`);
499
- }
500
- const progressText = formatProgressInfo(progress);
501
- if (progressText) {
502
- const progressLines = progressText.split("\n");
503
- progressLines.forEach((line) => {
504
- if (line.trim()) {
505
- lines.push(line);
506
- }
507
- });
508
- }
509
- if (project.isGitRepo && project.repoUrl) {
510
- lines.push(`\u2022 \u4ED3\u5E93\u5730\u5740\uFF1A${project.repoUrl}`);
511
- }
512
- return lines.join("\n");
513
- }
514
- buildFullMessage(context) {
515
- const title = this.buildTitle(context);
516
- const reason = this.buildReason(context);
517
- const progress = this.buildProgress(context);
518
- return `${title}
519
-
520
- ${reason}
521
-
522
- ${progress}`;
523
- }
524
- };
525
- function createMessageTemplate() {
526
- return new BeautifulMessageTemplate();
527
- }
528
- function getReasonConfig(eventType) {
529
- return REASON_CONFIGS[eventType];
530
- }
531
- async function buildMessageContext(eventType, eventPayload, originalEventType, directory, sessionContext) {
532
- const project = await extractProjectContext(directory || process.cwd());
533
- const progress = createProgressInfo(eventPayload, directory || process.cwd());
534
- return {
535
- project,
536
- progress,
537
- eventType,
538
- eventPayload,
539
- originalEventType,
540
- sessionID: sessionContext?.sessionID,
541
- sessionTitle: sessionContext?.sessionTitle,
542
- agentName: sessionContext?.agentName
543
- };
544
- }
545
- async function buildStructuredMessage(eventType, eventPayload, originalEventType, directory, sessionContext) {
546
- const context = await buildMessageContext(
547
- eventType,
548
- eventPayload,
549
- originalEventType,
550
- directory,
551
- sessionContext
552
- );
553
- const template = createMessageTemplate();
554
- return template.buildFullMessage(context);
555
- }
556
- export {
557
- BeautifulMessageTemplate,
558
- DefaultMessageTemplate,
559
- buildMessageContext,
560
- buildStructuredMessage,
561
- createMessageTemplate,
562
- getReasonConfig
563
- };
564
- //# sourceMappingURL=templates-PQN4V7CV.js.map