| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- import type { Plugin } from "@opencode-ai/plugin"
- interface WebhookConfig {
- url: string
- secret?: string
- enabled: boolean
- }
- export const StatusLightPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
- // 默认配置,可以通过环境变量或配置文件覆盖
- const config: WebhookConfig = {
- url: process.env.OPENCODE_WEBHOOK_URL || "http://localhost:8080/api/webhook",
- secret: process.env.OPENCODE_WEBHOOK_SECRET || "",
- enabled: process.env.OPENCODE_WEBHOOK_ENABLED !== "false",
- }
- console.log(`[StatusLight] Plugin initialized`)
- console.log(`[StatusLight] Webhook URL: ${config.url}`)
- console.log(`[StatusLight] Webhook enabled: ${config.enabled}`)
- // 发送webhook请求
- async function sendWebhook(eventType: string, data: any) {
- if (!config.enabled) {
- return
- }
- try {
- const payload = {
- type: eventType,
- timestamp: new Date().toISOString(),
- project: project?.name || "unknown",
- directory,
- worktree,
- data,
- }
- const headers: Record<string, string> = {
- "Content-Type": "application/json",
- }
- // 如果配置了密钥,添加签名头
- if (config.secret) {
- const encoder = new TextEncoder()
- const key = await crypto.subtle.importKey(
- "raw",
- encoder.encode(config.secret),
- { name: "HMAC", hash: "SHA-256" },
- false,
- ["sign"]
- )
- const signature = await crypto.subtle.sign(
- "HMAC",
- key,
- encoder.encode(JSON.stringify(payload))
- )
- const signatureArray = Array.from(new Uint8Array(signature))
- const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, "0")).join("")
- headers["X-Webhook-Signature"] = `sha256=${signatureHex}`
- }
- const response = await fetch(config.url, {
- method: "POST",
- headers,
- body: JSON.stringify(payload),
- })
- if (!response.ok) {
- console.error(`[StatusLight] Webhook failed: ${response.status} ${response.statusText}`)
- }
- } catch (error) {
- console.error(`[StatusLight] Webhook error:`, error)
- }
- }
- return {
- // 工具执行前
- "tool.execute.before": async (input, output) => {
- console.log(`[StatusLight] Tool executing: ${input.tool}`)
- await sendWebhook("tool.execute.before", {
- tool: input.tool,
- args: output.args,
- status: "running",
- })
- },
- // 工具执行后
- "tool.execute.after": async (input, output) => {
- console.log(`[StatusLight] Tool completed: ${input.tool}`)
- await sendWebhook("tool.execute.after", {
- tool: input.tool,
- args: output.args,
- result: output.result,
- status: "completed",
- })
- },
- // 会话状态变化
- "session.status": async (input, output) => {
- console.log(`[StatusLight] Session status: ${JSON.stringify(input)}`)
- await sendWebhook("session.status", {
- status: input,
- })
- },
- // 会话空闲
- "session.idle": async (input, output) => {
- console.log(`[StatusLight] Session idle`)
- await sendWebhook("session.idle", {
- status: "idle",
- })
- },
- // 会话错误
- "session.error": async (input, output) => {
- console.log(`[StatusLight] Session error: ${JSON.stringify(input)}`)
- await sendWebhook("session.error", {
- error: input,
- })
- },
- // 权限请求
- "permission.asked": async (input, output) => {
- console.log(`[StatusLight] Permission requested: ${JSON.stringify(input)}`)
- await sendWebhook("permission.asked", {
- permission: input,
- })
- },
- // 消息更新(包含工具状态)
- "message.part.updated": async (input, output) => {
- const part = input as any
- if (part.type === "tool") {
- console.log(`[StatusLight] Tool part updated: ${part.tool} - ${part.state?.status}`)
- await sendWebhook("message.part.updated", {
- part: {
- type: part.type,
- tool: part.tool,
- state: part.state,
- },
- })
- } else if (part.type === "reasoning") {
- console.log(`[StatusLight] Reasoning part updated`)
- await sendWebhook("message.part.updated", {
- part: {
- type: part.type,
- status: "reasoning",
- },
- })
- }
- },
- }
- }
|