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

@kestack/connect

v0.0.6

Published

Connects a computer to the cloud as a Connection its User reaches from anywhere: the ke-connect command, Cloud Connect on a Cloudflare Worker, and the code Plugin Code talks to

Readme

@kestack/connect

Connects a computer to the cloud as a Connection its User reaches from anywhere: the ke-connect command, Cloud Connect on a Cloudflare Worker, and the code Plugin Code talks to

Big picture

A coding agent runs on a computer, but its User wants to reach it from a phone or another laptop. Connect makes the computer a Connection: ke-connect serve signs it in to Cloud Connect as its User, opens a tunnel, and reports a Heartbeat with its address and Connection Key. An App lists its User's Connections from Cloud Connect, a Worker with a D1 database at connect.kishore.app, then talks to each Connection straight through its tunnel, at /<plugin>/<sha256>/rpc.

ke-connect itself stays small and rarely changes. What a Connection serves comes in Plugins: separate programs it runs, each downloaded at the version Cloud Connect names in its answer to every Heartbeat, and swapped for a new one while the tunnel stays up, through @kestack/cli-platform. The Connection checks the Connection Key and passes each Plugin's requests to that Plugin's own socket without reading them. The code Plugin keeps Projects and Sessions for Code: it runs each Session's Agent through @kestack/ai-toolkit, with the Claude Code or Codex installed on the computer and as one of its Harness Accounts, orders its Turns, stores its Raw records, and sends them to the browser through a std-toolkit Replica over rpc-toolkit. Sign-in comes from auth-toolkit. Why Connect owns this and ai-toolkit stops at the harness is root ADR 0011.

Each Connection has a name on its computer, main unless given, and keeps its files in its own folders, as cli-platform lays them out; one name runs as one process. In development each branch has its own Connection, dev-<branch>, and its own Cloud Connect, and every App's pnpm dev starts or reuses them: see docs/dev.md.

The words are in CONTEXT.md, the Actors in docs/kabuki-tree.md, and the decisions in docs/adr/.

Install

pnpm add @kestack/connect effect @kestack/kabuki @kestack/std-toolkit @kestack/rpc-toolkit @kestack/auth-toolkit @tanstack/db
npx @kestack/connect serve
  • effect: everything Connect exposes is an Effect value.
  • @kestack/kabuki: a Connection and the code Plugin run as Actors.
  • @kestack/std-toolkit: the stored records and the Replica an App reads them through.
  • @kestack/rpc-toolkit: the WebSocket an App reaches a Connection over.
  • @kestack/auth-toolkit: a Connection signs in to Cloud Connect as its User.
  • @tanstack/db: the collections the Replica fills.

Node 24 or newer is required. Sessions run the agents installed on the computer, which Connect does not bring: install Claude Code or Codex (npm install -g @openai/codex) and sign in with claude auth login or codex login.

Exports

./code

What Code's browser imports: the code Plugin's API and records, and the client and Replica that reach it on each Connection.

Every value an RPC sends is an ESchema, so the newer side reads what an older one sends; the older side fails with VersionGap, from ./cloud, naming which side it is.

