@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/{chunk-XEHYJVPH.mjs → chunk-BWAPSCEC.mjs} +143 -20
- package/dist/chunk-BWAPSCEC.mjs.map +1 -0
- package/dist/cli.js +145 -19
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +4 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +142 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-XEHYJVPH.mjs.map +0 -1
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|