diff --git a/README.md b/README.md index b306ffd..048edac 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,24 @@ # webhook-objects -A monorepo for working with webhook objects from a Gather user perspective. +> Note: These examples are built with AI, and are designed to be "food for thought", not recommendations that are fit for production use. -See the individual packages for more info: +A collection of quick examples for using Webhook Objects within [Gather 2.0](https://gather.town). -- [`@webhook-objects/client`](./packages/client) -- [`@webhook-objects/now-playing-inbox`](./packages/now-playing-inbox) -- [`@webhook-objects/gh-prs-inbox`](./packages/gh-prs-inbox) -- [`@webhook-objects/claude-status`](./packages/claude-status) -- [`@webhook-objects/low-battery-switch`](./packages/low-battery-switch) +## Getting Started + +To get started, you can explore our individual packages: + +* [`@webhook-objects/now-playing-inbox`](./packages/now-playing-inbox) +* [`@webhook-objects/gh-prs-inbox`](./packages/gh-prs-inbox) +* [`@webhook-objects/claude-status`](./packages/claude-status) +* [`@webhook-objects/low-battery-switch`](./packages/low-battery-switch) + +## Support + +We're committed to helping you get the most out of webhook-objects. That said, the examples provided here are just that - examples. We recommend using these as a jumping off point, not a recipe for production-quality solutions. + +Further - While PRs are welcome, they're reviewed on a best-effort basis. If you have specific questions, please reach out to our support team directly via [gather.town/contact-us](https://gather.town/contact-us). + +## License + +This project is dual licensed under MIT and Apache 2.0. diff --git a/packages/claude-status/README.md b/packages/claude-status/README.md index 72a162e..25d970b 100644 --- a/packages/claude-status/README.md +++ b/packages/claude-status/README.md @@ -1,10 +1,11 @@ # @webhook-objects/claude-status -PoC. Mirrors a Claude Code session's live status into a `status` webhook object, -driven by Claude's own hooks (no polling). +Mirrors a Claude Code session's live status into a `status` webhook object, driven by Claude Hooks. ## Usage +> Note: This example has a unique installation flow, due to challenges with `PATH` when running from within Claude. As a result, we don't use `tsx` to run this. + ```sh pnpm --filter @webhook-objects/claude-status build diff --git a/packages/client/README.md b/packages/client/README.md index a96d3f6..0fe904d 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,156 +1 @@ -# @webhook-objects/client - -A typed client for dispatching [Standard Webhooks](https://www.standardwebhooks.com/)-signed events to Gather webhook objects, plus the full set of TypeScript types describing every event, capability, preset, and response. - -The client signs each request (HMAC over `id`, `timestamp`, and body), `POST`s it to your object's webhook URL, and returns a strongly-typed response body. - -## Installation - -```bash -npm install @webhook-objects/client -# or -pnpm add @webhook-objects/client -``` - -### Runtime & `fetch` - -- **Browser / Node 18+**: nothing extra needed — the global `fetch` is used by default. -- **Older Node / explicit `undici`**: install [`undici`](https://github.com/nodejs/undici) (an optional peer dependency) and import from `@webhook-objects/client/node`, which defaults `fetchImpl` to undici's `fetch` and falls back to the global `fetch` when undici isn't present. - -```bash -pnpm add undici # only needed for the /node entry point's undici-backed default -``` - -## Entry points - -| Import | Use case | Default `fetch` | -| --------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------ | -| `@webhook-objects/client` | Auto-resolves to the browser build (and node build under the `node` condition) | global `fetch` | -| `@webhook-objects/client/browser` | Explicit browser build | global `fetch` | -| `@webhook-objects/client/node` | Node build | `undici` (lazy import), falling back to global `fetch` | -| `@webhook-objects/client/objects` | Types only — no client | n/a | - -## Usage - -### Send a capability event - -```ts -import { Client } from "@webhook-objects/client/node"; - -const client = new Client({ - url: "https://api.gather.town/api/v2/hooks/spaces//objects/", - secret: "whsec_...", -}); - -const res = await client.send({ - type: "counter.set", - timestamp: new Date().toISOString(), - data: { count: 1 }, -}); - -if (res.status === "dispatched" || res.status === "space_idle") { - // applied -} -``` - -The `event` argument is a discriminated union: the `type` (`"."`) determines the exact shape required for `data`. Unknown properties and mismatched payloads are rejected at compile time. - -### Probe object metadata (`webhook.ping`) - -```ts -const meta = await client.requestMetadata(); - -meta.status; // "pong" -meta.objectId; // string -meta.spaceId; // string -meta.preset; // "counter" | "switch" | "inbox" | "status" | null -meta.capabilities; // declared capability state for the preset -``` - -### Browser - -```ts -import { Client } from "@webhook-objects/client/browser"; - -const client = new Client({ url, secret }); -``` - -## API - -### `new Client(options)` - -| Option | Type | Description | -| -------------- | ---------------- | ---------------------------------------------------------------- | -| `url` | `URL \| string` | The object's webhook receiver URL. | -| `secret` | `string` | Standard Webhooks signing secret (`whsec_...`). | -| `signOptions?` | `WebhookOptions` | Passed through to `standardwebhooks`. | -| `fetchImpl?` | `FetchImpl` | Override the `fetch` used. Defaults per entry point. | -| `idImpl?` | `IdImpl` | Override webhook id generation. Defaults to `msg_${Date.now()}`. | - -> The `/node` `Client` accepts the same options; it only changes the default `fetchImpl`. - -### `client.send(event, init?)` - -Signs and `POST`s a capability event. Returns `Promise` (`{ status: "dispatched" }` or `{ status: "space_idle" }`). The optional `init` is a `RequestInit` minus `body`, `method`, and `window` (those are controlled by the client) — use it for custom headers, `signal`, etc. - -### `client.requestMetadata()` - -Sends a `webhook.ping` and returns `Promise` describing the object's preset and current capability state. - -### Errors - -`send` / `requestMetadata` reject with an `Error` when: - -- the response status is not `2xx` — `cause` is `{ response }`. -- the body isn't valid JSON — `cause` is `{ error, response }`. - -## Capabilities, methods & presets - -Events are addressed as `"."`. The capabilities and their method payloads: - -| Capability | Method | `data` | -| ---------- | ----------- | ---------------------------------------------------------------- | -| `info` | `set` | `{ name?, description? }` | -| `counter` | `set` | `{ count: number }` | -| `counter` | `increment` | `{ by?: number }` | -| `counter` | `reset` | `{}` | -| `switch` | `set_state` | `{ on: boolean }` | -| `switch` | `toggle` | `{}` | -| `status` | `set` | `{ state: "off" \| "on" \| "question" \| "alert" \| "working" }` | -| `status` | `reset` | `{}` | -| `activity` | `add` | `{ id, text, url? }` | -| `activity` | `remove` | `{ id }` | -| `activity` | `clear` | `{}` | - -Presets compose a fixed set of capabilities (every preset also includes `info`): - -| Preset | Capabilities | -| --------- | ----------------------------- | -| `counter` | `info`, `counter` | -| `switch` | `info`, `switch` | -| `inbox` | `info`, `activity`, `counter` | -| `status` | `info`, `status`, `activity` | - -## Types - -All event, capability, preset, and response types are exported from the package root and from `@webhook-objects/client/objects`. Notable ones: - -- `WebhookEvent` — the union of all sendable capability events. -- `PresetWebhookEvent

` — events accepted by an object of preset `P`. -- `WebhookEventResponseBody`, `PingResponseBody` — success bodies. -- `WebhookHttpResponse`, `WebhookErrorResponse`, `WebhookErrorCode` — the full receiver response surface. - -## Development - -```bash -pnpm test # vitest + coverage (node + chromium projects) -pnpm build # vite build (JS) + tsc (.d.ts into dist/types) -``` - -Tests run in two projects: `node` (all specs) and `chromium` (browser-safe specs, -via `@vitest/browser` + Playwright). Target one with `vitest run --project node` -or `vitest run --project chromium`. - -## License - -Apache-2.0 OR MIT +TODO: Remove in favor of 1P SDK. diff --git a/packages/gh-prs-inbox/README.md b/packages/gh-prs-inbox/README.md index 38e1aee..4cfbe10 100644 --- a/packages/gh-prs-inbox/README.md +++ b/packages/gh-prs-inbox/README.md @@ -1,8 +1,6 @@ # @webhook-objects/gh-prs-inbox -PoC. Polls GitHub for PRs awaiting your review and mirrors them into an `inbox` -webhook object — one activity entry per PR, plus a counter of how many are -waiting. +Uses the GitHub CLI to poll for PRs awaiting your review, mirroring them to an `inbox` webhook object. Requires the [`gh`](https://cli.github.com) CLI, authenticated (`gh auth login`). diff --git a/packages/low-battery-switch/README.md b/packages/low-battery-switch/README.md index e6ce166..ff37269 100644 --- a/packages/low-battery-switch/README.md +++ b/packages/low-battery-switch/README.md @@ -1,8 +1,6 @@ # @webhook-objects/low-battery-switch -PoC. Polls macOS power state and mirrors "running low" into a `switch` webhook -object — on when you're on battery at/below a threshold, off otherwise. A shared -"might drop off the call" light for the space. +Updates a `switch` webhook object based on the current macOS power state. Turned on when your battery is running low. macOS only — reads power via the built-in `pmset` (no extra deps). diff --git a/packages/now-playing-inbox/README.md b/packages/now-playing-inbox/README.md index c2e880e..8d2ac5e 100644 --- a/packages/now-playing-inbox/README.md +++ b/packages/now-playing-inbox/README.md @@ -1,9 +1,8 @@ # @webhook-objects/now-playing-inbox -macOS-only PoC. Watches what's playing (Spotify, then Music) and posts each new -track to an `inbox` webhook object's activity feed. +Watches your system for playing music (via Spotify, Applie Music), posting any new tracks to an `inbox` webhook object. -An inbox webhook object showing recently played songs +macOS only — Uses Apple Script to parse music application state. ## Usage diff --git a/packages/now-playing-inbox/docs/preview.png b/packages/now-playing-inbox/docs/preview.png deleted file mode 100644 index 423c583..0000000 Binary files a/packages/now-playing-inbox/docs/preview.png and /dev/null differ