| Export | What it does | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | CodeApi | The RPC group of the code Plugin: its commands and the projects, sessions, messages feeds. | | CodeClient | Service holding one Connection's CodeApi client over its reconnecting WebSocket. | | CodeClient.layer | Layer of CodeClient for a ws(s)://…/code/<sha256>/rpc URL, or a path in a browser, with the Connection Key if given. | | ConnectionAddress | Service the App gives: where a Connection's socket is, by Connection id, and its Connection Key, on each connect. | | CodeClients | Service holding every Connection's client by Connection id, one socket each, shared by whatever holds it. | | CodeClients.layer | Layer of CodeClients opening each Connection's socket at the address ConnectionAddress gives. | | CodeClients.make | Layer of CodeClients opening each Connection with a given Layer, for one server or a test. | | CodeClients.connection | Layer of one Connection's CodeClient and its socket, for everything under it. | | CodeClients.on | Runs an Effect that needs CodeClient against one Connection, by its id. | | CodeClients.stream | Runs a Stream that needs CodeClient against one Connection, its socket held while it runs. | | CodeClients.link | Stream of one Connection's Link now and on each change. | | link | Stream of the Link of the CodeClient in context: linking, linked or relinking. | | CodeReplica | The Replica of each Connection's records, keyed by Connection id: projects, sessions and messages. | | CodeReplicaLive | How CodeReplica syncs: every Project and Session, and a Session's Messages while a Query filters on its id. | | FolderListing | ESchema of a listFolder answer: the folder, its parent, up to 100 sub-folders, and if it was cut short. | | FolderEntry | ESchema of one sub-folder in a listing: its name, whether it is hidden, and whether it is a symlink. | | CreatedFolder | ESchema of a createFolder answer: the new folder's path, and why git init failed, if it did. | | WireEntity | ESchema of an entity in encoded form with its meta, as feeds send it and take it back as a cursor. | | Harness | Schema of a harness name: claude or codex. | | HarnessInfo | ESchema of one Harness Account as a new Session offers it: its harness, name, settings, default model, and whether its binary runs. | | SettingInfo | ESchema of one harness setting and the choices a picker offers. | | ModelInfo | Schema of one Language Model: id, name, and release date. | | ProjectSchema | Entity schema of a Project. | | ProjectValue | ProjectSchema.schema: a Project with its version, as addProject returns it. | | SessionSchema | Entity schema of a Session. | | SessionValue | SessionSchema.schema: a Session with its version, as startSession and forkSession return it. | | MessageSchema | Entity schema of a Message, the projected form a client reads. | | PartSchema | Schema of one Message part: text, thinking, tool call, attachment, turn start or end, and more. | | HarnessAccountSchema | Entity schema of a Harness Account: its harness, name, binary and config folder. | | HarnessAccountInfo | ESchema of a Harness Account as set, whether it is its harness's Default Account, the config folder it really uses, and what its binary resolved to. | | BinaryInfoSchema | Schema of a resolved binary: its path and what --version printed. | | AttachmentSchema | Schema of an Attachment: name, mime type, and base64 data. | | SkillSchema | Schema of a Skill: name, description, and an optional argument hint. | | AskAnswerSchema | Schema of an Ask's answer: Text, or Data shaped by the given JSON Schema. | | BackgroundJobSchema | Schema of one Background Job. | | BACKGROUND_JOB_KINDS | Tuple of Background Job kinds: shell, agent. | | SessionJobs | ESchema of one Session's Background Jobs, as the backgroundJobs stream sends them. | | AgentQuestionSchema | Schema of an Agent Question: its id, one to four Items, and when it was asked. | | QuestionItemSchema | Schema of one Item of an Agent Question: header, question, options, and whether it takes several, its own answer, or a secret. | | QuestionOptionSchema | Schema of one choice of an Item: its label and what choosing it means. | | QuestionAnswerSchema | Schema of an answer to an Agent Question: by Item id, the labels chosen, then what was typed. | | SessionQuestions | ESchema of one Session's Agent Questions, as the agentQuestions stream sends them. | | McpServerStatusSchema | Schema of one MCP server a Session's Agent is connected to. | | MCP_SERVER_STATES | Tuple of MCP server states, such as connected, failed, needs-auth, pending. | | ContextUsageSchema | Schema of Context Usage: tokens used, the window size, and rate limits. | | SpeechToTextState | ESchema of how Speech to Text stands: Off, Downloading with bytes of total, Ready, or Failed. | | FolderNotFound | Error: no folder at the path given. | | PermissionDenied | Error: this user may not read or write the folder. | | FolderExists | Error: something is already at the path createFolder would make. | | InvalidFolderName | Error: the name cannot be one new folder. | | OutsideHome | Error: the path, its symlinks resolved, is not inside the home folder. | | FolderUnreadable | Error: the folder could not be listed, or listing it took too long. | | ProjectNotFound | Error: no Project with this id. | | SessionNotFound | Error: no Session with this id. | | UnknownAccount | Error: this Connection has no Harness Account with that id. | | ModelsUnavailable | Error: the harness could not list its models. | | NotForkable | Error: the Session cannot be forked after the Turn asked for. | | AskFailed | Error: the harness could not answer the Ask. | | SkillsUnavailable | Error: the harness could not list its Skills. | | InvalidBinary | Error: the path is missing, not executable, or its --version failed. | | InvalidAccount | Error: a Harness Account has no name or no config folder, is unknown, or is a Default Account to remove. | | HarnessFailed | Error: a harness failed to answer for a live Session. | | SpeechToTextNotReady | Error: Speech to Text is off, still downloading, or failed on this Connection. | | TranscriptionFailed | Error: the model would not load, or the audio is not 16-bit PCM. |

./cloud

