@merittdev/horus 0.16.1 → 0.17.0

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.
Files changed (2) hide show
  1. package/dist/index.cjs +93 -5
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -65880,7 +65880,7 @@ init_cjs_shims();
65880
65880
 
65881
65881
  // ../../packages/core/src/version.ts
65882
65882
  init_cjs_shims();
65883
- var HORUS_VERSION = true ? "0.16.1" : "dev";
65883
+ var HORUS_VERSION = true ? "0.17.0" : "dev";
65884
65884
  var PINNED_SOURCE_VERSION = "2.0.2";
65885
65885
 
65886
65886
  // ../../packages/core/src/evidence.ts
@@ -70670,10 +70670,23 @@ var connectorsSchema = external_exports.object({
70670
70670
  ).optional()
70671
70671
  }).optional()
70672
70672
  }).default({});
70673
+ var notifySchema = external_exports.object({
70674
+ /** Only dispatch when an auto-investigation's confidence is >= this (0..1). Default 0.6. */
70675
+ minConfidence: external_exports.number().min(0).max(1).default(0.6),
70676
+ /** Generic outbound webhook (Slack-compatible JSON, HMAC-signed when `secret` is set). */
70677
+ webhook: external_exports.object({
70678
+ url: external_exports.string().url(),
70679
+ /** HMAC-SHA256 signing secret; when set, payloads carry an `X-Horus-Signature` header. */
70680
+ secret: external_exports.string().min(1).optional()
70681
+ }).optional(),
70682
+ /** Also push the headline to Horus Cloud (uses the existing cloud session, if signed in). */
70683
+ cloud: external_exports.boolean().default(false)
70684
+ }).optional();
70673
70685
  var environmentSchema = external_exports.object({
70674
70686
  name: external_exports.string().min(1),
70675
70687
  readOnly: external_exports.boolean().default(true),
70676
- connectors: connectorsSchema
70688
+ connectors: connectorsSchema,
70689
+ notify: notifySchema
70677
70690
  });
