首页
/ pi-mono扩展开发指南:构建自定义生态与外部服务集成

pi-mono扩展开发指南:构建自定义生态与外部服务集成

2026-03-17 04:28:04作者:申梦珏Efrain

第一章:扩展开发基础架构

技术要点提示

  • 扩展系统核心概念:Extensions作为功能扩展的统一接口
  • 模块化文件结构:遵循特定目录组织实现自动发现
  • 工具注册机制:静态声明与动态加载的双重支持
  • 上下文访问模型:通过ToolContext实现功能交互
  • 事件驱动架构:基于事件总线的扩展间通信

1.1 扩展系统架构解析

pi-mono的扩展系统采用插件化架构设计,允许开发者通过统一接口扩展核心功能。该系统基于TypeScript构建,提供类型安全的开发体验,同时支持动态加载与热更新。扩展可以添加新工具、修改UI行为、集成外部服务,或改变agent的决策逻辑。

扩展系统架构

图1:pi-mono交互式模式界面,展示了扩展加载后的功能界面

扩展系统主要由以下组件构成:

  • 扩展加载器:负责发现、验证和加载扩展
  • 上下文对象:提供扩展与核心系统的交互接口
  • 事件总线:实现扩展间的松耦合通信
  • 工具注册表:管理所有可用工具的元数据

1.2 从零构建扩展:从文件组织到功能实现

pi-mono扩展采用标准化的文件结构,确保系统能够自动发现并正确加载:

extensions/
  my-extension/           # 扩展根目录
    index.ts              # 扩展入口文件
    package.json          # 扩展元数据
    src/                  # 源代码目录
      tools/              # 工具定义
      components/         # UI组件
      utils/              # 辅助函数
    test/                 # 测试代码
    README.md             # 扩展文档

以下是一个基础扩展的实现示例,创建一个简单的天气查询工具:

