@hasna/events 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @hasna/events
2
2
 
3
- Shared event envelopes, subscription config, webhook delivery, and command dispatch for Hasna open-source apps.
3
+ Shared event envelopes, local channels, replay, and delivery transports for Hasna open-source apps.
4
4
 
5
5
  This package is local-first. By default it stores JSON files under `~/.hasna/events`:
6
6
 
@@ -58,9 +58,49 @@ Envelope fields are:
58
58
 
59
59
  `source` should be the emitting app or bounded context. `type` should use dot notation such as `ticket.created`, `repo.synced`, or `check.failed`.
60
60
 
61
+ ## OpenAutomations Trigger Ingress
62
+
63
+ `@hasna/events` is trigger ingress for OpenAutomations. It records and delivers
64
+ event envelopes, but it does not own durable automation runs, action queues,
65
+ approvals, DLQ state, or replay decisions. `@hasna/automations` consumes the
66
+ same envelope shape and materializes matching events into durable automation
67
+ runs.
68
+
69
+ For automation-triggered events:
70
+
71
+ - set `source` to the emitting app or bounded context
72
+ - set `type` with dot notation, such as `ticket.created`
73
+ - set `subject` when the event describes one stable domain object
74
+ - set `dedupeKey` when the producer has a stable business identity
75
+ - keep `id` stable for the specific emitted envelope
76
+ - put only serializable trigger data in `data`
77
+ - keep secrets out of `data` and `metadata`; pass secret references instead
78
+
79
+ OpenAutomations derives idempotency from `dedupeKey` first and falls back to
80
+ `id` when no dedupe key is present. Replaying events through `events events
81
+ replay` re-delivers envelopes; OpenAutomations is still responsible for deciding
82
+ whether that delivery creates a new run, returns the existing idempotent run, or
83
+ creates an explicit replay request.
84
+
85
+ ## OpenLoops Task Notifications
86
+
87
+ `@hasna/events` is also notification ingress for OpenLoops task-created routes.
88
+ It delivers `todos` envelopes to configured channels, but it does not import
89
+ OpenLoops, create workflow invocations, own admission queue state, run agents,
90
+ or decide worker retry/backpressure policy. OpenLoops is the consumer that
91
+ handles an envelope, dedupes/upserts a work item, admits it when capacity is
92
+ available, and records workflow run manifests under `.hasna/loops/runs`.
93
+
94
+ Replay remains delivery-only. Replaying a `todos.task.created` or
95
+ `task.created` envelope sends the event to matching channels again; OpenLoops
96
+ decides whether that replay is ignored as an already-admitted task, resumes
97
+ existing work, or creates an explicit replay work item.
98
+
61
99
  ## Channels And Filters
62
100
 
63
- Channels are reusable subscriptions. They can be enabled or disabled, filtered by source/type/subject/severity, and configured with transport-specific settings.
101
+ Channels are reusable notification routes. They can be enabled or disabled,
102
+ filtered by source/type/subject/severity, and configured with
103
+ transport-specific settings.
64
104
 
65
105
  ```ts
66
106
  await events.addChannel({
@@ -69,7 +109,7 @@ await events.addChannel({
69
109
  transport: "webhook",
70
110
  filters: [{ type: "ticket.*", severity: ["warning", "error", "critical"] }],
71
111
  webhook: {
72
- url: "https://example.com/webhooks/hasna",
112
+ url: "https://example.com/channels/hasna",
73
113
  secret: process.env.HASNA_WEBHOOK_SECRET,
74
114
  },
75
115
  retry: {
@@ -123,6 +163,10 @@ window.
123
163
  ## Command Transport
124
164
 
125
165
  Command channels run a local process and pass the event on stdin and environment variables.
166
+ For production task-created automation, route to tested package commands such as
167
+ `loops events handle todos-task` rather than long-lived local scripts. Scripts
168
+ like `scripts/handle-event.ts` are useful prototypes; repeated behavior should
169
+ move into the owning `open-*` package with tests and bounded evidence.
126
170
 
127
171
  ```ts
