@newrelic/preflight 1.18.2 → 1.18.3

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,96 @@
1
+ import { Agent } from 'undici';
2
+ import { createLogger } from '../shared/index.js';
3
+ import { validateSsrfUrl, createSsrfSafeLookup } from '../security/ssrf.js';
4
+ const logger = createLogger('homelab-forwarder');
5
+ const MAX_BUFFER_SIZE = 1000;
6
+ const FLUSH_INTERVAL_MS = 5_000;
7
+ export class HomelabForwarder {
8
+ serverUrl;
9
+ token;
10
+ developer;
11
+ sessionId;
12
+ buffer = [];
13
+ timer = null;
14
+ stopped = false;
15
+ // Built once and reused for every post() call. Its connect.lookup resolves
16
+ // and validates the address actually connected to (see
17
+ // createSsrfSafeLookup()'s doc comment) — allowPrivateNetworks: true
18
+ // because a homelab server is, by design, almost always at a private LAN
19
+ // address; only cloud metadata endpoints and non-http(s) schemes are
20
+ // blocked here, not RFC-1918/loopback ranges.
21
+ dispatcher;
22
+ constructor(opts) {
23
+ validateSsrfUrl('HomelabForwarder', new URL(opts.serverUrl), { allowPrivateNetworks: true });
24
+ this.serverUrl = opts.serverUrl;
25
+ this.token = opts.token;
26
+ this.developer = opts.developer;
27
+ this.sessionId = opts.sessionId;
28
+ this.dispatcher = new Agent({
29
+ connect: {
30
+ lookup: createSsrfSafeLookup('HomelabForwarder (resolved)', { allowPrivateNetworks: true }),
31
+ },
32
+ });
33
+ }
34
+ start() {
35
+ if (this.stopped)
36
+ return;
37
+ this.timer = setInterval(() => {
38
+ void this.flush();
39
+ }, FLUSH_INTERVAL_MS);
40
+ this.timer.unref?.();
41
+ }
42
+ enqueue(record) {
43
+ if (this.stopped)
44
+ return;
45
+ if (this.buffer.length >= MAX_BUFFER_SIZE) {
46
+ this.buffer.shift();
47
+ }
48
+ this.buffer.push(record);
49
+ }
50
+ async flush() {
51
+ if (this.buffer.length === 0)
52
+ return;
53
+ const batch = this.buffer.splice(0);
54
+ await this.post(batch);
55
+ }
56
+ async stop() {
57
+ this.stopped = true;
58
+ if (this.timer !== null) {
59
+ clearInterval(this.timer);
60
+ this.timer = null;
61
+ }
62
+ await this.flush();
63
+ await this.dispatcher.close().catch((err) => {
64
+ logger.warn('Failed to close homelab forwarder dispatcher', { error: String(err) });
65
+ });
66
+ }
67
+ async post(records) {
68
+ const payload = JSON.stringify({
69
+ developer: this.developer,
70
+ sessionId: this.sessionId,
71
+ records,
72
+ });
73
+ try {
74
+ const response = await fetch(`${this.serverUrl}/ingest`, {
75
+ method: 'POST',
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ Authorization: `Bearer ${this.token}`,
79
+ },
80
+ body: payload,
81
+ signal: AbortSignal.timeout(5_000),
82
+ dispatcher: this.dispatcher,
83
+ });
84
+ if (!response.ok) {
85
+ throw new Error(`HTTP ${response.status}`);
86
+ }
87
+ }
88
+ catch (err) {
89
+ logger.warn('Failed to forward events to homelab server', {
90
+ error: String(err),
91
+ count: records.length,
92
+ });
93
+ }
94
+ }
95
+ }
96
+ //# sourceMappingURL=homelab-forwarder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homelab-forwarder.js","sourceRoot":"","sources":["../../src/homelab/homelab-forwarder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { HomelabForwarder } from './homelab-forwarder.js';
2
+ export type { HomelabForwarderOptions } from './homelab-forwarder.js';
3
+ export { HomelabAccumulator } from './homelab-accumulator.js';
4
+ export type { HomelabIngestPayload } from './homelab-accumulator.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/homelab/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { HomelabForwarder } from './homelab-forwarder.js';
2
+ export { HomelabAccumulator } from './homelab-accumulator.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/homelab/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import 'dotenv/config';
3
3
  import { Command, Option } from 'commander';
4
4
  import { readFileSync, realpathSync } from 'node:fs';
5
+ import { timingSafeEqual } from 'node:crypto';
5
6
  import { resolve } from 'node:path';
6
7
  import { AlertLog } from './alerts/alert-log.js';
7
8
  import { AlertSnapshotCollector } from './alerts/alert-snapshot-collector.js';
@@ -75,6 +76,7 @@ import { SessionSpan } from './tracing/session-span.js';
75
76
  import { TaskSpanTracker } from './tracing/task-span-tracker.js';
76
77
  import { emitToolCallSpan } from './tracing/tool-call-span.js';
77
78
  import { NrIngestManager } from './transport/nr-ingest.js';
79
+ import { HomelabAccumulator, HomelabForwarder } from './homelab/index.js';
78
80
  import { VERSION } from './version.js';
79
81
  export { loadMcpConfig, redactSensitive } from './config.js';
80
82
  export { AmazonQAdapter, ClaudeCodeAdapter, ContinueAdapter, CopilotAdapter, createDefaultRegistry, CursorAdapter, GenericMcpAdapter, parseCopilotUsageResponse, PlatformRegistry, REPORT_SESSION_END_TOOL, REPORT_SESSION_START_TOOL, REPORT_TOOL_CALL_TOOL, validateReportToolCallInput, WindsurfAdapter, ZedAdapter, } from './platforms/index.js';
@@ -324,6 +326,7 @@ export function startDashboardRepoll(opts) {
324
326
  const SUBCOMMAND_NAMES = [
325
327
  'deploy-dashboards',
326
328
  'deploy-alerts',
329
+ 'server',
327
330
  'install',
328
331
  'uninstall',
329
332
  'setup',
@@ -335,8 +338,8 @@ const SUBCOMMAND_NAMES = [
335
338
  ];
336
339
  // Install-CLI subcommands — a subset of SUBCOMMAND_NAMES routed to runInstallCli.
337
340
  // Derived from SUBCOMMAND_NAMES to ensure a single source of truth: deploy-*
338
- // commands are excluded and handled by the commander path in dispatchSubcommand.
339
- const INSTALL_CLI_SUBCOMMANDS = SUBCOMMAND_NAMES.filter((s) => s !== 'deploy-dashboards' && s !== 'deploy-alerts');
341
+ // and server commands are excluded and handled by the commander path in dispatchSubcommand.
342
+ const INSTALL_CLI_SUBCOMMANDS = SUBCOMMAND_NAMES.filter((s) => s !== 'deploy-dashboards' && s !== 'deploy-alerts' && s !== 'server');
340
343
  function isSubcommand(value) {
341
344
  return typeof value === 'string' && SUBCOMMAND_NAMES.includes(value);
342
345
  }
@@ -392,6 +395,76 @@ export async function dispatchSubcommand(argv) {
392
395
  process.exitCode = code;
393
396
  });
394
397
  }
