@junando/worker 0.15.0 → 0.16.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.
- package/dist/handler.cjs +68 -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
|
|
@@ -81941,15 +81957,52 @@ function buildNotifierRegistry(config) {
|
|
|
81941
81957
|
return registry;
|
|
81942
81958
|
}
|
|
81943
81959
|
/**
|
|
81960
|
+
* Builds the notifier for a single named channel entry from the rules YAML's
|
|
81961
|
+
* `channels:` section.
|
|
81962
|
+
*
|
|
81963
|
+
* @throws {Error} if the channel's backend cannot actually be reached (e.g.
|
|
81964
|
+
* `type: slack` with no SLACK_BOT_TOKEN configured, or `type: teams` whose
|
|
81965
|
+
* `webhookUrlEnv` is unset) — fails fast at startup rather than at delivery
|
|
81966
|
+
* time, mid-incident.
|
|
81967
|
+
*/
|
|
81968
|
+
function buildChannelNotifier(name, channelConfig, config) {
|
|
81969
|
+
if (channelConfig.type === "slack") {
|
|
81970
|
+
if (!config.slackBotToken) {
|
|
81971
|
+
throw new Error(`Channel "${name}" (type: slack) requires SLACK_BOT_TOKEN to be set`);
|
|
81972
|
+
}
|
|
81973
|
+
return new SlackNotifier(config.slackBotToken, channelConfig.channel);
|
|
81974
|
+
}
|
|
81975
|
+
const webhookUrl = process.env[channelConfig.webhookUrlEnv];
|
|
81976
|
+
if (!webhookUrl) {
|
|
81977
|
+
throw new Error(`Channel "${name}" (type: teams) references env var "${channelConfig.webhookUrlEnv}", which is not set`);
|
|
81978
|
+
}
|
|
81979
|
+
let parsedUrl;
|
|
81980
|
+
try {
|
|
81981
|
+
parsedUrl = new URL(webhookUrl);
|
|
81982
|
+
} catch {
|
|
81983
|
+
throw new Error(`Channel "${name}": ${channelConfig.webhookUrlEnv} is not a valid URL`);
|
|
81984
|
+
}
|
|
81985
|
+
if (!parsedUrl.searchParams.has("api-version")) {
|
|
81986
|
+
throw new Error(`Channel "${name}": ${channelConfig.webhookUrlEnv} must include api-version= as a query parameter`);
|
|
81987
|
+
}
|
|
81988
|
+
return new TeamsNotifier(webhookUrl);
|
|
81989
|
+
}
|
|
81990
|
+
/**
|
|
81944
81991
|
* Creates the notifier for the application.
|
|
81945
81992
|
*
|
|
81946
81993
|
* When `config.rulesConfigPath` is set:
|
|
81947
81994
|
* - Reads and validates the rules YAML config
|
|
81948
|
-
* -
|
|
81995
|
+
* - Builds a ChannelRegistry from its `channels:` section, with the default
|
|
81996
|
+
* notifier as fallback
|
|
81949
81997
|
* - Wraps the default notifier with a RoutingNotifier for multi-channel dispatch
|
|
81950
81998
|
*
|
|
81951
81999
|
* When `config.rulesConfigPath` is NOT set:
|
|
81952
82000
|
* - Returns the default notifier directly (backward-compatible)
|
|
82001
|
+
*
|
|
82002
|
+
* @throws {Error} at startup if a rule's route/escalate action references a
|
|
82003
|
+
* channel with no matching entry under `channels:` — an operator must fix
|
|
82004
|
+
* the rules config rather than have the alert silently fall back to the
|
|
82005
|
+
* default channel mid-incident.
|
|
81953
82006
|
*/
|
|
81954
82007
|
function createNotifier(config) {
|
|
81955
82008
|
const registry = buildNotifierRegistry(config);
|
|
@@ -81958,21 +82011,24 @@ function createNotifier(config) {
|
|
|
81958
82011
|
logger$3.debug("RULES_CONFIG_PATH not set — rule engine disabled, using default notifier");
|
|
81959
82012
|
return defaultNotifier;
|
|
81960
82013
|
}
|
|
82014
|
+
const yamlContent = (0, node_fs.readFileSync)(config.rulesConfigPath, "utf-8");
|
|
82015
|
+
const ruleConfig = parseRuleConfig(yamlContent);
|
|
81961
82016
|
const channelRegistry = new ChannelRegistry();
|
|
81962
82017
|
channelRegistry.setDefault(defaultNotifier);
|
|
82018
|
+
for (const [name, channelConfig] of Object.entries(ruleConfig.channels)) {
|
|
82019
|
+
channelRegistry.register(name, buildChannelNotifier(name, channelConfig, config));
|
|
82020
|
+
}
|
|
81963
82021
|
const unresolved = collectUnresolvedChannels(config, channelRegistry);
|
|
81964
82022
|
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");
|
|
82023
|
+
throw new Error(`Rules reference undefined channels: ${unresolved.join(", ")}. Define them under ` + `"channels:" in ${config.rulesConfigPath}, or remove the reference.`);
|
|
81969
82024
|
}
|
|
81970
82025
|
return new RoutingNotifier(channelRegistry, defaultNotifier);
|
|
81971
82026
|
}
|
|
81972
82027
|
/**
|
|
81973
82028
|
* Collect the channel names referenced by Route/Escalate actions in the rules
|
|
81974
|
-
* config that have no notifier registered
|
|
81975
|
-
*
|
|
82029
|
+
* config that have no notifier registered against `registry` — i.e. no
|
|
82030
|
+
* matching entry under the rules YAML's `channels:` section. `createNotifier`
|
|
82031
|
+
* treats a non-empty result as a fail-fast startup error.
|
|
81976
82032
|
*
|
|
81977
82033
|
* Returns an empty list when no rules config is set.
|
|
81978
82034
|
*/
|
|
@@ -81983,7 +82039,7 @@ function collectUnresolvedChannels(config, registry) {
|
|
|
81983
82039
|
const yamlContent = (0, node_fs.readFileSync)(config.rulesConfigPath, "utf-8");
|
|
81984
82040
|
const ruleConfig = parseRuleConfig(yamlContent);
|
|
81985
82041
|
const referenced = new Set();
|
|
81986
|
-
for (const section of
|
|
82042
|
+
for (const section of [ruleConfig["pre-llm"], ruleConfig["post-llm"]]) {
|
|
81987
82043
|
for (const rule of section.rules) {
|
|
81988
82044
|
for (const action of rule.actions) {
|
|
81989
82045
|
if ("channel" in action) {
|
|
@@ -117486,6 +117542,7 @@ async function getUseCase() {
|
|
|
117486
117542
|
const traces = new LokiTraceRepository(config.lokiUrl ?? "");
|
|
117487
117543
|
const llm = createLLMProvider(config.llmProvider, config.llmApiKey, config.llmModel);
|
|
117488
117544
|
const notifier = createNotifier(config);
|
|
117545
|
+
const ruleEngine = createRuleEngine(config);
|
|
117489
117546
|
useCase = new ProcessIncidentUseCase({
|
|
117490
117547
|
dedup,
|
|
117491
117548
|
traces,
|
|
@@ -117493,7 +117550,8 @@ async function getUseCase() {
|
|
|
117493
117550
|
notifier,
|
|
117494
117551
|
logger,
|
|
117495
117552
|
dedupTtlSeconds: config.dedupTtlSeconds,
|
|
117496
|
-
onClustersBuilt: (count) => alertClusters.set(count)
|
|
117553
|
+
onClustersBuilt: (count) => alertClusters.set(count),
|
|
117554
|
+
...ruleEngine ? { ruleEngine } : {}
|
|
117497
117555
|
});
|
|
117498
117556
|
return useCase;
|
|
117499
117557
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junando/worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
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.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/aws-lambda": "^8.10.163",
|