@automagik/omni 2.260910.15 → 2.260910.17

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/dist/index.js CHANGED
@@ -115765,7 +115765,7 @@ var require_client2 = __commonJS((exports) => {
115765
115765
 
115766
115766
  // ../../node_modules/.bun/@sentry+node-core@10.52.0+2c50d7e904f6cfd7/node_modules/@sentry/node-core/build/cjs/sdk/esmLoader.js
115767
115767
  var require_esmLoader = __commonJS((exports) => {
115768
- var __filename = "/home/runner/work/omni/omni/.stable-source/node_modules/.bun/@sentry+node-core@10.52.0+2c50d7e904f6cfd7/node_modules/@sentry/node-core/build/cjs/sdk/esmLoader.js";
115768
+ var __filename = "/home/runner/work/omni/omni/node_modules/.bun/@sentry+node-core@10.52.0+2c50d7e904f6cfd7/node_modules/@sentry/node-core/build/cjs/sdk/esmLoader.js";
115769
115769
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
115770
115770
  var core = require_cjs();
115771
115771
  var importInTheMiddle = require_import_in_the_middle();
@@ -137198,7 +137198,7 @@ import { fileURLToPath } from "url";
137198
137198
  // package.json
137199
137199
  var package_default = {
137200
137200
  name: "@automagik/omni",
137201
- version: "2.260910.15",
137201
+ version: "2.260910.17",
137202
137202
  repository: {
137203
137203
  type: "git",
137204
137204
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -150668,6 +150668,744 @@ function createSlackCommand() {
150668
150668
  return cmd;
150669
150669
  }
150670
150670
 
150671
+ // src/commands/sources.ts
150672
+ import { randomBytes as randomBytes4 } from "crypto";
150673
+ init_config();
150674
+ init_output();
150675
+ // src/commands/sources/github/custom.github.issues.json
150676
+ var custom_github_issues_default = {
150677
+ $schema: "http://json-schema.org/draft-07/schema#",
150678
+ title: "custom.github.issues",
150679
+ description: "GitHub issues webhook delivery (X-GitHub-Event: issues). Minimal-but-real contract: the fields consumers rely on are required; everything else GitHub sends passes through (additionalProperties: true). Evolution rule: revisions must be additive-optional (issue #959).",
150680
+ type: "object",
150681
+ additionalProperties: true,
150682
+ required: ["action", "issue", "repository", "sender"],
150683
+ properties: {
150684
+ action: {
150685
+ type: "string",
150686
+ description: "What happened: opened, closed, edited, labeled, ..."
150687
+ },
150688
+ issue: {
150689
+ type: "object",
150690
+ additionalProperties: true,
150691
+ required: ["number"],
150692
+ properties: {
150693
+ number: { type: "integer", description: "Issue number" },
150694
+ title: { type: "string" },
150695
+ state: { type: "string" },
150696
+ html_url: { type: "string" }
150697
+ }
150698
+ },
150699
+ repository: {
150700
+ type: "object",
150701
+ additionalProperties: true,
150702
+ required: ["full_name"],
150703
+ properties: {
150704
+ full_name: {
150705
+ type: "string",
150706
+ description: "owner/repo, e.g. automagik-dev/omni"
150707
+ }
150708
+ }
150709
+ },
150710
+ sender: {
150711
+ type: "object",
150712
+ additionalProperties: true,
150713
+ required: ["login"],
150714
+ properties: {
150715
+ login: {
150716
+ type: "string",
150717
+ description: "GitHub username that triggered the delivery"
150718
+ }
150719
+ }
150720
+ }
150721
+ }
150722
+ };
150723
+ // src/commands/sources/github/custom.github.pull_request.json
150724
+ var custom_github_pull_request_default = {
150725
+ $schema: "http://json-schema.org/draft-07/schema#",
150726
+ title: "custom.github.pull_request",
150727
+ description: "GitHub pull_request webhook delivery (X-GitHub-Event: pull_request). Minimal-but-real contract: the fields consumers rely on are required; everything else GitHub sends passes through (additionalProperties: true). Evolution rule: revisions must be additive-optional (issue #959).",
150728
+ type: "object",
150729
+ additionalProperties: true,
150730
+ required: ["action", "number", "repository", "sender"],
150731
+ properties: {
150732
+ action: {
150733
+ type: "string",
150734
+ description: "What happened: opened, closed, synchronize, reopened, ..."
150735
+ },
150736
+ number: {
150737
+ type: "integer",
150738
+ description: "Pull request number"
150739
+ },
150740
+ pull_request: {
150741
+ type: "object",
150742
+ additionalProperties: true,
150743
+ properties: {
150744
+ title: { type: "string" },
150745
+ state: { type: "string" },
150746
+ merged: { type: "boolean" },
150747
+ html_url: { type: "string" }
150748
+ }
150749
+ },
150750
+ repository: {
150751
+ type: "object",
150752
+ additionalProperties: true,
150753
+ required: ["full_name"],
150754
+ properties: {
150755
+ full_name: {
150756
+ type: "string",
150757
+ description: "owner/repo, e.g. automagik-dev/omni"
150758
+ }
150759
+ }
150760
+ },
150761
+ sender: {
150762
+ type: "object",
150763
+ additionalProperties: true,
150764
+ required: ["login"],
150765
+ properties: {
150766
+ login: {
150767
+ type: "string",
150768
+ description: "GitHub username that triggered the delivery"
150769
+ }
150770
+ }
150771
+ }
150772
+ }
150773
+ };
150774
+ // src/commands/sources/github/custom.github.push.json
150775
+ var custom_github_push_default = {
150776
+ $schema: "http://json-schema.org/draft-07/schema#",
150777
+ title: "custom.github.push",
150778
+ description: "GitHub push webhook delivery (X-GitHub-Event: push). Minimal-but-real contract: the fields consumers rely on are required; everything else GitHub sends passes through (additionalProperties: true). Evolution rule: revisions must be additive-optional (issue #959).",
150779
+ type: "object",
150780
+ additionalProperties: true,
150781
+ required: ["ref", "repository", "sender"],
150782
+ properties: {
150783
+ ref: {
150784
+ type: "string",
150785
+ description: "Full git ref that was pushed, e.g. refs/heads/main"
150786
+ },
150787
+ before: {
150788
+ type: "string",
150789
+ description: "SHA of the most recent commit on ref before the push"
150790
+ },
150791
+ after: {
150792
+ type: "string",
150793
+ description: "SHA of the most recent commit on ref after the push"
150794
+ },
150795
+ commits: {
150796
+ type: "array",
150797
+ description: "Commits in the push (GitHub caps this at 2048 entries)",
150798
+ items: {
150799
+ type: "object",
150800
+ additionalProperties: true,
150801
+ properties: {
150802
+ id: { type: "string" },
150803
+ message: { type: "string" },
150804
+ url: { type: "string" }
150805
+ }
150806
+ }
150807
+ },
150808
+ repository: {
150809
+ type: "object",
150810
+ additionalProperties: true,
150811
+ required: ["full_name"],
150812
+ properties: {
150813
+ full_name: {
150814
+ type: "string",
150815
+ description: "owner/repo, e.g. automagik-dev/omni"
150816
+ }
150817
+ }
150818
+ },
150819
+ sender: {
150820
+ type: "object",
150821
+ additionalProperties: true,
150822
+ required: ["login"],
150823
+ properties: {
150824
+ login: {
150825
+ type: "string",
150826
+ description: "GitHub username that triggered the delivery"
150827
+ }
150828
+ }
150829
+ }
150830
+ }
150831
+ };
150832
+ // src/commands/sources/github/custom.github.release.json
150833
+ var custom_github_release_default = {
150834
+ $schema: "http://json-schema.org/draft-07/schema#",
150835
+ title: "custom.github.release",
150836
+ description: "GitHub release webhook delivery (X-GitHub-Event: release). Minimal-but-real contract: the fields consumers rely on are required; everything else GitHub sends passes through (additionalProperties: true). Evolution rule: revisions must be additive-optional (issue #959).",
150837
+ type: "object",
150838
+ additionalProperties: true,
150839
+ required: ["action", "release", "repository", "sender"],
150840
+ properties: {
150841
+ action: {
150842
+ type: "string",
150843
+ description: "What happened: published, created, edited, released, ..."
150844
+ },
150845
+ release: {
150846
+ type: "object",
150847
+ additionalProperties: true,
150848
+ required: ["tag_name"],
150849
+ properties: {
150850
+ tag_name: { type: "string", description: "Git tag of the release, e.g. v2.260907.4" },
150851
+ name: { type: "string" },
150852
+ draft: { type: "boolean" },
150853
+ prerelease: { type: "boolean" },
150854
+ html_url: { type: "string" }
150855
+ }
150856
+ },
150857
+ repository: {
150858
+ type: "object",
150859
+ additionalProperties: true,
150860
+ required: ["full_name"],
150861
+ properties: {
150862
+ full_name: {
150863
+ type: "string",
150864
+ description: "owner/repo, e.g. automagik-dev/omni"
150865
+ }
150866
+ }
150867
+ },
150868
+ sender: {
150869
+ type: "object",
150870
+ additionalProperties: true,
150871
+ required: ["login"],
150872
+ properties: {
150873
+ login: {
150874
+ type: "string",
150875
+ description: "GitHub username that triggered the delivery"
150876
+ }
150877
+ }
150878
+ }
150879
+ }
150880
+ };
150881
+
150882
+ // src/commands/webhooks.ts
150883
+ import { readFileSync as readFileSync18 } from "fs";
150884
+ init_zod();
150885
+ init_output();
150886
+ var SIGNATURE_ALGORITHMS = ["hmac-sha256", "hmac-sha1", "token-match"];
150887
+ var signatureSecretSchema = exports_external.string().min(8, "at least 8 characters").max(512, "at most 512 characters");
150888
+ var SECRET_SOURCE_FLAGS = "--signature-secret, --signature-secret-env, --signature-secret-stdin";
150889
+ async function readSecretFromStdin() {
150890
+ if (process.stdin.isTTY) {
150891
+ throw new Error('--signature-secret-stdin requires piped stdin (e.g. `printf %s "$SECRET" | omni webhooks ...`)');
150892
+ }
150893
+ const chunks = [];
150894
+ for await (const chunk of process.stdin) {
150895
+ chunks.push(Buffer.from(chunk));
150896
+ }
150897
+ return Buffer.concat(chunks).toString("utf-8");
150898
+ }
150899
+ async function resolveSignatureSecret(options3, readStdin = readSecretFromStdin) {
150900
+ const sources = [
150901
+ options3.signatureSecret !== undefined,
150902
+ options3.signatureSecretEnv !== undefined,
150903
+ options3.signatureSecretStdin === true
150904
+ ].filter(Boolean).length;
150905
+ if (sources === 0)
150906
+ return;
150907
+ if (sources > 1) {
150908
+ throw new Error(`Use only one of ${SECRET_SOURCE_FLAGS}`);
150909
+ }
150910
+ let raw2;
150911
+ let origin;
150912
+ if (options3.signatureSecretEnv !== undefined) {
150913
+ const name = options3.signatureSecretEnv;
150914
+ if (!name)
150915
+ throw new Error("--signature-secret-env requires a variable name");
150916
+ raw2 = process.env[name];
150917
+ origin = `environment variable ${name}`;
150918
+ if (raw2 === undefined || raw2 === "") {
150919
+ throw new Error(`${origin} is not set or empty`);
150920
+ }
150921
+ } else if (options3.signatureSecretStdin) {
150922
+ raw2 = (await readStdin()).replace(/\r?\n$/, "");
150923
+ origin = "stdin";
150924
+ if (!raw2)
150925
+ throw new Error("no secret received on stdin");
150926
+ } else {
150927
+ raw2 = options3.signatureSecret;
150928
+ origin = "--signature-secret";
150929
+ }
150930
+ const parsed = signatureSecretSchema.safeParse(raw2);
150931
+ if (!parsed.success) {
150932
+ const reason = parsed.error.issues.map((issue) => issue.message).join("; ");
150933
+ throw new Error(`Invalid signature secret from ${origin}: ${reason}`);
150934
+ }
150935
+ return parsed.data;
150936
+ }
150937
+ function buildSignatureConfig(options3) {
150938
+ const { signatureAlgorithm, signatureHeader, signaturePrefix } = options3;
150939
+ if (!signatureAlgorithm && !signatureHeader && !signaturePrefix)
150940
+ return;
150941
+ if (!signatureAlgorithm || !signatureHeader) {
150942
+ error("--signature-algorithm and --signature-header must be provided together");
150943
+ }
150944
+ const algorithm = SIGNATURE_ALGORITHMS.find((a2) => a2 === signatureAlgorithm);
150945
+ if (!algorithm) {
150946
+ error(`Invalid --signature-algorithm; expected one of: ${SIGNATURE_ALGORITHMS.join(", ")}`);
150947
+ }
150948
+ if (algorithm === "token-match" && signaturePrefix) {
150949
+ error("--signature-prefix is not applicable to token-match (the header carries the secret verbatim)");
150950
+ }
150951
+ return { algorithm, header: signatureHeader, prefix: signaturePrefix };
150952
+ }
150953
+ var eventTypeMappingSchema = exports_external.discriminatedUnion("source", [
150954
+ exports_external.object({
150955
+ source: exports_external.literal("header"),
150956
+ header: exports_external.string().min(1, "header must be 1-200 characters").max(200, "header must be 1-200 characters")
150957
+ }),
150958
+ exports_external.object({
150959
+ source: exports_external.literal("body"),
150960
+ path: exports_external.string().min(1, "path must be 1-200 characters").max(200, "path must be 1-200 characters")
150961
+ })
150962
+ ]);
150963
+ var EVENT_TYPE_MAPPING_HINT = 'expected {"source":"header","header":"X-GitHub-Event"} or {"source":"body","path":"event"}';
150964
+ function resolveEventTypeMapping(raw2) {
150965
+ if (raw2 === undefined)
150966
+ return;
150967
+ let text3 = raw2;
150968
+ if (raw2.startsWith("@")) {
150969
+ const path = raw2.slice(1);
150970
+ try {
150971
+ text3 = readFileSync18(path, "utf-8");
150972
+ } catch (err2) {
150973
+ const reason = err2 instanceof Error ? err2.message : "Unknown error";
150974
+ throw new Error(`Cannot read --event-type-mapping file '${path}': ${reason}`);
150975
+ }
150976
+ }
150977
+ let parsed;
150978
+ try {
150979
+ parsed = JSON.parse(text3);
150980
+ } catch {
150981
+ throw new Error(`Invalid JSON for --event-type-mapping; ${EVENT_TYPE_MAPPING_HINT}`);
150982
+ }
150983
+ const result = eventTypeMappingSchema.safeParse(parsed);
150984
+ if (!result.success) {
150985
+ const reason = result.error.issues.map((issue) => issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message).join("; ");
150986
+ throw new Error(`Invalid --event-type-mapping: ${reason}; ${EVENT_TYPE_MAPPING_HINT}`);
150987
+ }
150988
+ return result.data;
150989
+ }
150990
+ async function buildUpdateSourcePatch(options3) {
150991
+ const updates = buildLifecycleUpdates(options3);
150992
+ if (options3.name)
150993
+ updates.name = options3.name;
150994
+ if (options3.description)
150995
+ updates.description = options3.description;
150996
+ if (options3.idempotencyKeyTemplate)
150997
+ updates.idempotencyKeyTemplate = options3.idempotencyKeyTemplate;
150998
+ if (options3.strictSchemas !== undefined)
150999
+ updates.strictSchemas = options3.strictSchemas;
151000
+ if (options3.clearEventTypeMapping && options3.eventTypeMapping !== undefined) {
151001
+ throw new Error("Use only one of --event-type-mapping and --clear-event-type-mapping");
151002
+ }
151003
+ if (options3.clearEventTypeMapping)
151004
+ updates.eventTypeMapping = null;
151005
+ const eventTypeMapping = resolveEventTypeMapping(options3.eventTypeMapping);
151006
+ if (eventTypeMapping !== undefined)
151007
+ updates.eventTypeMapping = eventTypeMapping;
151008
+ if (options3.enable)
151009
+ updates.enabled = true;
151010
+ if (options3.disable)
151011
+ updates.enabled = false;
151012
+ if (options3.clearSignature) {
151013
+ updates.signatureConfig = null;
151014
+ return updates;
151015
+ }
151016
+ const signatureConfig = buildSignatureConfig(options3);
151017
+ if (signatureConfig)
151018
+ updates.signatureConfig = signatureConfig;
151019
+ const signatureSecret = await resolveSignatureSecret(options3);
151020
+ if (signatureSecret)
151021
+ updates.signatureSecret = signatureSecret;
151022
+ return updates;
151023
+ }
151024
+ function assertPairedSignatureOnCreate(signatureConfig, signatureSecret) {
151025
+ if (signatureConfig === undefined === (signatureSecret === undefined))
151026
+ return;
151027
+ if (signatureConfig === undefined) {
151028
+ throw new Error(`a signature secret (${SECRET_SOURCE_FLAGS}) requires --signature-algorithm and --signature-header`);
151029
+ }
151030
+ throw new Error(`--signature-algorithm and --signature-header require a signature secret (${SECRET_SOURCE_FLAGS})`);
151031
+ }
151032
+ var WINDOW_SEMANTICS = ["future_only", "includes_in_progress"];
151033
+ var MUTATION_POLICIES = ["same_id", "new_id"];
151034
+ function parseExpectedInterval(raw2) {
151035
+ if (raw2 === undefined)
151036
+ return;
151037
+ const seconds = Number(raw2);
151038
+ if (!Number.isInteger(seconds) || seconds < 1) {
151039
+ error("--expected-interval must be a positive integer number of seconds");
151040
+ }
151041
+ return seconds;
151042
+ }
151043
+ function parseWindowSemantics(raw2) {
151044
+ if (raw2 === undefined)
151045
+ return;
151046
+ const value = WINDOW_SEMANTICS.find((v4) => v4 === raw2);
151047
+ if (!value) {
151048
+ error(`Invalid --window-semantics; expected one of: ${WINDOW_SEMANTICS.join(", ")}`);
151049
+ }
151050
+ return value;
151051
+ }
151052
+ function parseMutationPolicy(raw2) {
151053
+ if (raw2 === undefined)
151054
+ return;
151055
+ const value = MUTATION_POLICIES.find((v4) => v4 === raw2);
151056
+ if (!value) {
151057
+ error(`Invalid --mutation-policy; expected one of: ${MUTATION_POLICIES.join(", ")}`);
151058
+ }
151059
+ return value;
151060
+ }
151061
+ function buildLifecycleUpdates(options3) {
151062
+ if (options3.clearCadence && options3.expectedInterval !== undefined) {
151063
+ error("Use only one of --expected-interval and --clear-cadence");
151064
+ }
151065
+ const updates = {};
151066
+ if (options3.clearCadence)
151067
+ updates.expectedIntervalSeconds = null;
151068
+ const expectedIntervalSeconds = parseExpectedInterval(options3.expectedInterval);
151069
+ if (expectedIntervalSeconds !== undefined)
151070
+ updates.expectedIntervalSeconds = expectedIntervalSeconds;
151071
+ const windowSemantics = parseWindowSemantics(options3.windowSemantics);
151072
+ if (windowSemantics !== undefined)
151073
+ updates.windowSemantics = windowSemantics;
151074
+ const mutationPolicy = parseMutationPolicy(options3.mutationPolicy);
151075
+ if (mutationPolicy !== undefined)
151076
+ updates.mutationPolicy = mutationPolicy;
151077
+ return updates;
151078
+ }
151079
+ function createWebhooksCommand() {
151080
+ const webhooks = new Command("webhooks").description("Manage webhook sources");
151081
+ webhooks.command("list").description("List webhook sources").option("--enabled", "Show only enabled sources").option("--disabled", "Show only disabled sources").action(async (options3) => {
151082
+ const client = getClient();
151083
+ try {
151084
+ let enabledFilter;
151085
+ if (options3.enabled)
151086
+ enabledFilter = true;
151087
+ if (options3.disabled)
151088
+ enabledFilter = false;
151089
+ const result = await client.webhooks.listSources({
151090
+ enabled: enabledFilter
151091
+ });
151092
+ const items = result.map((w) => ({
151093
+ id: w.id,
151094
+ name: w.name,
151095
+ enabled: w.enabled ? "yes" : "no",
151096
+ health: w.livenessStatus ?? "-",
151097
+ cadence: w.expectedIntervalSeconds != null ? `${w.expectedIntervalSeconds}s` : "-",
151098
+ createdAt: w.createdAt
151099
+ }));
151100
+ list(items, { emptyMessage: "No webhook sources found." });
151101
+ } catch (err2) {
151102
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151103
+ error(`Failed to list webhook sources: ${message2}`);
151104
+ }
151105
+ });
151106
+ webhooks.command("get <id>").description("Get webhook source details").action(async (id) => {
151107
+ const resolvedId = await resolveWebhookId(id);
151108
+ const client = getClient();
151109
+ try {
151110
+ const source = await client.webhooks.getSource(resolvedId);
151111
+ data(source);
151112
+ } catch (err2) {
151113
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151114
+ error(`Failed to get webhook source: ${message2}`);
151115
+ }
151116
+ });
151117
+ webhooks.command("create").description("Create a webhook source").requiredOption("--name <name>", "Webhook source name").option("--description <desc>", "Description").option("--disabled", "Create in disabled state").option("--headers <json>", `Expected headers as JSON (e.g., '{"X-Secret": true}')`).option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--idempotency-key-template <template>", "Delivery-identity key template for redelivery dedup (#958). Placeholders: {source}, {sha256(body)}, " + "{headers.<name>}, {payload.<dot.path>}. Defaults to '{source}:{sha256(body)}'").option("--strict-schemas", "Refuse deliveries whose event type has no enabled registered schema (dead-lettered as " + "schema_not_registered, #1000). Recommended for new sources. Defaults to off").option("--event-type-mapping <json>", "Semantic event-type extraction (#959): inline JSON or @path/to/file.json, e.g. " + `'{"source":"header","header":"X-GitHub-Event"}' or '{"source":"body","path":"event"}'. ` + "A mapped source emits custom.{source}.{event} instead of the collapsed custom.webhook.{source}").option("--expected-interval <seconds>", "Declared cadence: >=1 event or heartbeat per N seconds. Arms liveness supervision (#961)").option("--window-semantics <value>", `Declared window semantics: ${WINDOW_SEMANTICS.join(" or ")}`).option("--mutation-policy <value>", `Declared mutation re-emit policy: ${MUTATION_POLICIES.join(" or ")}`).action(async (options3) => {
151118
+ const client = getClient();
151119
+ try {
151120
+ let expectedHeaders;
151121
+ if (options3.headers) {
151122
+ try {
151123
+ expectedHeaders = JSON.parse(options3.headers);
151124
+ } catch {
151125
+ error("Invalid JSON for --headers");
151126
+ }
151127
+ }
151128
+ const signatureConfig = buildSignatureConfig(options3);
151129
+ const signatureSecret = await resolveSignatureSecret(options3);
151130
+ assertPairedSignatureOnCreate(signatureConfig, signatureSecret);
151131
+ const source = await client.webhooks.createSource({
151132
+ name: options3.name,
151133
+ description: options3.description,
151134
+ enabled: !options3.disabled,
151135
+ expectedHeaders,
151136
+ signatureConfig,
151137
+ signatureSecret,
151138
+ idempotencyKeyTemplate: options3.idempotencyKeyTemplate,
151139
+ strictSchemas: options3.strictSchemas,
151140
+ eventTypeMapping: resolveEventTypeMapping(options3.eventTypeMapping),
151141
+ expectedIntervalSeconds: parseExpectedInterval(options3.expectedInterval),
151142
+ windowSemantics: parseWindowSemantics(options3.windowSemantics),
151143
+ mutationPolicy: parseMutationPolicy(options3.mutationPolicy)
151144
+ });
151145
+ const details = {
151146
+ id: source.id,
151147
+ name: source.name,
151148
+ url: `POST /api/v2/webhooks/${source.name}`
151149
+ };
151150
+ if (signatureConfig) {
151151
+ details.publicUrl = `POST /api/v2/webhooks/ingress/${source.name}`;
151152
+ }
151153
+ success(`Webhook source created: ${source.id}`, details);
151154
+ } catch (err2) {
151155
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151156
+ error(`Failed to create webhook source: ${message2}`);
151157
+ }
151158
+ });
151159
+ webhooks.command("update <id>").description("Update a webhook source").option("--name <name>", "New name").option("--description <desc>", "New description").option("--enable", "Enable the webhook").option("--disable", "Disable the webhook").option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--clear-signature", "Remove the signature config and stored secret").option("--idempotency-key-template <template>", "Delivery-identity key template for redelivery dedup (#958). Placeholders: {source}, {sha256(body)}, " + "{headers.<name>}, {payload.<dot.path>}").option("--strict-schemas", "Refuse deliveries whose event type has no enabled registered schema (dead-lettered as " + "schema_not_registered, #1000)").option("--no-strict-schemas", "Return the source to opt-in pass-through for unregistered event types").option("--event-type-mapping <json>", "Semantic event-type extraction (#959): inline JSON or @path/to/file.json, e.g. " + `'{"source":"header","header":"X-GitHub-Event"}' or '{"source":"body","path":"event"}'`).option("--clear-event-type-mapping", "Remove the mapping (deliveries fall back to the collapsed custom.webhook.{source} type)").option("--expected-interval <seconds>", "Declared cadence: >=1 event or heartbeat per N seconds. (Re)arms liveness supervision (#961)").option("--clear-cadence", "Remove the declared cadence (disarms liveness supervision)").option("--window-semantics <value>", `Declared window semantics: ${WINDOW_SEMANTICS.join(" or ")}`).option("--mutation-policy <value>", `Declared mutation re-emit policy: ${MUTATION_POLICIES.join(" or ")}`).action(async (id, options3) => {
151160
+ const resolvedId = await resolveWebhookId(id);
151161
+ const client = getClient();
151162
+ try {
151163
+ const updates = await buildUpdateSourcePatch(options3);
151164
+ const source = await client.webhooks.updateSource(resolvedId, updates);
151165
+ success(`Webhook source updated: ${source.id}`, {
151166
+ id: source.id,
151167
+ name: source.name,
151168
+ enabled: source.enabled
151169
+ });
151170
+ } catch (err2) {
151171
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151172
+ error(`Failed to update webhook source: ${message2}`);
151173
+ }
151174
+ });
151175
+ webhooks.command("delete <id>").description("Delete a webhook source").action(async (id) => {
151176
+ const resolvedId = await resolveWebhookId(id);
151177
+ const client = getClient();
151178
+ try {
151179
+ await client.webhooks.deleteSource(resolvedId);
151180
+ success(`Webhook source deleted: ${resolvedId}`);
151181
+ } catch (err2) {
151182
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151183
+ error(`Failed to delete webhook source: ${message2}`);
151184
+ }
151185
+ });
151186
+ webhooks.command("trigger").description("Trigger a custom event").requiredOption("--type <type>", "Event type").requiredOption("--payload <json>", "Event payload as JSON").option("--instance <id>", "Instance ID").option("--correlation-id <id>", "Correlation ID").option("--causation-id <event-id>", "Parent event ID (keeps the causality tree for mid-flow emissions, #1072)").action(async (options3) => {
151187
+ const client = getClient();
151188
+ try {
151189
+ let payload;
151190
+ try {
151191
+ payload = JSON.parse(options3.payload);
151192
+ } catch {
151193
+ error("Invalid JSON for --payload");
151194
+ return;
151195
+ }
151196
+ const result = await client.webhooks.trigger({
151197
+ eventType: options3.type,
151198
+ payload,
151199
+ instanceId: options3.instance,
151200
+ correlationId: options3.correlationId,
151201
+ causationId: options3.causationId
151202
+ });
151203
+ success(`Event triggered: ${result.eventId}`, {
151204
+ eventId: result.eventId,
151205
+ eventType: result.eventType
151206
+ });
151207
+ } catch (err2) {
151208
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151209
+ error(`Failed to trigger event: ${message2}`);
151210
+ }
151211
+ });
151212
+ webhooks.command("heartbeat <source>").description('Record a connector heartbeat ("ran, zero events found") \u2014 resets the liveness window (#961)').action(async (source) => {
151213
+ const client = getClient();
151214
+ try {
151215
+ const result = await client.webhooks.heartbeat(source);
151216
+ success(`Heartbeat recorded for '${result.source}'`, {
151217
+ heartbeatAt: result.heartbeatAt,
151218
+ livenessStatus: result.livenessStatus ?? "unsupervised",
151219
+ expectedIntervalSeconds: result.expectedIntervalSeconds
151220
+ });
151221
+ } catch (err2) {
151222
+ const message2 = err2 instanceof Error ? err2.message : "Unknown error";
151223
+ error(`Failed to record heartbeat: ${message2}`);
151224
+ }
151225
+ });
151226
+ return webhooks;
151227
+ }
151228
+
151229
+ // src/commands/sources.ts
151230
+ var github = {
151231
+ name: "github",
151232
+ description: "GitHub repo webhooks (RFC #925 Phase 3 recipe)",
151233
+ signatureConfig: { algorithm: "hmac-sha256", header: "X-Hub-Signature-256", prefix: "sha256=" },
151234
+ idempotencyKeyTemplate: "github:{headers.x-github-delivery}",
151235
+ eventTypeMapping: { source: "header", header: "X-GitHub-Event" },
151236
+ expectedIntervalSeconds: 86400,
151237
+ schemas: { push: custom_github_push_default, pull_request: custom_github_pull_request_default, issues: custom_github_issues_default, release: custom_github_release_default },
151238
+ async ensureProviderWebhook(api, repo, hook) {
151239
+ if (!/^[\w.-]+\/[\w.-]+$/.test(repo))
151240
+ throw new Error(`--repo must be OWNER/NAME, got '${repo}'`);
151241
+ const body = {
151242
+ active: true,
151243
+ events: hook.events,
151244
+ config: { url: hook.url, content_type: "json", secret: hook.secret, insecure_ssl: "0" }
151245
+ };
151246
+ const existing = await api("GET", `/repos/${repo}/hooks`);
151247
+ const match = existing.find((h10) => h10.config?.url === hook.url);
151248
+ if (match) {
151249
+ await api("PATCH", `/repos/${repo}/hooks/${match.id}`, body);
151250
+ return String(match.id);
151251
+ }
151252
+ const created = await api("POST", `/repos/${repo}/hooks`, { name: "web", ...body });
151253
+ return String(created.id);
151254
+ }
151255
+ };
151256
+ var PRESETS = { github };
151257
+ var GITHUB_API = "https://api.github.com";
151258
+ async function ghAvailable() {
151259
+ try {
151260
+ return await Bun.spawn({ cmd: ["gh", "auth", "status"], stdout: "ignore", stderr: "ignore" }).exited === 0;
151261
+ } catch {
151262
+ return false;
151263
+ }
151264
+ }
151265
+ async function githubApiViaGh(method, path, body) {
151266
+ const cmd = ["gh", "api", "--method", method, path];
151267
+ if (body !== undefined)
151268
+ cmd.push("--input", "-");
151269
+ const proc = Bun.spawn({ cmd, stdin: body === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
151270
+ if (body !== undefined && proc.stdin) {
151271
+ proc.stdin.write(JSON.stringify(body));
151272
+ proc.stdin.end();
151273
+ }
151274
+ const [stdout, stderr, code] = await Promise.all([
151275
+ new Response(proc.stdout).text(),
151276
+ new Response(proc.stderr).text(),
151277
+ proc.exited
151278
+ ]);
151279
+ if (code !== 0)
151280
+ throw new Error(`gh api ${method} ${path} failed: ${stderr.trim() || stdout.trim()}`);
151281
+ return stdout.trim() ? JSON.parse(stdout) : undefined;
151282
+ }
151283
+ function githubApiViaToken(token) {
151284
+ return async (method, path, body) => {
151285
+ const resp = await fetch(`${GITHUB_API}${path}`, {
151286
+ method,
151287
+ headers: {
151288
+ authorization: `Bearer ${token}`,
151289
+ accept: "application/vnd.github+json",
151290
+ "content-type": "application/json"
151291
+ },
151292
+ body: body === undefined ? undefined : JSON.stringify(body)
151293
+ });
151294
+ if (!resp.ok)
151295
+ throw new Error(`GitHub API ${method} ${path} returned ${resp.status}: ${await resp.text()}`);
151296
+ return resp.status === 204 ? undefined : await resp.json();
151297
+ };
151298
+ }
151299
+ async function resolveGithubApi() {
151300
+ if (await ghAvailable())
151301
+ return githubApiViaGh;
151302
+ const token = process.env.GITHUB_TOKEN;
151303
+ if (token)
151304
+ return githubApiViaToken(token);
151305
+ throw new Error("no GitHub credentials: log in with `gh auth login` or set GITHUB_TOKEN (or pass --no-provider-webhook)");
151306
+ }
151307
+
151308
+ class StepFailure extends Error {
151309
+ step;
151310
+ completed;
151311
+ constructor(step, completed, cause) {
151312
+ super(cause instanceof Error ? cause.message : String(cause));
151313
+ this.step = step;
151314
+ this.completed = completed;
151315
+ }
151316
+ }
151317
+ var STEPS = ["source", "idempotency", "event-type-mapping", "schemas", "provider-webhook"];
151318
+ async function addSource(input) {
151319
+ const { preset, client } = input;
151320
+ const unknown2 = input.events.filter((e10) => !(e10 in preset.schemas));
151321
+ if (unknown2.length > 0) {
151322
+ throw new Error(`unknown ${preset.name} events: ${unknown2.join(", ")} (known: ${Object.keys(preset.schemas).join(", ")})`);
151323
+ }
151324
+ const webhookUrl = `${input.publicUrl.replace(/\/+$/, "")}/api/v2/webhooks/ingress/${preset.name}`;
151325
+ const completed = [];
151326
+ const run = async (step, fn) => {
151327
+ try {
151328
+ const value = await fn();
151329
+ completed.push(step);
151330
+ return value;
151331
+ } catch (err2) {
151332
+ throw new StepFailure(step, completed, err2);
151333
+ }
151334
+ };
151335
+ const source = await run(STEPS[0], async () => {
151336
+ const existing = (await client.listSources()).find((s2) => s2.name === preset.name);
151337
+ const body = {
151338
+ description: preset.description,
151339
+ signatureConfig: preset.signatureConfig,
151340
+ signatureSecret: input.secret,
151341
+ expectedIntervalSeconds: preset.expectedIntervalSeconds
151342
+ };
151343
+ if (existing)
151344
+ return { id: (await client.updateSource(existing.id, body)).id, created: false };
151345
+ return { id: (await client.createSource({ name: preset.name, enabled: true, ...body })).id, created: true };
151346
+ });
151347
+ await run(STEPS[1], () => client.updateSource(source.id, { idempotencyKeyTemplate: preset.idempotencyKeyTemplate }));
151348
+ await run(STEPS[2], () => client.updateSource(source.id, { eventTypeMapping: preset.eventTypeMapping }));
151349
+ await run(STEPS[3], async () => {
151350
+ for (const event of input.events) {
151351
+ const eventType = `custom.${preset.name}.${event}`;
151352
+ await input.registerSchema(eventType, preset.schemas[event], `${preset.name} ${event} deliveries`);
151353
+ }
151354
+ });
151355
+ let providerHookId;
151356
+ if (input.providerWebhook) {
151357
+ providerHookId = await run(STEPS[4], async () => preset.ensureProviderWebhook(await input.providerApi(), input.target, {
151358
+ url: webhookUrl,
151359
+ secret: input.secret,
151360
+ events: input.events
151361
+ }));
151362
+ }
151363
+ return { sourceId: source.id, created: source.created, webhookUrl, providerHookId, completed };
151364
+ }
151365
+ function createSourcesCommand() {
151366
+ const sources = new Command("sources").description("One-command onboarding of external webhook sources");
151367
+ sources.command("add <provider>").description(`Create or update a provider source end to end (presets: ${Object.keys(PRESETS).join(", ")})`).requiredOption("--repo <owner/name>", "Provider-side target (GitHub: OWNER/NAME)").option("--events <list>", "Comma-separated provider events (default: every bundled schema)").option("--public-url <url>", "Omni URL reachable from the provider (default: configured apiUrl)").option("--secret-env <VAR>", "Reuse the webhook secret from environment variable VAR instead of generating one").option("--no-provider-webhook", "Skip creating the webhook on the provider side").action(async (provider, options3) => {
151368
+ const preset = PRESETS[provider];
151369
+ if (!preset)
151370
+ error(`Unknown provider '${provider}'. Presets: ${Object.keys(PRESETS).join(", ")}`);
151371
+ const client = getClient();
151372
+ try {
151373
+ const secret = await resolveSignatureSecret({ signatureSecretEnv: options3.secretEnv }) ?? randomBytes4(32).toString("hex");
151374
+ const result = await addSource({
151375
+ preset,
151376
+ target: options3.repo,
151377
+ events: options3.events ? options3.events.split(",").map((e10) => e10.trim()) : Object.keys(preset.schemas),
151378
+ secret,
151379
+ publicUrl: options3.publicUrl ?? loadConfig().apiUrl ?? "http://localhost:8882",
151380
+ providerWebhook: options3.providerWebhook,
151381
+ client: client.webhooks,
151382
+ registerSchema: (eventType, schema3, description) => schemaApiRequest("", { method: "POST", body: JSON.stringify({ eventType, schema: schema3, description }) }),
151383
+ providerApi: resolveGithubApi
151384
+ });
151385
+ success(`Source ${result.created ? "created" : "updated"}: ${result.sourceId}`, {
151386
+ id: result.sourceId,
151387
+ name: preset.name,
151388
+ secret,
151389
+ webhookUrl: result.webhookUrl,
151390
+ providerHookId: result.providerHookId ?? "(skipped)",
151391
+ steps: result.completed.join(", ")
151392
+ });
151393
+ warn("The secret is shown once; store it now (re-run with --secret-env VAR to reuse it).");
151394
+ } catch (err2) {
151395
+ if (err2 instanceof StepFailure) {
151396
+ const remaining = STEPS.filter((s2) => !err2.completed.includes(s2));
151397
+ error(`Step '${err2.step}' failed: ${err2.message}`, {
151398
+ completed: err2.completed,
151399
+ remaining,
151400
+ resume: "re-run the same command; completed steps are updated in place, not duplicated"
151401
+ });
151402
+ }
151403
+ error(`Failed to add source: ${err2 instanceof Error ? err2.message : String(err2)}`);
151404
+ }
151405
+ });
151406
+ return sources;
151407
+ }
151408
+
150671
151409
  // src/commands/speak.ts
150672
151410
  import { writeFile as writeFile2 } from "fs/promises";
150673
151411
  init_output();
@@ -151881,7 +152619,7 @@ async function cleanupLegacyArtifacts(skipList) {
151881
152619
  init_output();
151882
152620
 
151883
152621
  // src/update-diagnostics.ts
151884
- import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "fs";
152622
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "fs";
151885
152623
  import { homedir as homedir13 } from "os";
151886
152624
  import { join as join18 } from "path";
151887
152625
  var UPDATE_DIAGNOSTICS_SCHEMA_VERSION = 1;
@@ -151916,7 +152654,7 @@ function tailFileLines(path, maxLines) {
151916
152654
  if (!existsSync19(path))
151917
152655
  return [];
151918
152656
  try {
151919
- const raw2 = readFileSync18(path, "utf8");
152657
+ const raw2 = readFileSync19(path, "utf8");
151920
152658
  const lines = raw2.split(/\r?\n/);
151921
152659
  if (lines.length > 0 && lines[lines.length - 1] === "")
151922
152660
  lines.pop();
@@ -152599,353 +153337,6 @@ ${lines.join(`
152599
153337
  return voice;
152600
153338
  }
152601
153339
 
152602
- // src/commands/webhooks.ts
152603
- import { readFileSync as readFileSync19 } from "fs";
152604
- init_zod();
152605
- init_output();
152606
- var SIGNATURE_ALGORITHMS = ["hmac-sha256", "hmac-sha1", "token-match"];
152607
- var signatureSecretSchema = exports_external.string().min(8, "at least 8 characters").max(512, "at most 512 characters");
152608
- var SECRET_SOURCE_FLAGS = "--signature-secret, --signature-secret-env, --signature-secret-stdin";
152609
- async function readSecretFromStdin() {
152610
- if (process.stdin.isTTY) {
152611
- throw new Error('--signature-secret-stdin requires piped stdin (e.g. `printf %s "$SECRET" | omni webhooks ...`)');
152612
- }
152613
- const chunks = [];
152614
- for await (const chunk of process.stdin) {
152615
- chunks.push(Buffer.from(chunk));
152616
- }
152617
- return Buffer.concat(chunks).toString("utf-8");
152618
- }
152619
- async function resolveSignatureSecret(options3, readStdin = readSecretFromStdin) {
152620
- const sources = [
152621
- options3.signatureSecret !== undefined,
152622
- options3.signatureSecretEnv !== undefined,
152623
- options3.signatureSecretStdin === true
152624
- ].filter(Boolean).length;
152625
- if (sources === 0)
152626
- return;
152627
- if (sources > 1) {
152628
- throw new Error(`Use only one of ${SECRET_SOURCE_FLAGS}`);
152629
- }
152630
- let raw2;
152631
- let origin;
152632
- if (options3.signatureSecretEnv !== undefined) {
152633
- const name = options3.signatureSecretEnv;
152634
- if (!name)
152635
- throw new Error("--signature-secret-env requires a variable name");
152636
- raw2 = process.env[name];
152637
- origin = `environment variable ${name}`;
152638
- if (raw2 === undefined || raw2 === "") {
152639
- throw new Error(`${origin} is not set or empty`);
152640
- }
152641
- } else if (options3.signatureSecretStdin) {
152642
- raw2 = (await readStdin()).replace(/\r?\n$/, "");
152643
- origin = "stdin";
152644
- if (!raw2)
152645
- throw new Error("no secret received on stdin");
152646
- } else {
152647
- raw2 = options3.signatureSecret;
152648
- origin = "--signature-secret";
152649
- }
152650
- const parsed = signatureSecretSchema.safeParse(raw2);
152651
- if (!parsed.success) {
152652
- const reason = parsed.error.issues.map((issue) => issue.message).join("; ");
152653
- throw new Error(`Invalid signature secret from ${origin}: ${reason}`);
152654
- }
152655
- return parsed.data;
152656
- }
152657
- function buildSignatureConfig(options3) {
152658
- const { signatureAlgorithm, signatureHeader, signaturePrefix } = options3;
152659
- if (!signatureAlgorithm && !signatureHeader && !signaturePrefix)
152660
- return;
152661
- if (!signatureAlgorithm || !signatureHeader) {
152662
- error("--signature-algorithm and --signature-header must be provided together");
152663
- }
152664
- const algorithm = SIGNATURE_ALGORITHMS.find((a2) => a2 === signatureAlgorithm);
152665
- if (!algorithm) {
152666
- error(`Invalid --signature-algorithm; expected one of: ${SIGNATURE_ALGORITHMS.join(", ")}`);
152667
- }
152668
- if (algorithm === "token-match" && signaturePrefix) {
152669
- error("--signature-prefix is not applicable to token-match (the header carries the secret verbatim)");
152670
- }
152671
- return { algorithm, header: signatureHeader, prefix: signaturePrefix };
152672
- }
152673
- var eventTypeMappingSchema = exports_external.discriminatedUnion("source", [
152674
- exports_external.object({
152675
- source: exports_external.literal("header"),
152676
- header: exports_external.string().min(1, "header must be 1-200 characters").max(200, "header must be 1-200 characters")
152677
- }),
152678
- exports_external.object({
152679
- source: exports_external.literal("body"),
152680
- path: exports_external.string().min(1, "path must be 1-200 characters").max(200, "path must be 1-200 characters")
152681
- })
152682
- ]);
152683
- var EVENT_TYPE_MAPPING_HINT = 'expected {"source":"header","header":"X-GitHub-Event"} or {"source":"body","path":"event"}';
152684
- function resolveEventTypeMapping(raw2) {
152685
- if (raw2 === undefined)
152686
- return;
152687
- let text3 = raw2;
152688
- if (raw2.startsWith("@")) {
152689
- const path = raw2.slice(1);
152690
- try {
152691
- text3 = readFileSync19(path, "utf-8");
152692
- } catch (err2) {
152693
- const reason = err2 instanceof Error ? err2.message : "Unknown error";
152694
- throw new Error(`Cannot read --event-type-mapping file '${path}': ${reason}`);
152695
- }
152696
- }
152697
- let parsed;
152698
- try {
152699
- parsed = JSON.parse(text3);
152700
- } catch {
152701
- throw new Error(`Invalid JSON for --event-type-mapping; ${EVENT_TYPE_MAPPING_HINT}`);
152702
- }
152703
- const result = eventTypeMappingSchema.safeParse(parsed);
152704
- if (!result.success) {
152705
- const reason = result.error.issues.map((issue) => issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message).join("; ");
152706
- throw new Error(`Invalid --event-type-mapping: ${reason}; ${EVENT_TYPE_MAPPING_HINT}`);
152707
- }
152708
- return result.data;
152709
- }
152710
- async function buildUpdateSourcePatch(options3) {
152711
- const updates = buildLifecycleUpdates(options3);
152712
- if (options3.name)
152713
- updates.name = options3.name;
152714
- if (options3.description)
152715
- updates.description = options3.description;
152716
- if (options3.idempotencyKeyTemplate)
152717
- updates.idempotencyKeyTemplate = options3.idempotencyKeyTemplate;
152718
- if (options3.strictSchemas !== undefined)
152719
- updates.strictSchemas = options3.strictSchemas;
152720
- if (options3.clearEventTypeMapping && options3.eventTypeMapping !== undefined) {
152721
- throw new Error("Use only one of --event-type-mapping and --clear-event-type-mapping");
152722
- }
152723
- if (options3.clearEventTypeMapping)
152724
- updates.eventTypeMapping = null;
152725
- const eventTypeMapping = resolveEventTypeMapping(options3.eventTypeMapping);
152726
- if (eventTypeMapping !== undefined)
152727
- updates.eventTypeMapping = eventTypeMapping;
152728
- if (options3.enable)
152729
- updates.enabled = true;
152730
- if (options3.disable)
152731
- updates.enabled = false;
152732
- if (options3.clearSignature) {
152733
- updates.signatureConfig = null;
152734
- return updates;
152735
- }
152736
- const signatureConfig = buildSignatureConfig(options3);
152737
- if (signatureConfig)
152738
- updates.signatureConfig = signatureConfig;
152739
- const signatureSecret = await resolveSignatureSecret(options3);
152740
- if (signatureSecret)
152741
- updates.signatureSecret = signatureSecret;
152742
- return updates;
152743
- }
152744
- function assertPairedSignatureOnCreate(signatureConfig, signatureSecret) {
152745
- if (signatureConfig === undefined === (signatureSecret === undefined))
152746
- return;
152747
- if (signatureConfig === undefined) {
152748
- throw new Error(`a signature secret (${SECRET_SOURCE_FLAGS}) requires --signature-algorithm and --signature-header`);
152749
- }
152750
- throw new Error(`--signature-algorithm and --signature-header require a signature secret (${SECRET_SOURCE_FLAGS})`);
152751
- }
152752
- var WINDOW_SEMANTICS = ["future_only", "includes_in_progress"];
152753
- var MUTATION_POLICIES = ["same_id", "new_id"];
152754
- function parseExpectedInterval(raw2) {
152755
- if (raw2 === undefined)
152756
- return;
152757
- const seconds = Number(raw2);
152758
- if (!Number.isInteger(seconds) || seconds < 1) {
152759
- error("--expected-interval must be a positive integer number of seconds");
152760
- }
152761
- return seconds;
152762
- }
152763
- function parseWindowSemantics(raw2) {
152764
- if (raw2 === undefined)
152765
- return;
152766
- const value = WINDOW_SEMANTICS.find((v4) => v4 === raw2);
152767
- if (!value) {
152768
- error(`Invalid --window-semantics; expected one of: ${WINDOW_SEMANTICS.join(", ")}`);
152769
- }
152770
- return value;
152771
- }
152772
- function parseMutationPolicy(raw2) {
152773
- if (raw2 === undefined)
152774
- return;
152775
- const value = MUTATION_POLICIES.find((v4) => v4 === raw2);
152776
- if (!value) {
152777
- error(`Invalid --mutation-policy; expected one of: ${MUTATION_POLICIES.join(", ")}`);
152778
- }
152779
- return value;
152780
- }
152781
- function buildLifecycleUpdates(options3) {
152782
- if (options3.clearCadence && options3.expectedInterval !== undefined) {
152783
- error("Use only one of --expected-interval and --clear-cadence");
152784
- }
152785
- const updates = {};
152786
- if (options3.clearCadence)
152787
- updates.expectedIntervalSeconds = null;
152788
- const expectedIntervalSeconds = parseExpectedInterval(options3.expectedInterval);
152789
- if (expectedIntervalSeconds !== undefined)
152790
- updates.expectedIntervalSeconds = expectedIntervalSeconds;
152791
- const windowSemantics = parseWindowSemantics(options3.windowSemantics);
152792
- if (windowSemantics !== undefined)
152793
- updates.windowSemantics = windowSemantics;
152794
- const mutationPolicy = parseMutationPolicy(options3.mutationPolicy);
152795
- if (mutationPolicy !== undefined)
152796
- updates.mutationPolicy = mutationPolicy;
152797
- return updates;
152798
- }
152799
- function createWebhooksCommand() {
152800
- const webhooks = new Command("webhooks").description("Manage webhook sources");
152801
- webhooks.command("list").description("List webhook sources").option("--enabled", "Show only enabled sources").option("--disabled", "Show only disabled sources").action(async (options3) => {
152802
- const client = getClient();
152803
- try {
152804
- let enabledFilter;
152805
- if (options3.enabled)
152806
- enabledFilter = true;
152807
- if (options3.disabled)
152808
- enabledFilter = false;
152809
- const result = await client.webhooks.listSources({
152810
- enabled: enabledFilter
152811
- });
152812
- const items = result.map((w) => ({
152813
- id: w.id,
152814
- name: w.name,
152815
- enabled: w.enabled ? "yes" : "no",
152816
- health: w.livenessStatus ?? "-",
152817
- cadence: w.expectedIntervalSeconds != null ? `${w.expectedIntervalSeconds}s` : "-",
152818
- createdAt: w.createdAt
152819
- }));
152820
- list(items, { emptyMessage: "No webhook sources found." });
152821
- } catch (err2) {
152822
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152823
- error(`Failed to list webhook sources: ${message2}`);
152824
- }
152825
- });
152826
- webhooks.command("get <id>").description("Get webhook source details").action(async (id) => {
152827
- const resolvedId = await resolveWebhookId(id);
152828
- const client = getClient();
152829
- try {
152830
- const source = await client.webhooks.getSource(resolvedId);
152831
- data(source);
152832
- } catch (err2) {
152833
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152834
- error(`Failed to get webhook source: ${message2}`);
152835
- }
152836
- });
152837
- webhooks.command("create").description("Create a webhook source").requiredOption("--name <name>", "Webhook source name").option("--description <desc>", "Description").option("--disabled", "Create in disabled state").option("--headers <json>", `Expected headers as JSON (e.g., '{"X-Secret": true}')`).option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--idempotency-key-template <template>", "Delivery-identity key template for redelivery dedup (#958). Placeholders: {source}, {sha256(body)}, " + "{headers.<name>}, {payload.<dot.path>}. Defaults to '{source}:{sha256(body)}'").option("--strict-schemas", "Refuse deliveries whose event type has no enabled registered schema (dead-lettered as " + "schema_not_registered, #1000). Recommended for new sources. Defaults to off").option("--event-type-mapping <json>", "Semantic event-type extraction (#959): inline JSON or @path/to/file.json, e.g. " + `'{"source":"header","header":"X-GitHub-Event"}' or '{"source":"body","path":"event"}'. ` + "A mapped source emits custom.{source}.{event} instead of the collapsed custom.webhook.{source}").option("--expected-interval <seconds>", "Declared cadence: >=1 event or heartbeat per N seconds. Arms liveness supervision (#961)").option("--window-semantics <value>", `Declared window semantics: ${WINDOW_SEMANTICS.join(" or ")}`).option("--mutation-policy <value>", `Declared mutation re-emit policy: ${MUTATION_POLICIES.join(" or ")}`).action(async (options3) => {
152838
- const client = getClient();
152839
- try {
152840
- let expectedHeaders;
152841
- if (options3.headers) {
152842
- try {
152843
- expectedHeaders = JSON.parse(options3.headers);
152844
- } catch {
152845
- error("Invalid JSON for --headers");
152846
- }
152847
- }
152848
- const signatureConfig = buildSignatureConfig(options3);
152849
- const signatureSecret = await resolveSignatureSecret(options3);
152850
- assertPairedSignatureOnCreate(signatureConfig, signatureSecret);
152851
- const source = await client.webhooks.createSource({
152852
- name: options3.name,
152853
- description: options3.description,
152854
- enabled: !options3.disabled,
152855
- expectedHeaders,
152856
- signatureConfig,
152857
- signatureSecret,
152858
- idempotencyKeyTemplate: options3.idempotencyKeyTemplate,
152859
- strictSchemas: options3.strictSchemas,
152860
- eventTypeMapping: resolveEventTypeMapping(options3.eventTypeMapping),
152861
- expectedIntervalSeconds: parseExpectedInterval(options3.expectedInterval),
152862
- windowSemantics: parseWindowSemantics(options3.windowSemantics),
152863
- mutationPolicy: parseMutationPolicy(options3.mutationPolicy)
152864
- });
152865
- const details = {
152866
- id: source.id,
152867
- name: source.name,
152868
- url: `POST /api/v2/webhooks/${source.name}`
152869
- };
152870
- if (signatureConfig) {
152871
- details.publicUrl = `POST /api/v2/webhooks/ingress/${source.name}`;
152872
- }
152873
- success(`Webhook source created: ${source.id}`, details);
152874
- } catch (err2) {
152875
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152876
- error(`Failed to create webhook source: ${message2}`);
152877
- }
152878
- });
152879
- webhooks.command("update <id>").description("Update a webhook source").option("--name <name>", "New name").option("--description <desc>", "New description").option("--enable", "Enable the webhook").option("--disable", "Disable the webhook").option("--signature-algorithm <alg>", "Signature verification: hmac-sha256, hmac-sha1, or token-match").option("--signature-header <name>", "Header carrying the signature/token (e.g., X-Hub-Signature-256)").option("--signature-prefix <prefix>", "Prefix before the hex digest (e.g., 'sha256=')").option("--signature-secret <secret>", "Shared secret for signature verification (write-only). Visible in shell history and process lists \u2014 prefer --signature-secret-env or --signature-secret-stdin").option("--signature-secret-env <VAR>", "Read the shared secret from environment variable VAR").option("--signature-secret-stdin", "Read the shared secret from stdin (trailing newline stripped)").option("--clear-signature", "Remove the signature config and stored secret").option("--idempotency-key-template <template>", "Delivery-identity key template for redelivery dedup (#958). Placeholders: {source}, {sha256(body)}, " + "{headers.<name>}, {payload.<dot.path>}").option("--strict-schemas", "Refuse deliveries whose event type has no enabled registered schema (dead-lettered as " + "schema_not_registered, #1000)").option("--no-strict-schemas", "Return the source to opt-in pass-through for unregistered event types").option("--event-type-mapping <json>", "Semantic event-type extraction (#959): inline JSON or @path/to/file.json, e.g. " + `'{"source":"header","header":"X-GitHub-Event"}' or '{"source":"body","path":"event"}'`).option("--clear-event-type-mapping", "Remove the mapping (deliveries fall back to the collapsed custom.webhook.{source} type)").option("--expected-interval <seconds>", "Declared cadence: >=1 event or heartbeat per N seconds. (Re)arms liveness supervision (#961)").option("--clear-cadence", "Remove the declared cadence (disarms liveness supervision)").option("--window-semantics <value>", `Declared window semantics: ${WINDOW_SEMANTICS.join(" or ")}`).option("--mutation-policy <value>", `Declared mutation re-emit policy: ${MUTATION_POLICIES.join(" or ")}`).action(async (id, options3) => {
152880
- const resolvedId = await resolveWebhookId(id);
152881
- const client = getClient();
152882
- try {
152883
- const updates = await buildUpdateSourcePatch(options3);
152884
- const source = await client.webhooks.updateSource(resolvedId, updates);
152885
- success(`Webhook source updated: ${source.id}`, {
152886
- id: source.id,
152887
- name: source.name,
152888
- enabled: source.enabled
152889
- });
152890
- } catch (err2) {
152891
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152892
- error(`Failed to update webhook source: ${message2}`);
152893
- }
152894
- });
152895
- webhooks.command("delete <id>").description("Delete a webhook source").action(async (id) => {
152896
- const resolvedId = await resolveWebhookId(id);
152897
- const client = getClient();
152898
- try {
152899
- await client.webhooks.deleteSource(resolvedId);
152900
- success(`Webhook source deleted: ${resolvedId}`);
152901
- } catch (err2) {
152902
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152903
- error(`Failed to delete webhook source: ${message2}`);
152904
- }
152905
- });
152906
- webhooks.command("trigger").description("Trigger a custom event").requiredOption("--type <type>", "Event type").requiredOption("--payload <json>", "Event payload as JSON").option("--instance <id>", "Instance ID").option("--correlation-id <id>", "Correlation ID").option("--causation-id <event-id>", "Parent event ID (keeps the causality tree for mid-flow emissions, #1072)").action(async (options3) => {
152907
- const client = getClient();
152908
- try {
152909
- let payload;
152910
- try {
152911
- payload = JSON.parse(options3.payload);
152912
- } catch {
152913
- error("Invalid JSON for --payload");
152914
- return;
152915
- }
152916
- const result = await client.webhooks.trigger({
152917
- eventType: options3.type,
152918
- payload,
152919
- instanceId: options3.instance,
152920
- correlationId: options3.correlationId,
152921
- causationId: options3.causationId
152922
- });
152923
- success(`Event triggered: ${result.eventId}`, {
152924
- eventId: result.eventId,
152925
- eventType: result.eventType
152926
- });
152927
- } catch (err2) {
152928
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152929
- error(`Failed to trigger event: ${message2}`);
152930
- }
152931
- });
152932
- webhooks.command("heartbeat <source>").description('Record a connector heartbeat ("ran, zero events found") \u2014 resets the liveness window (#961)').action(async (source) => {
152933
- const client = getClient();
152934
- try {
152935
- const result = await client.webhooks.heartbeat(source);
152936
- success(`Heartbeat recorded for '${result.source}'`, {
152937
- heartbeatAt: result.heartbeatAt,
152938
- livenessStatus: result.livenessStatus ?? "unsupervised",
152939
- expectedIntervalSeconds: result.expectedIntervalSeconds
152940
- });
152941
- } catch (err2) {
152942
- const message2 = err2 instanceof Error ? err2.message : "Unknown error";
152943
- error(`Failed to record heartbeat: ${message2}`);
152944
- }
152945
- });
152946
- return webhooks;
152947
- }
152948
-
152949
153340
  // src/commands/where.ts
152950
153341
  init_output();
152951
153342
  function createWhereCommand() {
@@ -153438,6 +153829,12 @@ var COMMANDS = [
153438
153829
  helpGroup: "Management",
153439
153830
  helpDescription: "Webhook management"
153440
153831
  },
153832
+ {
153833
+ create: createSourcesCommand,
153834
+ category: "advanced",
153835
+ helpGroup: "Management",
153836
+ helpDescription: "One-command external source onboarding (github, ...)"
153837
+ },
153441
153838
  {
153442
153839
  create: createTurnsCommand,
153443
153840
  category: "advanced",