Token导航 LogoToken导航TokenDH.com
研究检索操作浏览器github未标认证来源可访问许可证需确认审计异常

electrobun-rpc-patternsElectrobun RPC 模式

Agent Skill

electrobun-rpc-patterns 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

1,140

周安装

48

GitHub Stars

6

下载量

399
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:electrobun-rpc-patterns(Electrobun RPC 模式)
来源仓库:https://github.com/rajavijayach/electrobun-skills
仓库路径:skills/electrobun-rpc-patterns
安装命令:
npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-rpc-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-rpc-patterns

简介

electrobun-rpc-patterns 用于查找、检索和筛选相关信息,适合根据关键词快速定位候选结果。

  • 适用于 Electrobun 应用中类型安全和高性能 RPC 通信模式场景。
  • 提供共享类型定义、主进程处理器和网页视图通信等核心能力。
  • 安装命令:npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-rpc-patterns
  • 安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写操作。

SKILL.md

Electrobun RPC Patterns

Advanced patterns for type-safe, performant RPC communication in Electrobun.

Type-Safe RPC

Shared Types Definition

Create a shared types file that both main and webview can import:

src/types/rpc.ts:

// Main process handlers (exposed to webview)
export interface MainRpcHandlers {
  getUser(id: string): Promise<User>;
  saveFile(path: string, content: string): Promise<SaveResult>;
  listFiles(directory: string): Promise<FileInfo[]>;
  executeCommand(cmd: string, args: string[]): Promise<CommandResult>;
}

// Webview handlers (exposed to main)
export interface WebviewRpcHandlers {
  updateUI(data: UIUpdate): Promise<void>;
  showNotification(message: string, type: NotificationType): Promise<void>;
  refreshData(): Promise<void>;
}

export interface User {
  id: string;
  name: string;
  email: string;
}

export interface SaveResult {
  success: boolean;
  path?: string;
  error?: string;
}

export interface FileInfo {
  name: string;
  path: string;
  size: number;
  modified: Date;
}

export interface CommandResult {
  stdout: string;
  stderr: string;
  exitCode: number;
}

export interface UIUpdate {
  type: "data" | "status" | "error";
  payload: any;
}

export type NotificationType = "info" | "success" | "warning" | "error";

Type-Safe Main Process

src/bun/main.ts:

import { BrowserWindow } from "electrobun/bun";
import type { MainRpcHandlers, WebviewRpcHandlers } from "../types/rpc";

const win = new BrowserWindow({ /* ... */ });

// Define handlers with type checking
win.defineRpc<MainRpcHandlers>({
  handlers: {
    async getUser(id: string) {
      // Full type inference and checking
      const user = await database.users.findById(id);
      return {
        id: user.id,
        name: user.name,
        email: user.email,
      };
    },

    async saveFile(path: string, content: string) {
      try {
        await Bun.write(path, content);
        return { success: true, path };
      } catch (error) {
        return { success: false, error: error.message };
      }
    },

    async listFiles(directory: string) {
      const entries = await fs.readdir(directory, { withFileTypes: true });
      return entries.map(entry => ({
        name: entry.name,
        path: path.join(directory, entry.name),
        size: entry.size,
        modified: entry.mtime,
      }));
    },

    async executeCommand(cmd: string, args: string[]) {
      const proc = Bun.spawn([cmd, ...args]);
      const stdout = await new Response(proc.stdout).text();
      const stderr = await new Response(proc.stderr).text();
      const exitCode = await proc.exited;

      return { stdout, stderr, exitCode };
    }
  }
});

// Call webview methods with types
const webviewRpc = win.rpc as WebviewRpcHandlers;
await webviewRpc.updateUI({ type: "data", payload: data });
await webviewRpc.showNotification("File saved", "success");

Type-Safe Webview

src/views/mainview/index.ts:

import { Electroview } from "electrobun/browser";
import type { MainRpcHandlers, WebviewRpcHandlers } from "../../types/rpc";

const electroview = new Electroview();

// Type-safe RPC calls to main
const mainRpc = electroview.rpc as MainRpcHandlers;

async function loadUser(id: string) {
  // Full IntelliSense and type checking
  const user = await mainRpc.getUser(id);
  console.log(user.name, user.email);
}

async function saveDocument(path: string, content: string) {
  const result = await mainRpc.saveFile(path, content);
  if (result.success) {
    await mainRpc.showNotification("File saved successfully", "success");
  } else {
    console.error("Save failed:", result.error);
  }
}

