Add webhook listener, replay, and trigger commands#57
Conversation
The partial mock broke other test files in CI because Bun's mock.module replaces the module globally. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds meta, progress, track status/primary/name/language_code fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed:
--jsonmode silently skips event forwarding in listen- I moved the forwarding fetch logic to run before output formatting so events are forwarded regardless of whether
--jsonis enabled.
- I moved the forwarding fetch logic to run before output formatting so events are forwarded regardless of whether
- ✅ Fixed: Dedup with fallback ID silently drops subsequent events
- I replaced the
'unknown'fallback with a generated UUID when an event ID is missing so deduplication no longer collapses unrelated ID-less events.
- I replaced the
Or push these changes by commenting:
@cursor push 80e606e934
Preview (80e606e934)
diff --git a/src/commands/webhooks/listen.ts b/src/commands/webhooks/listen.ts
--- a/src/commands/webhooks/listen.ts
+++ b/src/commands/webhooks/listen.ts
@@ -130,7 +130,10 @@
continue;
}
- const eventId = (parsed.id as string) ?? 'unknown';
+ const eventId =
+ typeof parsed.id === 'string' && parsed.id.length > 0
+ ? parsed.id
+ : crypto.randomUUID();
const eventType = (parsed.type as string) ?? sseEvent.event;
const timestamp = new Date().toISOString();
@@ -145,32 +148,43 @@
await appendEvent(storedEvent);
eventCount++;
+ let forwardStatusCode: number | undefined;
+ let forwardErrored = false;
+
+ if (options.forwardTo && signingSecret) {
+ try {
+ const body = JSON.stringify(parsed);
+ const fwdResponse = await fetch(options.forwardTo, {
+ method: 'POST',
+ headers: buildSignedHeaders(body, signingSecret),
+ body,
+ });
+ forwardStatusCode = fwdResponse.status;
+ if (forwardStatusCode >= 200 && forwardStatusCode < 300) {
+ forwardSuccess++;
+ } else {
+ forwardFail++;
+ }
+ } catch {
+ forwardFail++;
+ forwardErrored = true;
+ }
+ }
+
if (options.json) {
console.log(JSON.stringify(parsed));
} else {
const time = new Date().toLocaleTimeString();
let line = `[${time}] ${eventType.padEnd(30)} ${eventId}`;
- if (options.forwardTo && signingSecret) {
- try {
- const body = JSON.stringify(parsed);
- const fwdResponse = await fetch(options.forwardTo, {
- method: 'POST',
- headers: buildSignedHeaders(body, signingSecret),
- body,
- });
- const status = fwdResponse.status;
- if (status >= 200 && status < 300) {
- forwardSuccess++;
- line += ` ${colors.green(`[${status}]`)}`;
- } else {
- forwardFail++;
- line += ` ${colors.red(`[${status}]`)}`;
- }
- } catch {
- forwardFail++;
- line += ` ${colors.red('[ERR]')}`;
+ if (forwardStatusCode !== undefined) {
+ if (forwardStatusCode >= 200 && forwardStatusCode < 300) {
+ line += ` ${colors.green(`[${forwardStatusCode}]`)}`;
+ } else {
+ line += ` ${colors.red(`[${forwardStatusCode}]`)}`;
}
+ } else if (forwardErrored) {
+ line += ` ${colors.red('[ERR]')}`;
}
console.log(line);Add normalize_audio, passthrough, is_live, test, static_renditions, and expanded meta fields (creator_id, external_id). Set errored progress state to match docs (-1 progress). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Handle \r\n, \r, and \n line endings per WHATWG SSE spec - Remove legacy static_renditions (plural) event types - Add video.asset.non_standard_input_detected with realistic payload Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
…ion checks and exceptions
| @@ -0,0 +1,49 @@ | |||
| import { Command } from '@cliffy/command'; | |||
| import { listEvents } from '../../../lib/events-store.ts'; | |||
There was a problem hiding this comment.
Maybe in a follow up PR, but we should update our tsconfig so we can just use @ references and clean some of these imports up.
There was a problem hiding this comment.
yep, will follow up 👍🏼
| process.exit(1); | ||
| } | ||
|
|
||
| if (!eventId && !options.all) { |
There was a problem hiding this comment.
All of these checks in here should probably use Cliffy's built in flag conflicts feature
There was a problem hiding this comment.
Good shout -- that looked like a good option, but Cliffy's flag conflicts only works on Flag options (not arguments)
eventId is an argument, whereas --all is an option
mux webhooks events replay 5e6135cb-43ec-bd34-e571-5df88227b5c8
mux webhooks events replay --all
So I think the manual check is appropriate here? Or maybe changing the API?
- we now refer to the selected env as the 'current' env (prev it was 'defualt') - but when creating the first env it gets the name 'default' so that was confusing
… for event-id and --all flag

Summary
mux webhooks listen— connects to Mux's SSE event stream and optionally forwards events to a local dev server URL with--forward-to. Displays a signing secret for webhook verification. Reconnects automatically with exponential backoff on connection loss.mux webhooks events list— lists locally stored webhook events captured duringlistensessions.mux webhooks events replay <event-id>— replays stored events to a local URL, re-signed with the local secret.mux webhooks trigger <event-type>— sends a synthetic webhook event (with realistic payload) directly to a local URL for testing handlers without creating real resources.mux whoami— displays the current authenticated environment and user info.Webhook signing secrets are scoped per CLI environment so switching environments requires updating
MUX_WEBHOOK_SECRETin the app. Events are deduplicated by ID in local storage.Bug fixes
v1.0.0-beta.1) to avoid overwriting the stable formulaQOL improvement
The term
defaultenv was used in code and in user-facing listing of envs. but that's an over-loaded term, because the CLI also names the first envdefault:before

current(for backwards-compat the
defaultEnvironmentin~/.config/mux/config.jsonis still calleddefaultEnvironment)Test plan
mux webhooks listen --forward-toconnects, receives events, forwards with signingmux webhooks listen --json --forward-toforwards events while outputting JSONmux webhooks trigger video.asset.ready --forward-tosends signed synthetic event, passesmux.webhooks.unwrap()verificationmux webhooks events listshows captured events without duplicatesmux webhooks events replay <id> --forward-tore-forwards with valid signaturetriggerlists all available types🤖 Generated with Claude Code
Note
High Risk
Adds new commands that open long-lived SSE connections, write local event/secret files, and forward signed HTTP requests, increasing surface area for reliability and security issues. Also deletes
bin/muxwhilepackage.jsonstill points to it, which can break npm installs/publishing if not updated elsewhere.Overview
Adds webhook developer tooling. Introduces
mux webhooks listen(SSE stream with reconnect/backoff),mux webhooks events list/replay(local storage + replay/forward), andmux webhooks trigger(locally generated example events), including per-environment persistent signing secrets and request signing viamux-signature.Adds
mux whoamito call the System API and print authenticated org/environment/token info, and extendslib/mux.tswithgetMuxBaseUrl()(supportsMUX_BASE_URL) plusgetAuthHeaders()for rawfetchcalls.Environment terminology cleanup. Renames config helpers from
defaulttocurrent(getCurrentEnvironment/setCurrentEnvironment) and updates call sites/tests andmux env listoutput wording.Release/packaging changes. Skips Homebrew tap updates for pre-release tags, removes internal note/todo files, and deletes the Node shim entrypoint
bin/mux(verify npmbinwiring remains valid).Written by Cursor Bugbot for commit b6f4d6e. This will update automatically on new commits. Configure here.