// index.ts - 扩展入口文件
import { Extension, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { createWeatherTool } from "./src/tools/weather-tool";

export default function activate(ctx: ExtensionContext) {
  // 注册工具
  ctx.tools.register(createWeatherTool());
  
  // 注册事件监听器
  ctx.events.on("session:started", () => {
    console.log("Weather extension activated");
  });
  
  return {
    // 扩展提供的API
    getWeatherHistory: () => {
      // 实现历史查询功能
    }
  };
}

// 可选的停用函数
export function deactivate(ctx: ExtensionContext) {
  // 清理资源
  ctx.tools.unregister("weather");
}

工具实现代码:

// src/tools/weather-tool.ts
import { Tool, ToolContext } from "@mariozechner/pi-coding-agent";

export function createWeatherTool(): Tool {
  return {
    name: "weather",
    description: "获取指定城市的天气信息",
    parameters: {
      type: "object",
      properties: {
        city: { 
          type: "string", 
          description: "城市名称,如'北京'或'New York'" 
        },
        units: {
          type: "string",
          enum: ["celsius", "fahrenheit"],
          default: "celsius",
          description: "温度单位"
        }
      },
      required: ["city"]
    },
    async execute(ctx: ToolContext, params) {
      // 工具实现逻辑
      ctx.ui.showStatus(`正在查询${params.city}的天气...`);
      
      // 实际实现将在这里调用外部API
      return `[模拟] ${params.city} 当前温度: 22°C, 晴朗`;
    }
  };
}

1.3 扩展注册与发现机制

pi-mono提供多种扩展注册方式,适应不同的开发和部署场景:

  1. 自动发现:放置在以下目录的扩展会被自动加载

    • 用户目录:~/.pi/agent/extensions/
    • 项目目录:./extensions/
    • 全局目录:/usr/local/share/pi/extensions/
  2. 手动注册:通过命令行参数指定扩展

    pi --extension ./path/to/my-extension
    
  3. 编程方式:在代码中动态注册

    import { loadExtension } from "@mariozechner/pi-coding-agent";
    
    async function setup() {
      const extension = await loadExtension("./my-extension");
      await extension.activate(ctx);
    }
    

1.4 常见问题

Q1: 扩展加载失败如何排查?
A1: 检查以下几点:确保package.json中的"main"字段指向正确的入口文件;验证扩展是否导出activate函数;查看日志文件~/.pi/agent/logs/extension-loader.log

Q2: 如何确保扩展之间的兼容性?
A2: 在package.json中声明扩展依赖和兼容版本:

{
  "pi": {
    "compatibility": {
      "pi-mono": ">=0.9.3 <1.0.0",
      "extensions": {
        "core-utils": "~1.2.0"
      }
    }
  }
}

Q3: 能否在扩展中覆盖内置功能?
A3: 可以通过"override"机制替换内置工具,但建议谨慎使用。使用ctx.tools.register(tool, { override: true })可以覆盖同名工具。

第二章:外部服务集成策略

技术要点提示

  • 认证机制选择:API密钥、OAuth2.0与令牌管理
  • 服务通信模式:REST、WebSocket与事件流处理
  • 错误处理策略:重试机制与降级方案
  • 数据转换层:请求/响应的标准化处理
  • 缓存策略:减少API调用与提升响应速度

2.1 API认证机制详解

pi-mono提供多种安全的API认证方式,适应不同服务的需求:

认证方式 适用场景 安全级别 实现复杂度
API密钥 服务器端服务、内部工具
OAuth2.0 第三方服务、用户授权
令牌存储 频繁访问的服务 中高
环境变量 开发环境、CI/CD

以下是实现OAuth2.0认证的示例代码:

// src/auth/weather-oauth.ts
import { OAuthProvider, OAuthSession } from "@mariozechner/pi-ai";

export class WeatherOAuthProvider extends OAuthProvider {
  constructor() {
    super({
      providerId: "weather-api",
      authorizationEndpoint: "https://api.weather.com/oauth/authorize",
      tokenEndpoint: "https://api.weather.com/oauth/token",
      clientId: "YOUR_CLIENT_ID",
      scopes: ["read:weather", "read:forecast"]
    });
  }
  
  async getAccessToken(): Promise<string> {
    // 尝试从缓存获取
    const cachedSession = await this.loadSession();
    if (cachedSession && !this.isExpired(cachedSession)) {
      return cachedSession.accessToken;
    }
    
    // 如需刷新令牌
    if (cachedSession?.refreshToken) {
      try {
        const newSession = await this.refreshToken(cachedSession.refreshToken);
        await this.saveSession(newSession);
        return newSession.accessToken;
      } catch (error) {
        console.warn("令牌刷新失败,需要重新授权");
      }
    }
    
    // 启动授权流程
    const session = await this.startAuthorizationFlow();
    await this.saveSession(session);
    return session.accessToken;
  }
}

在工具中使用认证服务:

// 在工具execute方法中
async execute(ctx: ToolContext, params) {
  // 获取OAuth提供器
  const oauthProvider = new WeatherOAuthProvider();
  const accessToken = await oauthProvider.getAccessToken();
  
  // 调用API
  const response = await fetch(
    `https://api.weather.com/v1/current.json?q=${params.city}`,
    {
      headers: {
        "Authorization": `Bearer ${accessToken}`
      }
    }
  );
  
  // 处理响应...
}

2.2 服务集成模式与实现

根据外部服务的特性,pi-mono支持多种集成模式:

2.2.1 REST API集成

最常见的集成方式,适用于大多数第三方服务:

// src/services/weather-rest-client.ts
import { HttpClient, ApiError } from "@mariozechner/pi-coding-agent";

export class WeatherRestClient {
  private client: HttpClient;
  
  constructor(private apiKey: string) {
    this.client = new HttpClient({
      baseUrl: "https://api.weather.com/v1",
      timeout: 10000,
      headers: {
        "Accept": "application/json",
        "User-Agent": "pi-mono-extension/1.0"
      }
    });
  }
  
  async getCurrentWeather(city: string, units: "celsius" | "fahrenheit" = "celsius"): Promise<WeatherData> {
    try {
      return await this.client.get("/current.json", {
        params: {
          q: city,
          units: units === "celsius" ? "m" : "f",
          key: this.apiKey
        }
      });
    } catch (error) {
      if (error instanceof ApiError) {
        if (error.status === 404) {
          throw new Error(`未找到城市: ${city}`);
        } else if (error.status === 401) {
          throw new Error("API密钥无效或已过期");
        }
      }
      throw new Error(`获取天气失败: ${error.message}`);
    }
  }
}

// 类型定义
interface WeatherData {
  location: {
    name: string;
    region: string;
    country: string;
  };
  current: {
    temp_c: number;
    temp_f: number;
    condition: {
      text: string;
      icon: string;
    };
    humidity: number;
    wind_kph: number;
  };
}

2.2.2 实时数据流集成

对于需要实时数据的场景,如股票行情、实时日志等:

// src/services/realtime-data-client.ts
import { EventEmitter } from "events";

export class RealtimeDataClient extends EventEmitter {
  private socket: WebSocket;
  private reconnectTimeout: NodeJS.Timeout;
  
  constructor(private url: string) {
    super();
    this.connect();
  }
  
  connect() {
    this.socket = new WebSocket(this.url);
    
    this.socket.onopen = () => {
      this.emit("connected");
      this.clearReconnectTimeout();
    };
    
    this.socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.emit("data", data);
    };
    
    this.socket.onerror = (error) => {
      this.emit("error", error);
      this.scheduleReconnect();
    };
    
    this.socket.onclose = () => {
      this.emit("disconnected");
      this.scheduleReconnect();
    };
  }
  
  private scheduleReconnect() {
    this.clearReconnectTimeout();
    this.reconnectTimeout = setTimeout(() => this.connect(), 5000);
  }
  
  private clearReconnectTimeout() {
    if (this.reconnectTimeout) {
      clearTimeout(this.reconnectTimeout);
    }
  }
  
  subscribe(topic: string) {
    this.socket.send(JSON.stringify({
      action: "subscribe",
      topic
    }));
  }
  
  unsubscribe(topic: string) {
    this.socket.send(JSON.stringify({
      action: "unsubscribe",
      topic
    }));
  }
  
  close() {
    this.clearReconnectTimeout();
    this.socket.close();
  }
}

