@neat.is/core 0.10.6-dev.20261008 → 0.10.6-dev.20261010
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/{chunk-EMFX3V77.js → chunk-52SCEPEW.js} +219 -9
- package/dist/chunk-52SCEPEW.js.map +1 -0
- package/dist/{chunk-6QMWJAWR.js → chunk-H2BAPN33.js} +108 -3
- package/dist/{chunk-6QMWJAWR.js.map → chunk-H2BAPN33.js.map} +1 -1
- package/dist/cli.cjs +358 -11
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +35 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +325 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +325 -9
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +107 -2
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-EMFX3V77.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeSpanToProject,
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-52SCEPEW.js";
|
|
5
5
|
import {
|
|
6
6
|
buildApi,
|
|
7
7
|
checkCompatibility,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
startStalenessLoop,
|
|
26
26
|
stitchTrace,
|
|
27
27
|
thresholdForEdgeType
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-H2BAPN33.js";
|
|
29
29
|
import {
|
|
30
30
|
ProjectNameCollisionError,
|
|
31
31
|
addProject,
|
package/dist/neatd.cjs
CHANGED
|
@@ -18008,10 +18008,11 @@ function locusOf(graph, ev) {
|
|
|
18008
18008
|
const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
|
|
18009
18009
|
const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
|
|
18010
18010
|
const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
|
|
18011
|
+
const symbol = node?.type === "SymbolNode" ? node.qualname : void 0;
|
|
18011
18012
|
return {
|
|
18012
18013
|
file,
|
|
18013
18014
|
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
18014
|
-
...
|
|
18015
|
+
...symbol ? { symbol } : {},
|
|
18015
18016
|
service: node?.service ?? ev.service,
|
|
18016
18017
|
provenance: import_types61.Provenance.OBSERVED
|
|
18017
18018
|
};
|
|
@@ -18107,7 +18108,8 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
18107
18108
|
const policyCards = applicable.map((p) => ({
|
|
18108
18109
|
policyName: p.policyName,
|
|
18109
18110
|
severity: p.severity,
|
|
18110
|
-
message: p.reason
|
|
18111
|
+
message: p.reason,
|
|
18112
|
+
onViolation: p.onViolation
|
|
18111
18113
|
}));
|
|
18112
18114
|
const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
|
|
18113
18115
|
type: d.type,
|
|
@@ -18134,8 +18136,111 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
|
18134
18136
|
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
18135
18137
|
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
18136
18138
|
};
|
|
18139
|
+
card.grade = gradeIncidentCard(card);
|
|
18137
18140
|
return import_types61.IncidentCardSchema.parse(card);
|
|
18138
18141
|
}
|
|
18142
|
+
var GRADE_PRIORS_VERSION = "sniper-architecture-priors-2026-10-08";
|
|
18143
|
+
var GRADE_WEIGHTS = {
|
|
18144
|
+
evidence: 3,
|
|
18145
|
+
locus: 2,
|
|
18146
|
+
tests: 2,
|
|
18147
|
+
reach: 1.5,
|
|
18148
|
+
kind: 1.5,
|
|
18149
|
+
chain: 1,
|
|
18150
|
+
recur: 1,
|
|
18151
|
+
div: 1
|
|
18152
|
+
};
|
|
18153
|
+
var PROVENANCE_WEIGHT = {
|
|
18154
|
+
OBSERVED: 1,
|
|
18155
|
+
EXTRACTED: 0.8,
|
|
18156
|
+
INFERRED: 0.4,
|
|
18157
|
+
FRONTIER: 0.3,
|
|
18158
|
+
STALE: 0.2
|
|
18159
|
+
};
|
|
18160
|
+
var REACH_NODES_MAX = 50;
|
|
18161
|
+
var TIMEOUT = /timeout|timed out|deadline/i;
|
|
18162
|
+
var round2 = (n) => Math.round(n * 1e4) / 1e4;
|
|
18163
|
+
function factor(name, value, evidence, reason) {
|
|
18164
|
+
return {
|
|
18165
|
+
value: value === null ? null : round2(value),
|
|
18166
|
+
weight: GRADE_WEIGHTS[name],
|
|
18167
|
+
evidence,
|
|
18168
|
+
...reason ? { reason } : {}
|
|
18169
|
+
};
|
|
18170
|
+
}
|
|
18171
|
+
function gradeIncidentCard(card) {
|
|
18172
|
+
const rc = card.rootCause;
|
|
18173
|
+
const chain = rc?.chain ?? [];
|
|
18174
|
+
const gates = {
|
|
18175
|
+
policyNotBlock: {
|
|
18176
|
+
passed: !(card.policies ?? []).some((p) => p.onViolation === "block"),
|
|
18177
|
+
evidence: ["policies[].onViolation"]
|
|
18178
|
+
},
|
|
18179
|
+
notSymptomOnly: {
|
|
18180
|
+
passed: rc?.classification !== "symptom-only",
|
|
18181
|
+
evidence: ["rootCause.classification"]
|
|
18182
|
+
},
|
|
18183
|
+
locusResolves: { passed: card.locus !== null, evidence: ["locus"] }
|
|
18184
|
+
};
|
|
18185
|
+
const gamma = Object.values(gates).every((g) => g.passed) ? 1 : 0;
|
|
18186
|
+
const carrying = chain.some((h) => h.carriesSignal === true) ? chain.filter((h) => h.carriesSignal === true) : chain;
|
|
18187
|
+
const hopWeights = carrying.map((h) => PROVENANCE_WEIGHT[h.provenance]);
|
|
18188
|
+
const evidence = hopWeights.length === 0 ? factor("evidence", null, ["rootCause.chain[].provenance"], "no root-cause chain on the card") : hopWeights.some((w) => w === void 0) ? factor("evidence", null, ["rootCause.chain[].provenance"], "a chain hop has a provenance with no weight") : factor("evidence", Math.min(...hopWeights), ["rootCause.chain[].provenance"]);
|
|
18189
|
+
const locus = card.locus ? factor("locus", card.locus.symbol ? 1 : 0.6, ["locus.symbol", "locus.file"]) : factor("locus", null, ["locus"], "no locus on the card (the locus gate fails)");
|
|
18190
|
+
const tests = factor(
|
|
18191
|
+
"tests",
|
|
18192
|
+
null,
|
|
18193
|
+
[],
|
|
18194
|
+
"the card carries no test-file classification or test-to-symbol edges"
|
|
18195
|
+
);
|
|
18196
|
+
const reach = card.blastRadius ? factor(
|
|
18197
|
+
"reach",
|
|
18198
|
+
Math.max(0, 1 - card.blastRadius.totalAffected / REACH_NODES_MAX),
|
|
18199
|
+
["blastRadius.totalAffected"],
|
|
18200
|
+
`nodes in the blast radius against a prior bound of ${REACH_NODES_MAX}`
|
|
18201
|
+
) : factor("reach", null, ["blastRadius"], "no blast radius on the card");
|
|
18202
|
+
const timeout = TIMEOUT.test(`${card.exceptionType ?? ""} ${card.message}`);
|
|
18203
|
+
const kindValue = timeout ? 0.3 : card.incidentKind === "exception" ? 1 : card.incidentKind === "5xx" || card.incidentKind === "status-error" ? 0.7 : card.incidentKind === "connector" ? 0.6 : 0.4;
|
|
18204
|
+
const kind = factor("kind", kindValue, ["incidentKind", "exceptionType", "message"]);
|
|
18205
|
+
const chainFactor = rc ? factor(
|
|
18206
|
+
"chain",
|
|
18207
|
+
1 / (1 + 0.25 * (Math.max(1, chain.length - 1) - 1)),
|
|
18208
|
+
["rootCause.chain"]
|
|
18209
|
+
) : factor("chain", null, ["rootCause"], "no root cause on the card");
|
|
18210
|
+
const occurrences = card.count ?? 1;
|
|
18211
|
+
const recurValue = Math.min(1, Math.log(1 + occurrences) / Math.log(11));
|
|
18212
|
+
const recur = factor(
|
|
18213
|
+
"recur",
|
|
18214
|
+
recurValue,
|
|
18215
|
+
["count"],
|
|
18216
|
+
card.count === void 0 ? "no coalesced count; one recorded occurrence" : void 0
|
|
18217
|
+
);
|
|
18218
|
+
const div = factor("div", (card.divergence ?? []).length > 0 ? 1 : 0.7, ["divergence"]);
|
|
18219
|
+
const factors = { evidence, locus, tests, reach, kind, chain: chainFactor, recur, div };
|
|
18220
|
+
let weighted = 0;
|
|
18221
|
+
let total = 0;
|
|
18222
|
+
for (const f of Object.values(factors)) {
|
|
18223
|
+
if (f.value === null) continue;
|
|
18224
|
+
weighted += f.weight * f.value;
|
|
18225
|
+
total += f.weight;
|
|
18226
|
+
}
|
|
18227
|
+
const C = round2(weighted / total);
|
|
18228
|
+
const G = round2(gamma * C);
|
|
18229
|
+
const band = gamma === 0 || G < 0.5 ? "out" : G >= 0.75 ? "full" : "diagnose-only";
|
|
18230
|
+
return {
|
|
18231
|
+
G,
|
|
18232
|
+
gamma,
|
|
18233
|
+
gates,
|
|
18234
|
+
C,
|
|
18235
|
+
factors,
|
|
18236
|
+
band,
|
|
18237
|
+
urgency: {
|
|
18238
|
+
value: null,
|
|
18239
|
+
reason: "f_fresh needs the time of the last OBSERVED signal at the node, which the card does not carry"
|
|
18240
|
+
},
|
|
18241
|
+
priorsVersion: GRADE_PRIORS_VERSION
|
|
18242
|
+
};
|
|
18243
|
+
}
|
|
18139
18244
|
|
|
18140
18245
|
// src/ask.ts
|
|
18141
18246
|
init_cjs_shims();
|
|
@@ -25272,12 +25377,22 @@ init_cjs_shims();
|
|
|
25272
25377
|
// src/connectors/kubernetes/client.ts
|
|
25273
25378
|
init_cjs_shims();
|
|
25274
25379
|
var import_node_https = require("https");
|
|
25380
|
+
var K8S_RATE_LIMIT = { capacity: 120, refillMs: 1e3 };
|
|
25275
25381
|
function deploymentsPath(namespace) {
|
|
25276
25382
|
return `/apis/apps/v1/namespaces/${namespace}/deployments`;
|
|
25277
25383
|
}
|
|
25278
25384
|
function podsPath(namespace) {
|
|
25279
25385
|
return `/api/v1/namespaces/${namespace}/pods`;
|
|
25280
25386
|
}
|
|
25387
|
+
function podLogPath(namespace, podName, opts = {}) {
|
|
25388
|
+
const params = new URLSearchParams();
|
|
25389
|
+
if (opts.container) params.set("container", opts.container);
|
|
25390
|
+
if (typeof opts.tailLines === "number" && Number.isFinite(opts.tailLines)) params.set("tailLines", String(Math.trunc(opts.tailLines)));
|
|
25391
|
+
if (typeof opts.limitBytes === "number" && Number.isFinite(opts.limitBytes)) params.set("limitBytes", String(Math.trunc(opts.limitBytes)));
|
|
25392
|
+
if (opts.previous) params.set("previous", "true");
|
|
25393
|
+
const qs = params.toString();
|
|
25394
|
+
return `${podsPath(namespace)}/${podName}/log${qs ? `?${qs}` : ""}`;
|
|
25395
|
+
}
|
|
25281
25396
|
function makeK8sFetchImpl(transport) {
|
|
25282
25397
|
const agent = new import_node_https.Agent({
|
|
25283
25398
|
...transport.ca ? { ca: transport.ca } : {},
|
|
@@ -25328,7 +25443,7 @@ async function listResource(transport, namespace, path84, opts = {}) {
|
|
|
25328
25443
|
{ method: "GET", headers: { ...transport.token ? bearerAuthHeader(transport.token) : {}, Accept: "application/json" } },
|
|
25329
25444
|
// accountKey: the (cluster, namespace) pair — an identifier, safe to log, the
|
|
25330
25445
|
// rate-limit bucket for one namespace on one cluster (ADR-131).
|
|
25331
|
-
{ provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
|
|
25446
|
+
{ provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, rateLimit: K8S_RATE_LIMIT, fetchImpl }
|
|
25332
25447
|
);
|
|
25333
25448
|
if (!res.ok) {
|
|
25334
25449
|
throw new Error(`kubernetes ${path84} failed: ${res.status} ${res.statusText}`);
|
|
@@ -25349,6 +25464,28 @@ async function fetchDeployments(transport, namespace, opts = {}) {
|
|
|
25349
25464
|
async function fetchPods(transport, namespace, opts = {}) {
|
|
25350
25465
|
return listResource(transport, namespace, podsPath(namespace), opts);
|
|
25351
25466
|
}
|
|
25467
|
+
async function fetchPodLog(transport, namespace, podName, opts = {}) {
|
|
25468
|
+
const base = opts.apiUrl ?? transport.server;
|
|
25469
|
+
const path84 = podLogPath(namespace, podName, {
|
|
25470
|
+
...opts.container ? { container: opts.container } : {},
|
|
25471
|
+
...opts.tailLines !== void 0 ? { tailLines: opts.tailLines } : {},
|
|
25472
|
+
...opts.limitBytes !== void 0 ? { limitBytes: opts.limitBytes } : {},
|
|
25473
|
+
...opts.previous ? { previous: true } : {}
|
|
25474
|
+
});
|
|
25475
|
+
const url = `${base.replace(/\/$/, "")}${path84}`;
|
|
25476
|
+
const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
|
|
25477
|
+
try {
|
|
25478
|
+
const res = await junctionFetch(
|
|
25479
|
+
url,
|
|
25480
|
+
{ method: "GET", headers: { ...transport.token ? bearerAuthHeader(transport.token) : {}, Accept: "text/plain" } },
|
|
25481
|
+
{ provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, rateLimit: K8S_RATE_LIMIT, fetchImpl }
|
|
25482
|
+
);
|
|
25483
|
+
if (!res.ok) return void 0;
|
|
25484
|
+
return await res.text();
|
|
25485
|
+
} catch {
|
|
25486
|
+
return void 0;
|
|
25487
|
+
}
|
|
25488
|
+
}
|
|
25352
25489
|
|
|
25353
25490
|
// src/connectors/kubernetes/kubeconfig.ts
|
|
25354
25491
|
init_cjs_shims();
|
|
@@ -25456,6 +25593,123 @@ function parseK8sTargetName(targetName) {
|
|
|
25456
25593
|
return { serviceName, fault };
|
|
25457
25594
|
}
|
|
25458
25595
|
|
|
25596
|
+
// src/connectors/kubernetes/process-context.ts
|
|
25597
|
+
init_cjs_shims();
|
|
25598
|
+
var DEFAULT_MAX_LOG_LINES = 50;
|
|
25599
|
+
var DEFAULT_MAX_LOG_BYTES2 = 2 * 1024;
|
|
25600
|
+
var DEFAULT_MAX_ENTRIES = 64;
|
|
25601
|
+
var DEFAULT_MAX_VALUE_CHARS = 512;
|
|
25602
|
+
var REDACTED = "***REDACTED***";
|
|
25603
|
+
var SECRET_KEY_RE = /TOKEN|SECRET|KEY|PASSW(?:OR)?D|PWD|(?:^|[^A-Z])PASS(?:$|[^A-Z])|CREDENTIAL|(?:^|[^A-Z])AUTH|PRIVATE|DSN|COOKIE|SESSION|SALT|SIGNATURE|CERT|CONN(?:ECTION)?_?STR/i;
|
|
25604
|
+
function isSecretKey(name) {
|
|
25605
|
+
return SECRET_KEY_RE.test(name);
|
|
25606
|
+
}
|
|
25607
|
+
var PRIVATE_KEY_BLOCK_RE = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g;
|
|
25608
|
+
var PRIVATE_KEY_TAIL_RE = /(?:^[A-Za-z0-9+/=]{32,}\r?\n)+-----END [A-Z ]*PRIVATE KEY-----/gm;
|
|
25609
|
+
var URL_USERINFO_RE = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^\s/@]*)@/g;
|
|
25610
|
+
var SECRET_PAIR_RE = /((?:^|[\s?&;,{("'])[A-Za-z0-9_.-]*?(?:passw(?:or)?d|pwd|(?<![a-z])pass(?![a-z])|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|client[_-]?key|credentials?|(?<![a-z])auth(?![a-z])|(?<![a-z])dsn(?![a-z]))[A-Za-z0-9_.-]*["']?\s*[=:]\s*["']?)([^\s&;,"'})]+)/gi;
|
|
25611
|
+
var AUTH_SCHEME_RE = /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{8,}/g;
|
|
25612
|
+
var KNOWN_TOKEN_RE = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}|\bAKIA[0-9A-Z]{16}\b|\bgh[pousr]_[A-Za-z0-9]{20,}|\bgithub_pat_[A-Za-z0-9_]{20,}|\bsk_(?:live|test)_[A-Za-z0-9]{10,}|\bxox[abprs]-[A-Za-z0-9-]{10,}|\bglpat-[A-Za-z0-9_-]{16,}|\bAIza[0-9A-Za-z_-]{30,}|\bneat_pat_[A-Za-z0-9_-]{16,}/g;
|
|
25613
|
+
function redactText(text) {
|
|
25614
|
+
return text.replace(PRIVATE_KEY_BLOCK_RE, REDACTED).replace(PRIVATE_KEY_TAIL_RE, REDACTED).replace(URL_USERINFO_RE, (_m, scheme, userinfo) => {
|
|
25615
|
+
const colon = userinfo.indexOf(":");
|
|
25616
|
+
return colon > 0 ? `${scheme}${userinfo.slice(0, colon)}:${REDACTED}@` : `${scheme}${REDACTED}@`;
|
|
25617
|
+
}).replace(SECRET_PAIR_RE, (_m, key) => `${key}${REDACTED}`).replace(AUTH_SCHEME_RE, (_m, scheme) => `${scheme} ${REDACTED}`).replace(KNOWN_TOKEN_RE, REDACTED);
|
|
25618
|
+
}
|
|
25619
|
+
function redactValue(name, value) {
|
|
25620
|
+
if (isSecretKey(name)) return REDACTED;
|
|
25621
|
+
return redactText(value);
|
|
25622
|
+
}
|
|
25623
|
+
function redactArg(arg) {
|
|
25624
|
+
const eq = /^(--?)?([A-Za-z0-9_.-]+)=([\s\S]*)$/.exec(arg);
|
|
25625
|
+
if (eq && isSecretKey(eq[2])) {
|
|
25626
|
+
return `${eq[1] ?? ""}${eq[2]}=${REDACTED}`;
|
|
25627
|
+
}
|
|
25628
|
+
return redactText(arg);
|
|
25629
|
+
}
|
|
25630
|
+
function isSecretFlag(arg) {
|
|
25631
|
+
const m = /^--?([A-Za-z0-9_.-]+)$/.exec(arg);
|
|
25632
|
+
return m !== null && isSecretKey(m[1]);
|
|
25633
|
+
}
|
|
25634
|
+
function capValue(s, max = DEFAULT_MAX_VALUE_CHARS) {
|
|
25635
|
+
return s.length > max ? `${s.slice(0, max)}\u2026` : s;
|
|
25636
|
+
}
|
|
25637
|
+
function describeEnvVar(ev) {
|
|
25638
|
+
const name = typeof ev.name === "string" ? ev.name : "";
|
|
25639
|
+
if (name.length === 0) return void 0;
|
|
25640
|
+
if (typeof ev.value === "string") {
|
|
25641
|
+
return `${name}=${capValue(redactValue(name, ev.value))}`;
|
|
25642
|
+
}
|
|
25643
|
+
const from = ev.valueFrom;
|
|
25644
|
+
if (from) {
|
|
25645
|
+
if (from.secretKeyRef) {
|
|
25646
|
+
const ref = typeof from.secretKeyRef.name === "string" && from.secretKeyRef.name.length > 0 ? from.secretKeyRef.name : "<unnamed>";
|
|
25647
|
+
return `${name}=<from secret ${ref}>`;
|
|
25648
|
+
}
|
|
25649
|
+
if (from.configMapKeyRef) {
|
|
25650
|
+
const ref = typeof from.configMapKeyRef.name === "string" && from.configMapKeyRef.name.length > 0 ? from.configMapKeyRef.name : "<unnamed>";
|
|
25651
|
+
const key = typeof from.configMapKeyRef.key === "string" && from.configMapKeyRef.key.length > 0 ? ` key ${from.configMapKeyRef.key}` : "";
|
|
25652
|
+
return `${name}=<from configMap ${ref}${key}>`;
|
|
25653
|
+
}
|
|
25654
|
+
if (from.fieldRef && typeof from.fieldRef.fieldPath === "string" && from.fieldRef.fieldPath.length > 0) {
|
|
25655
|
+
return `${name}=<from field ${from.fieldRef.fieldPath}>`;
|
|
25656
|
+
}
|
|
25657
|
+
if (from.resourceFieldRef && typeof from.resourceFieldRef.resource === "string" && from.resourceFieldRef.resource.length > 0) {
|
|
25658
|
+
return `${name}=<from resource ${from.resourceFieldRef.resource}>`;
|
|
25659
|
+
}
|
|
25660
|
+
return `${name}=<from reference>`;
|
|
25661
|
+
}
|
|
25662
|
+
return `${name}=`;
|
|
25663
|
+
}
|
|
25664
|
+
function buildContainerEnv(container) {
|
|
25665
|
+
const out = [];
|
|
25666
|
+
for (const ev of container.env ?? []) {
|
|
25667
|
+
const d = describeEnvVar(ev);
|
|
25668
|
+
if (d !== void 0) out.push(d);
|
|
25669
|
+
if (out.length >= DEFAULT_MAX_ENTRIES) break;
|
|
25670
|
+
}
|
|
25671
|
+
return out;
|
|
25672
|
+
}
|
|
25673
|
+
function buildContainerArgs(container) {
|
|
25674
|
+
const raw = [];
|
|
25675
|
+
for (const c of container.command ?? []) if (typeof c === "string") raw.push(c);
|
|
25676
|
+
for (const a of container.args ?? []) if (typeof a === "string") raw.push(a);
|
|
25677
|
+
const out = [];
|
|
25678
|
+
for (let i = 0; i < raw.length && out.length < DEFAULT_MAX_ENTRIES; i++) {
|
|
25679
|
+
const arg = raw[i];
|
|
25680
|
+
out.push(capValue(redactArg(arg)));
|
|
25681
|
+
const next = raw[i + 1];
|
|
25682
|
+
if (isSecretFlag(arg) && next !== void 0 && !next.startsWith("-") && out.length < DEFAULT_MAX_ENTRIES) {
|
|
25683
|
+
out.push(REDACTED);
|
|
25684
|
+
i++;
|
|
25685
|
+
}
|
|
25686
|
+
}
|
|
25687
|
+
return out;
|
|
25688
|
+
}
|
|
25689
|
+
function capProcessLog(text, maxLines = DEFAULT_MAX_LOG_LINES, maxBytes = DEFAULT_MAX_LOG_BYTES2) {
|
|
25690
|
+
const trimmed = text.replace(/\s+$/, "");
|
|
25691
|
+
if (trimmed.length === 0) return "";
|
|
25692
|
+
const lines = trimmed.split("\n");
|
|
25693
|
+
const tail = lines.length > maxLines ? lines.slice(lines.length - maxLines) : lines;
|
|
25694
|
+
let out = tail.join("\n");
|
|
25695
|
+
if (out.length > maxBytes) out = out.slice(out.length - maxBytes);
|
|
25696
|
+
return out;
|
|
25697
|
+
}
|
|
25698
|
+
function buildProcessContext(input) {
|
|
25699
|
+
const out = {};
|
|
25700
|
+
if (typeof input.log === "string") {
|
|
25701
|
+
const capped = capProcessLog(redactText(input.log));
|
|
25702
|
+
if (capped.length > 0) out.processLog = capped;
|
|
25703
|
+
}
|
|
25704
|
+
if (input.container) {
|
|
25705
|
+
const args = buildContainerArgs(input.container);
|
|
25706
|
+
if (args.length > 0) out.containerArgs = args;
|
|
25707
|
+
const env = buildContainerEnv(input.container);
|
|
25708
|
+
if (env.length > 0) out.containerEnv = env;
|
|
25709
|
+
}
|
|
25710
|
+
return out;
|
|
25711
|
+
}
|
|
25712
|
+
|
|
25459
25713
|
// src/connectors/kubernetes/map.ts
|
|
25460
25714
|
function serviceNameFor(deployment, config) {
|
|
25461
25715
|
const name = deployment.metadata?.name ?? "";
|
|
@@ -25473,6 +25727,9 @@ function podsForDeployment(deployment, pods) {
|
|
|
25473
25727
|
function nowIso2() {
|
|
25474
25728
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
25475
25729
|
}
|
|
25730
|
+
function podLogKey(podName, container) {
|
|
25731
|
+
return container ? `${podName}\0${container}` : podName;
|
|
25732
|
+
}
|
|
25476
25733
|
function podLevelFault(deployment, pods) {
|
|
25477
25734
|
const name = deployment.metadata?.name ?? "";
|
|
25478
25735
|
const owned = podsForDeployment(deployment, pods);
|
|
@@ -25490,7 +25747,9 @@ function podLevelFault(deployment, pods) {
|
|
|
25490
25747
|
fault: "image-pull",
|
|
25491
25748
|
message: `Deployment ${name} cannot pull image ${image} (${reason})`,
|
|
25492
25749
|
timestamp: pod.status?.startTime ?? nowIso2(),
|
|
25493
|
-
attributes: attrs
|
|
25750
|
+
attributes: attrs,
|
|
25751
|
+
pod,
|
|
25752
|
+
...typeof cs.name === "string" && cs.name.length > 0 ? { container: cs.name } : {}
|
|
25494
25753
|
};
|
|
25495
25754
|
}
|
|
25496
25755
|
if (reason === CRASH_LOOP_REASON && !crash) crash = { cs, pod };
|
|
@@ -25512,11 +25771,47 @@ function podLevelFault(deployment, pods) {
|
|
|
25512
25771
|
fault: "crash-loop",
|
|
25513
25772
|
message: `Deployment ${name} is crashlooping (restarts: ${restarts}); ${detail}`,
|
|
25514
25773
|
timestamp: term?.finishedAt ?? pod.status?.startTime ?? nowIso2(),
|
|
25515
|
-
attributes: attrs
|
|
25774
|
+
attributes: attrs,
|
|
25775
|
+
pod,
|
|
25776
|
+
...typeof cs.name === "string" && cs.name.length > 0 ? { container: cs.name } : {}
|
|
25516
25777
|
};
|
|
25517
25778
|
}
|
|
25518
25779
|
return null;
|
|
25519
25780
|
}
|
|
25781
|
+
function faultedContainer(finding) {
|
|
25782
|
+
const containers = finding.pod?.spec?.containers;
|
|
25783
|
+
if (!Array.isArray(containers) || containers.length === 0) return void 0;
|
|
25784
|
+
if (finding.container) {
|
|
25785
|
+
const named2 = containers.find((c) => c.name === finding.container);
|
|
25786
|
+
if (named2) return named2;
|
|
25787
|
+
}
|
|
25788
|
+
return containers.length === 1 ? containers[0] : void 0;
|
|
25789
|
+
}
|
|
25790
|
+
function mergeProcessContext(finding, logs) {
|
|
25791
|
+
const container = faultedContainer(finding);
|
|
25792
|
+
const podName = finding.pod?.metadata?.name;
|
|
25793
|
+
const log = logs && podName ? logs.get(podLogKey(podName, finding.container)) : void 0;
|
|
25794
|
+
if (!container && log === void 0) return;
|
|
25795
|
+
const ctx = buildProcessContext({
|
|
25796
|
+
...log !== void 0 ? { log } : {},
|
|
25797
|
+
...container ? { container } : {}
|
|
25798
|
+
});
|
|
25799
|
+
if (ctx.processLog !== void 0) finding.attributes["k8s.processLog"] = ctx.processLog;
|
|
25800
|
+
if (ctx.containerArgs !== void 0) finding.attributes["k8s.containerArgs"] = ctx.containerArgs;
|
|
25801
|
+
if (ctx.containerEnv !== void 0) finding.attributes["k8s.containerEnv"] = ctx.containerEnv;
|
|
25802
|
+
}
|
|
25803
|
+
function faultedPods(deployments, pods, config) {
|
|
25804
|
+
const out = [];
|
|
25805
|
+
const expectedZero = config.expectedZero ? new Set(config.expectedZero) : void 0;
|
|
25806
|
+
for (const deployment of deployments) {
|
|
25807
|
+
const finding = classifyDeployment(deployment, pods, expectedZero);
|
|
25808
|
+
if (!finding || finding.fault !== "crash-loop") continue;
|
|
25809
|
+
const podName = finding.pod?.metadata?.name;
|
|
25810
|
+
if (typeof podName !== "string" || podName.length === 0) continue;
|
|
25811
|
+
out.push({ podName, ...finding.container ? { container: finding.container } : {} });
|
|
25812
|
+
}
|
|
25813
|
+
return out;
|
|
25814
|
+
}
|
|
25520
25815
|
function classifyDeployment(deployment, pods, expectedZero) {
|
|
25521
25816
|
const name = deployment.metadata?.name ?? "";
|
|
25522
25817
|
const desired = typeof deployment.spec?.replicas === "number" ? deployment.spec.replicas : 1;
|
|
@@ -25544,12 +25839,13 @@ function classifyDeployment(deployment, pods, expectedZero) {
|
|
|
25544
25839
|
attributes: { "k8s.desiredReplicas": desired, "k8s.readyReplicas": ready }
|
|
25545
25840
|
};
|
|
25546
25841
|
}
|
|
25547
|
-
function mapDeploymentToSignal(deployment, pods, config) {
|
|
25842
|
+
function mapDeploymentToSignal(deployment, pods, config, logs) {
|
|
25548
25843
|
const name = deployment.metadata?.name;
|
|
25549
25844
|
if (typeof name !== "string" || name.length === 0) return null;
|
|
25550
25845
|
const expectedZero = config.expectedZero ? new Set(config.expectedZero) : void 0;
|
|
25551
25846
|
const finding = classifyDeployment(deployment, pods, expectedZero);
|
|
25552
25847
|
if (!finding) return null;
|
|
25848
|
+
mergeProcessContext(finding, logs);
|
|
25553
25849
|
const serviceName = serviceNameFor(deployment, config);
|
|
25554
25850
|
const namespace = deployment.metadata?.namespace ?? config.namespace;
|
|
25555
25851
|
const attributes = {
|
|
@@ -25602,12 +25898,12 @@ function deployStateSignal(deployment, pods, config) {
|
|
|
25602
25898
|
deployState: observedDeployState(deployment, pods)
|
|
25603
25899
|
};
|
|
25604
25900
|
}
|
|
25605
|
-
function mapWorkloadsToSignals(deployments, pods, config) {
|
|
25901
|
+
function mapWorkloadsToSignals(deployments, pods, config, logs) {
|
|
25606
25902
|
const out = [];
|
|
25607
25903
|
for (const deployment of deployments) {
|
|
25608
25904
|
const deployState = deployStateSignal(deployment, pods, config);
|
|
25609
25905
|
if (deployState) out.push(deployState);
|
|
25610
|
-
const incident = mapDeploymentToSignal(deployment, pods, config);
|
|
25906
|
+
const incident = mapDeploymentToSignal(deployment, pods, config, logs);
|
|
25611
25907
|
if (incident) out.push(incident);
|
|
25612
25908
|
}
|
|
25613
25909
|
return out;
|
|
@@ -25706,6 +26002,8 @@ async function startK8sSubstratePolling(input) {
|
|
|
25706
26002
|
}
|
|
25707
26003
|
|
|
25708
26004
|
// src/connectors/kubernetes/index.ts
|
|
26005
|
+
var PROCESS_LOG_TAIL_LINES = 200;
|
|
26006
|
+
var PROCESS_LOG_LIMIT_BYTES = 64 * 1024;
|
|
25709
26007
|
var KubernetesConnector = class {
|
|
25710
26008
|
constructor(config, fetchImpl) {
|
|
25711
26009
|
this.config = config;
|
|
@@ -25726,7 +26024,25 @@ var KubernetesConnector = class {
|
|
|
25726
26024
|
fetchDeployments(transport, namespace, opts),
|
|
25727
26025
|
fetchPods(transport, namespace, opts)
|
|
25728
26026
|
]);
|
|
25729
|
-
|
|
26027
|
+
const logs = await this.fetchFaultedPodLogs(transport, namespace, deployments, pods, opts);
|
|
26028
|
+
return mapWorkloadsToSignals(deployments, pods, this.config, logs);
|
|
26029
|
+
}
|
|
26030
|
+
async fetchFaultedPodLogs(transport, namespace, deployments, pods, opts) {
|
|
26031
|
+
const logs = /* @__PURE__ */ new Map();
|
|
26032
|
+
const targets = faultedPods(deployments, pods, this.config);
|
|
26033
|
+
await Promise.all(
|
|
26034
|
+
targets.map(async (t) => {
|
|
26035
|
+
const text = await fetchPodLog(transport, namespace, t.podName, {
|
|
26036
|
+
...t.container ? { container: t.container } : {},
|
|
26037
|
+
tailLines: PROCESS_LOG_TAIL_LINES,
|
|
26038
|
+
limitBytes: PROCESS_LOG_LIMIT_BYTES,
|
|
26039
|
+
previous: true,
|
|
26040
|
+
...opts
|
|
26041
|
+
});
|
|
26042
|
+
if (typeof text === "string" && text.length > 0) logs.set(podLogKey(t.podName, t.container), text);
|
|
26043
|
+
})
|
|
26044
|
+
);
|
|
26045
|
+
return logs;
|
|
25730
26046
|
}
|
|
25731
26047
|
};
|
|
25732
26048
|
function createKubernetesConnector(graph, config, fetchImpl) {
|