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

@dex-ai/tools-extension

v0.1.9

Published

Core tool Extensions for @dex-ai/sdk — file read/write/edit, search, bash. Each tool is its own Extension.

Downloads

353

Readme

@dex-ai/tools

Core tool Extensions for @dex-ai/sdk. Each tool is its own Extension — compose the ones you want, skip the ones you don't.

| Extension | Tool | What it does | Sandboxed | Destructive | |------------------------|-------------|-----------------------------------------------------------|-----------|-------------| | readFileExtension | read | Read text, normalized images, or base64 binary files. | yes (cwd) | no | | readImageExtension | read_image| Compatibility image-only reader; prefer read. | yes (cwd) | no | | writeFileExtension | write_file| Create or overwrite a file. | yes (cwd) | yes | | editFileExtension | edit_file | Atomic multi-edit by exact string match. | yes (cwd) | yes | | searchExtension | search | grep regex over contents, or glob over paths. | yes (cwd) | no | | bashExtension | bash | Run sh -c <command> with timeout + captured output. | no | yes |

Install + use

import { DexAgent } from '@dex-ai/runtime';
import { openai } from '@dex-ai/openai';
import {
  readFileExtension, readImageExtension,
  writeFileExtension, editFileExtension,
  searchExtension, bashExtension,
  allFsExtensions,
} from '@dex-ai/tools';

const agent = new DexAgent({
  provider: openai({ modelId: 'gpt-4.1' }),
  extensions: [
    // pick individually...
    readFileExtension({ cwd: process.cwd() }),
    searchExtension({ cwd: process.cwd() }),

    // compatibility only; `read` handles images too
    readImageExtension({ cwd: process.cwd() }),

    // ...or bulk register the full fs set:
    ...allFsExtensions({ cwd: process.cwd() }),

    bashExtension({ cwd: process.cwd() }),
  ],
});

Subpath imports are also available for bundle-size control:

import { bashExtension } from '@dex-ai/tools/bash';

Tool contracts

read

params: { path: string, lineStart?: number, lineEnd?: number }
text output: { type: 'text', value: string }
image output: {
  type: 'content',
  value: [
    { type: 'text', text: string },
    { type: 'image', image: Uint8Array, mediaType: string }
  ]
}
binary output: {
  type: 'content',
  value: [
    { type: 'text', text: string },
    { type: 'file', data: string /* base64 */, mediaType: string, name: string }
  ]
}
image behavior: supported raster images are normalized to fit within 1080p at medium JPEG quality before being returned.
line ranges: lineStart/lineEnd are supported for text files only.
errors: path-outside-cwd, file-not-found, not-a-regular-file, too-large (>10MB text/binary, >20MB image input)

read_image compatibility tool

params: { path: string }
output: same image content shape as `read`
supported: image/png, image/jpeg, image/webp, image/gif
behavior: normalizes images to fit within 1080p at medium JPEG quality before returning.
errors: path-outside-cwd, file-not-found, not-a-regular-file, too-large (>20MB), unsupported-image-type

write_file

params: { path: string, content: string }
output: { type: 'json', value: { bytesWritten: number, created: boolean } }
behavior: creates parent dirs if missing; overwrites existing files

edit_file

params: {
  path: string,
  edits: Array<{ oldString: string, newString: string, replaceAll?: boolean }>,
}
output: { type: 'json', value: { appliedEdits: number } }
semantics: all-or-nothing. Each oldString must appear exactly once unless
           replaceAll is true. Overlapping ranges fail. File must exist.
           Use write_file to create new files.

search

params: {
  mode: 'grep' | 'find',
  pattern: string,          // grep: regex source;  find: glob (*, **, ?)
  path?: string,            // defaults to cwd
  maxResults?: number,      // default 100
  caseInsensitive?: boolean // grep-only
}
output: { type: 'json', value: { matches: [...], truncated: boolean } }
behavior: skips node_modules, .git, dist by default unless the caller scopes
          `path` into them.

bash

params: { command: string, timeoutMs?: number }  // default 120_000ms
output: { type: 'json', value: { stdout, stderr, exitCode, timedOut } }
behavior: runs via /bin/sh -c in cwd. Timeout kills the process and returns
          timedOut: true rather than throwing.

Approval — it's not here

This package ships zero approval logic. The SDK treats approval as cross-cutting via the onToolCall hook; apps install their own approver extension. The typical shape:

import type { Extension, ToolCall, ToolResult } from '@dex-ai/sdk';

function approvalExtension(opts: {
  gates: (call: ToolCall) => boolean;                 // returns true if this call needs approval
  ask: (call: ToolCall) => Promise<boolean>;          // async prompt; returns true to allow
}): Extension {
  return {
    name: 'approval',
    async onToolCall(call): Promise<ToolResult | void> {
      if (!opts.gates(call)) return;                  // no gate — run the tool
      const ok = await opts.ask(call);
      if (ok) return;                                 // approved — pass through
      return {                                        // rejected — model sees this as a tool-result
        toolCallId: call.toolCallId,
        toolName: call.toolName,
        output: { type: 'error-text', value: 'User rejected this tool call.' },
      };
    },
  };
}

// wire it up:
const agent = new DexAgent({
  provider,
  extensions: [
    bashExtension({ cwd }),
    writeFileExtension({ cwd }),
    approvalExtension({
      gates: (call) => ['bash', 'write_file', 'edit_file'].includes(call.toolName),
      ask: (call) => ui.confirm(`Run ${call.toolName}: ${JSON.stringify(call.input)}?`),
    }),
  ],
});

Rejected tool calls come back to the model as a normal tool-result message containing error-text. The model decides what to do next (try a different tool, stop, ask the user).

Sandbox details

The fs tools resolve every path against cwd and reject anything outside. Specifically:

  • ../ traversal is rejected (paths are normalized).
  • Absolute paths outside cwd are rejected.
  • Symlinks are followed via fs.realpath — a symlink inside cwd that points outside is rejected.

bash is not sandboxed at the filesystem level. It runs with cwd as its working directory but any approved shell command can read/write anywhere the process has permission to. Approval is the gate — use onToolCall.

Testing

bun test

50 tests across 6 files as of v0.1 — every tool exercised end-to-end through a DexAgent + scripted fake Provider.