2.3 数据转换与标准化

外部服务返回的数据格式往往各不相同,实现统一的数据转换层可以提高扩展的可维护性:

// src/transformers/weather-transformer.ts
import { WeatherData, WeatherCondition, NormalizedWeather } from "../types";

export class WeatherTransformer {
  static toNormalizedFormat(rawData: WeatherData): NormalizedWeather {
    // 映射不同API的天气状况到统一的枚举
    const conditionMap: Record<string, WeatherCondition> = {
      "sunny": "clear",
      "partly cloudy": "partly-cloudy",
      "cloudy": "cloudy",
      "rain": "rain",
      "snow": "snow",
      "thunder": "thunderstorm"
    };
    
    const rawCondition = rawData.current.condition.text.toLowerCase();
    const normalizedCondition = conditionMap[rawCondition] || "unknown";
    
    return {
      location: {
        name: rawData.location.name,
        region: rawData.location.region,
        country: rawData.location.country,
        coordinates: {
          // 如果API提供经纬度
          latitude: rawData.location.lat || 0,
          longitude: rawData.location.lon || 0
        }
      },
      current: {
        temperature: {
          celsius: rawData.current.temp_c,
          fahrenheit: rawData.current.temp_f
        },
        condition: normalizedCondition,
        humidity: rawData.current.humidity,
        windSpeed: {
          kph: rawData.current.wind_kph,
          mph: rawData.current.wind_kph * 0.621371
        },
        timestamp: new Date().toISOString()
      },
      source: "weather-api"
    };
  }
}

