@camstack/addon-terminal 0.1.69 → 0.1.70
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/addon.js +41 -1
- package/dist/addon.mjs +41 -1
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -13061,7 +13061,12 @@ var ConfigEntrySchema = object({
|
|
|
13061
13061
|
value: unknown(),
|
|
13062
13062
|
description: string().optional()
|
|
13063
13063
|
});
|
|
13064
|
-
|
|
13064
|
+
/**
|
|
13065
|
+
* Only `manual` since D356: the operator picks, nothing links itself. The
|
|
13066
|
+
* field survives on the wire because a viewer already OTA'd parses it —
|
|
13067
|
+
* drop it once no shipped client reads `mode`.
|
|
13068
|
+
*/
|
|
13069
|
+
var LinkedDevicesModeSchema = _enum(["manual"]);
|
|
13065
13070
|
/** One resolved linked device — the compact projection consumers need. */
|
|
13066
13071
|
var LinkedDeviceSchema = object({
|
|
13067
13072
|
deviceId: number(),
|
|
@@ -47511,6 +47516,29 @@ var TerminalInstanceReconcileCoordinator = class {
|
|
|
47511
47516
|
return this.running;
|
|
47512
47517
|
}
|
|
47513
47518
|
};
|
|
47519
|
+
/**
|
|
47520
|
+
* Instances bound to a node the cluster no longer lists at all.
|
|
47521
|
+
*
|
|
47522
|
+
* An OFFLINE node is still in the topology; a FORGOTTEN one
|
|
47523
|
+
* (`clusterNodes.forgetNode`) is not. An instance bound to a forgotten node
|
|
47524
|
+
* warned every minute ("terminal profiles unavailable … node hub-test is
|
|
47525
|
+
* offline", 2026-09-05) and could never come back, so it is removed — but
|
|
47526
|
+
* only when two consecutive reads agree (D49): a topology answer that lost a
|
|
47527
|
+
* node once must not destroy an operator's configuration on its own.
|
|
47528
|
+
*/
|
|
47529
|
+
function instancesOnForgottenNodes(instances, knownNodeIds, absentBefore) {
|
|
47530
|
+
const absentNow = /* @__PURE__ */ new Set();
|
|
47531
|
+
const remove = [];
|
|
47532
|
+
for (const instance of instances) {
|
|
47533
|
+
if (knownNodeIds.has(instance.nodeId)) continue;
|
|
47534
|
+
absentNow.add(instance.id);
|
|
47535
|
+
if (absentBefore.has(instance.id)) remove.push(instance);
|
|
47536
|
+
}
|
|
47537
|
+
return {
|
|
47538
|
+
remove,
|
|
47539
|
+
absentNow
|
|
47540
|
+
};
|
|
47541
|
+
}
|
|
47514
47542
|
//#endregion
|
|
47515
47543
|
//#region src/profiles.ts
|
|
47516
47544
|
/**
|
|
@@ -48039,6 +48067,8 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
48039
48067
|
/** Prevent repeat writes when a legacy device stays live after migration. */
|
|
48040
48068
|
migratedTerminalCameraConfigIds = /* @__PURE__ */ new Set();
|
|
48041
48069
|
terminalCameraTombstones = /* @__PURE__ */ new Set();
|
|
48070
|
+
/** Instance ids whose node was absent from the last topology read (D49 streak). */
|
|
48071
|
+
instancesOnAbsentNodes = /* @__PURE__ */ new Set();
|
|
48042
48072
|
instanceMutationQueue = new TerminalInstanceMutationQueue();
|
|
48043
48073
|
terminalReconcileCoordinator = new TerminalInstanceReconcileCoordinator(this.instanceMutationQueue);
|
|
48044
48074
|
glancesPythonPath = "";
|
|
@@ -48219,6 +48249,16 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
48219
48249
|
const nodes = topology.filter((node) => typeof node.id === "string" && node.id.length > 0);
|
|
48220
48250
|
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
48221
48251
|
for (const cachedNodeId of this.cameraProfilesByNode.keys()) if (!nodeIds.has(cachedNodeId)) this.cameraProfilesByNode.delete(cachedNodeId);
|
|
48252
|
+
const forgotten = instancesOnForgottenNodes(this.terminalInstances(), nodeIds, this.instancesOnAbsentNodes);
|
|
48253
|
+
this.instancesOnAbsentNodes = forgotten.absentNow;
|
|
48254
|
+
for (const instance of forgotten.remove) {
|
|
48255
|
+
this.ctx.logger.info("terminal instance removed — its node is no longer in the cluster", { meta: {
|
|
48256
|
+
instanceId: instance.id,
|
|
48257
|
+
nodeId: instance.nodeId,
|
|
48258
|
+
name: instance.name
|
|
48259
|
+
} });
|
|
48260
|
+
await this.instanceMutationQueue.run(() => this.deleteTerminalInstanceUnlocked(instance.id));
|
|
48261
|
+
}
|
|
48222
48262
|
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
48223
48263
|
await Promise.all(nodes.map(async (node) => {
|
|
48224
48264
|
try {
|
package/dist/addon.mjs
CHANGED
|
@@ -13038,7 +13038,12 @@ var ConfigEntrySchema = object({
|
|
|
13038
13038
|
value: unknown(),
|
|
13039
13039
|
description: string().optional()
|
|
13040
13040
|
});
|
|
13041
|
-
|
|
13041
|
+
/**
|
|
13042
|
+
* Only `manual` since D356: the operator picks, nothing links itself. The
|
|
13043
|
+
* field survives on the wire because a viewer already OTA'd parses it —
|
|
13044
|
+
* drop it once no shipped client reads `mode`.
|
|
13045
|
+
*/
|
|
13046
|
+
var LinkedDevicesModeSchema = _enum(["manual"]);
|
|
13042
13047
|
/** One resolved linked device — the compact projection consumers need. */
|
|
13043
13048
|
var LinkedDeviceSchema = object({
|
|
13044
13049
|
deviceId: number(),
|
|
@@ -47488,6 +47493,29 @@ var TerminalInstanceReconcileCoordinator = class {
|
|
|
47488
47493
|
return this.running;
|
|
47489
47494
|
}
|
|
47490
47495
|
};
|
|
47496
|
+
/**
|
|
47497
|
+
* Instances bound to a node the cluster no longer lists at all.
|
|
47498
|
+
*
|
|
47499
|
+
* An OFFLINE node is still in the topology; a FORGOTTEN one
|
|
47500
|
+
* (`clusterNodes.forgetNode`) is not. An instance bound to a forgotten node
|
|
47501
|
+
* warned every minute ("terminal profiles unavailable … node hub-test is
|
|
47502
|
+
* offline", 2026-09-05) and could never come back, so it is removed — but
|
|
47503
|
+
* only when two consecutive reads agree (D49): a topology answer that lost a
|
|
47504
|
+
* node once must not destroy an operator's configuration on its own.
|
|
47505
|
+
*/
|
|
47506
|
+
function instancesOnForgottenNodes(instances, knownNodeIds, absentBefore) {
|
|
47507
|
+
const absentNow = /* @__PURE__ */ new Set();
|
|
47508
|
+
const remove = [];
|
|
47509
|
+
for (const instance of instances) {
|
|
47510
|
+
if (knownNodeIds.has(instance.nodeId)) continue;
|
|
47511
|
+
absentNow.add(instance.id);
|
|
47512
|
+
if (absentBefore.has(instance.id)) remove.push(instance);
|
|
47513
|
+
}
|
|
47514
|
+
return {
|
|
47515
|
+
remove,
|
|
47516
|
+
absentNow
|
|
47517
|
+
};
|
|
47518
|
+
}
|
|
47491
47519
|
//#endregion
|
|
47492
47520
|
//#region src/profiles.ts
|
|
47493
47521
|
/**
|
|
@@ -48016,6 +48044,8 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
48016
48044
|
/** Prevent repeat writes when a legacy device stays live after migration. */
|
|
48017
48045
|
migratedTerminalCameraConfigIds = /* @__PURE__ */ new Set();
|
|
48018
48046
|
terminalCameraTombstones = /* @__PURE__ */ new Set();
|
|
48047
|
+
/** Instance ids whose node was absent from the last topology read (D49 streak). */
|
|
48048
|
+
instancesOnAbsentNodes = /* @__PURE__ */ new Set();
|
|
48019
48049
|
instanceMutationQueue = new TerminalInstanceMutationQueue();
|
|
48020
48050
|
terminalReconcileCoordinator = new TerminalInstanceReconcileCoordinator(this.instanceMutationQueue);
|
|
48021
48051
|
glancesPythonPath = "";
|
|
@@ -48196,6 +48226,16 @@ var TerminalAddon = class extends BaseAddon {
|
|
|
48196
48226
|
const nodes = topology.filter((node) => typeof node.id === "string" && node.id.length > 0);
|
|
48197
48227
|
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
48198
48228
|
for (const cachedNodeId of this.cameraProfilesByNode.keys()) if (!nodeIds.has(cachedNodeId)) this.cameraProfilesByNode.delete(cachedNodeId);
|
|
48229
|
+
const forgotten = instancesOnForgottenNodes(this.terminalInstances(), nodeIds, this.instancesOnAbsentNodes);
|
|
48230
|
+
this.instancesOnAbsentNodes = forgotten.absentNow;
|
|
48231
|
+
for (const instance of forgotten.remove) {
|
|
48232
|
+
this.ctx.logger.info("terminal instance removed — its node is no longer in the cluster", { meta: {
|
|
48233
|
+
instanceId: instance.id,
|
|
48234
|
+
nodeId: instance.nodeId,
|
|
48235
|
+
name: instance.name
|
|
48236
|
+
} });
|
|
48237
|
+
await this.instanceMutationQueue.run(() => this.deleteTerminalInstanceUnlocked(instance.id));
|
|
48238
|
+
}
|
|
48199
48239
|
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
48200
48240
|
await Promise.all(nodes.map(async (node) => {
|
|
48201
48241
|
try {
|