@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.
package/dist/cli.js CHANGED
@@ -37,15 +37,106 @@ var path6 = __toESM(require("path"));
37
37
  var fs = __toESM(require("fs"));
38
38
  var path = __toESM(require("path"));
39
39
  var import_fastify = __toESM(require("fastify"));
40
- function resolveUiDistDir(dataDir) {
41
- const candidates = [
42
- path.resolve(__dirname, "../../addon-agent-ui/dist"),
43
- path.join(dataDir, "addons", "@camstack", "addon-agent-ui", "dist"),
44
- path.join(dataDir, "agent-ui")
45
- ];
46
- for (const dir of candidates) {
47
- if (fs.existsSync(path.join(dir, "index.html"))) return dir;
40
+
41
+ // src/derive-hub-url.ts
42
+ var HUB_API_PORT = 4443;
43
+ function deriveHubUrlFromHubAddress(hubAddress) {
44
+ const trimmed = hubAddress.trim();
45
+ if (trimmed.length === 0) return void 0;
46
+ const afterAt = trimmed.includes("@") ? trimmed.split("@")[1] ?? "" : trimmed;
47
+ const authority = afterAt.split("/")[0] ?? "";
48
+ const host = extractHostFromAuthority(authority);
49
+ if (host === void 0) return void 0;
50
+ return `https://${host}:${HUB_API_PORT}`;
51
+ }
52
+ function deriveHubUrlForExport(hubUrlWasExplicit, hubAddress) {
53
+ if (hubUrlWasExplicit) return void 0;
54
+ if (hubAddress === void 0) return void 0;
55
+ return deriveHubUrlFromHubAddress(hubAddress);
56
+ }
57
+ function extractHostFromAuthority(authority) {
58
+ const value = authority.trim();
59
+ if (value.length === 0) return void 0;
60
+ if (value.startsWith("[")) {
61
+ const end = value.indexOf("]");
62
+ if (end > 0) return value.slice(0, end + 1);
63
+ return void 0;
64
+ }
65
+ const host = value.split(":")[0] ?? "";
66
+ return host.length > 0 ? host : void 0;
67
+ }
68
+ var HUB_NODE_ID = "hub";
69
+ var IPV4_MAPPED_PREFIX = "::ffff:";
70
+ var IPV4_DOTTED_QUAD = /^\d{1,3}(?:\.\d{1,3}){3}$/;
71
+ function pickIpv4(ipList) {
72
+ if (!ipList) return null;
73
+ for (const ip of ipList) {
74
+ if (ip.includes(":")) continue;
75
+ if (ip.startsWith("127.")) continue;
76
+ return ip;
77
+ }
78
+ return null;
79
+ }
80
+ function normalizeObservedHost(raw) {
81
+ if (raw === void 0 || raw === null) return void 0;
82
+ const trimmed = raw.trim();
83
+ if (trimmed.length === 0) return void 0;
84
+ if (trimmed.startsWith("[")) return trimmed;
85
+ if (trimmed.toLowerCase().startsWith(IPV4_MAPPED_PREFIX)) {
86
+ const mapped = trimmed.slice(IPV4_MAPPED_PREFIX.length);
87
+ if (IPV4_DOTTED_QUAD.test(mapped)) return mapped;
88
+ }
89
+ if (trimmed.includes(":")) return `[${trimmed}]`;
90
+ return trimmed;
91
+ }
92
+ function deriveHubUrlFromRegistry(nodes) {
93
+ const hub = nodes.find((node) => node.id === HUB_NODE_ID);
94
+ if (hub === void 0) return void 0;
95
+ const host = normalizeObservedHost(hub.udpAddress) ?? pickIpv4(hub.ipList) ?? hub.hostname;
96
+ if (host === void 0 || host.length === 0) return void 0;
97
+ return `https://${host}:${HUB_API_PORT}`;
98
+ }
99
+
100
+ // src/agent-http.ts
101
+ function compareVersions(a, b) {
102
+ if (a === null && b === null) return 0;
103
+ if (a === null) return -1;
104
+ if (b === null) return 1;
105
+ const pa = a.split(".").map((s) => Number.parseInt(s, 10) || 0);
106
+ const pb = b.split(".").map((s) => Number.parseInt(s, 10) || 0);
107
+ const len = Math.max(pa.length, pb.length);
108
+ for (let i = 0; i < len; i++) {
109
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
110
+ if (diff !== 0) return diff;
111
+ }
112
+ return 0;
113
+ }
114
+ function readUiDistCandidate(baseDir) {
115
+ const dir = path.join(baseDir, "dist");
116
+ if (!fs.existsSync(path.join(dir, "index.html"))) return null;
117
+ let version = null;
118
+ try {
119
+ const parsed = JSON.parse(fs.readFileSync(path.join(baseDir, "package.json"), "utf-8"));
120
+ version = typeof parsed.version === "string" ? parsed.version : null;
121
+ } catch {
122
+ version = null;
48
123
  }
124
+ return { dir, version };
125
+ }
126
+ function pickUiDist(installed, bundled) {
127
+ if (installed && bundled) {
128
+ return compareVersions(bundled.version, installed.version) > 0 ? bundled.dir : installed.dir;
129
+ }
130
+ return installed?.dir ?? bundled?.dir ?? null;
131
+ }
132
+ function resolveUiDistDir(dataDir, bundledAddonsDir, siblingDistDir = path.resolve(__dirname, "../../addon-agent-ui/dist")) {
133
+ if (fs.existsSync(path.join(siblingDistDir, "index.html"))) return siblingDistDir;
134
+ const installed = readUiDistCandidate(path.join(dataDir, "addons", "@camstack", "addon-agent-ui"));
135
+ const bundled = bundledAddonsDir ? readUiDistCandidate(path.join(bundledAddonsDir, "addon-agent-ui")) : null;
136
+ const picked = pickUiDist(installed, bundled);
137
+ if (picked) return picked;
138
+ const legacy = path.join(dataDir, "agent-ui");
139
+ if (fs.existsSync(path.join(legacy, "index.html"))) return legacy;
49
140
  return null;
50
141
  }
51
142
  function readConfigFile(configPath) {
@@ -68,15 +159,6 @@ function getRegistryNodes(broker) {
68
159
  return [];
69
160
  }
70
161
  }
71
- function pickIpv4(ipList) {
72
- if (!ipList) return null;
73
- for (const ip of ipList) {
74
- if (ip.includes(":")) continue;
75
- if (ip.startsWith("127.")) continue;
76
- return ip;
77
- }
78
- return null;
79
- }
80
162
  function resolveHubEndpoint(nodes) {
81
163
  const hub = nodes.find((n) => n.id === "hub");
82
164
  if (!hub) return null;
@@ -224,19 +306,26 @@ async function createAgentHttpServer(getBroker, config) {
224
306
  const nodes = getRegistryNodes(b);
225
307
  return mapDiscoveredNodes(nodes, b.nodeID);
226
308
  });
227
- const uiDir = resolveUiDistDir(config.dataDir);
309
+ const uiDir = resolveUiDistDir(config.dataDir, process.env["CAMSTACK_BUNDLED_ADDONS_DIR"]);
228
310
  if (uiDir) {
229
311
  const fastifyStatic = await import("@fastify/static");
230
312
  await app.register(fastifyStatic.default, {
231
313
  root: uiDir,
232
314
  prefix: "/",
233
315
  wildcard: false,
234
- decorateReply: false
316
+ // MUST stay true: the SPA-fallback below uses `reply.sendFile`, which
317
+ // only exists when the plugin decorates Reply. With `false` every
318
+ // fallback request 500'd with "reply.sendFile is not a function".
319
+ decorateReply: true
235
320
  });
236
321
  app.setNotFoundHandler(async (req, reply) => {
237
322
  if (req.url.startsWith("/api/") || req.url.startsWith("/health")) {
238
323
  return reply.status(404).send({ error: "Not found" });
239
324
  }
325
+ if (req.url.startsWith("/assets/")) {
326
+ console.warn(`[Agent] UI asset not found (stale index.html?): ${req.url}`);
327
+ return reply.status(404).send({ error: "Asset not found" });
328
+ }
240
329
  return reply.type("text/html").sendFile("index.html");
241
330
  });
242
331
  console.log(`[Agent] UI served from: ${uiDir}`);
@@ -1005,6 +1094,14 @@ var import_system4 = require("@camstack/system");
1005
1094
  var agentLogManager = new import_system4.LogManager(5e3);
1006
1095
  async function startAgent(configPath) {
1007
1096
  const config = loadAgentConfig(configPath);
1097
+ const hubUrlWasExplicit = process.env["CAMSTACK_HUB_URL"] !== void 0;
1098
+ const derivedHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, config.hubAddress);
1099
+ if (derivedHubUrl !== void 0) {
1100
+ process.env["CAMSTACK_HUB_URL"] = derivedHubUrl;
1101
+ console.log(
1102
+ `[Agent] Derived CAMSTACK_HUB_URL=${derivedHubUrl} from hub address "${config.hubAddress}"`
1103
+ );
1104
+ }
1008
1105
  if (config.hubAddress) {
1009
1106
  console.log(
1010
1107
  `[Agent] Starting node "${config.nodeId}" (name="${config.name}") connecting to ${config.hubAddress}`
@@ -1051,6 +1148,13 @@ async function startAgent(configPath) {
1051
1148
  console.log(
1052
1149
  `[Agent] New config: hub=${fresh.hubAddress ?? "discovery"}, secret=${fresh.secret ? "yes" : "none"}`
1053
1150
  );
1151
+ const freshHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, fresh.hubAddress);
1152
+ if (freshHubUrl !== void 0 && process.env["CAMSTACK_HUB_URL"] !== freshHubUrl) {
1153
+ process.env["CAMSTACK_HUB_URL"] = freshHubUrl;
1154
+ console.log(
1155
+ `[Agent] Derived CAMSTACK_HUB_URL=${freshHubUrl} from hub address "${fresh.hubAddress}"`
1156
+ );
1157
+ }
1054
1158
  broker = (0, import_system3.createBroker)({
1055
1159
  nodeID: config.nodeId,
1056
1160
  mode: "agent",
@@ -1315,6 +1419,25 @@ async function startAgent(configPath) {
1315
1419
  (0, import_system3.registerEventBusService)(broker);
1316
1420
  broker.createService((0, import_system3.createHwAccelService)((0, import_system3.createKernelHwAccel)()));
1317
1421
  await broker.start();
1422
+ let hubUrlFromRegistry;
1423
+ const reconcileHubUrlFromRegistry = () => {
1424
+ if (hubUrlWasExplicit) return;
1425
+ const current = process.env["CAMSTACK_HUB_URL"];
1426
+ if (current !== void 0 && current !== hubUrlFromRegistry) return;
1427
+ const fromRegistry = deriveHubUrlFromRegistry(
1428
+ getRegistryNodes(broker)
1429
+ );
1430
+ if (fromRegistry === void 0 || fromRegistry === current) return;
1431
+ process.env["CAMSTACK_HUB_URL"] = fromRegistry;
1432
+ hubUrlFromRegistry = fromRegistry;
1433
+ console.log(`[Agent] Derived CAMSTACK_HUB_URL=${fromRegistry} from live hub connection`);
1434
+ };
1435
+ reconcileHubUrlFromRegistry();
1436
+ broker.localBus.on("$node.connected", (data) => {
1437
+ const node = data.node;
1438
+ if (node?.id !== "hub") return;
1439
+ reconcileHubUrlFromRegistry();
1440
+ });
1318
1441
  let udsEventBridgeDispose = null;
1319
1442
  if (agentUdsRegistry !== void 0) {
1320
1443
  const agentBrokerEventBus = (0, import_system3.getBrokerEventBus)(broker);
@@ -1795,6 +1918,9 @@ Environment variables (override CLI args):
1795
1918
  CAMSTACK_DATA_DIR Data directory path
1796
1919
  CAMSTACK_LOG_LEVEL Log level
1797
1920
  CAMSTACK_CLUSTER_SECRET Cluster secret
1921
+ CAMSTACK_HUB_URL Derived automatically from the hub address; set
1922
+ only to override the cross-node stream/recording
1923
+ pull host
1798
1924
 
1799
1925
  Examples:
1800
1926
  camstack-agent --hub 192.168.1.100