398
+ else if (sub === 'server') {
399
+ program
400
+ .command('server')
401
+ .description('Start Preflight in homelab server mode — receives events forwarded from remote clients and accumulates them to disk (no dashboard yet — see docs/homelab.md)')
402
+ .option('--port <port>', 'Port to bind to (default: 7777 or NEW_RELIC_AI_HOMELAB_SERVER_PORT)')
403
+ .option('--token <token>', 'Bearer token for /ingest auth (default: NEW_RELIC_AI_HOMELAB_TOKEN)')
404
+ .option('--bind <address>', 'Bind address (default: 0.0.0.0 or NEW_RELIC_AI_HOMELAB_BIND_ADDRESS)')
405
+ .option('--config <path>', 'Path to config file')
406
+ .action(async (opts) => {
407
+ if (opts.port)
408
+ process.env.NEW_RELIC_AI_HOMELAB_SERVER_PORT = opts.port;
409
+ if (opts.token)
410
+ process.env.NEW_RELIC_AI_HOMELAB_TOKEN = opts.token;
411
+ if (opts.bind)
412
+ process.env.NEW_RELIC_AI_HOMELAB_BIND_ADDRESS = opts.bind;
413
+ // server mode never needs cloud credentials — force local so loadMcpConfig
414
+ // doesn't require licenseKey/accountId.
415
+ process.env.NR_AI_MODE = 'local';
416
+ const config = loadConfigOrDie({ config: opts.config });
417
+ const token = config.homelabToken;
418
+ if (!token) {
419
+ process.stderr.write('[preflight] error: homelab server requires a token. Set --token or NEW_RELIC_AI_HOMELAB_TOKEN.\n');
420
+ process.exit(1);
421
+ }
422
+ const accumulator = new HomelabAccumulator({ storagePath: config.storagePath });
423
+ accumulator.start();
424
+ const bus = new LiveEventBus();
425
+ const dashboardServer = new DashboardServer({
426
+ port: config.homelabServer.port,
427
+ host: config.homelabServer.bindAddress,
428
+ bus,
429
+ serverMode: true,
430
+ ingestHandler: (authHeader, body) => {
431
+ const expected = `Bearer ${token}`;
432
+ const isAuthed = authHeader !== undefined &&
433
+ authHeader.length === expected.length &&
434
+ timingSafeEqual(Buffer.from(authHeader), Buffer.from(expected));
435
+ if (!isAuthed)
436
+ return 401;
437
+ const result = accumulator.handleIngest(body);
438
+ return result === 'ok' ? 204 : 400;
439
+ },
440
+ });
441
+ await dashboardServer.start();
442
+ logger.info('Homelab server ready', {
443
+ port: config.homelabServer.port,
444
+ bind: config.homelabServer.bindAddress,
445
+ });
446
+ // Keep the action (and therefore program.parseAsync) alive until a
447
+ // signal fires — without this, the action returns immediately and
448
+ // dispatchSubcommand exits the process.
449
+ await new Promise((resolve) => {
450
+ let shutting = false;
451
+ const shutdown = async () => {
452
+ if (shutting)
453
+ return;
454
+ shutting = true;
455
+ accumulator.stop();
456
+ await dashboardServer.stop();
457
+ resolve();
458
+ };
459
+ process.on('SIGTERM', () => {
460
+ void shutdown();
461
+ });
462
+ process.on('SIGINT', () => {
463
+ void shutdown();
464
+ });
465
+ });
466
+ });
467
+ }
395
468
  else {
396
469
  program
397
470
  .command('deploy-alerts')
@@ -582,6 +655,7 @@ async function main() {
582
655
  }, getPendingConfirmationCapMs());
583
656
  pendingConfirmationCapTimer.unref?.();
584
657
  };
658
+ let homelabForwarder = null;
585
659
  let shuttingDown = false;
586
660
  const shutdown = async () => {
587
661
  if (shuttingDown)
@@ -652,6 +726,7 @@ async function main() {
652
726
  nrIngest ? nrIngest.stop() : Promise.resolve(),
653
727
  mcpServer ? mcpServer.close() : Promise.resolve(),
654
728
  proxyManager ? proxyManager.stop() : Promise.resolve(),
729
+ homelabForwarder ? homelabForwarder.stop() : Promise.resolve(),
655
730
  ]);
656
731
  for (const r of stopResults) {
657
732
  if (r.status === 'rejected') {
@@ -1549,6 +1624,28 @@ async function main() {
1549
1624
  });
1550
1625
  capturedNrIngest = nrIngest;
1551
1626
  }
1627
+ // Start homelab forwarder if configured. Construction validates
1628
+ // homelabServerUrl (scheme + cloud-metadata-endpoint check) and can
1629
+ // throw — caught here so a bad/malicious URL only disables forwarding,
1630
+ // matching the "forwarding failures are non-fatal" contract in
1631
+ // docs/homelab.md, rather than crashing MCP startup.
1632
+ if (config.homelabServerUrl && config.homelabToken) {
1633
+ try {
1634
+ homelabForwarder = new HomelabForwarder({
1635
+ serverUrl: config.homelabServerUrl,
1636
+ token: config.homelabToken,
1637
+ developer: config.developer,
1638
+ sessionId: sessionTraceId ?? 'unknown',
1639
+ });
1640
+ homelabForwarder.start();
1641
+ }
1642
+ catch (err) {
1643
+ homelabForwarder = null;
1644
+ logger.error('Homelab forwarder failed to start — continuing without it', {
1645
+ error: String(err),
1646
+ });
1647
+ }
1648
+ }
1552
1649
  const capturedAlertEngine = alertEngine;
1553
1650
  const capturedAlertSnapshotCollector = alertSnapshotCollector;
1554
1651
  budgetTracker.setOnThreshold((event) => {
@@ -1777,6 +1874,7 @@ async function main() {
1777
1874
  });
1778
1875
  }
1779
1876
  }
1877
+ homelabForwarder?.enqueue(record);
1780
1878
  },
1781
1879
  onTokenEvent: (tokenEvent) => {
1782
1880
  if (!costTracker || !config)