agent-relay 8.9.0 → 8.9.2

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.
Files changed (56) hide show
  1. package/dist/cli/agent-relay-mcp.d.ts +2 -53
  2. package/dist/cli/agent-relay-mcp.d.ts.map +1 -1
  3. package/dist/cli/agent-relay-mcp.js +14 -944
  4. package/dist/cli/agent-relay-mcp.js.map +1 -1
  5. package/dist/cli/commands/fleet.d.ts.map +1 -1
  6. package/dist/cli/commands/fleet.js +62 -13
  7. package/dist/cli/commands/fleet.js.map +1 -1
  8. package/dist/cli/lib/broker-dashboard.d.ts +12 -0
  9. package/dist/cli/lib/broker-dashboard.d.ts.map +1 -0
  10. package/dist/cli/lib/broker-dashboard.js +431 -0
  11. package/dist/cli/lib/broker-dashboard.js.map +1 -0
  12. package/dist/cli/lib/broker-lifecycle.d.ts.map +1 -1
  13. package/dist/cli/lib/broker-lifecycle.js +1 -428
  14. package/dist/cli/lib/broker-lifecycle.js.map +1 -1
  15. package/dist/cli/mcp/action-schema.d.ts +20 -0
  16. package/dist/cli/mcp/action-schema.d.ts.map +1 -0
  17. package/dist/cli/mcp/action-schema.js +132 -0
  18. package/dist/cli/mcp/action-schema.js.map +1 -0
  19. package/dist/cli/mcp/action-tools.d.ts +10 -0
  20. package/dist/cli/mcp/action-tools.d.ts.map +1 -0
  21. package/dist/cli/mcp/action-tools.js +114 -0
  22. package/dist/cli/mcp/action-tools.js.map +1 -0
  23. package/dist/cli/mcp/inbox.d.ts +9 -0
  24. package/dist/cli/mcp/inbox.d.ts.map +1 -0
  25. package/dist/cli/mcp/inbox.js +49 -0
  26. package/dist/cli/mcp/inbox.js.map +1 -0
  27. package/dist/cli/mcp/messaging-tools.d.ts +9 -0
  28. package/dist/cli/mcp/messaging-tools.d.ts.map +1 -0
  29. package/dist/cli/mcp/messaging-tools.js +315 -0
  30. package/dist/cli/mcp/messaging-tools.js.map +1 -0
  31. package/dist/cli/mcp/resources.d.ts +39 -0
  32. package/dist/cli/mcp/resources.d.ts.map +1 -0
  33. package/dist/cli/mcp/resources.js +158 -0
  34. package/dist/cli/mcp/resources.js.map +1 -0
  35. package/dist/cli/mcp/telemetry.d.ts +9 -0
  36. package/dist/cli/mcp/telemetry.d.ts.map +1 -0
  37. package/dist/cli/mcp/telemetry.js +176 -0
  38. package/dist/cli/mcp/telemetry.js.map +1 -0
  39. package/dist/cli/mcp/tool-results.d.ts +29 -0
  40. package/dist/cli/mcp/tool-results.d.ts.map +1 -0
  41. package/dist/cli/mcp/tool-results.js +39 -0
  42. package/dist/cli/mcp/tool-results.js.map +1 -0
  43. package/dist/cli/mcp/tool-shapes.d.ts +13 -0
  44. package/dist/cli/mcp/tool-shapes.d.ts.map +1 -0
  45. package/dist/cli/mcp/tool-shapes.js +16 -0
  46. package/dist/cli/mcp/tool-shapes.js.map +1 -0
  47. package/dist/cli/mcp/types.d.ts +38 -0
  48. package/dist/cli/mcp/types.d.ts.map +1 -0
  49. package/dist/cli/mcp/types.js +2 -0
  50. package/dist/cli/mcp/types.js.map +1 -0
  51. package/dist/cli/mcp/workspace.d.ts +10 -0
  52. package/dist/cli/mcp/workspace.d.ts.map +1 -0
  53. package/dist/cli/mcp/workspace.js +36 -0
  54. package/dist/cli/mcp/workspace.js.map +1 -0
  55. package/dist/index.cjs +1 -1
  56. package/package.json +8 -8
@@ -1,8 +1,8 @@
1
1
  import fs from 'node:fs';
2
- import os from 'node:os';
3
2
  import path from 'node:path';
