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

gmessages

v0.1.2

Published

A TypeScript client for Google Messages for web, with per-field provenance verified against Google's own descriptors. The protocol layer of Cast.

Readme

gmessages

A TypeScript client for Google Messages for web, whose protocol shapes are verified against Google's own descriptors rather than inferred.

import { connect, sendMessage, listConversations, messagesOf, nodeFileStore, GOOGLE_ENDPOINTS, GOOGLE_WEB_API_KEY } from "gmessages";

const client = await connect({
  endpoints: GOOGLE_ENDPOINTS,
  apiKey: GOOGLE_WEB_API_KEY,          // the relay requires it; it is the public web-client key, not a secret
  ...(await nodeFileStore("./session.json")),  // session blob in, rotations persisted; or wire your own store
  fetchImpl: fetch,
  onEvent: (event) => {
    if (event.kind === "push") for (const m of messagesOf(event.update)) console.log(m.text);
  },
});

const conversations = await listConversations(client.operations, { count: 25 });
for (const c of conversations) console.log(c.conversationId, c.participants);

await sendMessage(client.operations, { conversationId: "…", text: "hello", participantId: "…" });

session.json comes from pairing with the phone — docs/QUICKSTART.md walks from a signed-in browser to a first received message. Read docs/SECURITY.md before storing that file anywhere: it holds account-wide credentials.

What it does

  • Receive — holds the long-poll stream open and decodes every push into a typed message, with text, attachments, reactions and delivery status.
  • Read — conversations (inbox and archive, paged), message history, contacts, thumbnails.
  • Pair — from an account's cookies, with a person confirming a code on the handset. No browser export needed.
  • Send — text and media, into an existing thread or a number you have never messaged.
  • Act — reactions, mark read, mark unread, typing, archive, unarchive, block, unblock, delete.
  • Media — encrypted upload and download, both directions, chunked AES-GCM as the client does it. An attachment arrives with no handle until it is resolved (getFullSizeMedia), which is a request, not a wait — and it completes a moment after it returns rather than in its reply.
  • Delivery — reports sending / accepted / delivered / read, and tells you when a thread can never report delivery at all.

What it does not do

Read this part before adopting.

  • Pairing needs a person at the phone. pairFromCookies takes a signed-in account's cookies and returns session keys, and it has done so against a live account. It cannot be unattended: the protocol shows a code that someone has to compare on the handset and confirm, which is the point of the design rather than a gap in this one. convertLegacySession remains the path for a session a browser already established. See docs/PAIRING.md.
  • SMS cannot report delivery. Not a gap here: SMS threads stop at accepted and no further status arrives. RCS threads report delivered and read. reportsDelivery() tells you which you have.
  • Roughly twenty protocol actions are unimplemented — stickers, RCS group management, settings. Each would be an unverified shape, so they are absent rather than guessed.
  • Media send is proven over MMS, not RCS. This build has uploaded a file and had the relay deliver it on an SMS thread. RCS media has not been exercised, and it is not a safe extrapolation: Google's own client uses a second upload endpoint for RCS and falls back to this one only when RCS degrades to MMS. This library only ever uses the endpoint MMS uses.
  • Tested against one account. Broad in capability, narrow in population.
  • Not run unattended for long. Reconnect and cookie rotation are implemented and not yet proven over days.
  • Google's terms may prohibit automated access. Your account, your call. This library never attempts to circumvent a sign-in check, and acquiring cookies is a manual step for that reason.

Verification

The protocol shapes are not asserted, they are checked. Google's client ships a descriptor-driven binary serializer; the test suite loads it, plants one value in one field at a time, and reads back the tag Google emits — so every field number and wire type comes from Google's compiler rather than from anyone's reading of minified source.

descriptor oracle     298 messages, 1269 fields, 0 mismatched, 0 unchecked
codec differential  30,218 slots,  0 failed

Those suites need Google's bundle, which is not redistributable. They are excluded from collection when it is absent, so a fresh clone runs the offline suite and reports the rest as skipped rather than silently passing. capture/MANIFEST.json pins the exact bundle — URL, versions, SHA-256 per module — so the run can be reproduced. See PROVENANCE.md.

Where this came from

This client is the protocol layer of Cast.

Cast is a self-hosted harness for multi-user Claude Code agents that run as isolated container processes, with per-channel identity, an access-control layer, and scheduling. An agent that can send messages needs more than a protocol client: it needs to know who is allowed to message whom, which account it speaks as, and how a human authorises an action. This library deliberately supplies none of that — it will send wherever it is told — and Cast is the layer that decides.

That division is why the API is shaped the way it is, and why the capabilities are free functions over a dispatcher rather than methods on an object. docs/AGENTS.md covers what an agent needs beyond the wire, and how the two fit together.

The protocol layer is independently useful and independently tested, which is why it is published on its own. Nothing here depends on Cast.

Install

pnpm add gmessages

Node 20 or newer. MIT.

Contributing

pnpm test runs the suite. The oracle suites need Google's bundle, so a fresh clone reports them skipped rather than passed. pnpm run typecheck is separate and is part of prepublishOnly.

Comments here are for load-bearing constraints, the kind a reader would otherwise simplify away and break. They ship with the build — the declaration files carry them into a consumer's editor, so a comment explaining where a shape came from is part of the API surface. If a change needs justifying, the test that would fail without it is still the durable form.