// Define webview handlers with types
electroview.defineRpc<WebviewRpcHandlers>({
  handlers: {
    async updateUI(data) {
      if (data.type === "data") {
        renderData(data.payload);
      } else if (data.type === "error") {
        showError(data.payload);
      }
    },

    async showNotification(message, type) {
      const notification = document.createElement("div");
      notification.className = `notification ${type}`;
      notification.textContent = message;
      document.body.appendChild(notification);

      setTimeout(() => notification.remove(), 3000);
    },

    async refreshData() {
      const data = await mainRpc.getData();
      renderData(data);
    }
  }
});

Error Handling

Robust Error Handling Pattern

// Custom error types
class RpcError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: any
  ) {
    super(message);
    this.name = "RpcError";
  }
}

class ValidationError extends RpcError {
  constructor(message: string, details?: any) {
    super(message, "VALIDATION_ERROR", details);
  }
}

class NotFoundError extends RpcError {
  constructor(resource: string) {
    super(`${resource} not found`, "NOT_FOUND");
  }
}

// Main process with error handling
win.defineRpc({
  handlers: {
    async getUser(id: string) {
      // Validate input
      if (!id || typeof id !== "string") {
        throw new ValidationError("Invalid user ID", { id });
      }

      const user = await database.users.findById(id);

      if (!user) {
        throw new NotFoundError("User");
      }

      return user;
    },

    async saveFile(path: string, content: string) {
      try {
        // Validate path
        if (path.includes("..")) {
          throw new ValidationError("Invalid file path");
        }

        await Bun.write(path, content);
        return { success: true };
      } catch (error) {
        if (error instanceof RpcError) {
          throw error;
        }

        // Wrap unknown errors
        throw new RpcError(
          "Failed to save file",
          "SAVE_ERROR",
          { originalError: error.message }
        );
      }
    }
  }
});

// Webview with error handling
async function loadUser(id: string) {
  try {
    const user = await mainRpc.getUser(id);
    displayUser(user);
  } catch (error) {
    if (error.code === "NOT_FOUND") {
      showError("User not found");
    } else if (error.code === "VALIDATION_ERROR") {
      showError("Invalid input");
    } else {
      showError("Failed to load user");
      console.error(error);
    }
  }
}

Retry Strategy

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  delay = 1000
): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries - 1) {
        throw error;
      }

      console.warn(`Attempt ${attempt + 1} failed, retrying...`);
      await new Promise(resolve => setTimeout(resolve, delay * (attempt + 1)));
    }
  }

  throw new Error("Max retries exceeded");
}

// Usage in webview
const user = await withRetry(() => mainRpc.getUser(id));

Performance Optimization

Batching Requests

class BatchRpc {
  private batch: Array<{
    method: string;
    args: any[];
    resolve: (value: any) => void;
    reject: (error: any) => void;
  }> = [];

  private batchTimeout: Timer | null = null;
  private readonly batchDelay = 10; // ms

  constructor(private rpc: any) {}

  async call(method: string, ...args: any[]) {
    return new Promise((resolve, reject) => {
      this.batch.push({ method, args, resolve, reject });

      if (!this.batchTimeout) {
        this.batchTimeout = setTimeout(() => this.flush(), this.batchDelay);
      }
    });
  }

  private async flush() {
    if (this.batch.length === 0) return;

    const currentBatch = this.batch.splice(0);
    this.batchTimeout = null;

    try {
      const results = await this.rpc.executeBatch(
        currentBatch.map(({ method, args }) => ({ method, args }))
      );

      currentBatch.forEach((item, index) => {
        if (results[index].error) {
          item.reject(results[index].error);
        } else {
          item.resolve(results[index].result);
        }
      });
    } catch (error) {
      currentBatch.forEach(item => item.reject(error));
    }
  }
}

// Main process batch handler
win.defineRpc({
  handlers: {
    async executeBatch(requests: Array<{ method: string; args: any[] }>) {
      return await Promise.all(
        requests.map(async ({ method, args }) => {
          try {
            const result = await this.handlers[method](...args);
            return { result };
          } catch (error) {
            return { error: error.message };
          }
        })
      );
    }
  }
});

// Usage
const batchRpc = new BatchRpc(mainRpc);
const [user1, user2, user3] = await Promise.all([
  batchRpc.call("getUser", "1"),
  batchRpc.call("getUser", "2"),
  batchRpc.call("getUser", "3"),
]);

Caching Layer

class CachedRpc {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private readonly ttl = 60000; // 1 minute

  constructor(private rpc: any) {}

  async call(method: string, ...args: any[]) {
    const cacheKey = `${method}:${JSON.stringify(args)}`;
    const cached = this.cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }

    const result = await this.rpc[method](...args);
    this.cache.set(cacheKey, { data: result, timestamp: Date.now() });

    return result;
  }

  invalidate(method?: string) {
    if (method) {
      // Invalidate specific method
      for (const [key] of this.cache) {
        if (key.startsWith(method + ":")) {
          this.cache.delete(key);
        }
      }
    } else {
      // Invalidate all
      this.cache.clear();
    }
  }
}

// Usage
const cachedRpc = new CachedRpc(mainRpc);
const user = await cachedRpc.call("getUser", id); // Fetches
const user2 = await cachedRpc.call("getUser", id); // Cached

Streaming Data

Chunk-based Streaming

// Main process - stream large data in chunks
win.defineRpc({
  handlers: {
    async* streamFile(path: string, chunkSize = 1024 * 64) {
      const file = Bun.file(path);
      const reader = file.stream().getReader();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        yield {
          chunk: Array.from(value),
          progress: (file.size - reader.closed) / file.size
        };
      }
    },

    async streamLogs(processId: string) {
      const process = processes.get(processId);
      const reader = process.stdout.getReader();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        yield { line: new TextDecoder().decode(value) };
      }
    }
  }
});

// Webview - consume stream
for await (const chunk of mainRpc.streamFile("/large/file.dat")) {
  processChunk(chunk.chunk);
  updateProgress(chunk.progress);
}

Event-Based Updates

// Main process - emit events
class DataService {
  private listeners = new Set<(data: any) => void>();

  subscribe(callback: (data: any) => void) {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback);
  }

  emit(data: any) {
    this.listeners.forEach(cb => cb(data));
  }

  async watch(path: string) {
    const watcher = fs.watch(path);

    for await (const event of watcher) {
      this.emit({ type: "file-change", path, event });
    }
  }
}

const dataService = new DataService();

win.defineRpc({
  handlers: {
    async subscribeToUpdates() {
      const unsubscribe = dataService.subscribe(async (data) => {
        await win.rpc.handleUpdate(data);
      });

      return { success: true };
    }
  }
});

// Webview - handle updates
electroview.defineRpc({
  handlers: {
    async handleUpdate(data: any) {
      console.log("Received update:", data);
      updateUI(data);
    }
  }
});

// Subscribe
await mainRpc.subscribeToUpdates();

Advanced Patterns

Request/Response Correlation

class CorrelatedRpc {
  private pending = new Map<string, {
    resolve: (value: any) => void;
    reject: (error: any) => void;
    timeout: Timer;
  }>();

  private requestId = 0;

  constructor(private electroview: any) {
    electroview.defineRpc({
      handlers: {
        handleResponse: async (id: string, result: any, error: any) => {
          const pending = this.pending.get(id);
          if (!pending) return;

          clearTimeout(pending.timeout);
          this.pending.delete(id);

          if (error) {
            pending.reject(error);
          } else {
            pending.resolve(result);
          }
        }
      }
    });
  }

  async call(method: string, ...args: any[]) {
    const id = `req-${++this.requestId}`;

    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        this.pending.delete(id);
        reject(new Error("Request timeout"));
      }, 30000);

      this.pending.set(id, { resolve, reject, timeout });

      this.electroview.rpc.sendRequest(id, method, args);
    });
  }
}

Bi-directional Sync

class DataSync {
  private localData: any = {};
  private syncing = false;

  constructor(
    private mainRpc: any,
    private electroview: any
  ) {
    this.setupHandlers();
  }

  private setupHandlers() {
    this.electroview.defineRpc({
      handlers: {
        syncFromMain: async (data: any) => {
          if (!this.syncing) {
            this.localData = data;
            this.render();
          }
        }
      }
    });
  }

  async syncToMain() {
    this.syncing = true;
    try {
      const serverData = await this.mainRpc.updateData(this.localData);
      this.localData = serverData;
      this.render();
    } finally {
      this.syncing = false;
    }
  }

  async pull() {
    const serverData = await this.mainRpc.getData();
    this.localData = serverData;
    this.render();
  }

  private render() {
    // Update UI with localData
  }
}

Resources

For more on Electrobun:

  • Core skill: electrobun - Basic RPC setup
  • Window management: electrobun-window-management - Window-to-window RPC
  • Debugging: electrobun-debugging - Debug RPC issues

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

35.62%
按下载量换算142

Claude

27.9%
按下载量换算111

Cursor

17.67%
按下载量换算71

Gemini CLI

9.58%
按下载量换算38

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

可疑

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills