@neat.is/core 0.10.6-dev.20261010 → 0.10.7-dev.20261011

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.
@@ -0,0 +1,42 @@
1
+ // src/incidents-render.ts
2
+ function deployHintLines(ev) {
3
+ const a = ev.attributes;
4
+ if (!a) return [];
5
+ const s = (k) => typeof a[k] === "string" ? a[k] : void 0;
6
+ const n = (k) => typeof a[k] === "number" ? a[k] : void 0;
7
+ const arr = (k) => Array.isArray(a[k]) ? a[k].filter((x) => typeof x === "string") : [];
8
+ const fault = s("k8s.fault");
9
+ const env = arr("k8s.containerEnv");
10
+ const args = arr("k8s.containerArgs");
11
+ const log = s("k8s.processLog");
12
+ if (!fault && env.length === 0 && args.length === 0 && !log) return [];
13
+ const out = [];
14
+ if (fault) {
15
+ const bits = [fault];
16
+ const wr = s("k8s.waitingReason");
17
+ if (wr) bits.push(wr);
18
+ const rc = n("k8s.restartCount");
19
+ if (rc !== void 0) bits.push(`restarts=${rc}`);
20
+ const ec = n("k8s.exitCode");
21
+ if (ec !== void 0) bits.push(`exit=${ec}`);
22
+ const tr = s("k8s.terminatedReason");
23
+ if (tr) bits.push(tr);
24
+ out.push(`deploy: ${bits.join(" ")}`);
25
+ }
26
+ const cap = (xs, max) => {
27
+ const joined = xs.join(" ");
28
+ return joined.length > max ? `${joined.slice(0, max)}\u2026(+${xs.length} entries)` : joined;
29
+ };
30
+ if (env.length > 0) out.push(`env: ${cap(env, 700)}`);
31
+ if (args.length > 0) out.push(`args: ${cap(args, 300)}`);
32
+ if (log) {
33
+ const tail = log.split("\n").filter((l) => l.trim().length > 0).slice(-4);
34
+ if (tail.length > 0) out.push(`log: ${tail.join(" \u23CE ")}`);
35
+ }
36
+ return out;
37
+ }
38
+
39
+ export {
40
+ deployHintLines
41
+ };
42
+ //# sourceMappingURL=chunk-AEMLUMHC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/incidents-render.ts"],"sourcesContent":["import type { ErrorEvent } from '@neat.is/types'\n\n// The OBSERVED deploy/process/config context an incident may carry (ADR-237/240):\n// the deploy fault, the faulted container's redacted env/args, and its boot-time\n// stdout tail. Rendered compactly so the incident surfaces the wrong host / bad\n// flag / crash traceback inline instead of leaving it to the card.\n//\n// One renderer, two readers: the CLI `neat incidents` output (cli-client.ts) and\n// the MCP get_incident_history tool (packages/mcp) both call this, so the two\n// never drift (core #1373 — atomic, no feature mirrors another's fields).\n// Returns [] for a non-k8s incident, so those lines read exactly as before.\nexport function deployHintLines(ev: ErrorEvent): string[] {\n const a = ev.attributes\n if (!a) return []\n const s = (k: string): string | undefined => (typeof a[k] === 'string' ? (a[k] as string) : undefined)\n const n = (k: string): number | undefined => (typeof a[k] === 'number' ? (a[k] as number) : undefined)\n const arr = (k: string): string[] => (Array.isArray(a[k]) ? (a[k] as unknown[]).filter((x) => typeof x === 'string') as string[] : [])\n const fault = s('k8s.fault')\n const env = arr('k8s.containerEnv')\n const args = arr('k8s.containerArgs')\n const log = s('k8s.processLog')\n if (!fault && env.length === 0 && args.length === 0 && !log) return []\n const out: string[] = []\n if (fault) {\n const bits = [fault]\n const wr = s('k8s.waitingReason'); if (wr) bits.push(wr)\n const rc = n('k8s.restartCount'); if (rc !== undefined) bits.push(`restarts=${rc}`)\n const ec = n('k8s.exitCode'); if (ec !== undefined) bits.push(`exit=${ec}`)\n const tr = s('k8s.terminatedReason'); if (tr) bits.push(tr)\n out.push(`deploy: ${bits.join(' ')}`)\n }\n const cap = (xs: string[], max: number): string => {\n const joined = xs.join(' ')\n return joined.length > max ? `${joined.slice(0, max)}…(+${xs.length} entries)` : joined\n }\n if (env.length > 0) out.push(`env: ${cap(env, 700)}`)\n if (args.length > 0) out.push(`args: ${cap(args, 300)}`)\n if (log) {\n const tail = log.split('\\n').filter((l) => l.trim().length > 0).slice(-4)\n if (tail.length > 0) out.push(`log: ${tail.join(' ⏎ ')}`)\n }\n return out\n}\n"],"mappings":";AAWO,SAAS,gBAAgB,IAA0B;AACxD,QAAM,IAAI,GAAG;AACb,MAAI,CAAC,EAAG,QAAO,CAAC;AAChB,QAAM,IAAI,CAAC,MAAmC,OAAO,EAAE,CAAC,MAAM,WAAY,EAAE,CAAC,IAAe;AAC5F,QAAM,IAAI,CAAC,MAAmC,OAAO,EAAE,CAAC,MAAM,WAAY,EAAE,CAAC,IAAe;AAC5F,QAAM,MAAM,CAAC,MAAyB,MAAM,QAAQ,EAAE,CAAC,CAAC,IAAK,EAAE,CAAC,EAAgB,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,IAAgB,CAAC;AACpI,QAAM,QAAQ,EAAE,WAAW;AAC3B,QAAM,MAAM,IAAI,kBAAkB;AAClC,QAAM,OAAO,IAAI,mBAAmB;AACpC,QAAM,MAAM,EAAE,gBAAgB;AAC9B,MAAI,CAAC,SAAS,IAAI,WAAW,KAAK,KAAK,WAAW,KAAK,CAAC,IAAK,QAAO,CAAC;AACrE,QAAM,MAAgB,CAAC;AACvB,MAAI,OAAO;AACT,UAAM,OAAO,CAAC,KAAK;AACnB,UAAM,KAAK,EAAE,mBAAmB;AAAG,QAAI,GAAI,MAAK,KAAK,EAAE;AACvD,UAAM,KAAK,EAAE,kBAAkB;AAAG,QAAI,OAAO,OAAW,MAAK,KAAK,YAAY,EAAE,EAAE;AAClF,UAAM,KAAK,EAAE,cAAc;AAAG,QAAI,OAAO,OAAW,MAAK,KAAK,QAAQ,EAAE,EAAE;AAC1E,UAAM,KAAK,EAAE,sBAAsB;AAAG,QAAI,GAAI,MAAK,KAAK,EAAE;AAC1D,QAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,EAAE;AAAA,EACtC;AACA,QAAM,MAAM,CAAC,IAAc,QAAwB;AACjD,UAAM,SAAS,GAAG,KAAK,IAAI;AAC3B,WAAO,OAAO,SAAS,MAAM,GAAG,OAAO,MAAM,GAAG,GAAG,CAAC,WAAM,GAAG,MAAM,cAAc;AAAA,EACnF;AACA,MAAI,IAAI,SAAS,EAAG,KAAI,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC,EAAE;AACpD,MAAI,KAAK,SAAS,EAAG,KAAI,KAAK,SAAS,IAAI,MAAM,GAAG,CAAC,EAAE;AACvD,MAAI,KAAK;AACP,UAAM,OAAO,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE;AACxE,QAAI,KAAK,SAAS,EAAG,KAAI,KAAK,QAAQ,KAAK,KAAK,UAAK,CAAC,EAAE;AAAA,EAC1D;AACA,SAAO;AACT;","names":[]}
@@ -28,7 +28,7 @@ import {
28
28
  startConnectorPolling,
29
29
  startPersistLoop,
30
30
  startStalenessLoop
31
- } from "./chunk-H2BAPN33.js";
31
+ } from "./chunk-6K22WGO7.js";
32
32
  import {
33
33
  listProjects,
34
34
  pruneRegistry,
@@ -140,7 +140,7 @@ function credentialRecord(provider, cred) {
140
140
  return { token: cred.accessToken };
141
141
  }
142
142
  }
143
- function hostedOptions(provider, summary, serviceName, firebaseServiceMap) {
143
+ function hostedOptions(provider, summary, serviceName, firebaseServiceMap, serviceMaps = {}) {
144
144
  switch (provider) {
145
145
  case "supabase": {
146
146
  const ref = summary.projectRef;
@@ -159,9 +159,12 @@ function hostedOptions(provider, summary, serviceName, firebaseServiceMap) {
159
159
  case "firebase": {
160
160
  return { inferServices: true, excludeCloudRun: true, ...firebaseServiceMap ?? {} };
161
161
  }
162
+ // Same no-setup rule as Firebase: infer the owning service from the graph; an unknown one stays coarse.
163
+ // NEAT_CLOUD_RUN_SERVICE_MAP / NEAT_GCP_LB_BACKEND_SERVICE_MAP are optional overrides whose entries win.
162
164
  case "cloud-run":
165
+ return { inferServices: true, ...serviceMaps.cloudRun ? { serviceMap: serviceMaps.cloudRun } : {} };
163
166
  case "gcp-lb":
164
- return { inferServices: true };
167
+ return { inferServices: true, ...serviceMaps.gcpLb ? { backendServiceMap: serviceMaps.gcpLb } : {} };
165
168
  default:
166
169
  return null;
167
170
  }
@@ -186,6 +189,19 @@ function parseFirebaseServiceMap(raw) {
186
189
  return void 0;
187
190
  }
188
191
  }
192
+ function parseServiceNameMap(raw) {
193
+ if (!raw) return void 0;
194
+ try {
195
+ const parsed = JSON.parse(raw);
196
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
197
+ const entries = Object.entries(parsed);
198
+ if (entries.length === 0) return void 0;
199
+ if (entries.some(([, v]) => typeof v !== "string" || v.length === 0)) return void 0;
200
+ return Object.fromEntries(entries);
201
+ } catch {
202
+ return void 0;
203
+ }
204
+ }
189
205
  function pushHandlesPath(projectDir) {
190
206
  return join(projectDir, "neat-out", "connectors-hosted.json");
191
207
  }
@@ -307,7 +323,10 @@ function startConnectionLoops(c, input, startLoop) {
307
323
  onSkip?.(name, "no project selected yet \u2014 not pullable");
308
324
  continue;
309
325
  }
310
- const options = hostedOptions(name, c, project, input.firebaseServiceMap);
326
+ const options = hostedOptions(name, c, project, input.firebaseServiceMap, {
327
+ ...input.cloudRunServiceMap ? { cloudRun: input.cloudRunServiceMap } : {},
328
+ ...input.gcpLbBackendServiceMap ? { gcpLb: input.gcpLbBackendServiceMap } : {}
329
+ });
311
330
  if (!options) {
312
331
  onSkip?.(name, "no hosted option mapping for this provider");
313
332
  continue;
@@ -417,6 +436,8 @@ async function maybeStartHostedConnectors(input) {
417
436
  projectDir: input.projectDir,
418
437
  project: input.project,
419
438
  ...parseFirebaseServiceMap(env.NEAT_FIREBASE_SERVICE_MAP) ? { firebaseServiceMap: parseFirebaseServiceMap(env.NEAT_FIREBASE_SERVICE_MAP) } : {},
439
+ ...parseServiceNameMap(env.NEAT_CLOUD_RUN_SERVICE_MAP) ? { cloudRunServiceMap: parseServiceNameMap(env.NEAT_CLOUD_RUN_SERVICE_MAP) } : {},
440
+ ...parseServiceNameMap(env.NEAT_GCP_LB_BACKEND_SERVICE_MAP) ? { gcpLbBackendServiceMap: parseServiceNameMap(env.NEAT_GCP_LB_BACKEND_SERVICE_MAP) } : {},
420
441
  ...input.errorsPath ? { errorsPath: input.errorsPath } : {},
421
442
  ...input.onSkip ? { onSkip: input.onSkip } : {},
422
443
  ...input.intervalMs !== void 0 ? { intervalMs: input.intervalMs } : {}
@@ -534,6 +555,15 @@ async function fetchPodLog(transport, namespace, podName, opts = {}) {
534
555
  return void 0;
535
556
  }
536
557
  }
558
+ function hasLogContent(text) {
559
+ return typeof text === "string" && text.trim().length > 0;
560
+ }
561
+ async function fetchPodProcessLog(transport, namespace, podName, opts = {}) {
562
+ const previous = await fetchPodLog(transport, namespace, podName, { ...opts, previous: true });
563
+ if (hasLogContent(previous)) return previous;
564
+ const current = await fetchPodLog(transport, namespace, podName, { ...opts, previous: false });
565
+ return hasLogContent(current) ? current : void 0;
566
+ }
537
567
 
538
568
  // src/connectors/kubernetes/kubeconfig.ts
539
569
  import { readFileSync } from "fs";
@@ -1072,11 +1102,10 @@ var KubernetesConnector = class {
1072
1102
  const targets = faultedPods(deployments, pods, this.config);
1073
1103
  await Promise.all(
1074
1104
  targets.map(async (t) => {
1075
- const text = await fetchPodLog(transport, namespace, t.podName, {
1105
+ const text = await fetchPodProcessLog(transport, namespace, t.podName, {
1076
1106
  ...t.container ? { container: t.container } : {},
1077
1107
  tailLines: PROCESS_LOG_TAIL_LINES,
1078
1108
  limitBytes: PROCESS_LOG_LIMIT_BYTES,
1079
- previous: true,
1080
1109
  ...opts
1081
1110
  });
1082
1111
  if (typeof text === "string" && text.length > 0) logs.set(podLogKey(t.podName, t.container), text);
@@ -2012,4 +2041,4 @@ export {
2012
2041
  resolveHost,
2013
2042
  startDaemon
2014
2043
  };
2015
- //# sourceMappingURL=chunk-52SCEPEW.js.map
2044
+ //# sourceMappingURL=chunk-S5S2FYCQ.js.map