4
3
  import { HarnessDriverClient } from '@agent-relay/harness-driver';
5
4
  import { track } from '../telemetry/index.js';
5
+ import { getDefaultDashboardRelayUrl, isDebugLikeLoggingEnabled, normalizeDashboardPath, resolveDashboardPortWithFallback, resolveDashboardRelayUrl, startDashboardWithFallback, waitForDashboard, } from './broker-dashboard.js';
6
6
  import { buildBundledAgentRelayMcpCommand } from './agent-relay-mcp-command.js';
7
7
  import { errorClassName } from './telemetry-helpers.js';
8
8
  import { createImplicitLocalFleetNode, fleetStatusPath, startFleetSidecar, } from './fleet-sidecar.js';
@@ -176,19 +176,6 @@ function startImplicitLocalFleetSidecar(paths, relay, options, deps, teamsConfig
176
176
  warn: (message) => deps.warn(message),
177
177
  });
178
178
  }
179
- async function resolveDashboardPortWithFallback(dashboardPort, dashboardPortCandidates, deps) {
180
- for (let attempt = 0; attempt < dashboardPortCandidates; attempt += 1) {
181
- const candidatePort = dashboardPort + attempt;
182
- const inUse = await deps.isPortInUse(candidatePort);
183
- if (!inUse) {
184
- if (attempt > 0) {
185
- deps.warn(`Dashboard port ${dashboardPort} is already in use; trying ${candidatePort}`);
186
- }
187
- return candidatePort;
188
- }
189
- }
190
- throw new Error(`Failed to find an available dashboard port near ${dashboardPort}.`);
191
- }
192
179
  function isBrokerAlreadyRunningError(message) {
193
180
  return /another broker instance is already running in this directory/i.test(message);
194
181
  }
@@ -543,420 +530,6 @@ async function waitForBrokerReadiness(paths, deps, waitMs, requireApi) {
543
530
  }
544
531
  return latest;
545
532
  }
