npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pulonia/moongazer

v0.6.0

Published

Provider-agnostic TypeScript agent runtime with typed tool calling and streaming events.

Readme

moongazer

轻量级、框架无关的 TypeScript 库,用于构建带工具调用 (tool-use) 能力的 LLM 代理循环。

English | Chinese

概述

moongazer 将 LLM 流式补全抽象为 ChatTransport 接口,并在此基础上提供事件驱动的代理运行时。它不绑定任何具体模型提供商——你可以直接使用内置的 OpenAI 适配器,或为其他提供商编写自定义适配器。

核心特性

  • 提供者无关 — 通过 ChatTransport 接口适配任何 LLM 提供商
  • 类型安全工具 — 用 TypeBox schema 定义工具;execute 入参类型由 schema 推断,运行时通过 Value.Default + Value.Assert 对模型 JSON 填充默认值并严格校验(不合法参数会报错而非静默转换)
  • 推理内容 — 从支持 reasoning_content 的模型(如 OpenAI o1/o3)中流式输出 reasoning 增量事件
  • 工具调用 — 原生支持函数调用,自动拼接流式工具参数片段
  • 生命周期 hooks — 在模型请求、工具授权/审计、结果改写和继续决策等边界插入 agent 级、run 级或工具级逻辑
  • 事件驱动 — 代理运行时通过订阅者模式暴露 AgentEvent,便于日志、存储和 UI 集成
  • 中止支持 — 安全地中止正在运行中的轮次,保留已收到的内容
  • 最小依赖 — 仅 @sinclair/typebox 一个运行时依赖(OpenAI 适配器只定义 TypeScript 类型,无 openai 包依赖)

安装

pnpm add @pulonia/moongazer

API 文档

API 文档

快速开始

import { createAgent, createOpenAITransport, defineTool, Type } from "@pulonia/moongazer";
import type { OpenAIRawStream } from "@pulonia/moongazer";

// 1. 定义一个工具
const getWeather = defineTool({
  name: "get_weather",
  description: "获取指定城市的天气",
  parameters: Type.Object({
    city: Type.String(),
  }),
  execute: async ({ city }) => {
    return `Weather in ${city}: sunny, 22°C`;
  },
});

// 2. 创建 OpenAI 传输层
const rawStream: OpenAIRawStream = async function* (request, signal) {
  const response = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
    },
    body: JSON.stringify({ ...request, model: "gpt-4o", stream: true }),
    signal,
  });
  const reader = response.body!.getReader();
  // ... 解析 SSE 分片并 yield OpenAIChatChunk 对象
};

const transport = createOpenAITransport(rawStream);

// 3. 创建代理并运行
const agent = createAgent({ transport, tools: [getWeather] });

const handle = agent.run({
  messages: [{ role: "user", content: "北京今天天气怎么样?" }],
  hooks: {
    beforeToolExecute: ({ tool }) => {
      if (tool?.name === "get_weather" && !isLocationAllowed()) {
        return { result: "<tool_error>weather access is not allowed</tool_error>" };
      }
    },
    afterToolExecute: ({ result }) => ({ result: redact(result) }),
  },
});

handle.subscribe((event) => {
  if (event.type === "content") console.log(event.delta);
  if (event.type === "reasoning") console.log(event.delta);
});

示例项目

Demo

许可

MIT