2.4 常见问题

Q1: 如何处理API调用频率限制?
A1: 实现请求限流机制:

import { RateLimiter } from "limiter";

// 创建限流器:每分钟最多60个请求
const limiter = new RateLimiter({ tokensPerInterval: 60, interval: "minute" });

async function throttledApiCall() {
  // 等待令牌可用
  await limiter.removeTokens(1);
  // 执行API调用
  return fetchApiData();
}

Q2: 如何确保API密钥安全?
A2: 使用pi-mono的安全存储API:

// 存储密钥
await ctx.secrets.set("weather-api-key", "your-secret-key");

// 检索密钥
const apiKey = await ctx.secrets.get("weather-api-key");

Q3: 外部服务不可用时如何处理?
A3: 实现降级策略和缓存机制:

async function getWeatherWithFallback(city: string) {
  try {
    // 尝试获取实时数据
    const data = await weatherClient.getCurrentWeather(city);
    // 更新缓存
    await ctx.cache.set(`weather:${city}`, data, { ttl: 3600 });
    return data;
  } catch (error) {
    // 尝试从缓存获取
    const cached = await ctx.cache.get(`weather:${city}`);
    if (cached) {
      ctx.ui.showWarning("使用缓存数据,可能不是最新的");
      return cached;
    }
    // 完全失败时返回默认数据或错误
    throw new Error("无法获取天气数据,服务暂时不可用");
  }
}

第三章:扩展生态构建与最佳实践

技术要点提示

  • 扩展打包与分发:npm包结构与版本管理
  • 性能优化策略:内存管理与资源释放
  • 安全最佳实践:输入验证与权限控制
  • 测试策略:单元测试与集成测试
  • 文档规范:API文档与使用示例

3.1 扩展打包与分发

将扩展打包为npm包是分享和分发的最佳方式。以下是一个扩展的package.json示例:

{
  "name": "pi-weather-extension",
  "version": "1.0.0",
  "description": "Weather extension for pi-mono",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "watch": "tsc --watch",
    "test": "vitest run",
    "lint": "biome check src",
    "prepare": "npm run build"
  },
  "keywords": ["pi-mono", "extension", "weather"],
  "author": "",
  "license": "MIT",
  "pi": {
    "type": "extension",
    "displayName": "Weather Tools",
    "description": "Adds weather查询 capabilities to pi-mono",
    "icon": "icon.png",
    "categories": ["tools", "external-services"],
    "compatibility": {
      "pi-mono": ">=0.9.3"
    }
  },
  "dependencies": {
    "@mariozechner/pi-coding-agent": "^0.9.3"
  },
  "devDependencies": {
    "typescript": "^5.2.2",
    "vitest": "^1.2.2",
    "@biomejs/biome": "^1.4.1"
  }
}

打包与发布流程:

# 构建扩展
npm run build

# 本地测试安装
npm pack
pi --extension pi-weather-extension-1.0.0.tgz

# 发布到npm
npm publish

3.2 性能优化策略

扩展性能直接影响pi-mono的整体体验,以下是关键优化点:

3.2.1 内存管理

  • 避免内存泄漏:及时清理事件监听器

    // 不好的做法 - 可能导致内存泄漏
    ctx.events.on("data", (data) => {
      processData(data);
    });
    
    // 好的做法 - 提供清理机制
    function activate(ctx: ExtensionContext) {
      const handler = (data) => processData(data);
      ctx.events.on("data", handler);
      
      return {
        deactivate() {
          ctx.events.off("data", handler);
        }
      };
    }
    
  • 大型数据处理:使用流处理代替一次性加载

    // 处理大型JSON文件
    import { createReadStream } from "fs";
    import { createInterface } from "readline";
    
    async function processLargeDataFile(path: string) {
      const stream = createReadStream(path, { encoding: "utf-8" });
      const rl = createInterface({ input: stream });
      
      for await (const line of rl) {
        // 逐行处理
        const data = JSON.parse(line);
        processRecord(data);
      }
    }
    