546
- function pickDashboardStaticDir(candidates, deps) {
547
- const existingCandidates = Array.from(new Set(candidates)).filter((candidate) => deps.fs.existsSync(candidate));
548
- if (existingCandidates.length === 0) {
549
- return null;
550
- }
551
- const pageMarkerPriority = [
552
- ['metrics.html', path.join('metrics', 'index.html')],
553
- ['app.html'],
554
- ['index.html'],
555
- ];
556
- for (const markerGroup of pageMarkerPriority) {
557
- const withMarker = existingCandidates.find((candidate) => markerGroup.some((marker) => deps.fs.existsSync(path.join(candidate, marker))));
558
- if (withMarker) {
559
- return withMarker;
560
- }
561
- }
562
- return existingCandidates[0];
563
- }
564
- function getHomeDashboardRoot(deps) {
565
- const homeDir = deps.env.HOME || deps.env.USERPROFILE || os.homedir();
566
- return path.join(homeDir, '.agentworkforce/relay', 'dashboard');
567
- }
568
- function getPriorDashboardRoot(deps) {
569
- const homeDir = deps.env.HOME || deps.env.USERPROFILE || '';
570
- if (!homeDir) {
571
- return null;
572
- }
573
- return path.join(homeDir, '.relay', 'dashboard');
574
- }
575
- function getDashboardRootFromBinary(dashboardBinary, deps) {
576
- if (!dashboardBinary || dashboardBinary.endsWith('.js') || dashboardBinary.endsWith('.ts')) {
577
- return null;
578
- }
579
- const binaryDir = path.dirname(dashboardBinary);
580
- if (path.basename(binaryDir) !== 'bin') {
581
- return null;
582
- }
583
- const homeDir = deps.env.HOME || deps.env.USERPROFILE || '';
584
- const resolvedBinaryDir = path.resolve(binaryDir);
585
- const ignoredBinDirs = [
586
- homeDir ? path.join(homeDir, '.local', 'bin') : null,
587
- path.join('/usr/local', 'bin'),
588
- ]
589
- .filter((candidate) => Boolean(candidate))
590
- .map((candidate) => path.resolve(candidate));
591
- if (ignoredBinDirs.includes(resolvedBinaryDir)) {
592
- return null;
593
- }
594
- return path.join(path.dirname(binaryDir), 'dashboard');
595
- }
596
- function resolveDashboardStaticDir(dashboardBinary, deps) {
597
- const explicitStaticDir = deps.env.RELAY_DASHBOARD_STATIC_DIR ?? deps.env.STATIC_DIR;
598
- if (explicitStaticDir && explicitStaticDir.trim()) {
599
- return explicitStaticDir;
600
- }
601
- if (!dashboardBinary) {
602
- return null;
603
- }
604
- if (dashboardBinary.endsWith('.js') || dashboardBinary.endsWith('.ts')) {
605
- const dashboardServerOutDir = path.resolve(path.dirname(dashboardBinary), '..', 'out');
606
- const siblingDashboardOutDir = path.resolve(path.dirname(dashboardBinary), '..', '..', 'dashboard', 'out');
607
- return pickDashboardStaticDir([dashboardServerOutDir, siblingDashboardOutDir], deps);
608
- }
609
- // Installs place UI assets under the install dir (~/.agentworkforce/relay/dashboard/out
610
- // by default, or next to a custom install's bin/ directory). ~/.relay/dashboard/out is
611
- // read as a fallback for installs predating that move.
612
- const installDashboardRoot = getDashboardRootFromBinary(dashboardBinary, deps);
613
- const priorDashboardRoot = getPriorDashboardRoot(deps);
614
- const candidates = [
615
- installDashboardRoot ? path.join(installDashboardRoot, 'out') : null,
616
- path.join(getHomeDashboardRoot(deps), 'out'),
617
- priorDashboardRoot ? path.join(priorDashboardRoot, 'out') : null,
618
- ].filter((candidate) => Boolean(candidate));
619
- return pickDashboardStaticDir(candidates, deps);
620
- }
621
- function normalizeLocalhostRelayUrl(relayUrl) {
622
- try {
623
- const parsed = new URL(relayUrl);
624
- if (parsed.hostname === 'localhost') {
625
- parsed.hostname = '127.0.0.1';
626
- }
627
- return parsed.toString().replace(/\/+$/, '');
628
- }
629
- catch {
630
- return relayUrl;
631
- }
632
- }
633
- function getDefaultDashboardRelayUrl(apiPort) {
634
- return normalizeLocalhostRelayUrl(`http://localhost:${apiPort}`);
635
- }
636
- function resolveDashboardRelayUrl(apiPort, deps) {
637
- const explicitRelayUrl = deps.env.RELAY_DASHBOARD_RELAY_URL;
638
- if (explicitRelayUrl && explicitRelayUrl.trim()) {
639
- return normalizeLocalhostRelayUrl(explicitRelayUrl.trim());
640
- }
641
- return getDefaultDashboardRelayUrl(apiPort);
642
- }
643
- function isDebugLikeLoggingEnabled(deps) {
644
- const rawLevel = String(deps.env.RUST_LOG ?? '').toLowerCase();
645
- return rawLevel.includes('debug') || rawLevel.includes('trace');
646
- }
647
- function getDashboardSpawnEnv(deps, relayUrl, enableVerboseLogging, relayApiKey, brokerApiKey) {
648
- const env = {
649
- ...deps.env,
650
- RELAY_URL: relayUrl,
651
- VERBOSE: enableVerboseLogging || deps.env.VERBOSE === 'true' ? 'true' : deps.env.VERBOSE,
652
- };
653
- // Pass the workspace key so the dashboard can make Agent Relay calls
654
- // (e.g. posting thread replies) without requiring a relaycast.json file.
655
- if (relayApiKey) {
656
- if (!env.RELAY_WORKSPACE_KEY) {
657
- env.RELAY_WORKSPACE_KEY = relayApiKey;
658
- }
659
- if (!env.RELAY_API_KEY) {
660
- env.RELAY_API_KEY = relayApiKey;
661
- }
662
- }
663
- // Pass the broker API key so the dashboard can authenticate with the
664
- // broker's HTTP API (e.g. /api/spawn, /api/spawned).
665
- if (brokerApiKey) {
666
- env.RELAY_BROKER_API_KEY = brokerApiKey;
667
- }
668
- return env;
669
- }
670
- function getDashboardSpawnArgs(paths, port, apiPort, dashboardBinary, relayUrl, enableVerboseLogging, deps) {
671
- const args = ['--port', String(port), '--data-dir', paths.dataDir];
672
- args.push('--relay-url', relayUrl);
673
- const staticDir = resolveDashboardStaticDir(dashboardBinary, deps);
674
- if (staticDir) {
675
- args.push('--static-dir', staticDir);
676
- }
677
- if (enableVerboseLogging) {
678
- args.push('--verbose');
679
- }
680
- return args;
681
- }
682
- function normalizeDashboardPath(rawDashboardPath) {
683
- const trimmed = rawDashboardPath?.trim();
684
- if (!trimmed)
685
- return undefined;
686
- if (trimmed.startsWith('/')) {
687
- return trimmed;
688
- }
689
- return `/${trimmed}`;
690
- }
691
- function startDashboard(paths, port, apiPort, deps, enableVerboseLogging, dashboardBinaryOverride, relayApiKey, brokerApiKey) {
692
- const dashboardBinary = dashboardBinaryOverride === undefined ? deps.findDashboardBinary() : dashboardBinaryOverride;
693
- const relayUrl = resolveDashboardRelayUrl(apiPort, deps);
694
- const shouldEnableVerbose = enableVerboseLogging || isDebugLikeLoggingEnabled(deps);
695
- const args = getDashboardSpawnArgs(paths, port, apiPort, dashboardBinary, relayUrl, shouldEnableVerbose, deps);
696
- const launchTarget = dashboardBinary
697
- ? dashboardBinary.endsWith('.js')
698
- ? `node ${dashboardBinary}`
699
- : dashboardBinary
700
- : 'npx --yes @agent-relay/dashboard-server@latest';
701
- const spawnOpts = {
702
- stdio: ['ignore', 'pipe', 'pipe'],
703
- env: getDashboardSpawnEnv(deps, relayUrl, shouldEnableVerbose, relayApiKey, brokerApiKey),
704
- };
705
- if (shouldEnableVerbose) {
706
- deps.log(`[dashboard] Starting: ${launchTarget} ${args.join(' ')}`);
707
- }
708
- let child;
709
- if (dashboardBinary) {
710
- // If the binary is a .js file (local dev), run it with node
711
- if (dashboardBinary.endsWith('.js')) {
712
- child = deps.spawnProcess('node', [dashboardBinary, ...args], spawnOpts);
713
- }
714
- else {
715
- child = deps.spawnProcess(dashboardBinary, args, spawnOpts);
716
- }
717
- }
718
- else {
719
- child = deps.spawnProcess('npx', ['--yes', '@agent-relay/dashboard-server@latest', ...args], spawnOpts);
720
- }
721
- // Capture stderr for error reporting
722
- const childAny = child;
723
- let stderrBuf = '';
724
- const logChunk = (chunk, logger, prefix) => {
725
- if (!shouldEnableVerbose) {
726
- return;
727
- }
728
- const text = chunk.toString();
729
- for (const line of text.split(/\r?\n/)) {
730
- const trimmed = line.trim();
731
- if (trimmed) {
732
- logger(`[dashboard] ${prefix}: ${trimmed}`);
733
- }
734
- }
735
- };
736
- childAny.stdout?.on?.('data', (chunk) => {
737
- logChunk(chunk, deps.log, 'stdout');
738
- });
739
- childAny.stderr?.on?.('data', (chunk) => {
740
- stderrBuf += chunk.toString();
741
- logChunk(chunk, deps.warn, 'stderr');
742
- });
743
- // Report early crashes
744
- childAny.on?.('exit', (...exitArgs) => {
745
- const code = exitArgs[0];
746
- const signal = exitArgs[1];
747
- if (code !== null && code !== 0) {
748
- deps.error(`Dashboard process exited with code ${code}`);
749
- if (stderrBuf.trim()) {
750
- deps.error(stderrBuf.trim().split('\n').slice(-5).join('\n'));
751
- }
752
- }
753
- else if (signal && signal !== 'SIGINT' && signal !== 'SIGTERM') {
754
- deps.error(`Dashboard process killed by signal ${signal}`);
755
- }
756
- });
757
- return child;
758
- }
759
- async function resolveStartedDashboardPort(process, preferredPort, deps) {
760
- return new Promise((resolve) => {
761
- let resolved = false;
762
- const processAny = process;
763
- const detach = () => {
764
- process.stdout?.off?.('data', extractPort);
765
- process.stdout?.removeListener?.('data', extractPort);
766
- process.stderr?.off?.('data', extractPort);
767
- process.stderr?.removeListener?.('data', extractPort);
768
- processAny.off?.('exit', handleExit);
769
- processAny.removeListener?.('exit', handleExit);
770
- clearTimeout(timer);
771
- };
772
- const timer = setTimeout(() => {
773
- if (resolved)
774
- return;
775
- resolved = true;
776
- detach();
777
- deps.warn(`Dashboard did not report its bound port quickly; assuming requested port ${preferredPort}`);
778
- resolve(preferredPort);
779
- }, 3000);
780
- const finalize = (port) => {
781
- if (resolved)
782
- return;
783
- resolved = true;
784
- detach();
785
- resolve(port);
786
- };
787
- const handleExit = (...exitArgs) => {
788
- const code = exitArgs[0];
789
- const signal = exitArgs[1];
790
- if (resolved) {
791
- return;
792
- }
793
- resolved = true;
794
- detach();
795
- if (code !== null && code !== 0) {
796
- deps.warn(`Dashboard exited before reporting its port (code: ${code}).`);
797
- }
798
- else if (signal && signal !== 'SIGINT' && signal !== 'SIGTERM') {
799
- deps.warn(`Dashboard exited before reporting its port (signal: ${signal}).`);
800
- }
801
- else {
802
- deps.warn('Dashboard exited before reporting its bound port.');
803
- }
804
- resolve(null);
805
- };
806
- const extractPort = (...chunkArgs) => {
807
- const firstChunk = chunkArgs[0];
808
- if (!firstChunk) {
809
- return;
810
- }
811
- const chunk = Buffer.isBuffer(firstChunk)
812
- ? firstChunk
813
- : typeof firstChunk === 'string'
814
- ? Buffer.from(firstChunk)
815
- : Buffer.from(JSON.stringify(firstChunk));
816
- const match = chunk.toString().match(/Server running at http:\/\/localhost:(\d+)/i);
817
- if (!match?.[1]) {
818
- return;
819
- }
820
- const parsed = Number.parseInt(match[1], 10);
821
- if (!Number.isNaN(parsed)) {
822
- finalize(parsed);
823
- }
824
- };
825
- process.stdout?.on?.('data', extractPort);
826
- process.stderr?.on?.('data', extractPort);
827
- processAny.on?.('exit', handleExit);
828
- });
829
- }
830
- /**
831
- * Check if the cached dashboard UI assets match the installed dashboard-server
832
- * binary version. If they are stale (or missing a version marker), re-download
833
- * the latest assets from the relay-dashboard GitHub release.
834
- */
835
- async function refreshDashboardAssetsIfStale(dashboardBinary, deps) {
836
- if (!dashboardBinary || dashboardBinary.endsWith('.js') || dashboardBinary.endsWith('.ts')) {
837
- // Dev mode or npx — skip
838
- return;
839
- }
840
- // Get installed binary version (async to avoid blocking event loop)
841
- let binaryVersion;
842
- try {
843
- const versionResult = await deps.execCommand(`${JSON.stringify(dashboardBinary)} --version`);
844
- binaryVersion = versionResult.stdout.trim();
845
- }
846
- catch {
847
- return; // Can't determine version — skip
848
- }
849
- if (!binaryVersion) {
850
- return;
851
- }
852
- const targetDir = getDashboardRootFromBinary(dashboardBinary, deps) ?? getHomeDashboardRoot(deps);
853
- const assetsDir = path.join(targetDir, 'out');
854
- const versionFile = path.join(targetDir, '.version');
855
- // Check if assets match the binary version
856
- try {
857
- const cachedVersion = deps.fs.readFileSync(versionFile, 'utf-8').trim();
858
- if (cachedVersion === binaryVersion) {
859
- return; // Up to date
860
- }
861
- }
862
- catch {
863
- // No version file — need to download if assets exist but are unversioned,
864
- // or if assets don't exist at all
865
- if (deps.fs.existsSync(assetsDir)) {
866
- // Assets exist but no version marker — they're from an old install
867
- }
868
- else {
869
- // No assets at all — need to download
870
- }
871
- }
872
- deps.log(`Updating dashboard UI assets (${binaryVersion})...`);
873
- const uiUrl = 'https://github.com/AgentWorkforce/relay-dashboard/releases/latest/download/dashboard-ui.tar.gz';
874
- let tempDir;
875
- let tempFile;
876
- try {
877
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `dashboard-ui-${deps.pid}-`));
878
- tempFile = path.join(tempDir, 'dashboard-ui.tar.gz');
879
- // Download (async to avoid blocking event loop during network I/O)
880
- await deps.execCommand(`curl -fsSL --max-time 30 ${JSON.stringify(uiUrl)} -o ${JSON.stringify(tempFile)}`);
881
- // Verify it's a valid gzip
882
- const header = Buffer.alloc(2);
883
- const fd = fs.openSync(tempFile, 'r');
884
- fs.readSync(fd, header, 0, 2, 0);
885
- fs.closeSync(fd);
886
- if (header[0] !== 0x1f || header[1] !== 0x8b) {
887
- if (tempFile)
888
- deps.fs.unlinkSync(tempFile);
889
- return; // Not a valid gzip file
890
- }
891
- // Remove old assets and extract (async to avoid blocking event loop)
892
- deps.fs.rmSync(assetsDir, { recursive: true, force: true });
893
- deps.fs.mkdirSync(targetDir, { recursive: true });
894
- await deps.execCommand(`tar -xzf ${JSON.stringify(tempFile)} -C ${JSON.stringify(targetDir)}`);
895
- if (tempFile)
896
- deps.fs.unlinkSync(tempFile);
897
- // Write version marker only after confirming extraction succeeded
898
- if (deps.fs.existsSync(path.join(assetsDir, 'index.html'))) {
899
- deps.fs.writeFileSync(versionFile, binaryVersion);
900
- deps.log(`Dashboard UI assets updated to ${binaryVersion}`);
901
- }
902
- else {
903
- deps.warn('Dashboard UI extraction may be incomplete — skipping version marker');
904
- }
905
- }
906
- catch {
907
- // Best-effort — don't block startup
908
- try {
909
- if (tempFile)
910
- deps.fs.unlinkSync(tempFile);
911
- }
912
- catch {
913
- /* ignore */
914
- }
915
- }
916
- finally {
917
- try {
918
- if (tempDir)
919
- deps.fs.rmSync(tempDir, { recursive: true, force: true });
920
- }
921
- catch {
922
- /* ignore */
923
- }
924
- }
925
- }
926
- async function startDashboardWithFallback(paths, dashboardPort, apiPort, deps, enableVerboseLogging, relayApiKey, brokerApiKey) {
927
- const preferredBinary = deps.findDashboardBinary();
928
- await refreshDashboardAssetsIfStale(preferredBinary, deps);
929
- let process = startDashboard(paths, dashboardPort, apiPort, deps, enableVerboseLogging, preferredBinary, relayApiKey, brokerApiKey);
930
- let port = await resolveStartedDashboardPort(process, dashboardPort, deps);
931
- if (port === null && preferredBinary) {
932
- deps.warn('Retrying dashboard startup using npx @agent-relay/dashboard-server@latest');
933
- process = startDashboard(paths, dashboardPort, apiPort, deps, enableVerboseLogging, null, relayApiKey, brokerApiKey);
934
- port = await resolveStartedDashboardPort(process, dashboardPort, deps);
935
- }
936
- return { process, port };
937
- }
938
- async function waitForDashboard(port, process, deps, isShuttingDown) {
939
- for (let i = 0; i < 20; i++) {
940
- await new Promise((r) => setTimeout(r, 500));
941
- if (process.killed) {
942
- if (!isShuttingDown()) {
943
- deps.warn(`Warning: Dashboard process exited before becoming ready on port ${port}`);
944
- }
945
- return;
946
- }
947
- try {
948
- const resp = await fetch(`http://localhost:${port}/health`);
949
- if (resp.ok)
950
- return; // Dashboard is up
951
- }
952
- catch {
953
- // Not ready yet
954
- }
955
- }
956
- if (!isShuttingDown()) {
957
- deps.warn(`Warning: Dashboard not responding on port ${port} after 10s`);
958
- }
959
- }
960
533
  async function discoverExistingBrokerApiPort(preferredApiPort, maxAttempts, deps) {
961
534
  const attempts = Math.max(1, maxAttempts);
962
535
  for (let attempt = 0; attempt < attempts; attempt += 1) {