@camstack/server 1.2.489 → 1.2.491
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/agent/agent-http.js
CHANGED
|
@@ -385,10 +385,11 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
385
385
|
// eslint-disable-next-line oxc/no-async-endpoint-handlers
|
|
386
386
|
app.post('/api/agent/config', async (req, reply) => {
|
|
387
387
|
// `joinToken` is redeemed, never stored; `meshTls` only ever comes from an
|
|
388
|
-
// enrollment, never
|
|
388
|
+
// enrollment (or is cleared via `clearMeshTls`), never pasted as PEM.
|
|
389
389
|
const body = req.body ?? {};
|
|
390
390
|
const joinToken = body['joinToken'];
|
|
391
|
-
const
|
|
391
|
+
const clearMeshTls = body['clearMeshTls'] === true;
|
|
392
|
+
const patch = Object.fromEntries(Object.entries(body).filter(([k]) => k !== 'joinToken' && k !== 'meshTls' && k !== 'clearMeshTls'));
|
|
392
393
|
const existing = readConfigFile(config.configPath);
|
|
393
394
|
// Join-token enrollment (mesh mTLS Release A): the hub address comes from
|
|
394
395
|
// the token and wins over a `hubAddress` posted in the same form. Nothing is
|
|
@@ -411,6 +412,9 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
411
412
|
merged['hubAddress'] = enrolled.hubAddress;
|
|
412
413
|
merged['meshTls'] = enrolled.meshTls;
|
|
413
414
|
}
|
|
415
|
+
else if (clearMeshTls) {
|
|
416
|
+
delete merged['meshTls'];
|
|
417
|
+
}
|
|
414
418
|
writeConfigFile(config.configPath, merged);
|
|
415
419
|
if (enrolled !== null) {
|
|
416
420
|
console.log(`[Agent] Enrolled in the mesh CA via join token — hub ${enrolled.hubAddress}, ` +
|
|
@@ -432,11 +436,13 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
432
436
|
// false-positive "Restart required" prompts on no-op saves.
|
|
433
437
|
const reconnectRelevant = ['hubAddress', 'secret'];
|
|
434
438
|
const needsReconnect = enrolled !== null ||
|
|
439
|
+
clearMeshTls ||
|
|
435
440
|
reconnectRelevant.some((k) => Object.prototype.hasOwnProperty.call(patch, k) && patch[k] !== existing[k]);
|
|
436
441
|
return {
|
|
437
442
|
success: true,
|
|
438
443
|
restartRequired: needsReconnect,
|
|
439
444
|
...(enrolled !== null ? { enrolled: true } : {}),
|
|
445
|
+
...(clearMeshTls ? { clearedMeshTls: true } : {}),
|
|
440
446
|
};
|
|
441
447
|
});
|
|
442
448
|
// -- Agent reconnect (applies new hub/secret config) ----------------
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentOfflineData = agentOfflineData;
|
|
4
|
+
exports.nodeDisplayName = nodeDisplayName;
|
|
5
|
+
/**
|
|
6
|
+
* The payload of `agent.offline`, built from what Moleculer told us.
|
|
7
|
+
*
|
|
8
|
+
* `unexpected` is Moleculer's own verdict on `$node.disconnected`: `true` when
|
|
9
|
+
* the heartbeat or the socket was lost, `false` when the node sent its
|
|
10
|
+
* DISCONNECT on a clean stop. The notification centre turns it into the body
|
|
11
|
+
* of the "node offline" notification (D820).
|
|
12
|
+
*/
|
|
13
|
+
function agentOfflineData(agentId, unexpected, nodeName) {
|
|
14
|
+
return {
|
|
15
|
+
agentId,
|
|
16
|
+
...(nodeName !== undefined ? { nodeName } : {}),
|
|
17
|
+
...(unexpected !== undefined
|
|
18
|
+
? { reason: unexpected ? 'connection-lost' : 'shutdown' }
|
|
19
|
+
: {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** The history's display name for a node — only a real one, never the id echoed back. */
|
|
23
|
+
function nodeDisplayName(nodeId, history) {
|
|
24
|
+
const name = history?.find((row) => row.id === nodeId)?.name;
|
|
25
|
+
return name !== undefined && name.length > 0 && name !== nodeId ? name : undefined;
|
|
26
|
+
}
|
|
@@ -42,6 +42,7 @@ const system_1 = require("@camstack/system");
|
|
|
42
42
|
const types_1 = require("@camstack/types");
|
|
43
43
|
const agent_addon_backfill_1 = require("./agent-addon-backfill");
|
|
44
44
|
const agent_delivery_ledger_1 = require("./agent-delivery-ledger");
|
|
45
|
+
const agent_liveness_event_1 = require("./agent-liveness-event");
|
|
45
46
|
const cluster_node_history_mirror_1 = require("./cluster-node-history-mirror");
|
|
46
47
|
const node_directory_1 = require("./node-directory");
|
|
47
48
|
/** Turn a forget outcome into the sentence the operator reads. */
|
|
@@ -477,12 +478,13 @@ class AgentRegistryService {
|
|
|
477
478
|
return;
|
|
478
479
|
if (kind === 'agent') {
|
|
479
480
|
console.log(`[agent-registry] Agent connected: ${node.id}`);
|
|
481
|
+
const nodeName = this.knownNodeName(node.id);
|
|
480
482
|
this.eventBus.emit({
|
|
481
483
|
id: (0, node_crypto_1.randomUUID)(),
|
|
482
484
|
timestamp: new Date(),
|
|
483
485
|
source: { type: 'core', id: 'agent-registry' },
|
|
484
486
|
category: types_1.EventCategory.AgentOnline,
|
|
485
|
-
data: { agentId: node.id },
|
|
487
|
+
data: { agentId: node.id, ...(nodeName !== undefined ? { nodeName } : {}) },
|
|
486
488
|
});
|
|
487
489
|
}
|
|
488
490
|
else {
|
|
@@ -495,12 +497,12 @@ class AgentRegistryService {
|
|
|
495
497
|
});
|
|
496
498
|
}
|
|
497
499
|
});
|
|
498
|
-
this.broker.localBus.on('$node.disconnected', ({ node }) => {
|
|
500
|
+
this.broker.localBus.on('$node.disconnected', ({ node, unexpected }) => {
|
|
499
501
|
const kind = classifyNode(node.id);
|
|
500
502
|
if (!kind)
|
|
501
503
|
return;
|
|
502
504
|
if (kind === 'agent') {
|
|
503
|
-
console.log(`[agent-registry] Agent disconnected: ${node.id}`);
|
|
505
|
+
console.log(`[agent-registry] Agent disconnected: ${node.id}${unexpected === true ? ' (unexpected)' : ''}`);
|
|
504
506
|
// A1: stamp the disconnect time as the node's `lastActive` so its
|
|
505
507
|
// offline row reports an honest "last seen". Best-effort, no-op if the
|
|
506
508
|
// node was never snapshotted while online.
|
|
@@ -510,7 +512,7 @@ class AgentRegistryService {
|
|
|
510
512
|
timestamp: new Date(),
|
|
511
513
|
source: { type: 'core', id: 'agent-registry' },
|
|
512
514
|
category: types_1.EventCategory.AgentOffline,
|
|
513
|
-
data:
|
|
515
|
+
data: (0, agent_liveness_event_1.agentOfflineData)(node.id, unexpected, this.knownNodeName(node.id)),
|
|
514
516
|
});
|
|
515
517
|
}
|
|
516
518
|
else {
|
|
@@ -557,6 +559,10 @@ class AgentRegistryService {
|
|
|
557
559
|
// Registry shape varies across Moleculer versions — never fatal.
|
|
558
560
|
}
|
|
559
561
|
}
|
|
562
|
+
/** The durable history's display name for a node, when it knows a real one. */
|
|
563
|
+
knownNodeName(nodeId) {
|
|
564
|
+
return (0, agent_liveness_event_1.nodeDisplayName)(nodeId, this.historyMirror?.peek() ?? null);
|
|
565
|
+
}
|
|
560
566
|
// ---- Placement reconciliation ----
|
|
561
567
|
/**
|
|
562
568
|
* Boot-time reconciliation pass. Agents already connected when the hub
|
|
@@ -42,6 +42,14 @@ class ClusterNodeHistoryMirror {
|
|
|
42
42
|
const rows = await this.refresh();
|
|
43
43
|
return rows ?? this.entry?.rows ?? null;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The last successful read, however old, without reading. For a synchronous
|
|
47
|
+
* caller that only wants a label (the node liveness events' display name) —
|
|
48
|
+
* a stale name is still the right name, and no answer is `null`.
|
|
49
|
+
*/
|
|
50
|
+
peek() {
|
|
51
|
+
return this.entry?.rows ?? null;
|
|
52
|
+
}
|
|
45
53
|
/** Drop the mirror (a node was forgotten): the next `current()` must re-read. */
|
|
46
54
|
invalidate() {
|
|
47
55
|
this.generation += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.491",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
]
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
41
|
-
"@camstack/addon-agent-ui": "1.2.
|
|
40
|
+
"@camstack/addon-admin-ui": "1.2.364",
|
|
41
|
+
"@camstack/addon-agent-ui": "1.2.198",
|
|
42
42
|
"@camstack/addon-auth": "1.2.135",
|
|
43
43
|
"@camstack/addon-decoder-nodeav": "1.2.122",
|
|
44
44
|
"@camstack/addon-notifiers": "1.2.129",
|
|
45
45
|
"@camstack/addon-pipeline": "1.2.347",
|
|
46
46
|
"@camstack/addon-pipeline-orchestrator": "1.2.301",
|
|
47
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
47
|
+
"@camstack/addon-post-analysis": "1.2.349",
|
|
48
48
|
"@camstack/sdk": "1.2.128",
|
|
49
|
-
"@camstack/system": "1.2.
|
|
50
|
-
"@camstack/types": "1.2.
|
|
51
|
-
"@camstack/ui-library": "1.2.
|
|
49
|
+
"@camstack/system": "1.2.385",
|
|
50
|
+
"@camstack/types": "1.2.335",
|
|
51
|
+
"@camstack/ui-library": "1.2.256",
|
|
52
52
|
"@fastify/compress": "^9.0.0",
|
|
53
53
|
"@fastify/cookie": "^11.0.2",
|
|
54
54
|
"@fastify/cors": "^11.2.0",
|