@junando/worker 0.15.1 → 0.16.1
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/handler.cjs +69 -10
- package/package.json +2 -2
package/dist/handler.cjs
CHANGED
|
@@ -19669,6 +19669,12 @@ let RuleEvaluationPhase = /* @__PURE__ */ function(RuleEvaluationPhase) {
|
|
|
19669
19669
|
RuleEvaluationPhase["PostLlm"] = "post-llm";
|
|
19670
19670
|
return RuleEvaluationPhase;
|
|
19671
19671
|
}({});
|
|
19672
|
+
/** Notifier backend a logical channel resolves to */
|
|
19673
|
+
let ChannelType = /* @__PURE__ */ function(ChannelType) {
|
|
19674
|
+
ChannelType["Slack"] = "slack";
|
|
19675
|
+
ChannelType["Teams"] = "teams";
|
|
19676
|
+
return ChannelType;
|
|
19677
|
+
}({});
|
|
19672
19678
|
const AlertCountSchema = object({
|
|
19673
19679
|
min: number$1().optional(),
|
|
19674
19680
|
max: number$1().optional()
|
|
@@ -19718,9 +19724,19 @@ const RuleSchema = object({
|
|
|
19718
19724
|
requiresRollback: boolean$1().optional()
|
|
19719
19725
|
});
|
|
19720
19726
|
const RuleSectionSchema = object({ rules: array(RuleSchema).default([]) });
|
|
19727
|
+
const SLACK_CHANNEL_SCHEMA = object({
|
|
19728
|
+
type: literal("slack"),
|
|
19729
|
+
channel: string$1().startsWith("#")
|
|
19730
|
+
});
|
|
19731
|
+
const TEAMS_CHANNEL_SCHEMA = object({
|
|
19732
|
+
type: literal("teams"),
|
|
19733
|
+
webhookUrlEnv: string$1().min(1)
|
|
19734
|
+
});
|
|
19735
|
+
const ChannelConfigSchema = discriminatedUnion("type", [SLACK_CHANNEL_SCHEMA, TEAMS_CHANNEL_SCHEMA]);
|
|
19721
19736
|
const RuleConfigurationSchema = object({
|
|
19722
19737
|
["pre-llm"]: RuleSectionSchema,
|
|
19723
|
-
["post-llm"]: RuleSectionSchema
|
|
19738
|
+
["post-llm"]: RuleSectionSchema,
|
|
19739
|
+
channels: record(string$1(), ChannelConfigSchema).default({})
|
|
19724
19740
|
});
|
|
19725
19741
|
|
|
19726
19742
|
//#endregion
|
|
@@ -74129,6 +74145,7 @@ function sanitizeEndpointPath(endpointPath) {
|
|
|
74129
74145
|
if (!endpointPath) return "unknown";
|
|
74130
74146
|
return endpointPath.replaceAll("`", "").slice(0, 200);
|
|
74131
74147
|
}
|
|
74148
|
+
const ROLLBACK_PROBABLE_CAUSE_MAX_LEN = 500;
|
|
74132
74149
|
var SlackNotifier = class {
|
|
74133
74150
|
botToken;
|
|
74134
74151
|
channel;
|
|
@@ -74244,7 +74261,8 @@ var SlackNotifier = class {
|
|
|
74244
74261
|
serviceName: cluster.serviceName,
|
|
74245
74262
|
endpointPath: cluster.endpointPath,
|
|
74246
74263
|
alertType: cluster.alertType,
|
|
74247
|
-
urgencyLevel: analysis.urgency_level
|
|
74264
|
+
urgencyLevel: analysis.urgency_level,
|
|
74265
|
+
probableCause: analysis.probable_cause.slice(0, ROLLBACK_PROBABLE_CAUSE_MAX_LEN)
|
|
74248
74266
|
}),
|
|
74249
74267
|
confirm: {
|
|
74250
74268
|
title: {
|
|
@@ -81941,15 +81959,52 @@ function buildNotifierRegistry(config) {
|
|
|
81941
81959
|
return registry;
|
|
81942
81960
|
}
|
|
81943
81961
|
/**
|
|
81962
|
+
* Builds the notifier for a single named channel entry from the rules YAML's
|
|
81963
|
+
* `channels:` section.
|
|
81964
|
+
*
|
|
81965
|
+
* @throws {Error} if the channel's backend cannot actually be reached (e.g.
|
|
81966
|
+
* `type: slack` with no SLACK_BOT_TOKEN configured, or `type: teams` whose
|
|
81967
|
+
* `webhookUrlEnv` is unset) — fails fast at startup rather than at delivery
|
|
81968
|
+
* time, mid-incident.
|
|
81969
|
+
*/
|
|
81970
|
+
function buildChannelNotifier(name, channelConfig, config) {
|
|
81971
|
+
if (channelConfig.type === "slack") {
|
|
81972
|
+
if (!config.slackBotToken) {
|
|
81973
|
+
throw new Error(`Channel "${name}" (type: slack) requires SLACK_BOT_TOKEN to be set`);
|
|
81974
|
+
}
|
|
81975
|
+
return new SlackNotifier(config.slackBotToken, channelConfig.channel);
|
|
81976
|
+
}
|
|
81977
|
+
const webhookUrl = process.env[channelConfig.webhookUrlEnv];
|
|
81978
|
+
if (!webhookUrl) {
|
|
81979
|
+
throw new Error(`Channel "${name}" (type: teams) references env var "${channelConfig.webhookUrlEnv}", which is not set`);
|
|
81980
|
+
}
|
|
81981
|
+
let parsedUrl;
|
|
81982
|
+
try {
|
|
81983
|
+
parsedUrl = new URL(webhookUrl);
|
|
81984
|
+
} catch {
|
|
81985
|
+
throw new Error(`Channel "${name}": ${channelConfig.webhookUrlEnv} is not a valid URL`);
|
|
81986
|
+
}
|
|
81987
|
+
if (!parsedUrl.searchParams.has("api-version")) {
|
|
81988
|
+
throw new Error(`Channel "${name}": ${channelConfig.webhookUrlEnv} must include api-version= as a query parameter`);
|
|
81989
|
+
}
|
|
81990
|
+
return new TeamsNotifier(webhookUrl);
|
|
81991
|
+
}
|
|
81992
|
+
/**
|
|
81944
81993
|
* Creates the notifier for the application.
|
|
81945
81994
|
*
|
|
81946
81995
|
* When `config.rulesConfigPath` is set:
|
|
81947
81996
|
* - Reads and validates the rules YAML config
|
|
81948
|
-
* -
|
|
81997
|
+
* - Builds a ChannelRegistry from its `channels:` section, with the default
|
|
81998
|
+
* notifier as fallback
|
|
81949
81999
|
* - Wraps the default notifier with a RoutingNotifier for multi-channel dispatch
|
|
81950
82000
|
*
|
|
81951
82001
|
* When `config.rulesConfigPath` is NOT set:
|
|
81952
82002
|
* - Returns the default notifier directly (backward-compatible)
|
|
82003
|
+
*
|
|
82004
|
+
* @throws {Error} at startup if a rule's route/escalate action references a
|
|
82005
|
+
* channel with no matching entry under `channels:` — an operator must fix
|
|
82006
|
+
* the rules config rather than have the alert silently fall back to the
|
|
82007
|
+
* default channel mid-incident.
|
|
81953
82008
|
*/
|
|
81954
82009
|
function createNotifier(config) {
|
|
81955
82010
|
const registry = buildNotifierRegistry(config);
|
|
@@ -81958,21 +82013,24 @@ function createNotifier(config) {
|
|
|
81958
82013
|
logger$3.debug("RULES_CONFIG_PATH not set — rule engine disabled, using default notifier");
|
|
81959
82014
|
return defaultNotifier;
|
|
81960
82015
|
}
|
|
82016
|
+
const yamlContent = (0, node_fs.readFileSync)(config.rulesConfigPath, "utf-8");
|
|
82017
|
+
const ruleConfig = parseRuleConfig(yamlContent);
|
|
81961
82018
|
const channelRegistry = new ChannelRegistry();
|
|
81962
82019
|
channelRegistry.setDefault(defaultNotifier);
|
|
82020
|
+
for (const [name, channelConfig] of Object.entries(ruleConfig.channels)) {
|
|
82021
|
+
channelRegistry.register(name, buildChannelNotifier(name, channelConfig, config));
|
|
82022
|
+
}
|
|
81963
82023
|
const unresolved = collectUnresolvedChannels(config, channelRegistry);
|
|
81964
82024
|
if (unresolved.length > 0) {
|
|
81965
|
-
|
|
81966
|
-
channels: unresolved,
|
|
81967
|
-
rulesConfigPath: config.rulesConfigPath
|
|
81968
|
-
}, "Rules reference channels with no registered notifier — these will be delivered to the default channel");
|
|
82025
|
+
throw new Error(`Rules reference undefined channels: ${unresolved.join(", ")}. Define them under ` + `"channels:" in ${config.rulesConfigPath}, or remove the reference.`);
|
|
81969
82026
|
}
|
|
81970
82027
|
return new RoutingNotifier(channelRegistry, defaultNotifier);
|
|
81971
82028
|
}
|
|
81972
82029
|
/**
|
|
81973
82030
|
* Collect the channel names referenced by Route/Escalate actions in the rules
|
|
81974
|
-
* config that have no notifier registered
|
|
81975
|
-
*
|
|
82031
|
+
* config that have no notifier registered against `registry` — i.e. no
|
|
82032
|
+
* matching entry under the rules YAML's `channels:` section. `createNotifier`
|
|
82033
|
+
* treats a non-empty result as a fail-fast startup error.
|
|
81976
82034
|
*
|
|
81977
82035
|
* Returns an empty list when no rules config is set.
|
|
81978
82036
|
*/
|
|
@@ -81983,7 +82041,7 @@ function collectUnresolvedChannels(config, registry) {
|
|
|
81983
82041
|
const yamlContent = (0, node_fs.readFileSync)(config.rulesConfigPath, "utf-8");
|
|
81984
82042
|
const ruleConfig = parseRuleConfig(yamlContent);
|
|
81985
82043
|
const referenced = new Set();
|
|
81986
|
-
for (const section of
|
|
82044
|
+
for (const section of [ruleConfig["pre-llm"], ruleConfig["post-llm"]]) {
|
|
81987
82045
|
for (const rule of section.rules) {
|
|
81988
82046
|
for (const action of rule.actions) {
|
|
81989
82047
|
if ("channel" in action) {
|
|
@@ -82027,6 +82085,7 @@ var NoopRollbackActionHandler = class {
|
|
|
82027
82085
|
endpointPath: request.endpointPath,
|
|
82028
82086
|
alertType: request.alertType,
|
|
82029
82087
|
urgencyLevel: request.urgencyLevel,
|
|
82088
|
+
probableCause: request.probableCause,
|
|
82030
82089
|
triggeredBy: request.triggeredBy,
|
|
82031
82090
|
correlationId: request.correlationId,
|
|
82032
82091
|
messageTs: request.messageTs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junando/worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "AWS Lambda SQS worker — processes alert events and dispatches notifications for Junando",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@aws-sdk/client-ssm": "^3.1127.0",
|
|
23
23
|
"ioredis": "^6.0.0",
|
|
24
24
|
"zod": "^4.5.4",
|
|
25
|
-
"@junando/core": "0.
|
|
25
|
+
"@junando/core": "0.16.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/aws-lambda": "^8.10.163",
|