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

@fishmem/sdk

v0.1.0

Published

Universal TypeScript SDK for FishMem Cloud and self-hosted FishMem.

Readme

@fishmem/sdk

Official TypeScript SDK for FishMem Cloud, self-hosted FishMem, and the local FishMem Desktop process.

npm install @fishmem/sdk
import { FishMem } from "@fishmem/sdk";

const fishmem = new FishMem({
  apiKey: process.env.FISHMEM_API_KEY!,
});

await fishmem.memories.addAndWait(
  {
    content: "The user prefers concise answers.",
    user_id: "alex",
  },
  { idempotencyKey: "alex-answer-style-v1" },
);

const { results } = await fishmem.memories.search({
  query: "How should I answer Alex?",
  user_id: "alex",
});

Inferred writes are asynchronous: addAsync returns a durable Event receipt, events.wait polls it, and addAndWait is the convenience form above. Already-distilled infer:false writes remain synchronous. fishmem.memories also exposes search, list, listAll, get, update, audited feedback, durable batch update/delete, delete-all, and history. Batch calls accept up to 1,000 unique IDs, require an idempotency key, and return an operation for polling. Long-form text uses the separate documents resource:

import { readFile } from "node:fs/promises";

const source = await fishmem.documents.ingest(
  {
    source_key: "handbook/deployments.md",
    content: "# Deployments\n\nProduction deploys require...",
    mime_type: "text/markdown",
    user_id: "alex",
  },
  { idempotencyKey: "handbook-deployments-v1" },
);

const uploaded = await fishmem.documents.upload(
  {
    file: new Blob([await readFile("./handbook.pdf")], {
      type: "application/pdf",
    }),
    filename: "handbook.pdf",
    source_key: "handbook/product.pdf",
    user_id: "alex",
  },
  { idempotencyKey: "handbook-pdf-v1" },
);

const completed = await fishmem.operations.wait(uploaded.operation.id, {
  intervalMs: 1_000,
  timeoutMs: 10 * 60_000,
});

const evidence = await fishmem.documents.search({
  query: "What is required before deployment?",
  user_id: "alex",
});
const original = await fishmem.documents.content(source.document.id);

documents.upload() creates an immutable source asset, uploads exact bytes, queues Docling extraction, and returns without waiting for extraction. HTTP deployments accept supported PDF, Office, EPUB, email, image, and text files up to 25 MB and 300 extracted pages. The raw file and lossless structure are retained; extracted Markdown, chunks, and indexes are rebuildable.

Direct documents.ingest() is the synchronous path for already-textual UTF-8 content up to 1 MB. documents exposes ingest, upload, search, list, listAll, get, content, and delete, plus createUpload, getUpload, and completeUpload for manual lifecycle control. Cloudflare uses R2 for raw files and artifacts; Node/Docker uses a durable asset directory. The client also exposes:

  • health.get;
  • entities.list, entities.listAll, entities.get, and idempotent entities.delete for structural user, agent, and run scopes;
  • state.get and state.history;
  • profile.get;
  • operations.list, operations.get, and operations.wait;
  • events.list, events.get, and events.wait for privacy-safe memory inference status;
  • exports.create and imports.create.

Feedback is available on both HTTP and Desktop clients:

await fishmem.memories.setFeedback(
  "mem_123",
  { rating: "negative", reason: "Out of date", request_id: "req_123" },
  { idempotencyKey: "req-123-feedback-v1" },
);
const current = await fishmem.memories.getFeedback("mem_123");

The default package entry uses only standard web APIs. The same import works in Node 20+, Bun, Deno (npm:@fishmem/sdk), Cloudflare Workers, and Vercel Functions. It applies no hidden retries; pass an idempotency key when a mutation may be retried.

See the complete SDK documentation for every resource, runtime, error, pagination, and idempotency contract.

Desktop

The explicit Desktop entry is Node-only because it invokes the installed fishmem CLI without a shell:

import { FishMemDesktop } from "@fishmem/sdk/desktop";

const desktop = new FishMemDesktop();

await desktop.memories.add(
  {
    content: "Use the canonical deployment pipeline.",
    infer: false,
  },
  { idempotencyKey: "deployment-pipeline-v1" },
);

const { results } = await desktop.memories.search({
  query: "How do we deploy?",
});

const source = await desktop.documents.ingest(
  {
    source_key: "handbook/deployments.md",
    content: "# Deployments\n\nProduction deploys require...",
    mime_type: "text/markdown",
  },
  { idempotencyKey: "local-handbook-deployments-v1" },
);
const uploaded = await desktop.documents.upload(
  {
    file: new Blob(["# Local exact source\n"], {
      type: "text/markdown",
    }),
    filename: "local.md",
    agent_id: "codex",
  },
  { idempotencyKey: "local-upload-v1" },
);
const evidence = await desktop.documents.search({
  query: "What is required before deployment?",
});
const original = await desktop.documents.content(source.document.id);

for await (const entity of desktop.entities.listAll({ type: "user" })) {
  console.log(entity.id, entity.total_memories);
}

Desktop accepts already-distilled records with infer=false, uses only its local multilingual E5 embedding model, and never falls back to a hosted provider. desktop.documents has the same source-corpus surface as the HTTP client for textual documents. Desktop file upload is deliberately UTF-8-only and capped at 1 MB; it does not run Docling/OCR or call remote embedding or file-processing providers. SDK payloads travel over stdin, so long source text is not exposed in the process argument list or constrained by the OS argument-length limit. desktop.entities uses the same canonical scope aggregation and response shape as the HTTP client; it does not create a second local entity store.