What an App imports to reach Cloud Connect: its client, and the Connections it lists.

| Export | What it does | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | CloudClient | Service holding the CloudApi client over HTTP. | | CloudClient.layer | Layer of CloudClient for Cloud Connect's base URL; the caller's auth signs every call. | | Connection | ESchema of a Connection as its User sees it: its last Report, its Last Seen, each Plugin's latest Release, and the Minimum Connect. | | ConnectionStatus | ESchema of how far a Connection's tunnel got: installing cloudflared, opening, failed, or up. | | addressOf | The public address a Connection Status is up at, or null. | | isOutdated | Whether a Connection runs a ke-connect older than the Minimum Connect, and so should be updated. | | pluginPath | The path a Connection serves a Plugin at one Release: /<name>/<sha256>/rpc. | | PLUGIN_MISMATCH | The close code, 4409, of a socket that asked for a Release the Connection does not run. | | PluginMismatch | ESchema of that close's reason: the hash the Connection runs, or null. | | mismatchOf | What a socket's close code and reason say of the Release asked for: a PluginMismatch, or null for any other close. | | VersionGap | Error of every CodeClient and CloudClient call that failed because one side is older: client or server. | | authInfo | How a Connection signs in to the Cloud Connect at a base URL, from its /auth; fails when it cannot be asked. |

./worker

| Export | What it does | | ------------------ | ----------------------------------------------------------------------------------- | | serve | Cloud Connect on a Worker's D1 database, and the Plugins' files from its R2 bucket. | | serveOver | The same over any std-toolkit SQLite driver and auth Guard, for tests. | | connectionsTable | The std Table Cloud Connect keeps Connections in, for SQLite.setup and deploys. |

./dev

| Export | What it does | | --------------- | ------------------------------------------------------------------------------------------------ | | withConnect | An App's pnpm dev: makes sure the branch's Connect runs, then runs the App with CONNECT_URL. | | ensureConnect | Makes sure the branch's Connect runs, starting it detached if not, and takes a lease on it. | | runConnectDev | Runs this branch's Cloud Connect under alchemy dev and a Connection on it. | | branchOf | The branch a checkout is, and its Cloud Connect's URL, as Portless names them. |

Usage

Serve a Connection

Install Claude Code or Codex first. Run npx @kestack/connect serve on a computer, sign in with the device code it prints, and the computer is listed as one of your Connections within a few seconds. ke-connect with no command opens a wizard that builds one. The commands, as src/connection/tests/connection.test.ts runs them:

ke-connect serve                 # the Connection `main`
ke-connect serve work --port 0   # another one, `work`, on any free port
ke-connect serve work            # `work is already running at https://….trycloudflare.com`
ke-connect list                  # every Connection: running or not, its address, its last start
ke-connect remove work           # stops it, forgets it in Cloud Connect, signs out, deletes its folders
  • It signs in through the Auth Worker that Cloud Connect's /auth names, and keeps the Connection's id, Connection Key and Cloud Connect in connection.json; serving it against another Cloud Connect is refused. It keeps what /auth said in auth.json, so a Connection that signed in before starts while Cloud Connect cannot be reached, or does not answer in five seconds: it signs in as last time, runs its Plugins at the Releases it last wanted, and keeps sending its Heartbeat until Cloud Connect answers. A first Sign-in needs Cloud Connect. Signing in again, once Cloud Connect refuses the Sign-in, asks /auth first, so an Auth Worker it moved to is the one used, and kept.
  • It runs each Plugin at the Release Cloud Connect names, or the one it last named while Cloud Connect cannot be reached, passes /<plugin>/<sha256>/rpc to it while it runs that Release and closes any other with 4409, asks every caller for the Connection Key, and opens a cloudflared tunnel to its port; any other path is 404.
  • Its Heartbeat reports the tunnel's address and each Plugin State every 15 seconds. A forgotten Connection signs out and stops; one below the Minimum Connect stops, saying how to update.

Talk to a Connection from a browser

An App lists its User's Connections from Cloud Connect, says where each one is through ConnectionAddress, and reads each Connection's records through CodeReplicaLive, one Instance per Connection. Trimmed from src/plugins/code/serve/tests/serve.test.ts and Code's reach.ts.

import { Effect, Layer, Schema } from 'effect';
import { Actor, Runtime } from '@kestack/kabuki';
import { Query, Replicas } from '@kestack/std-toolkit/replica';
import { Store } from '@kestack/std-toolkit/replica/idb';
import { addressOf, CloudClient } from '@kestack/connect/cloud';
import {
  CodeClients,
  CodeReplica,
  CodeReplicaLive,
  ConnectionAddress,
} from '@kestack/connect/code';