3.2.2 执行效率

  • 缓存频繁计算结果

    import { Cache } from "@mariozechner/pi-coding-agent";
    
    const cache = new Cache({ ttl: 300 }); // 5分钟缓存
    
    async function expensiveCalculation(input: string) {
      const cacheKey = `calc:${input}`;
      const cached = await cache.get(cacheKey);
      
      if (cached) return cached;
      
      // 执行耗时计算
      const result = await performExpensiveCalculation(input);
      
      // 存入缓存
      await cache.set(cacheKey, result);
      return result;
    }
    
  • 并行处理

    // 使用Promise.all处理并行任务
    async function fetchMultipleResources(urls: string[]) {
      // 限制并发数为5
      const concurrency = 5;
      const results = [];
      
      for (let i = 0; i < urls.length; i += concurrency) {
        const batch = urls.slice(i, i + concurrency);
        const batchResults = await Promise.all(
          batch.map(url => fetchResource(url))
        );
        results.push(...batchResults);
      }
      
      return results;
    }
    

3.3 安全最佳实践

扩展开发必须重视安全问题,特别是处理用户数据和系统资源时:

3.3.1 输入验证

所有用户输入和外部数据必须经过严格验证:

import { z } from "zod";

// 定义验证模式
const WeatherParamsSchema = z.object({
  city: z.string().min(1).max(100),
  units: z.enum(["celsius", "fahrenheit"]).optional()
});

// 在工具中使用验证
async execute(ctx: ToolContext, params) {
  // 验证输入
  const result = WeatherParamsSchema.safeParse(params);
  if (!result.success) {
    throw new Error(`参数验证失败: ${result.error.message}`);
  }
  
  const validatedParams = result.data;
  // 继续处理...
}

3.3.2 权限控制

实现细粒度的权限检查:

// 检查是否有权限执行文件操作
async function writeToFile(ctx: ToolContext, path: string, content: string) {
  // 检查路径是否在允许的范围内
  if (!ctx.security.isPathAllowed(path)) {
    throw new Error(`没有权限写入文件: ${path}`);
  }
  
  // 检查是否有写入权限
  if (!await ctx.security.hasPermission("file:write")) {
    throw new Error("没有文件写入权限,请联系管理员");
  }
  
  // 执行写入操作
  return ctx.fs.writeFile(path, content);
}

3.4 测试策略

完善的测试确保扩展质量和兼容性:

3.4.1 单元测试

使用vitest测试工具功能:

// test/unit/weather-tool.test.ts
import { describe, it, expect, vi } from "vitest";
import { createWeatherTool } from "../../src/tools/weather-tool";

describe("Weather Tool", () => {
  it("should validate parameters correctly", async () => {
    const tool = createWeatherTool();
    const ctx = {
      ui: { showStatus: vi.fn() }
    } as any;
    
    // 测试缺少必填参数
    await expect(tool.execute(ctx, {})).rejects.toThrow("参数验证失败");
    
    // 测试有效参数
    const result = await tool.execute(ctx, { city: "北京" });
    expect(result).toContain("北京");
  });
});

3.4.2 集成测试

测试扩展与pi-mono核心系统的交互:

// test/integration/extension.test.ts
import { describe, it, expect, vi, beforeAll, afterAll } from "vitest";
import { ExtensionContext, createTestContext } from "@mariozechner/pi-coding-agent/testing";
import activate from "../../src/index";

describe("Weather Extension Integration", () => {
  let ctx: ExtensionContext;
  
  beforeAll(async () => {
    // 创建测试上下文
    ctx = await createTestContext();
    // 激活扩展
    await activate(ctx);
  });
  
  afterAll(async () => {
    // 清理
    await ctx.dispose();
  });
  
  it("should register weather tool", () => {
    const tools = ctx.tools.getAll();
    const weatherTool = tools.find(t => t.name === "weather");
    expect(weatherTool).toBeDefined();
  });
  
  it("should execute weather tool correctly", async () => {
    const tools = ctx.tools.getAll();
    const weatherTool = tools.find(t => t.name === "weather");
    
    const result = await weatherTool.execute(ctx, { city: "上海" });
    expect(result).toContain("上海");
  });
});