128
172
  await events.addChannel({
@@ -192,16 +236,16 @@ const events = new EventsClient({
192
236
  The package exposes `events` and `hasna-events`.
193
237
 
194
238
  ```bash
195
- events webhooks add https://example.com/webhooks/hasna \
239
+ events channels add https://example.com/channels/hasna \
196
240
  --id ops \
197
241
  --type "ticket.*" \
198
242
  --secret "$HASNA_WEBHOOK_SECRET" \
199
243
  --retry-attempts 3 \
200
244
  --retry-backoff-ms 500
201
245
 
202
- events webhooks list
203
- events webhooks test ops
204
- events webhooks remove ops
246
+ events channels list
247
+ events channels test ops
248
+ events channels remove ops
205
249
  ```
206
250
 
207
251
  Field filters can match nested `data` or `metadata` values. Plain
@@ -216,7 +260,7 @@ legacy source/type/subject filters. For field paths ending in `_path` or `.path`
216
260
  `*` matches one path segment and `**` matches recursively.
217
261
 
218
262
  ```bash
219
- events webhooks add loops \
263
+ events channels add loops \
220
264
  --id open-source-task-route \
221
265
  --transport command \
222
266
  --source todos \
@@ -233,18 +277,18 @@ events webhooks add loops \
233
277
  --arg todos-task
234
278
 
235
279
  # Command args that begin with dashes can be passed either form:
236
- events webhooks add events --id json-route --transport command --arg --json
237
- events webhooks add events --id json-route --transport command --arg=--json
280
+ events channels add events --id json-route --transport command --arg --json
281
+ events channels add events --id json-route --transport command --arg=--json
238
282
 
239
283
  # For nested CLIs, put child positional args and flags after an explicit delimiter.
240
- events webhooks add events --id nested-route --transport command -- handle todos-task --json
284
+ events channels add events --id nested-route --transport command -- handle todos-task --json
241
285
 
242
- events webhooks match open-source-task-route \
286
+ events channels match open-source-task-route \
243
287
  --source todos \
244
288
  --type task.created \
245
289
  --metadata '{"project_path":"/home/hasna/workspace/hasna/opensource/open-events","route_enabled":true}'
246
290
 
247
- events webhooks test open-source-task-route --honor-filters \
291
+ events channels test open-source-task-route --honor-filters \
248
292
  --source todos \
249
293
  --type task.created \
250
294
  --metadata '{"project_path":"/tmp/outside","route_enabled":true}'
@@ -279,7 +323,7 @@ Use `--json` for script-friendly output and `--dir <path>` for isolated data.
279
323
 
280
324
  ## App Integration Pattern
281
325
 
282
- Apps should keep event emission near durable state changes and avoid hardcoding app-specific webhooks. The common pattern is:
326
+ Apps should keep event emission near durable state changes and avoid hardcoding app-specific channels. The common pattern is:
283
327
 
284
328
  ```ts
285
329
  import { EventsClient } from "@hasna/events";
package/dist/cli/index.js CHANGED
@@ -862,12 +862,12 @@ function printHelp(options = {}) {
862
862
  console.log(`${name} ${version()}
863
863
 
864
864
  Usage:
865
- ${name} [--dir <path>] [--json] webhooks add <url|command> [options]
866
- ${name} [--dir <path>] [--json] webhooks list
867
- ${name} [--dir <path>] [--json] webhooks remove <id>
868
- ${name} [--dir <path>] [--json] webhooks test <id>
869
- ${name} [--dir <path>] [--json] webhooks match <id>
870
- ${name} [--dir <path>] [--json] webhooks status
865
+ ${name} [--dir <path>] [--json] channels add <url|command> [options]
866
+ ${name} [--dir <path>] [--json] channels list
867
+ ${name} [--dir <path>] [--json] channels remove <id>
868
+ ${name} [--dir <path>] [--json] channels test <id>
869
+ ${name} [--dir <path>] [--json] channels match <id>
870
+ ${name} [--dir <path>] [--json] channels status
871
871
  ${name} [--dir <path>] [--json] status
872
872
  ${name} [--dir <path>] [--json] events emit <type>${options.source ? "" : " --source <source>"} [options]
873
873
  ${name} [--dir <path>] [--json] events list [--limit <n>]
@@ -876,17 +876,17 @@ Usage:
876
876
  Environment:
877
877
  HASNA_EVENTS_DIR or HASNA_EVENTS_HOME overrides the default ${getEventsDataDir()}`);
878
878
  }
879
- function printWebhooksHelp(options = {}) {
879
+ function printChannelsHelp(options = {}) {
880
880
  const name = commandName(options);
881
- console.log(`${name} webhooks
881
+ console.log(`${name} channels
882
882
 
883
883
  Usage:
884
- ${name} [--dir <path>] [--json] webhooks add <url|command> [options]
885
- ${name} [--dir <path>] [--json] webhooks list
886
- ${name} [--dir <path>] [--json] webhooks remove <id>
887
- ${name} [--dir <path>] [--json] webhooks test <id>
888
- ${name} [--dir <path>] [--json] webhooks match <id>
889
- ${name} [--dir <path>] [--json] webhooks status
884
+ ${name} [--dir <path>] [--json] channels add <url|command> [options]
885
+ ${name} [--dir <path>] [--json] channels list
886
+ ${name} [--dir <path>] [--json] channels remove <id>
887
+ ${name} [--dir <path>] [--json] channels test <id>
888
+ ${name} [--dir <path>] [--json] channels match <id>
889
+ ${name} [--dir <path>] [--json] channels status
890
890
 
891
891
  Options:
892
892
  --id <id> Channel id for add
@@ -898,20 +898,20 @@ Options:
898
898
  --metadata <path=value|path!=value> Event metadata field filter, repeatable; strings, dot paths, array-member matching, * segment wildcard, ** recursive wildcard
899
899
  --data-json <path=json|path!=json> Event data field filter with typed JSON value
900
900
  --metadata-json <path=json|path!=json> Event metadata field filter with typed JSON value
901
- --honor-filters On webhooks test, skip delivery when the sample event does not match filters
901
+ --honor-filters On channels test, skip delivery when the sample event does not match filters
902
902
  --transport <kind> webhook or command
903
903
  --secret <secret> Webhook signing secret
904
904
  --header <name=value> Webhook header, repeatable
905
905
  --redact <path> Redaction path, repeatable
906
906
  --no-deliver Available on events emit`);
907
907
  }
908
- function printWebhookAddHelp(options = {}) {
908
+ function printChannelAddHelp(options = {}) {
909
909
  const name = commandName(options);
910
- console.log(`${name} webhooks add
910
+ console.log(`${name} channels add
911
911
 
912
912
  Usage:
913
- ${name} [--dir <path>] [--json] webhooks add <url|command> [options]
914
- ${name} [--dir <path>] [--json] webhooks add <command> --transport command [options] -- [command-args...]
913
+ ${name} [--dir <path>] [--json] channels add <url|command> [options]
914
+ ${name} [--dir <path>] [--json] channels add <command> --transport command [options] -- [command-args...]
915
915
 
916
916
  Options:
917
917
  --id <id> Channel id
@@ -935,10 +935,10 @@ Options:
935
935
  --disabled Create channel disabled
936
936
 
937
937
  Examples:
938
- ${name} webhooks add https://example.com/webhooks/hasna --id ops --retry-attempts 3 --retry-backoff-ms 500
939
- ${name} webhooks add bun --id command-hook --transport command --arg run --arg ./handler.ts --arg --json
940
- ${name} webhooks add bun --id command-hook --transport command --arg=--json
941
- ${name} webhooks add bun --id command-hook --transport command -- run ./handler.ts --json`);
938
+ ${name} channels add https://example.com/channels/hasna --id ops --retry-attempts 3 --retry-backoff-ms 500
939
+ ${name} channels add bun --id command-hook --transport command --arg run --arg ./handler.ts --arg --json
940
+ ${name} channels add bun --id command-hook --transport command --arg=--json
941
+ ${name} channels add bun --id command-hook --transport command -- run ./handler.ts --json`);
942
942
  }
943
943
  function printEventsHelp(options = {}) {
944
944
  const name = commandName(options);
@@ -957,7 +957,7 @@ Options:
957
957
  --dedupe-key <key> Deduplicate repeated events
958
958
  --data <json> JSON object payload
959
959
  --metadata <json> JSON object metadata
960
- --no-deliver Record without delivering webhooks
960
+ --no-deliver Record without delivering channels
961
961
  --dry-run Preview replay matches without delivery`);
962
962
  }
963
963
  async function runEventsCli(argv = process.argv.slice(2), options = {}) {
@@ -981,20 +981,20 @@ async function runEventsCli(argv = process.argv.slice(2), options = {}) {
981
981
  }
982
982
  const store = new JsonEventsStore(parsed.dir);
983
983
  const client = new EventsClient({ store });
984
- if (group === "webhooks") {
984
+ if (group === "channels") {
985
985
  if (!command || command === "--help" || command === "-h") {
986
- printWebhooksHelp(options);
986
+ printChannelsHelp(options);
987
987
  return;
988
988
  }
989
989
  if (command === "add" && (tail[0] === "--help" || tail[0] === "-h")) {
990
- printWebhookAddHelp(options);
990
+ printChannelAddHelp(options);
991
991
  return;
992
992
  }
993
993
  if (tail.includes("--help") || tail.includes("-h")) {
994
- printWebhooksHelp(options);
994
+ printChannelsHelp(options);
995
995
  return;
996
996
  }
997
- await handleWebhooks(client, command, tail, parsed, options);
997
+ await handleChannels(client, command, tail, parsed, options);
998
998
  return;
999
999
  }
1000
1000
  if (group === "events") {
@@ -1011,7 +1011,7 @@ async function runEventsCli(argv = process.argv.slice(2), options = {}) {
1011
1011
  }
1012
1012
  throw new Error(`Unknown command group: ${group}`);
1013
1013
  }
1014
- async function handleWebhooks(client, command, tail, parsed, options) {
1014
+ async function handleChannels(client, command, tail, parsed, options) {
1015
1015
  if (command === "add") {
1016
1016
  const { args, delimiterArgs } = splitDelimiter(tail);
1017
1017
  const transport = takeOption(args, "--transport") ?? "webhook";
@@ -1028,7 +1028,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
1028
1028
  const filters = parseFilter(args);
1029
1029
  const target = args[0];
1030
1030
  if (!target)
1031
- throw new Error("webhooks add requires a URL or command target");
1031
+ throw new Error("channels add requires a URL or command target");
1032
1032
  const now2 = new Date().toISOString();
1033
1033
  const channel = {
1034
1034
  id,
@@ -1077,7 +1077,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
1077
1077
  if (command === "remove") {
1078
1078
  const id = tail[0];
1079
1079
  if (!id)
1080
- throw new Error("webhooks remove requires a channel id");
1080
+ throw new Error("channels remove requires a channel id");
1081
1081
  const removed = await client.removeChannel(id);
1082
1082
  output(parsed, { removed }, () => console.log(removed ? `Removed ${id}` : `Channel not found: ${id}`));
1083
1083
  return;
@@ -1086,7 +1086,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
1086
1086
  const args = [...tail];
1087
1087
  const id = args.shift();
1088
1088
  if (!id)
1089
- throw new Error("webhooks test requires a channel id");
1089
+ throw new Error("channels test requires a channel id");
1090
1090
  const honorFilters = takeFlag(args, "--honor-filters");
1091
1091
  const result = await client.testChannel(id, {
1092
1092
  source: takeOption(args, "--source") ?? options.source ?? "hasna.events",
@@ -1103,7 +1103,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
1103
1103
  const args = [...tail];
1104
1104
  const id = args.shift();
1105
1105
  if (!id)
1106
- throw new Error("webhooks match requires a channel id");
1106
+ throw new Error("channels match requires a channel id");
1107
1107
  const result = await client.matchChannel(id, {
1108
1108
  source: takeOption(args, "--source") ?? options.source ?? "hasna.events",
1109
1109
  type: takeOption(args, "--type") ?? "events.test",
@@ -1115,7 +1115,7 @@ async function handleWebhooks(client, command, tail, parsed, options) {
1115
1115
  output(parsed, result, () => console.log(`${result.matched ? "matched" : "skipped"}: ${result.channelId}`));
1116
1116
  return;
1117
1117
  }
1118
- throw new Error(`Unknown webhooks command: ${command ?? ""}`);
1118
+ throw new Error(`Unknown channels command: ${command ?? ""}`);
1119
1119
  }
1120
1120
  function splitDelimiter(values) {
1121
1121
  const delimiterIndex = values.indexOf("--");
@@ -5,10 +5,10 @@ export interface RegisterEventsCommandsOptions {
5
5
  source: string;
6
6
  dataDir?: string;
7
7
  createClient?: () => EventsClient;
8
- webhooksCommandName?: string;
8
+ channelsCommandName?: string;
9
9
  eventsCommandName?: string;
10
10
  }
11
- export declare function registerWebhookCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
11
+ export declare function registerChannelCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
12
12
  export declare function registerEventCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): CommanderCommandLike;
13
13
  export declare function registerEventsCommands(program: CommanderLike, options: RegisterEventsCommandsOptions): void;
14
14
  export {};
package/dist/commander.js CHANGED
@@ -795,9 +795,9 @@ function hasJsonOption(options) {
795
795
  function wantsJson(actionOptions, command) {
796
796
  return hasJsonOption(actionOptions) || hasJsonOption(command);
797
797
  }
798
- function registerWebhookCommands(program, options) {
799
- const webhooks = program.command(options.webhooksCommandName ?? "webhooks").description("Manage Hasna event webhook subscriptions");
800
- webhooks.command("add").description("Add or replace a webhook or command subscription").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "Subscription/channel identifier").option("--transport <kind>", "Transport kind: webhook or command", "webhook").option("--name <name>", "Display name").option("--type <pattern>", "Event type filter, e.g. todos.task.*").option("--source <pattern>", "Event source filter").option("--subject <pattern>", "Event subject filter").option("--severity <pattern>", "Event severity filter").option("--data <path=value...>", "Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--metadata <path=value...>", "Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--data-json <path=json...>", "Event data field filter with typed JSON value; path!=json negatives supported", collectValues, []).option("--metadata-json <path=json...>", "Event metadata field filter with typed JSON value; path!=json negatives supported", collectValues, []).option("--secret <secret>", "Webhook HMAC secret").option("--header <name=value...>", "Webhook header", collectValues, []).option("--arg <arg...>", "Command argument", collectValues, []).option("--timeout-ms <ms>", "Transport timeout in milliseconds", parseNumber).option("--retry-attempts <n>", "Maximum delivery attempts", parseNumber).option("--retry-backoff-ms <ms>", "Initial retry backoff in milliseconds", parseNumber).option("--redact <path...>", "Event field path to redact before delivery", collectValues, []).option("--disabled", "Create channel disabled", false).option("-j, --json", "Print JSON output", false).action(async (target, actionOptions, command) => {
798
+ function registerChannelCommands(program, options) {
799
+ const channels = program.command(options.channelsCommandName ?? "channels").description("Manage Hasna event channels");
800
+ channels.command("add").description("Add or replace a channel").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "Channel identifier").option("--transport <kind>", "Transport kind: webhook or command", "webhook").option("--name <name>", "Display name").option("--type <pattern>", "Event type filter, e.g. todos.task.*").option("--source <pattern>", "Event source filter").option("--subject <pattern>", "Event subject filter").option("--severity <pattern>", "Event severity filter").option("--data <path=value...>", "Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--metadata <path=value...>", "Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--data-json <path=json...>", "Event data field filter with typed JSON value; path!=json negatives supported", collectValues, []).option("--metadata-json <path=json...>", "Event metadata field filter with typed JSON value; path!=json negatives supported", collectValues, []).option("--secret <secret>", "Webhook HMAC secret").option("--header <name=value...>", "Webhook header", collectValues, []).option("--arg <arg...>", "Command argument", collectValues, []).option("--timeout-ms <ms>", "Transport timeout in milliseconds", parseNumber).option("--retry-attempts <n>", "Maximum delivery attempts", parseNumber).option("--retry-backoff-ms <ms>", "Initial retry backoff in milliseconds", parseNumber).option("--redact <path...>", "Event field path to redact before delivery", collectValues, []).option("--disabled", "Create channel disabled", false).option("-j, --json", "Print JSON output", false).action(async (target, actionOptions, command) => {
801
801
  const timestamp = new Date().toISOString();
802
802
  const channel = {
803
803
  id: actionOptions.id,
@@ -820,29 +820,29 @@ function registerWebhookCommands(program, options) {
820
820
  const saved = await createClient(options).addChannel(channel);
821
821
  print(sanitizeChannelForOutput(saved), wantsJson(actionOptions, command), `Added ${saved.transport} channel ${saved.id}`);
822
822
  });
823
- webhooks.command("list").description("List configured subscriptions").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
824
- const channels = await createClient(options).listChannels();
823
+ channels.command("list").description("List configured channels").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
824
+ const channels2 = await createClient(options).listChannels();
825
825
  if (wantsJson(actionOptions, command)) {
826
- console.log(JSON.stringify(sanitizeChannelsForOutput(channels), null, 2));
826
+ console.log(JSON.stringify(sanitizeChannelsForOutput(channels2), null, 2));
827
827
  return;
828
828
  }
829
- if (!channels.length) {
829
+ if (!channels2.length) {
830
830
  console.log("No channels configured.");
831
831
  return;
832
832
  }
833
- for (const channel of channels) {
833
+ for (const channel of channels2) {
834
834
  console.log(`${channel.id} ${channel.enabled ? "enabled" : "disabled"} ${channel.transport} ${channel.webhook?.url ?? channel.command?.command ?? channel.transport}`);
835
835
  }
836
836
  });
837
- webhooks.command("status").description("Show events webhook storage status").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
837
+ channels.command("status").description("Show events channel storage status").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
838
838
  const status = await getEventsStatus(options.dataDir);
839
839
  print(status, wantsJson(actionOptions, command), `events dataDir: ${status.dataDir}`);
840
840
  });
841
- webhooks.command("remove").description("Remove a subscription").argument("<id>", "Subscription/channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
841
+ channels.command("remove").description("Remove a channel").argument("<id>", "Channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
842
842
  const removed = await createClient(options).removeChannel(id);
843
843
  print({ removed }, wantsJson(actionOptions, command), removed ? `Removed ${id}` : `Channel not found: ${id}`);
844
844
  });
845
- webhooks.command("test").description("Send a test event to one subscription").argument("<id>", "Subscription/channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events test delivery").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("--honor-filters", "Skip delivery when the sample event does not match channel filters", false).option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
845
+ channels.command("test").description("Send a test event to one channel").argument("<id>", "Channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events test delivery").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("--honor-filters", "Skip delivery when the sample event does not match channel filters", false).option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
846
846
  const result = await createClient(options).testChannel(id, {
847
847
  source: actionOptions.source ?? options.source,
848
848
  type: actionOptions.type,
@@ -853,7 +853,7 @@ function registerWebhookCommands(program, options) {
853
853
  }, { honorFilters: actionOptions.honorFilters });
854
854
  print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
855
855
  });
856
- webhooks.command("match").description("Check whether a sample event matches one subscription without delivering").argument("<id>", "Subscription/channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events match preview").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
856
+ channels.command("match").description("Check whether a sample event matches one channel without delivering").argument("<id>", "Channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events match preview").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
857
857
  const result = await createClient(options).matchChannel(id, {
858
858
  source: actionOptions.source ?? options.source,
859
859
  type: actionOptions.type,
@@ -864,7 +864,7 @@ function registerWebhookCommands(program, options) {
864
864
  });
865
865
  print(result, wantsJson(actionOptions, command), `${result.matched ? "matched" : "skipped"}: ${result.channelId}`);
866
866
  });
867
- return webhooks;
867
+ return channels;
868
868
  }
869
869
  function registerEventCommands(program, options) {
870
870
  const events = program.command(options.eventsCommandName ?? "events").description("Emit, list, and replay Hasna events");
@@ -912,7 +912,7 @@ function registerEventCommands(program, options) {
912
912
  return events;
913
913
  }
914
914
  function registerEventsCommands(program, options) {
915
- registerWebhookCommands(program, options);
915
+ registerChannelCommands(program, options);
916
916
  registerEventCommands(program, options);
917
917
  }
918
918
  function parseNumber(value) {
@@ -926,7 +926,7 @@ function collectValues(value, previous) {
926
926
  return previous;
927
927
  }
928
928
  export {
929
- registerWebhookCommands,
930
929
  registerEventsCommands,
931
- registerEventCommands
930
+ registerEventCommands,
931
+ registerChannelCommands
932
932
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hasna/events",
3
- "version": "0.1.11",
4
- "description": "Shared event envelopes, local subscriptions, and webhook delivery for Hasna open-source apps",
3
+ "version": "0.1.13",
4
+ "description": "Shared event envelopes, local channels, replay, and delivery transports for Hasna open-source apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "keywords": [
54
54
  "events",
55
- "webhooks",
55
+ "channels",
56
56
  "cli",
57
57
  "typescript",
58
58
  "bun",