const address = Layer.effect(
  ConnectionAddress,
  Effect.map(CloudClient, (cloud) =>
    ConnectionAddress.of({
      of: (connectionId) =>
        Effect.map(Effect.orDie(cloud.List({})), (connections) => {
          const connection = connections.find(({ id }) => id === connectionId)!;
          const url = addressOf(connection.status)!.replace('https', 'wss');
          return { url: `${url}/code/rpc`, key: connection.key };
        }),
    }),
  ),
);

const Projects = Query.make('Projects', {
  props: Schema.Struct({ connectionId: Schema.String }),
  from: ({ props }) =>
    Effect.all({ code: CodeReplica.get(props.connectionId) }),
  query:
    (_, { code }) =>
    (q) =>
      q.from({ p: code.projects }),
});

const Root = Actor.define('Root', {}).build({
  provides: () => Replicas.layer,
  children: {
    replicas: Actor.child(Replicas.actor([CodeReplicaLive])),
    projects: Actor.child(Projects, { props: () => ({ connectionId: 'm1' }) }),
  },
});

Runtime.start(
  Root,
  Layer.mergeAll(
    Store.idb(),
    CodeClients.layer.pipe(
      Layer.provide(address),
      Layer.provide(CloudClient.layer('https://connect.kishore.app')),
    ),
  ),
);
  • CloudClient calls are signed by the App's auth-toolkit browser() Accounts, provided above it.
  • Each Connection gets its own socket, opened at the address ConnectionAddress gives on each connect, so a Connection back at a new tunnel address is reached there.
  • CodeClients.on(connectionId, …) runs a command, such as addProject, against one Connection.

Run Cloud Connect on a Worker

serve from ./worker is Cloud Connect over a D1 database, signing in through an Auth Worker, letting the listed origins call with credentials, telling every Connection the latest Release of each Plugin and the Minimum Connect, and serving the Shelf. Shaped after src/cloud/worker/worker.server.ts, which alchemy.run.ts deploys.

import { serve } from '@kestack/connect/worker';

interface Env {
  readonly AUTH_URL: string;
  readonly ALLOWED_ORIGINS: string;
  readonly PLUGINS?: string;
  readonly MIN_CONNECT?: string;
  readonly DB: D1Database;
  readonly PLUGIN_FILES?: R2Bucket;
}

let served: ((request: Request) => Promise<Response>) | undefined;

export default {
  fetch: (request: Request, env: Env) =>
    (served ??= serve({
      auth: { url: env.AUTH_URL },
      origins: env.ALLOWED_ORIGINS.split(','),
      database: env.DB,
      plugins: env.PLUGINS ? JSON.parse(env.PLUGINS) : {},
      ...(env.MIN_CONNECT ? { minConnect: env.MIN_CONNECT } : {}),
      ...(env.PLUGIN_FILES ? { files: env.PLUGIN_FILES } : {}),
    }))(request),
};
  • ALLOWED_ORIGINS takes comma-separated patterns such as https://*.kishore.app.
  • PLUGINS is {"code":{"hash":"<sha256>","url":"<where to read it>"}}, the latest Release of each Plugin; pnpm build:plugin makes dist/plugin/code.mjs and prints its sha256, and whether it is the latest Release in src/plugins/code/releases.json.
  • GET /plugins/<name>/<sha256>.mjs answers that file from files, cached for good; anything else there is 404.
  • MIN_CONNECT is the Minimum Connect, the oldest ke-connect let run the Plugins; a Connection below it is Outdated.
  • Set the D1 table up from connectionsTable before the first request.
  • In this monorepo, a change to the code Plugin is released with pnpm -F @kestack/connect release:plugin code "<what changed>", which builds it and appends its hash to its Release Log. pnpm -F @kestack/connect build:plugin && pnpm -F @kestack/connect deploy:prod deploys it to connect.kishore.app: the deploy refuses a build that is not the latest Release, uploads code.mjs to the Stage's R2 bucket unless it is there, then points PLUGINS at it, and sets MIN_CONNECT from minConnect in package.json (ADR 0006). A personal Stage, alchemy deploy --stage <name>, has no URL its Connections could read the Shelf at, so it refuses to deploy; develop under alchemy dev.