70678
70691
  var projectSchema = external_exports.object({
70679
70692
  name: external_exports.string().min(1),
@@ -70919,7 +70932,8 @@ function resolveEnvironment(config2, opts) {
70919
70932
  readOnly: environment.readOnly,
70920
70933
  repositories: repositories2,
70921
70934
  path: primary?.path ?? "",
70922
- connectors: resolved
70935
+ connectors: resolved,
70936
+ ...environment.notify !== void 0 ? { notify: environment.notify } : {}
70923
70937
  };
70924
70938
  }
70925
70939
  var CONFIG_EXAMPLES = {
@@ -118235,6 +118249,70 @@ async function runPacket(hintOrId, opts) {
118235
118249
  // ../../packages/cli/src/commands/watch.ts
118236
118250
  init_cjs_shims();
118237
118251
  var import_picocolors19 = __toESM(require_picocolors(), 1);
118252
+
118253
+ // ../../packages/cli/src/lib/notify-sink.ts
118254
+ init_cjs_shims();
118255
+ var import_node_crypto11 = require("crypto");
118256
+ function buildWebhookPayload(h3) {
118257
+ const pct2 = `${Math.round(h3.confidence * 100)}%`;
118258
+ return {
118259
+ text: `\u{1F52D} Horus: ${h3.hint} \u2192 ${h3.cause} (${pct2})`,
118260
+ investigationId: h3.investigationId,
118261
+ hint: h3.hint,
118262
+ cause: h3.cause,
118263
+ confidence: h3.confidence
118264
+ };
118265
+ }
118266
+ function signPayload(body2, secret) {
118267
+ return `sha256=${(0, import_node_crypto11.createHmac)("sha256", secret).update(body2).digest("hex")}`;
118268
+ }
118269
+ function shouldNotify(confidence, notify) {
118270
+ if (notify === void 0) return false;
118271
+ if (notify.webhook === void 0 && notify.cloud !== true) return false;
118272
+ return confidence >= notify.minConfidence;
118273
+ }
118274
+ async function postWebhook(url, secret, h3, timeoutMs) {
118275
+ const body2 = JSON.stringify(buildWebhookPayload(h3));
118276
+ const headers = { "content-type": "application/json" };
118277
+ if (secret !== void 0) headers["x-horus-signature"] = signPayload(body2, secret);
118278
+ const controller = new AbortController();
118279
+ const timer2 = setTimeout(() => controller.abort(), timeoutMs);
118280
+ try {
118281
+ const res = await fetch(url, { method: "POST", headers, body: body2, signal: controller.signal });
118282
+ if (!res.ok) return { target: "webhook", ok: false, detail: `HTTP ${res.status}` };
118283
+ return { target: "webhook", ok: true, detail: `HTTP ${res.status}` };
118284
+ } catch (err2) {
118285
+ return { target: "webhook", ok: false, detail: err2.message };
118286
+ } finally {
118287
+ clearTimeout(timer2);
118288
+ }
118289
+ }
118290
+ async function dispatchNotify(report, notify, opts = {}) {
118291
+ if (notify === void 0) return [];
118292
+ const h3 = {
118293
+ investigationId: report.id,
118294
+ hint: report.hint,
118295
+ cause: report.cause,
118296
+ confidence: report.confidence
118297
+ };
118298
+ if (!shouldNotify(report.confidence, notify)) return [];
118299
+ const timeoutMs = opts.timeoutMs ?? 5e3;
118300
+ const results = [];
118301
+ if (notify.webhook !== void 0) {
118302
+ results.push(await postWebhook(notify.webhook.url, notify.webhook.secret, h3, timeoutMs));
118303
+ }
118304
+ if (notify.cloud === true && opts.cloudPush !== void 0) {
118305
+ try {
118306
+ await opts.cloudPush();
118307
+ results.push({ target: "cloud", ok: true, detail: "pushed" });
118308
+ } catch (err2) {
118309
+ results.push({ target: "cloud", ok: false, detail: err2.message });
118310
+ }
118311
+ }
118312
+ return results;
118313
+ }
118314
+
118315
+ // ../../packages/cli/src/commands/watch.ts
118238
118316
  function detectNewSentryIncidents(issues, seen) {
118239
118317
  const fresh = [];
118240
118318
  for (const issue2 of issues) {
@@ -118364,6 +118442,16 @@ async function runWatch(opts) {
118364
118442
  console.log(
118365
118443
  `${import_picocolors19.default.bold("\u25CF")} ${incident.hint} ${import_picocolors19.default.dim("\u2192")} ${cause} ${import_picocolors19.default.dim(`(${(confidence * 100).toFixed(0)}%)`)} ${import_picocolors19.default.dim(`id: ${report.id}`)}${persisted}`
118366
118444
  );
118445
+ if (shouldNotify(confidence, renv.notify)) {
118446
+ const results = await dispatchNotify(
118447
+ { id: report.id, confidence, hint: incident.hint, cause },
118448
+ renv.notify
118449
+ );
118450
+ for (const r of results) {
118451
+ if (r.ok) console.error(import_picocolors19.default.dim(`[watch] notified ${r.target} (${r.detail})`));
118452
+ else console.error(import_picocolors19.default.yellow(`[watch] notify ${r.target} failed \u2014 ${r.detail}`));
118453
+ }
118454
+ }
118367
118455
  } catch (err2) {
118368
118456
  console.error(
118369
118457
  import_picocolors19.default.yellow(`[watch] investigation failed for "${incident.ref}" \u2014 ${err2.message}`)
@@ -125233,7 +125321,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
125233
125321
  // ../../packages/cli/src/commands/telemetry.ts
125234
125322
  init_cjs_shims();
125235
125323
  var import_picocolors56 = __toESM(require_picocolors(), 1);
125236
- var import_node_crypto11 = require("crypto");
125324
+ var import_node_crypto12 = require("crypto");
125237
125325
 
125238
125326
  // ../../packages/cli/src/lib/telemetry/transport.ts
125239
125327
  init_cjs_shims();
@@ -125425,7 +125513,7 @@ async function runTelemetryDisableContent() {
125425
125513
  return 0;
125426
125514
  }
125427
125515
  async function runTelemetryResetId() {
125428
- const next = (0, import_node_crypto11.randomUUID)();
125516
+ const next = (0, import_node_crypto12.randomUUID)();
125429
125517
  updateTelemetryState((s5) => {
125430
125518
  s5.installId = next;
125431
125519
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merittdev/horus",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "description": "Local-first, source-aware production-incident investigation engine",
5
5
  "type": "module",
6
6
  "bin": {