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

@buerli.io/ai

v0.0.1

Published

Portable AI agent panel for buerli-based CAD applications

Downloads

89

Readme

@buerli.io/ai

AI agent for buerli/ClassCAD apps. Drops a chat panel into your app that creates and modifies 3D geometry through natural language — any tool-calling LLM, all CAD operations executed locally in the browser.

Install

npm install @buerli.io/ai

Peers (your buerli app has them already): @buerli.io/classcad, @buerli.io/core, @react-three/fiber ≥8, react ≥18, zustand ≥4.

Setup — provider & models

The agent talks to any LLM through a small LLMProvider interface. Pick one:

| You have | Use | | --- | --- | | GitHub Copilot subscription | bundled copilot-proxy + createAutoProvider (below) | | OpenAI / Azure key | createAutoProvider({ apiKey, endpoint? }) | | Anthropic key | createAnthropicProvider({ apiKey }) | | Local AI (Ollama, LM Studio, vLLM) | createOpenAIProvider({ endpoint: 'http://localhost:11434/v1/chat/completions', model }) | | Something else | implement LLMProvider (one chat() method — see Custom integration) |

createAutoProvider is the recommended default: it reads the endpoint's /models and routes each model to the right API surface automatically (gpt-5.x/codex → Responses, Claude/Gemini → Chat Completions), and discovers per-model context windows and thinking levels — so the panel's model/thinking pickers just work. Endpoints without /models fall back to Chat Completions. (The single-surface adapters it wraps — createOpenAIProvider, createResponsesProvider — are exported too if you want to pin one surface.)

GitHub Copilot (development)

The browser can't call api.githubcopilot.com (no CORS, short-lived session tokens), so the package ships a tiny local proxy:

npx copilot-proxy auth     # one-time GitHub device-flow login; scaffolds ./.env.local
npx copilot-proxy models   # list models your plan exposes
npx copilot-proxy          # run on http://localhost:8788

auth writes a ready-to-edit ./.env.local:

AI_AGENT_API_KEY=copilot-proxy            # placeholder; the proxy injects the real token
AI_AGENT_ENDPOINT=http://localhost:8788/v1
AI_AGENT_MODEL=gpt-5.5                    # default; the panel picker can switch any time
AI_AGENT_REASONING_EFFORT=medium          # default thinking level
COPILOT_OAUTH_TOKEN=ghu_…                 # SERVER-ONLY secret — never bundle into the client

COPILOT_OAUTH_TOKEN must never reach the browser. If your app forwards .env.local into client code (Vite import.meta.env, Docusaurus customFields), expose an allowlist of safe keys — never the whole file. Keep .env.local gitignored. Port override: COPILOT_PROXY_PORT.

createCopilotProvider({ token }) also exists for Node/VS Code contexts where you already hold a Copilot session token (it does not work in the browser).

Usage (React / react-three-fiber)

import { createCadAgent, createAutoProvider, initAgentAsync } from '@buerli.io/ai'
import { useBuerli, BuerliGeometry } from '@buerli.io/react'
import { Canvas } from '@react-three/fiber'

await initAgentAsync() // once at startup — loads bundled ClassCAD docs + method registry

const { AgentPanel, AgentCanvas } = createCadAgent({
  provider: createAutoProvider({ apiKey: ENV.AI_AGENT_API_KEY, endpoint: ENV.AI_AGENT_ENDPOINT }),
  modelName: ENV.AI_AGENT_MODEL,            // default model (picker can change it)
  reasoningEffort: 'medium',                // default thinking level
  // maxTokens, contextLimit — optional fallbacks; per-model values are auto-discovered
  // maxIterations: 25                      — tool-loop cap
  // systemPrompt / extraContext            — see Custom prompts
})

function App() {
  const drawingId = useBuerli((s) => s.drawing.active || '')
  const [open, setOpen] = useState(true)
  return (
    <div style={{ position: 'relative', width: '100%', height: '100%' }}>
      <Canvas>
        <AgentCanvas />                      {/* invisible — lets the agent snapshot the view */}
        <BuerliGeometry drawingId={drawingId} />
      </Canvas>
      <AgentPanel drawingId={drawingId} open={open} onClose={() => setOpen(false)} />
    </div>
  )
}

<AgentPanel> props: drawingId (required), open, onClose, position ('right' | 'left' | 'bottom'), className, theme ({ bg, text, accent, userBubble, assistantBubble, width }), and extraContext (per-mount domain prompt — overrides the factory default, useful when one agent serves several screens).

What you get, all capability-driven (each control only appears when the provider/model actually supports it):

  • Model picker + thinking picker + context ring in the footer — fed by the provider's /models discovery.
  • Attachments — images (vision) and CAD files (STEP/IGES/STL…) via the + button.
  • Stop to abort a running turn; per-tool status chips with results and errors.
  • Source panel (</> in the header) — the session as a runnable, syntax-highlighted buerli script: runtime IDs threaded into variables, preconditions and failures as comments, one-click copy.

Custom prompts

The default system prompt makes the agent a general ClassCAD expert. Add your domain on top with extraContext (recommended — keeps the base expertise), or replace the whole prompt with systemPrompt:

createCadAgent({
  provider,
  extraContext: `## This app: parametric pipe runs
Segments are named Default, Pipe1, …; expressions: length, outerDiam, thickness.
Angles in radians (UI shows degrees). Always recalc after edits.`,
})

