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

@aprovan/workspace

v0.2.0

Published

The Aprovan workspace — chat, apps, workflows, MCP, and the credential/permission gateway in one process

Readme

Workspace

@aprovan/workspace — the Aprovan platform in one process: chat, apps, workflows and cron, MCP, VCS, notifications, and the credential/permission gateway underneath them.

One artifact, three ways to run it:

npx @aprovan/workspace start                          # npm CLI
docker run -p 4000:4000 -v ./data:/data \
  ghcr.io/aprovanlabs/workspace                       # container (ECS runs this)
import { startWorkspace } from "@aprovan/workspace";   // embedded (desktop app)
const workspace = await startWorkspace({ port: 4000 });

WORKSPACE_MODE decides where state lives — local (SQLite on disk, auth off, no AWS account) or aws (DynamoDB + S3 + Cognito). See docs/local-mode.md for the parity contract and docs/deployment.md for how it is deployed.

Routes

| Prefix | What | |---|---| | /api/gateway/* | REST API — credentials, fs, llm, apps, workflows, tools, audit | | /api/mcp | public MCP endpoint | | /.well-known/* | RFC 9728 OAuth resource metadata, resolved at the domain root | | /apps/* | live published app pages | | /health | liveness, for container and load-balancer probes |

Hosted MCP Install Flow (OAuth 2.0 with DCR)

Hosted MCP clients (e.g. Claude connectors) authenticate against the gateway using the standard OAuth 2.0 Authorization Code flow with PKCE. The install flow uses RFC 7591 Dynamic Client Registration so clients do not need a pre-shared secret:

MCP client                      Gateway                    Cognito Hosted UI
    │                              │                               │
    │  POST /oauth/register        │                               │
    │  { redirect_uris: [...] }    │                               │
    │─────────────────────────────>│                               │
    │                              │  CreateUserPoolClient         │
    │                              │──────────────────────────────>│
    │                              │  { ClientId, ClientSecret }   │
    │                              │<──────────────────────────────│
    │  { client_id, client_secret }│                               │
    │<─────────────────────────────│                               │
    │                              │                               │
    │  Redirect user to:           │                               │
    │  /oauth2/authorize           │                               │
    │  ?client_id=<client_id>      │                               │
    │  &code_challenge=<S256>      │                               │
    │  &response_type=code ...     │                               │
    │──────────────────────────────────────────────────────────────>
    │                              │                               │
    │  User completes login        │                               │
    │<──────────────────────────────────────────────────────────────
    │  ?code=<auth_code>           │                               │
    │                              │                               │
    │  POST /oauth2/token          │                               │
    │  code=... code_verifier=...  │                               │
    │──────────────────────────────────────────────────────────────>
    │  { access_token, refresh_token }                             │
    │<──────────────────────────────────────────────────────────────

The gateway's POST /oauth/register is the only new endpoint. /oauth2/authorize and /oauth2/token are served directly by the Cognito Hosted UI — the gateway is not in that path.

Step 1 — Dynamic Client Registration

POST /oauth/register
Content-Type: application/json

{
  "redirect_uris": ["https://your-mcp-client.example.com/callback"],
  "client_name": "My MCP Client",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "openid email profile"
}

Response 201 Created:

{
  "client_id": "7abc123...",
  "client_secret": "...",
  "client_id_issued_at": 1751338800,
  "client_secret_expires_at": 0,
  "redirect_uris": ["https://your-mcp-client.example.com/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "client_secret_basic",
  "client_name": "My MCP Client",
  "scope": "openid email profile"
}
  • redirect_uris — required; must be HTTPS (or http://localhost for local development).
  • grant_types — optional, defaults to ["authorization_code", "refresh_token"].
  • scope — optional, defaults to openid email profile.
  • client_secret_expires_at: 0 means the secret does not expire.

Step 2 — Authorization Code + PKCE

Redirect the user to:

https://<COGNITO_DOMAIN>/oauth2/authorize
  ?response_type=code
  &client_id=<client_id>
  &redirect_uri=<redirect_uri>
  &scope=openid+email+profile
  &code_challenge=<S256_code_challenge>
  &code_challenge_method=S256

Step 3 — Token Exchange

POST https://<COGNITO_DOMAIN>/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=<client_id>
&client_secret=<client_secret>
&code=<auth_code>
&redirect_uri=<redirect_uri>
&code_verifier=<code_verifier>

The MCP client stores the returned refresh_token in its install context for subsequent API calls.

Environment Variables

WORKSPACE_MODE is the only variable local development needs; everything below is either an aws-mode detail (supplied by the CDK task definition or /aprovan/<env>/env in SSM) or a tuning knob.

Runtime

| Variable | Default | Description | |---|---|---| | WORKSPACE_MODE | local | local (SQLite) or aws (DynamoDB + S3 + Cognito) | | WORKSPACE_PORT | 4000 | HTTP port | | WORKSPACE_DATA_DIR | ~/.aprovan | local-mode data directory (/data in the container) | | WORKSPACE_CRON | on | set 0 to opt out of contending for the cron lease | | WORKSPACE_CRON_SECRET | — | guards POST /hooks/cron/tick; required wherever auth is on | | WORKSPACE_ALLOW_INSECURE | — | set 1 to allow aws mode with auth off | | APROVAN_ENVIRONMENT | prd | which /aprovan/<env>/env bundle aws mode loads |

Identity

| Variable | Default | Description | |---|---|---| | OIDC_ISSUER / OIDCAUDIENCE | — | Cognito issuer + app client id; their presence is what turns auth on | | COGNITO_USER_POOL_ID | (required for DCR) | user pool for dynamic client registration | | COGNITO_DOMAIN | — | Cognito hosted UI domain (e.g. auth.example.com) | | COGNITO_REGION | AWS_REGION → us-east-1 | AWS region for Cognito |

Storage (aws mode)

| Variable | Default | Description | |---|---|---| | FS_BUCKET | (required) | workspace-fs content blobs; aws mode refuses to start without it | | FS_TABLE | FsFiles | workspace-fs pointer/version index | | RECORDS_TABLE | Records | record store | | CREDENTIALS_TABLE | Credentials | credential storage | | PERMISSIONS_TABLE | Permissions | permission storage | | AUDIT_TABLE | Audit | audit log |

Tuning

| Variable | Default | Description | |---|---|---| | GATEWAY_RATE_LIMIT_RPS | 10 | Requests/second per caller+provider | | GATEWAY_RATE_LIMIT_BURST | 20 | Burst capacity | | PROVIDER_CACHE_SIZE | 20 | Max providers held in the LRU provider module cache | | SANDBOX_MEMORY_LIMIT_MB | 32 | Per-guest heap ceiling for workflow scripts | | SANDBOX_POOL_MAX | 2 | Concurrent workflow guests; each costs a fixed 16 MiB of wasm memory | | TOOL_LIST_CACHE_TTL_MS | 300000 (5 min) | Per-workspace TTL for the GET /tools tool-list cache | | OTEL_EXPORTER_OTLP_ENDPOINT | (disabled) | OTLP/HTTP collector root. Setting it registers a tracer provider; leaving it unset means the OpenTelemetry SDK is never imported | | OTEL_SERVICE_NAME | workspace | service.name on exported spans |

Developing

pnpm dev          # tsx watch, local mode
pnpm test         # full suite; AWS-backend suites need `docker compose up -d` at the repo root
pnpm build        # tsc