@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.
- package/db/drizzle/0064_durable_consumers.sql +46 -0
- package/db/drizzle/meta/_journal.json +7 -0
- package/dist/commands/events.d.ts +47 -0
- package/dist/commands/events.d.ts.map +1 -1
- package/dist/index.js +211 -11
- package/dist/server/index.js +728 -251
- package/package.json +1 -1
|
@@ -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
|
|
122947
|
+
let errorMessage3;
|
|
122883
122948
|
let errorType = semantic_conventions_1.ERROR_TYPE_VALUE_OTHER;
|
|
122884
122949
|
if (typeof reason === "string" || reason === undefined) {
|
|
122885
|
-
|
|
122950
|
+
errorMessage3 = reason;
|
|
122886
122951
|
} else if (typeof reason === "object" && Object.prototype.hasOwnProperty.call(reason, "message")) {
|
|
122887
|
-
|
|
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:
|
|
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.
|
|
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
|
|
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 =
|
|
151175
|
+
const message2 = errorMessage2(error2);
|
|
150976
151176
|
return {
|
|
150977
151177
|
name,
|
|
150978
151178
|
state: "errored",
|