@automagik/omni 2.260908.10 → 2.260908.12

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.
@@ -0,0 +1,46 @@
1
+ -- Durable named event consumers (#989, RFC #925 G7 — the durable half of the
2
+ -- event-consumer surface; the one-shot half, `omni events wait`, shipped in
3
+ -- PR #972).
4
+ --
5
+ -- omni_events.journal_seq: a monotonic journal position assigned by a
6
+ -- sequence at insert. The durable-consumer cursor is "last acked journal_seq"
7
+ -- and delivery resumes strictly after it. receivedAt cannot anchor an
8
+ -- at-least-once cursor — it carries the PUBLISHER's clock and lands out of
9
+ -- order under consumer retries. BIGSERIAL backfills existing rows (physical
10
+ -- order; they predate every consumer, so the order among them is moot).
11
+ --
12
+ -- durable_consumers: the registry — name (globally unique), event-type filter
13
+ -- (trailing-* prefix glob, the #966 contract), optional payload conditions
14
+ -- (same matcher as `events wait --filter` / automation triggers), and the
15
+ -- cursor. NOT consumer_offsets: that table is owned by the NATS subscription
16
+ -- layer (gap detection over NATS stream sequences); this registry has an
17
+ -- API-managed lifecycle and a different sequence space (the journal IS the
18
+ -- replay source, NATS is transport).
19
+ --
20
+ -- TENANCY: global — no tenant_id column, following the event_schemas
21
+ -- precedent (0056): the RLS coverage gate requires every tenant_id-bearing
22
+ -- table to be in the frozen G0 manifest, the G1 tenant plane, or the
23
+ -- runtime-denied exclusions. Event reads stay tenant-policed through
24
+ -- omni_events' own RLS; per-tenant consumer ownership joins additively in
25
+ -- the G6+ ownership pass.
26
+ --
27
+ -- Hand-written following the 0043/0044/0052 precedent (additive, idempotent).
28
+ --
29
+ -- NOTE: no explicit BEGIN/COMMIT — the boot migrator executes this file on a
30
+ -- pooled postgres-js connection, which rejects raw transaction control.
31
+
32
+ ALTER TABLE "omni_events" ADD COLUMN IF NOT EXISTS "journal_seq" bigserial;
33
+ --> statement-breakpoint
34
+ CREATE UNIQUE INDEX IF NOT EXISTS "omni_events_journal_seq_uq" ON "omni_events" ("journal_seq");
35
+ --> statement-breakpoint
36
+ CREATE TABLE IF NOT EXISTS "durable_consumers" (
37
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
38
+ "name" varchar(100) NOT NULL,
39
+ "event_type" varchar(255) NOT NULL,
40
+ "filters" jsonb,
41
+ "cursor" bigint DEFAULT 0 NOT NULL,
42
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
43
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
44
+ );
45
+ --> statement-breakpoint
46
+ CREATE UNIQUE INDEX IF NOT EXISTS "durable_consumers_name_uq" ON "durable_consumers" ("name");
@@ -449,6 +449,13 @@
449
449
  "when": 1786300000000,
450
450
  "tag": "0063_automation_manifest_provenance",
451
451
  "breakpoints": true
452
+ },
453
+ {
454
+ "idx": 64,
455
+ "version": "7",
456
+ "when": 1786400000000,
457
+ "tag": "0064_durable_consumers",
458
+ "breakpoints": true
452
459
  }
453
460
  ]
454
461
  }