3.5 常见问题

Q1: 如何处理不同pi-mono版本的兼容性?
A1: 实现版本适配层:

// src/compatibility.ts
import { version } from "@mariozechner/pi-coding-agent";
import semver from "semver";

export function getApiClient() {
  if (semver.gte(version, "1.0.0")) {
    return new NewApiClient();
  } else {
    return new LegacyApiClient();
  }
}

Q2: 扩展如何存储用户配置?
A2: 使用设置API:

// 存储配置
await ctx.settings.set("weather.defaultCity", "Beijing");
await ctx.settings.set("weather.units", "celsius");

// 读取配置
const defaultCity = await ctx.settings.get("weather.defaultCity", "Shanghai");
const units = await ctx.settings.get("weather.units", "celsius");

Q3: 如何为扩展提供用户界面?
A3: 使用TUI组件系统:

// src/components/weather-display.ts
import { Box, Text, Widget } from "@mariozechner/pi-tui";

export function createWeatherWidget(data: NormalizedWeather): Widget {
  return Box({
    title: `天气 - ${data.location.name}`,
    border: "line",
    children: [
      Text(`温度: ${data.current.temperature.celsius}°C`),
      Text(`状况: ${data.current.condition}`),
      Text(`湿度: ${data.current.humidity}%`),
      Text(`风速: ${data.current.windSpeed.kph} km/h`)
    ]
  });
}

// 在扩展中注册
ctx.ui.registerWidget("weather", createWeatherWidget);

第四章:扩展开发检查清单与资源导航

4.1 扩展开发检查清单

开发扩展时,使用以下清单确保质量和兼容性:

功能完整性

  • [ ] 所有工具实现完整的参数验证
  • [ ] 错误处理覆盖所有可能的异常情况
  • [ ] 提供清晰的错误消息和用户指导
  • [ ] 实现必要的默认值和回退机制

性能与安全

  • [ ] 验证所有用户输入和外部数据
  • [ ] 实现适当的缓存策略减少API调用
  • [ ] 清理所有事件监听器避免内存泄漏
  • [ ] 不存储敏感信息在代码或日志中
  • [ ] 限制并发请求数量防止过载

可维护性

  • [ ] 代码遵循项目的代码风格指南
  • [ ] 关键功能有单元测试覆盖
  • [ ] 提供完整的JSDoc注释
  • [ ] 文档包含安装、配置和使用说明
  • [ ] 声明明确的版本依赖和兼容性

发布准备

  • [ ] 构建过程生成优化的生产代码
  • [ ] 包含必要的元数据在package.json中
  • [ ] 测试与目标pi-mono版本的兼容性
  • [ ] 准备变更日志记录功能和修复

4.2 资源导航

官方文档

示例扩展

开发工具

社区资源

  • 扩展注册表:packages/coding-agent/docs/extensions-registry.md
  • 常见问题解答:packages/coding-agent/docs/faq.md
  • 贡献指南:CONTRIBUTING.md

4.3 高级扩展示例

以下是一个完整的高级扩展示例,展示了前面讨论的多种技术:

// 完整的天气扩展实现
import { Extension, ExtensionContext, Tool, ToolContext } from "@mariozechner/pi-coding-agent";
import { WeatherRestClient } from "./src/services/weather-rest-client";
import { WeatherTransformer } from "./src/transformers/weather-transformer";
import { WeatherOAuthProvider } from "./src/auth/weather-oauth";
import { createWeatherWidget } from "./src/components/weather-display";
import { z } from "zod";