Teach it your model's structure, naming, units, and the exact calls for common operations — the more concrete, the fewer tool-discovery turns the agent needs. When replacing systemPrompt, you can compose with the exported DEFAULT_SYSTEM_PROMPT to keep the base ClassCAD expertise.

What the agent can do (tools)

| Tool | Purpose | | --- | --- | | call_api | Any v1.<domain>.<method> ClassCAD call (also facade.* and drawing APIs) | | call_api_batch | Many calls in one turn; later calls reference earlier results ("$0.id") | | tree / find / inspect | Read the structure tree, search nodes, full node detail | | get_selection / set_selection | Read or set the user's 3D selection | | list_methods / describe_method | Discover and document API methods (bundled docs) | | snapshot | See the 3D viewport (PNG → vision) | | load_file | Import a user-attached CAD file | | download | Export STEP/STL/OFB as a download button in the chat | | delegate | Hand a sub-task to a specialist sub-agent |

Everything executes in the browser against the buerli API — no extra server for CAD. (TOOL_SCHEMAS and executeTool are exported for tests or custom executors.)

Production

Never ship API keys in the browser. Point the provider at your backend and authenticate your users there:

// client
createAutoProvider({ apiKey: userSessionToken, endpoint: 'https://your-app.com/api/ai' })
// server: verify the user, attach the real provider key, forward the body verbatim.

The bundled Copilot proxy is a development convenience, not a multi-user production gateway.

Custom integration (your own UI)

<AgentPanel> is a thin view over createAgentStore() — build your own panel on the same store and keep the full agent (tools, batching, attachments, cancel):

import { createAgentStore, createAutoProvider, initAgentAsync } from '@buerli.io/ai'
import type { AgentConfig, UIMessage } from '@buerli.io/ai'

await initAgentAsync()
const useAgent = createAgentStore() // zustand — React hook AND vanilla store

const config: AgentConfig = {
  provider: createAutoProvider({ apiKey: '…', endpoint: '…' }),
  drawingId,
  model: 'gpt-5.4',            // optional per-message override
  reasoningEffort: 'none',     // optional
  extraContext: MY_PROMPT,     // optional
}

function MyPanel() {
  const messages = useAgent((s) => s.messages)   // UIMessage[]
  const isRunning = useAgent((s) => s.isRunning)
  const error = useAgent((s) => s.error)
  const usage = useAgent((s) => s.usage)         // { inputTokens, outputTokens }

  const send = (text: string) => useAgent.getState().sendMessage(text, config)
  // attachments: sendMessage(text, config, images?: ImageInput[], files?: FileAttachment[])

  return (
    <>
      {messages.map((m: UIMessage, i) => {
        if (m.type === 'assistant') return <Md key={i} text={m.text} />
        if (m.type === 'thinking') return <Collapsed key={i} text={m.text} />
        // m.type === 'tool': { name, label, status: 'running'|'done'|'error',
        //                      detail?, image? (snapshot data-url), download? }
        return <ToolChip key={i} {...m} />
      })}
      {error && <Error text={error} />}
      <Input onSubmit={send} disabled={isRunning} />
      {isRunning && <button onClick={() => useAgent.getState().stop()}>Stop</button>}
    </>
  )
}
  • Non-React: same store via useAgent.getState() / useAgent.subscribe().
  • useAgent((s) => s.codeLog) (CodeEvent[]) holds the API-call log for a source view.
  • reset() clears the conversation.

Snapshots without <AgentCanvas> — register a capturer once and the snapshot tool works with any viewer:

import { setSnapshotCapturer, createCanvasCapturer } from '@buerli.io/ai'
setSnapshotCapturer(createCanvasCapturer(myCanvasElement)) // or a custom SnapshotCapturer → base64 PNG

Model/thinking pickersprovider.getCapabilities() returns { models: ModelOption[] } (ids, context limits, reasoning levels); render your own picker and pass the choice as config.model / config.reasoningEffort.

Fully headless — drive the raw event loop, no store, no React:

import { runAgentLoop } from '@buerli.io/ai'
for await (const ev of runAgentLoop('Create a 50mm box', [], config)) {
  // ev.type: 'text' | 'thinking' | 'tool_start' | 'tool_end'
  //        | 'subagent_start' | 'subagent_end' | 'usage' | 'error' | 'done'
}

Custom provider — one method; must support tool-use blocks. Optionally add getCapabilities() to power the pickers:

const myProvider: LLMProvider = {
  async chat({ system, messages, tools, max_tokens, model, reasoningEffort }) {
    // call your LLM; return { content: ContentBlock[], stop_reason: 'end_turn' | 'tool_use' }
  },
}

initAgent({ skillBundle, methodRegistry }) is the synchronous init variant — pass the JSONs yourself (import bundle from '@classcad/skill/bundle.json' with { type: 'json' }) if you can't await at startup or run in pure-Node ESM.

Development

npm run typecheck
npm run build        # clean + tsc → dist/ — runs automatically on publish

All ClassCAD knowledge (the doc bundle and the method registry, both generated in the classcad-skill repo from @classcad/api-js) ships in the @classcad/skill runtime dependency — initAgentAsync() imports it from there, so there is nothing to re-bundle or vendor here. Bump that dependency to pick up new docs.