@@ -117,6 +117,53 @@ export declare function matchesWaitEvent(event: WaitEventRow, filters: StreamFil
117
117
  * accepted transport (the issue's non-goal keeps SSE/WS out of scope).
118
118
  */
119
119
  export declare function waitForEvent(client: OmniClient, params: WaitParams): Promise<WaitEventRow | null>;
120
+ /** A durable consumer as the API returns it (registration + live lag). */
121
+ interface ConsumerData {
122
+ id: string;
123
+ name: string;
124
+ eventType: string;
125
+ filters: AutomationCondition[] | null;
126
+ cursor: number;
127
+ head: number;
128
+ lag: number;
129
+ createdAt: string;
130
+ updatedAt: string;
131
+ }
132
+ /**
133
+ * Raw CLI→API call for the consumer endpoints — the generated SDK does not
134
+ * cover them yet (`schemaApiRequest` precedent; the CLI only ever talks to
135
+ * the API, never the database).
136
+ */
137
+ export declare function consumersApiRequest<T>(path: string, init?: {
138
+ method?: string;
139
+ body?: string;
140
+ }): Promise<T>;
141
+ export declare function summarizeConsumerRow(row: ConsumerData): Record<string, unknown>;
142
+ export interface FollowParams {
143
+ consumer: string;
144
+ /** Max rows scanned per pull page. */
145
+ limit: number;
146
+ /** Server-side long-poll window per pull when the journal is idle. */
147
+ waitMs: number;
148
+ /** Advance the cursor as pages are printed (default). false = peek one page, exit. */
149
+ ack: boolean;
150
+ /** Exit 0 once caught up (no new rows scanned) instead of tailing forever. */
151
+ untilIdle: boolean;
152
+ /** External stop signal (SIGINT). */
153
+ isStopped?: () => boolean;
154
+ /** Line sink — defaults to output.raw (stdout). Injected by tests. */
155
+ emit?: (line: string) => void;
156
+ }
157
+ /**
158
+ * The `omni events follow` loop: pull a page from the stored cursor, print
159
+ * each event as a JSON line, ack the SCANNED cursor (so filtered-out rows are
160
+ * skipped too), repeat. Resumes exactly-after-cursor across restarts because
161
+ * progress lives server-side.
162
+ *
163
+ * With `ack: false` the stored cursor never moves, so a loop would replay the
164
+ * same page forever — peek mode prints ONE page and returns.
165
+ */
166
+ export declare function followConsumer(params: FollowParams): Promise<void>;
120
167
  export declare function createEventsCommand(): Command;
121
168
  export {};
122
169
  //# sourceMappingURL=events.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/commands/events.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/commands/events.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -41075,15 +41075,66 @@ function extractMessages(context, promptOverride) {
41075
41075
  return { error: "message content not found in payload" };
41076
41076
  return [messageContent];
41077
41077
  }
41078
+ function buildChatlessMessages(context, promptOverride) {
41079
+ if (promptOverride !== undefined) {
41080
+ const rendered = substituteTemplate(promptOverride, context);
41081
+ if (!rendered)
41082
+ return { error: "promptOverride rendered to an empty string" };
41083
+ return [rendered];
41084
+ }
41085
+ if (context.event) {
41086
+ return [
41087
+ JSON.stringify({
41088
+ id: context.event.id,
41089
+ type: context.event.type,
41090
+ payload: context.payload,
41091
+ metadata: context.event.metadata,
41092
+ timestamp: context.event.timestamp
41093
+ })
41094
+ ];
41095
+ }
41096
+ return [JSON.stringify(context.payload)];
41097
+ }
41098
+ function buildChatlessAgentCallContext(config2, context, agentId) {
41099
+ const messagesResult = buildChatlessMessages(context, config2.promptOverride);
41100
+ if ("error" in messagesResult)
41101
+ return messagesResult;
41102
+ const automationId = context.automation?.id ?? "manual";
41103
+ const scope = context.event?.metadata.correlationId ?? context.event?.id ?? generateId();
41104
+ const sessionKey = `automation:${automationId}:${scope}`;
41105
+ return {
41106
+ context: {
41107
+ chatless: true,
41108
+ instanceId: "",
41109
+ agentId,
41110
+ sessionKey,
41111
+ automationId,
41112
+ chatId: sessionKey,
41113
+ senderId: sessionKey,
41114
+ messages: messagesResult,
41115
+ event: context.event ? {
41116
+ id: context.event.id,
41117
+ type: context.event.type,
41118
+ correlationId: context.event.metadata.correlationId
41119
+ } : undefined
41120
+ }
41121
+ };
41122
+ }
41078
41123
  function extractAgentCallContext(config2, context) {
41079
41124
  const instanceId = config2.providerId ? substituteTemplate(config2.providerId, context) : context.payload.instanceId;
41080
- if (!instanceId) {
41081
- return { error: "instanceId is required (from payload or config.providerId template)" };
41082
- }
41083
41125
  const fromObj = context.payload.from;
41084
41126
  const chatId = context.payload.chatId ?? fromObj?.id;
41085
41127
  const senderId = fromObj?.id ?? context.payload.senderId ?? chatId;
41086
41128
  const senderName = fromObj?.name ?? context.payload.senderName ?? context.payload.chatName;
41129
+ if (!instanceId || !chatId) {
41130
+ const chatlessAgentId = config2.agentId ? substituteTemplate(config2.agentId, context) : "";
41131
+ if (chatlessAgentId) {
41132
+ return buildChatlessAgentCallContext(config2, context, chatlessAgentId);
41133
+ }
41134
+ }
41135
+ if (!instanceId) {
41136
+ return { error: "instanceId is required (from payload or config.providerId template)" };
41137
+ }
41087
41138
  if (!chatId)
41088
41139
  return { error: "chatId not found in payload" };
41089
41140
  if (!senderId)
@@ -81354,6 +81405,7 @@ __export(exports_schema, {
81354
81405
  eventTypes: () => eventTypes,
81355
81406
  eventSchemas: () => eventSchemas,
81356
81407
  eventPayloads: () => eventPayloads,
81408
+ durableConsumers: () => durableConsumers,
81357
81409
  deliveryStatuses: () => deliveryStatuses,
81358
81410
  debounceMode: () => debounceMode,
81359
81411
  deadLetterStatuses: () => deadLetterStatuses,
@@ -81414,7 +81466,7 @@ __export(exports_schema, {
81414
81466
  accessRules: () => accessRules,
81415
81467
  accessModes: () => accessModes
81416
81468
  });
81417
- var channelTypes, agentTypes, agentSystems, agentEntityTypes, debounceMode, splitDelayMode, supersedeMode, replyFilterMode, agentSessionStrategies, ruleTypes, accessModes, settingValueTypes, apiKeyStatuses, apiKeyProfiles, eventTypes, contentTypes, chatTypes, messageSources, messageTypes, messageStatuses, deliveryStatuses, scheduledMessageDeliveryModes, scheduledMessageStatuses, jobStatuses, providerSchemas, agentProviders, agents, agentRoutes, agentSessions, apiKeys, apiKeyAuditLogs, apiKeysRelations, apiKeyAuditLogsRelations, instances, whatsappTemplates, whatsappFlowKeys, scheduledMessages, persons, platformIdentities, conversations, chats, chatParticipants, omniGroups, messages2, omniEvents, handoffLogs, closeContactOutcomes, closeContactLogs, accessRules, globalSettings, settingChangeHistory, batchJobs, syncJobTypes, syncJobs, mediaContent, chatIdMappings, pluginStorage, agentProvidersRelations, agentsRelations, instancesRelations, syncJobsRelations, personsRelations, platformIdentitiesRelations, conversationsRelations, chatsRelations, chatParticipantsRelations, messagesRelations, omniEventsRelations, accessRulesRelations, globalSettingsRelations, settingChangeHistoryRelations, batchJobsRelations, mediaContentRelations, chatIdMappingsRelations, deadLetterStatuses, deadLetterEvents, payloadStorageConfig, payloadStages, eventPayloads, webhookSignatureAlgorithms, connectorLivenessStatuses, connectorWindowSemanticsValues, connectorMutationPolicies, webhookSources, eventSchemas, conditionOperators, actionTypes, automationDebounceModes, automations2, automationLogStatuses, automationLogs, consumerOffsets, automationsRelations, automationLogsRelations, triggerLogs, triggerLogsRelations, agentRoutesRelations, agentTaskStatuses, agentTasks, agentTasksRelations, turnStatuses, turnActions, turns, turnsRelations, followUpDisarmReasons, chatFollowUpState, chatFollowUpStateRelations, processedEvents, genieHosts, tenantStatuses, principalTypes, principalStatuses, tenantRoles, membershipStatuses, credentialClasses, authCredentialStatuses, platformApiKeyStatuses, tenants, principals, tenantMemberships, tenantRolePolicies, platformApiKeys, tenantKeyLineage, authCredentials, tenantAuditLogs, platformAuditLogs, platformProviderCatalog, tenantProviderConfig, platformSettings, tenantSettings, platformSettingChangeHistory, tenantSettingChangeHistory, platformPluginStorage, tenantPluginStorage, platformPayloadStorageConfig, tenantPayloadStorageOverrides, tenantMigrationLedger, tenantMigrationLedgerHistory;
81469
+ var channelTypes, agentTypes, agentSystems, agentEntityTypes, debounceMode, splitDelayMode, supersedeMode, replyFilterMode, agentSessionStrategies, ruleTypes, accessModes, settingValueTypes, apiKeyStatuses, apiKeyProfiles, eventTypes, contentTypes, chatTypes, messageSources, messageTypes, messageStatuses, deliveryStatuses, scheduledMessageDeliveryModes, scheduledMessageStatuses, jobStatuses, providerSchemas, agentProviders, agents, agentRoutes, agentSessions, apiKeys, apiKeyAuditLogs, apiKeysRelations, apiKeyAuditLogsRelations, instances, whatsappTemplates, whatsappFlowKeys, scheduledMessages, persons, platformIdentities, conversations, chats, chatParticipants, omniGroups, messages2, omniEvents, handoffLogs, closeContactOutcomes, closeContactLogs, accessRules, globalSettings, settingChangeHistory, batchJobs, syncJobTypes, syncJobs, mediaContent, chatIdMappings, pluginStorage, agentProvidersRelations, agentsRelations, instancesRelations, syncJobsRelations, personsRelations, platformIdentitiesRelations, conversationsRelations, chatsRelations, chatParticipantsRelations, messagesRelations, omniEventsRelations, accessRulesRelations, globalSettingsRelations, settingChangeHistoryRelations, batchJobsRelations, mediaContentRelations, chatIdMappingsRelations, deadLetterStatuses, deadLetterEvents, payloadStorageConfig, payloadStages, eventPayloads, webhookSignatureAlgorithms, connectorLivenessStatuses, connectorWindowSemanticsValues, connectorMutationPolicies, webhookSources, eventSchemas, conditionOperators, actionTypes, automationDebounceModes, automations2, automationLogStatuses, automationLogs, consumerOffsets, durableConsumers, automationsRelations, automationLogsRelations, triggerLogs, triggerLogsRelations, agentRoutesRelations, agentTaskStatuses, agentTasks, agentTasksRelations, turnStatuses, turnActions, turns, turnsRelations, followUpDisarmReasons, chatFollowUpState, chatFollowUpStateRelations, processedEvents, genieHosts, tenantStatuses, principalTypes, principalStatuses, tenantRoles, membershipStatuses, credentialClasses, authCredentialStatuses, platformApiKeyStatuses, tenants, principals, tenantMemberships, tenantRolePolicies, platformApiKeys, tenantKeyLineage, authCredentials, tenantAuditLogs, platformAuditLogs, platformProviderCatalog, tenantProviderConfig, platformSettings, tenantSettings, platformSettingChangeHistory, tenantSettingChangeHistory, platformPluginStorage, tenantPluginStorage, platformPayloadStorageConfig, tenantPayloadStorageOverrides, tenantMigrationLedger, tenantMigrationLedgerHistory;
81418
81470
  var init_schema2 = __esm(() => {
81419
81471
  init_events();
81420
81472
  init_types4();
@@ -82150,10 +82202,12 @@ var init_schema2 = __esm(() => {
82150
82202
  idempotencyKey: text("idempotency_key"),
82151
82203
  metadata: jsonb("metadata").$type(),
82152
82204
  causationId: uuid("causation_id"),
82205
+ journalSeq: bigserial("journal_seq", { mode: "number" }),
82153
82206
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
82154
82207
  tenantId: uuid("tenant_id").references(() => tenants.id, { onDelete: "restrict" })
82155
82208
  }, (table3) => ({
82156
82209
  causationIdx: index("omni_events_causation_idx").on(table3.causationId),
82210
+ journalSeqUq: uniqueIndex("omni_events_journal_seq_uq").on(table3.journalSeq),
82157
82211
  tenantIdx: index("omni_events_tenant_idx").on(table3.tenantId),
82158
82212
  tenantIdUq: uniqueIndex("omni_events_tenant_id_uq").on(table3.tenantId, table3.id),
82159
82213
  idempotencyKeyUq: uniqueIndex("omni_events_idempotency_key_uq").on(table3.idempotencyKey),
@@ -82748,6 +82802,17 @@ var init_schema2 = __esm(() => {
82748
82802
  lastEventId: uuid("last_event_id"),
82749
82803
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
82750
82804
  });
82805
+ durableConsumers = pgTable("durable_consumers", {
82806
+ id: uuid("id").primaryKey().defaultRandom(),
82807
+ name: varchar("name", { length: 100 }).notNull(),
82808
+ eventType: varchar("event_type", { length: 255 }).notNull(),
82809
+ filters: jsonb("filters").$type(),
82810
+ cursor: bigint2("cursor", { mode: "number" }).notNull().default(0),
82811
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
82812
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
82813
+ }, (table3) => ({
82814
+ nameUq: uniqueIndex("durable_consumers_name_uq").on(table3.name)
82815
+ }));
82751
82816
  automationsRelations = relations(automations2, ({ many }) => ({
82752
82817
  logs: many(automationLogs)
82753
82818
  }));
@@ -122879,12 +122944,12 @@ var require_instrumentation8 = __commonJS((exports) => {
122879
122944
  pendingMetrics.forEach((m10) => m10());
122880
122945
  return result;
122881
122946
  }).catch((reason) => {
122882
- let errorMessage2;
122947
+ let errorMessage3;
122883
122948
  let errorType = semantic_conventions_1.ERROR_TYPE_VALUE_OTHER;
122884
122949
  if (typeof reason === "string" || reason === undefined) {
122885
- errorMessage2 = reason;
122950
+ errorMessage3 = reason;
122886
122951
  } else if (typeof reason === "object" && Object.prototype.hasOwnProperty.call(reason, "message")) {
122887
- errorMessage2 = reason.message;
122952
+ errorMessage3 = reason.message;
122888
122953
  errorType = reason.constructor.name;
122889
122954
  }
122890
122955
  pendingMetrics.forEach((m10) => m10(errorType));
@@ -122892,7 +122957,7 @@ var require_instrumentation8 = __commonJS((exports) => {
122892
122957
  span.setAttribute(semantic_conventions_1.ATTR_ERROR_TYPE, errorType);
122893
122958
  span.setStatus({
122894
122959
  code: api_1.SpanStatusCode.ERROR,
122895
- message: errorMessage2
122960
+ message: errorMessage3
122896
122961
  });
122897
122962
  });
122898
122963
  throw reason;
@@ -136695,7 +136760,7 @@ import { fileURLToPath } from "url";
136695
136760
  // package.json
136696
136761
  var package_default = {
136697
136762
  name: "@automagik/omni",
136698
- version: "2.260908.10",
136763
+ version: "2.260908.12",
136699
136764
  repository: {
136700
136765
  type: "git",
136701
136766
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -143773,9 +143838,119 @@ async function waitForEvent(client, params) {
143773
143838
  await new Promise((resolve2) => setTimeout(resolve2, Math.min(params.pollMs, remaining)));
143774
143839
  }
143775
143840
  }
143841
+ async function consumersApiRequest(path, init = {}) {
143842
+ const config2 = loadConfig();
143843
+ const baseUrl = config2.apiUrl ?? "http://localhost:8882";
143844
+ const resp = await fetch(`${baseUrl}/api/v2/events/consumers${path}`, {
143845
+ method: init.method ?? "GET",
143846
+ body: init.body,
143847
+ headers: { "content-type": "application/json", "x-api-key": config2.apiKey ?? "" }
143848
+ });
143849
+ if (!resp.ok) {
143850
+ throw new Error(`API returned ${resp.status}: ${await resp.text()}`);
143851
+ }
143852
+ return await resp.json();
143853
+ }
143854
+ function summarizeConsumerRow(row) {
143855
+ return {
143856
+ name: row.name,
143857
+ eventType: row.eventType,
143858
+ filters: row.filters?.length ? row.filters.map((f10) => `${f10.field} ${f10.operator} ${JSON.stringify(f10.value)}`).join(" AND ") : "-",
143859
+ cursor: row.cursor,
143860
+ lag: row.lag,
143861
+ updatedAt: row.updatedAt
143862
+ };
143863
+ }
143864
+ async function followConsumer(params) {
143865
+ const emit = params.emit ?? raw;
143866
+ let acked = -1;
143867
+ for (;; ) {
143868
+ if (params.isStopped?.())
143869
+ return;
143870
+ const page = await consumersApiRequest(`/${encodeURIComponent(params.consumer)}/pull?limit=${params.limit}&waitMs=${params.waitMs}`, { method: "POST" });
143871
+ for (const item of page.items) {
143872
+ emit(JSON.stringify(item));
143873
+ }
143874
+ if (!params.ack) {
143875
+ await flushStdout();
143876
+ return;
143877
+ }
143878
+ if (page.cursor > acked) {
143879
+ await consumersApiRequest(`/${encodeURIComponent(params.consumer)}/ack`, {
143880
+ method: "POST",
143881
+ body: JSON.stringify({ cursor: page.cursor })
143882
+ });
143883
+ }
143884
+ const progressed = page.items.length > 0 || page.hasMore || acked >= 0 && page.cursor > acked;
143885
+ acked = Math.max(acked, page.cursor);
143886
+ if (params.untilIdle && !progressed && !page.hasMore) {
143887
+ await flushStdout();
143888
+ return;
143889
+ }
143890
+ if (!progressed && params.waitMs < 1000) {
143891
+ await new Promise((resolve2) => setTimeout(resolve2, 1000));
143892
+ }
143893
+ }
143894
+ }
143895
+ function createConsumersCommand() {
143896
+ const consumers = new Command("consumers").description("Manage durable event consumers (named cursors over the event journal)");
143897
+ consumers.command("create <name>").description("Register a durable consumer").requiredOption("--type <type>", "Event type filter (trailing * = prefix glob, e.g. custom.github.*)").option("--filter <k=v>", "Payload condition, repeatable (dot paths + JSON values \u2014 same matcher as events wait)", (value, previous) => [...previous, value], []).option("--from-beginning", "Start the cursor at 0 (replay the full journal) instead of the current head").action(async (name, options) => {
143898
+ try {
143899
+ const filters = parseWaitFilters(WaitOptionsSchema.shape.filter.parse(options.filter));
143900
+ const result = await consumersApiRequest("", {
143901
+ method: "POST",
143902
+ body: JSON.stringify({
143903
+ name,
143904
+ eventType: options.type,
143905
+ filters: filters.length ? filters : undefined,
143906
+ startFrom: options.fromBeginning ? "beginning" : undefined
143907
+ })
143908
+ });
143909
+ success(`Consumer registered: ${result.data.name} (cursor ${result.data.cursor})`, {
143910
+ name: result.data.name,
143911
+ eventType: result.data.eventType,
143912
+ cursor: result.data.cursor,
143913
+ lag: result.data.lag
143914
+ });
143915
+ } catch (err2) {
143916
+ error(`Failed to create consumer: ${errorMessage(err2)}`);
143917
+ }
143918
+ });
143919
+ consumers.command("ls").description("List durable consumers with their cursor and lag").action(async () => {
143920
+ try {
143921
+ const result = await consumersApiRequest("");
143922
+ list(result.items.map(summarizeConsumerRow), { emptyMessage: "No durable consumers registered." });
143923
+ } catch (err2) {
143924
+ error(`Failed to list consumers: ${errorMessage(err2)}`);
143925
+ }
143926
+ });
143927
+ consumers.command("inspect <name>").description("Show one consumer: filter, cursor, journal head, lag").action(async (name) => {
143928
+ try {
143929
+ const result = await consumersApiRequest(`/${encodeURIComponent(name)}`);
143930
+ data(result.data);
143931
+ } catch (err2) {
143932
+ error(`Failed to inspect consumer: ${errorMessage(err2)}`);
143933
+ }
143934
+ });
143935
+ consumers.command("rm <name>").description("Delete a consumer registration (the journal itself is untouched)").action(async (name) => {
143936
+ try {
143937
+ await consumersApiRequest(`/${encodeURIComponent(name)}`, { method: "DELETE" });
143938
+ success(`Consumer removed: ${name}`);
143939
+ } catch (err2) {
143940
+ error(`Failed to remove consumer: ${errorMessage(err2)}`);
143941
+ }
143942
+ });
143943
+ return consumers;
143944
+ }
143945
+ function errorMessage(err2) {
143946
+ if (err2 instanceof exports_external.ZodError)
143947
+ return err2.issues.map((i6) => i6.message).join("; ");
143948
+ return err2 instanceof Error ? err2.message : "Unknown error";
143949
+ }
143776
143950
  function createEventsCommand() {
143777
143951
  const events2 = new Command("events").description("Query events");
143778
143952
  events2.addCommand(createSchemaCommand());
143953
+ events2.addCommand(createConsumersCommand());
143779
143954
  events2.command("list").description("List events").option("--instance <id>", "Filter by instance ID").option("--channel <type>", "Filter by channel type").option("--type <type>", "Filter by event type (trailing * = prefix glob, e.g. custom.*)").option("--chat-id <id>", "Filter by chat ID").option("--since <time>", "Events since (e.g., 24h, 7d, or ISO timestamp)").option("--until <time>", "Events until (ISO timestamp)").option("--limit <n>", "Limit results", (v4) => Number.parseInt(v4, 10), 50).action(async (options) => {
143780
143955
  const client = getClient();
143781
143956
  try {
@@ -143848,6 +144023,31 @@ function createEventsCommand() {
143848
144023
  error(`Failed to wait for event: ${message2}`);
143849
144024
  }
143850
144025
  });
144026
+ events2.command("follow").description("Tail the journal through a durable consumer: resumes from its stored cursor, prints JSON lines, acks as it goes.").requiredOption("--consumer <name>", "Durable consumer to follow (create with: omni events consumers create)").option("--limit <n>", "Max journal rows scanned per pull", (v4) => Number.parseInt(v4, 10), 100).option("--wait-ms <n>", "Server-side long-poll window per pull when idle (max 30000)", (v4) => Number.parseInt(v4, 10), 1e4).option("--no-ack", "Peek: print one page without advancing the cursor, then exit").option("--until-idle", "Exit 0 once caught up with the journal instead of tailing forever").action(async (options) => {
144027
+ let stopped = false;
144028
+ const shutdown = () => {
144029
+ stopped = true;
144030
+ };
144031
+ const processEvents = process;
144032
+ processEvents.on("SIGINT", shutdown);
144033
+ processEvents.on("SIGTERM", shutdown);
144034
+ try {
144035
+ await followConsumer({
144036
+ consumer: options.consumer,
144037
+ limit: Math.min(Math.max(options.limit, 1), 500),
144038
+ waitMs: Math.min(Math.max(options.waitMs, 0), 30000),
144039
+ ack: options.ack,
144040
+ untilIdle: options.untilIdle === true,
144041
+ isStopped: () => stopped
144042
+ });
144043
+ } catch (err2) {
144044
+ error(`Failed to follow consumer: ${errorMessage(err2)}`);
144045
+ } finally {
144046
+ processEvents.off("SIGINT", shutdown);
144047
+ processEvents.off("SIGTERM", shutdown);
144048
+ await flushStdout();
144049
+ }
144050
+ });
143851
144051
  events2.command("get <id>").description("Get full details for a single event by ID").action(async (id) => {
143852
144052
  const client = getClient();
143853
144053
  try {
@@ -150968,11 +151168,11 @@ function createNatsReplySidecarArtifact() {
150968
151168
  };
150969
151169
  }
150970
151170
  var REGISTRY = [createNatsReplySidecarArtifact()];
150971
- function errorMessage(error2) {
151171
+ function errorMessage2(error2) {
150972
151172
  return error2 instanceof Error ? error2.message : String(error2);
150973
151173
  }
150974
151174
  function erroredOutcome(name, phase, error2) {
150975
- const message2 = errorMessage(error2);
151175
+ const message2 = errorMessage2(error2);
150976
151176
  return {
150977
151177
  name,
150978
151178
  state: "errored",