// 参数验证模式
const WeatherParamsSchema = z.object({
  city: z.string().min(1).max(100),
  units: z.enum(["celsius", "fahrenheit"]).default("celsius")
});

// 工具实现
function createWeatherTool(): Tool {
  return {
    name: "weather",
    description: "获取指定城市的天气信息",
    parameters: WeatherParamsSchema.shape,
    async execute(ctx: ToolContext, params) {
      // 验证参数
      const result = WeatherParamsSchema.safeParse(params);
      if (!result.success) {
        throw new Error(`参数错误: ${result.error.message}`);
      }
      const validatedParams = result.data;
      
      // 显示状态
      ctx.ui.showStatus(`正在查询${validatedParams.city}的天气...`);
      
      try {
        // 获取认证令牌
        const oauthProvider = new WeatherOAuthProvider();
        const accessToken = await oauthProvider.getAccessToken();
        
        // 创建API客户端
        const client = new WeatherRestClient(accessToken);
        
        // 调用API
        const rawData = await client.getCurrentWeather(
          validatedParams.city,
          validatedParams.units
        );
        
        // 转换数据格式
        const normalizedData = WeatherTransformer.toNormalizedFormat(rawData);
        
        // 缓存结果
        await ctx.cache.set(
          `weather:${validatedParams.city}:${validatedParams.units}`,
          normalizedData,
          { ttl: 3600 } // 缓存1小时
        );
        
        // 返回格式化结果
        return `当前${normalizedData.location.name}天气: 
  温度: ${normalizedData.current.temperature.celsius}°C
  状况: ${normalizedData.current.condition}
  湿度: ${normalizedData.current.humidity}%
  风速: ${normalizedData.current.windSpeed.kph} km/h`;
        
      } catch (error) {
        // 尝试从缓存获取
        const cached = await ctx.cache.get(
          `weather:${validatedParams.city}:${validatedParams.units}`
        );
        
        if (cached) {
          ctx.ui.showWarning("使用缓存数据,可能不是最新的");
          return `[缓存] 当前${cached.location.name}天气: 
  温度: ${cached.current.temperature.celsius}°C
  状况: ${cached.current.condition}`;
        }
        
        throw error;
      }
    }
  };
}

// 扩展激活函数
export default function activate(ctx: ExtensionContext) {
  // 注册工具
  ctx.tools.register(createWeatherTool());
  
  // 注册UI组件
  ctx.ui.registerWidget("weather", createWeatherWidget);
  
  // 注册命令
  ctx.commands.register({
    id: "weather:current",
    description: "显示当前天气",
    async execute() {
      const defaultCity = await ctx.settings.get("weather.defaultCity", "北京");
      return ctx.tools.execute("weather", { city: defaultCity });
    }
  });
  
  // 监听设置变化
  ctx.settings.onDidChange("weather.defaultCity", (newValue) => {
    ctx.ui.showMessage(`默认城市已更新为: ${newValue}`);
  });
  
  // 提供扩展API
  return {
    async getCurrentWeather(city: string) {
      return ctx.tools.execute("weather", { city });
    }
  };
}

// 扩展停用函数
export function deactivate(ctx: ExtensionContext) {
  // 清理资源
  ctx.tools.unregister("weather");
  ctx.ui.unregisterWidget("weather");
  ctx.commands.unregister("weather:current");
}

4.4 总结

pi-mono的扩展系统为开发者提供了强大而灵活的方式来扩展平台功能。通过遵循本文介绍的最佳实践和模式,你可以构建高质量、安全且性能优良的扩展,为pi-mono生态系统贡献价值。

无论是创建简单的工具还是复杂的外部服务集成,扩展开发都遵循相同的核心原则:模块化设计、类型安全、事件驱动和用户体验优先。随着pi-mono生态系统的不断发展,扩展将成为定制和增强AI agent能力的关键方式。

开始构建你的第一个扩展,释放pi-mono的全部潜力!

登录后查看全文
热门项目推荐
相关项目推荐