@camstack/agent 1.1.18 → 1.1.20

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.
@@ -605,17 +605,108 @@ function createAgentService(deps) {
605
605
  import * as fs4 from "fs";
606
606
  import * as path4 from "path";
607
607
  import Fastify from "fastify";
608
- function resolveUiDistDir(dataDir) {
609
- const candidates = [
610
- path4.resolve(__dirname, "../../addon-agent-ui/dist"),
611
- path4.join(dataDir, "addons", "@camstack", "addon-agent-ui", "dist"),
612
- path4.join(dataDir, "agent-ui")
613
- ];
614
- for (const dir of candidates) {
615
- if (fs4.existsSync(path4.join(dir, "index.html"))) return dir;
608
+
609
+ // src/derive-hub-url.ts
610
+ var HUB_API_PORT = 4443;
611
+ function deriveHubUrlFromHubAddress(hubAddress) {
612
+ const trimmed = hubAddress.trim();
613
+ if (trimmed.length === 0) return void 0;
614
+ const afterAt = trimmed.includes("@") ? trimmed.split("@")[1] ?? "" : trimmed;
615
+ const authority = afterAt.split("/")[0] ?? "";
616
+ const host = extractHostFromAuthority(authority);
617
+ if (host === void 0) return void 0;
618
+ return `https://${host}:${HUB_API_PORT}`;
619
+ }
620
+ function deriveHubUrlForExport(hubUrlWasExplicit, hubAddress) {
621
+ if (hubUrlWasExplicit) return void 0;
622
+ if (hubAddress === void 0) return void 0;
623
+ return deriveHubUrlFromHubAddress(hubAddress);
624
+ }
625
+ function extractHostFromAuthority(authority) {
626
+ const value = authority.trim();
627
+ if (value.length === 0) return void 0;
628
+ if (value.startsWith("[")) {
629
+ const end = value.indexOf("]");
630
+ if (end > 0) return value.slice(0, end + 1);
631
+ return void 0;
632
+ }
633
+ const host = value.split(":")[0] ?? "";
634
+ return host.length > 0 ? host : void 0;
635
+ }
636
+ var HUB_NODE_ID = "hub";
637
+ var IPV4_MAPPED_PREFIX = "::ffff:";
638
+ var IPV4_DOTTED_QUAD = /^\d{1,3}(?:\.\d{1,3}){3}$/;
639
+ function pickIpv4(ipList) {
640
+ if (!ipList) return null;
641
+ for (const ip of ipList) {
642
+ if (ip.includes(":")) continue;
643
+ if (ip.startsWith("127.")) continue;
644
+ return ip;
616
645
  }
617
646
  return null;
618
647
  }
648
+ function normalizeObservedHost(raw) {
649
+ if (raw === void 0 || raw === null) return void 0;
650
+ const trimmed = raw.trim();
651
+ if (trimmed.length === 0) return void 0;
652
+ if (trimmed.startsWith("[")) return trimmed;
653
+ if (trimmed.toLowerCase().startsWith(IPV4_MAPPED_PREFIX)) {
654
+ const mapped = trimmed.slice(IPV4_MAPPED_PREFIX.length);
655
+ if (IPV4_DOTTED_QUAD.test(mapped)) return mapped;
656
+ }
657
+ if (trimmed.includes(":")) return `[${trimmed}]`;
658
+ return trimmed;
659
+ }
660
+ function deriveHubUrlFromRegistry(nodes) {
661
+ const hub = nodes.find((node) => node.id === HUB_NODE_ID);
662
+ if (hub === void 0) return void 0;
663
+ const host = normalizeObservedHost(hub.udpAddress) ?? pickIpv4(hub.ipList) ?? hub.hostname;
664
+ if (host === void 0 || host.length === 0) return void 0;
665
+ return `https://${host}:${HUB_API_PORT}`;
666
+ }
667
+
668
+ // src/agent-http.ts
669
+ function compareVersions(a, b) {
670
+ if (a === null && b === null) return 0;
671
+ if (a === null) return -1;
672
+ if (b === null) return 1;
673
+ const pa = a.split(".").map((s) => Number.parseInt(s, 10) || 0);
674
+ const pb = b.split(".").map((s) => Number.parseInt(s, 10) || 0);
675
+ const len = Math.max(pa.length, pb.length);
676
+ for (let i = 0; i < len; i++) {
677
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
678
+ if (diff !== 0) return diff;
679
+ }
680
+ return 0;
681
+ }
682
+ function readUiDistCandidate(baseDir) {
683
+ const dir = path4.join(baseDir, "dist");
684
+ if (!fs4.existsSync(path4.join(dir, "index.html"))) return null;
685
+ let version = null;
686
+ try {
687
+ const parsed = JSON.parse(fs4.readFileSync(path4.join(baseDir, "package.json"), "utf-8"));
688
+ version = typeof parsed.version === "string" ? parsed.version : null;
689
+ } catch {
690
+ version = null;
691
+ }
692
+ return { dir, version };
693
+ }
694
+ function pickUiDist(installed, bundled) {
695
+ if (installed && bundled) {
696
+ return compareVersions(bundled.version, installed.version) > 0 ? bundled.dir : installed.dir;
697
+ }
698
+ return installed?.dir ?? bundled?.dir ?? null;
699
+ }
700
+ function resolveUiDistDir(dataDir, bundledAddonsDir, siblingDistDir = path4.resolve(__dirname, "../../addon-agent-ui/dist")) {
701
+ if (fs4.existsSync(path4.join(siblingDistDir, "index.html"))) return siblingDistDir;
702
+ const installed = readUiDistCandidate(path4.join(dataDir, "addons", "@camstack", "addon-agent-ui"));
703
+ const bundled = bundledAddonsDir ? readUiDistCandidate(path4.join(bundledAddonsDir, "addon-agent-ui")) : null;
704
+ const picked = pickUiDist(installed, bundled);
705
+ if (picked) return picked;
706
+ const legacy = path4.join(dataDir, "agent-ui");
707
+ if (fs4.existsSync(path4.join(legacy, "index.html"))) return legacy;
708
+ return null;
709
+ }
619
710
  function readConfigFile(configPath) {
620
711
  if (!fs4.existsSync(configPath)) return {};
621
712
  try {
@@ -636,15 +727,6 @@ function getRegistryNodes(broker) {
636
727
  return [];
637
728
  }
638
729
  }
639
- function pickIpv4(ipList) {
640
- if (!ipList) return null;
641
- for (const ip of ipList) {
642
- if (ip.includes(":")) continue;
643
- if (ip.startsWith("127.")) continue;
644
- return ip;
645
- }
646
- return null;
647
- }
648
730
  function resolveHubEndpoint(nodes) {
649
731
  const hub = nodes.find((n) => n.id === "hub");
650
732
  if (!hub) return null;
@@ -792,19 +874,26 @@ async function createAgentHttpServer(getBroker, config) {
792
874
  const nodes = getRegistryNodes(b);
793
875
  return mapDiscoveredNodes(nodes, b.nodeID);
794
876
  });
795
- const uiDir = resolveUiDistDir(config.dataDir);
877
+ const uiDir = resolveUiDistDir(config.dataDir, process.env["CAMSTACK_BUNDLED_ADDONS_DIR"]);
796
878
  if (uiDir) {
797
879
  const fastifyStatic = await import("@fastify/static");
798
880
  await app.register(fastifyStatic.default, {
799
881
  root: uiDir,
800
882
  prefix: "/",
801
883
  wildcard: false,
802
- decorateReply: false
884
+ // MUST stay true: the SPA-fallback below uses `reply.sendFile`, which
885
+ // only exists when the plugin decorates Reply. With `false` every
886
+ // fallback request 500'd with "reply.sendFile is not a function".
887
+ decorateReply: true
803
888
  });
804
889
  app.setNotFoundHandler(async (req, reply) => {
805
890
  if (req.url.startsWith("/api/") || req.url.startsWith("/health")) {
806
891
  return reply.status(404).send({ error: "Not found" });
807
892
  }
893
+ if (req.url.startsWith("/assets/")) {
894
+ console.warn(`[Agent] UI asset not found (stale index.html?): ${req.url}`);
895
+ return reply.status(404).send({ error: "Asset not found" });
896
+ }
808
897
  return reply.type("text/html").sendFile("index.html");
809
898
  });
810
899
  console.log(`[Agent] UI served from: ${uiDir}`);
@@ -1026,6 +1115,14 @@ import { LogManager } from "@camstack/system";
1026
1115
  var agentLogManager = new LogManager(5e3);
1027
1116
  async function startAgent(configPath) {
1028
1117
  const config = loadAgentConfig(configPath);
1118
+ const hubUrlWasExplicit = process.env["CAMSTACK_HUB_URL"] !== void 0;
1119
+ const derivedHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, config.hubAddress);
1120
+ if (derivedHubUrl !== void 0) {
1121
+ process.env["CAMSTACK_HUB_URL"] = derivedHubUrl;
1122
+ console.log(
1123
+ `[Agent] Derived CAMSTACK_HUB_URL=${derivedHubUrl} from hub address "${config.hubAddress}"`
1124
+ );
1125
+ }
1029
1126
  if (config.hubAddress) {
1030
1127
  console.log(
1031
1128
  `[Agent] Starting node "${config.nodeId}" (name="${config.name}") connecting to ${config.hubAddress}`
@@ -1072,6 +1169,13 @@ async function startAgent(configPath) {
1072
1169
  console.log(
1073
1170
  `[Agent] New config: hub=${fresh.hubAddress ?? "discovery"}, secret=${fresh.secret ? "yes" : "none"}`
1074
1171
  );
1172
+ const freshHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, fresh.hubAddress);
1173
+ if (freshHubUrl !== void 0 && process.env["CAMSTACK_HUB_URL"] !== freshHubUrl) {
1174
+ process.env["CAMSTACK_HUB_URL"] = freshHubUrl;
1175
+ console.log(
1176
+ `[Agent] Derived CAMSTACK_HUB_URL=${freshHubUrl} from hub address "${fresh.hubAddress}"`
1177
+ );
1178
+ }
1075
1179
  broker = createBroker({
1076
1180
  nodeID: config.nodeId,
1077
1181
  mode: "agent",
@@ -1336,6 +1440,25 @@ async function startAgent(configPath) {
1336
1440
  registerEventBusService(broker);
1337
1441
  broker.createService(createHwAccelService(createKernelHwAccel()));
1338
1442
  await broker.start();
1443
+ let hubUrlFromRegistry;
1444
+ const reconcileHubUrlFromRegistry = () => {
1445
+ if (hubUrlWasExplicit) return;
1446
+ const current = process.env["CAMSTACK_HUB_URL"];
1447
+ if (current !== void 0 && current !== hubUrlFromRegistry) return;
1448
+ const fromRegistry = deriveHubUrlFromRegistry(
1449
+ getRegistryNodes(broker)
1450
+ );
1451
+ if (fromRegistry === void 0 || fromRegistry === current) return;
1452
+ process.env["CAMSTACK_HUB_URL"] = fromRegistry;
1453
+ hubUrlFromRegistry = fromRegistry;
1454
+ console.log(`[Agent] Derived CAMSTACK_HUB_URL=${fromRegistry} from live hub connection`);
1455
+ };
1456
+ reconcileHubUrlFromRegistry();
1457
+ broker.localBus.on("$node.connected", (data) => {
1458
+ const node = data.node;
1459
+ if (node?.id !== "hub") return;
1460
+ reconcileHubUrlFromRegistry();
1461
+ });
1339
1462
  let udsEventBridgeDispose = null;
1340
1463
  if (agentUdsRegistry !== void 0) {
1341
1464
  const agentBrokerEventBus = getBrokerEventBus(broker);
@@ -1745,4 +1868,4 @@ export {
1745
1868
  startAgentHttpServer,
1746
1869
  startAgent
1747
1870
  };
1748
- //# sourceMappingURL=chunk-XEHYJVPH.mjs.map
1871
+ //# sourceMappingURL=chunk-BWAPSCEC.mjs.map