camstack 1.2.213 → 1.2.214

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.
@@ -7149,9 +7149,9 @@ var require_chunk_Cek0wNdY = __commonJS({
7149
7149
  }
7150
7150
  });
7151
7151
 
7152
- // ../system/dist/dist-D98erEr-.js
7153
- var require_dist_D98erEr = __commonJS({
7154
- "../system/dist/dist-D98erEr-.js"(exports) {
7152
+ // ../system/dist/dist-BVrPqzF0.js
7153
+ var require_dist_BVrPqzF0 = __commonJS({
7154
+ "../system/dist/dist-BVrPqzF0.js"(exports) {
7155
7155
  "use strict";
7156
7156
  var zod = __require("zod");
7157
7157
  __require("crypto");
@@ -62827,6 +62827,12 @@ var require_dist_D98erEr = __commonJS({
62827
62827
  instanceId: zod.z.string(),
62828
62828
  cameraStableId: zod.z.string(),
62829
62829
  nodeId: zod.z.string(),
62830
+ /**
62831
+ * False while the node the instance is bound to is not in the cluster
62832
+ * topology (forgotten, renamed, re-paired). The instance is kept; the
62833
+ * operator re-points it with `updateInstance({ targetNodeId })`.
62834
+ */
62835
+ nodeInCluster: zod.z.boolean(),
62830
62836
  profileId: zod.z.string(),
62831
62837
  profileLabel: zod.z.string(),
62832
62838
  name: zod.z.string(),
@@ -62886,6 +62892,8 @@ var require_dist_D98erEr = __commonJS({
62886
62892
  }),
62887
62893
  updateInstance: method(zod.z.object({
62888
62894
  instanceId: zod.z.string().min(1),
62895
+ /** Re-point the instance to another node; the profile must exist there. */
62896
+ targetNodeId: zod.z.string().min(1).max(256).optional(),
62889
62897
  name: zod.z.string().trim().min(1).max(160).optional(),
62890
62898
  executable: zod.z.string().max(1024).optional(),
62891
62899
  args: zod.z.array(zod.z.string().max(2048)).max(64).optional(),
@@ -66058,7 +66066,14 @@ var require_dist_D98erEr = __commonJS({
66058
66066
  col: zod.z.number().int().min(0).max(24),
66059
66067
  rowSpan: zod.z.number().int().min(1).max(24),
66060
66068
  colSpan: zod.z.number().int().min(1).max(24),
66061
- title: zod.z.string().min(1).max(40).optional()
66069
+ title: zod.z.string().min(1).max(40).optional(),
66070
+ /**
66071
+ * How much room a divider takes: `compact` (the default) is a slim rule,
66072
+ * `normal` a full grid row. Other frames ignore it.
66073
+ */
66074
+ size: zod.z.enum(["compact", "normal"]).optional(),
66075
+ /** Keep the title in the layout but draw only the rule. */
66076
+ hideTitle: zod.z.boolean().optional()
66062
66077
  });
66063
66078
  function rectsOverlap(a, b) {
66064
66079
  return a.row < b.row + b.rowSpan && b.row < a.row + a.rowSpan && a.col < b.col + b.colSpan && b.col < a.col + a.colSpan;
@@ -112533,11 +112548,11 @@ ${values.join("\n")}` : `${blockName} :`;
112533
112548
  }
112534
112549
  });
112535
112550
 
112536
- // ../system/dist/device-ca-Cf7XE3OX.js
112537
- var require_device_ca_Cf7XE3OX = __commonJS({
112538
- "../system/dist/device-ca-Cf7XE3OX.js"(exports) {
112551
+ // ../system/dist/device-ca-COyWgERw.js
112552
+ var require_device_ca_COyWgERw = __commonJS({
112553
+ "../system/dist/device-ca-COyWgERw.js"(exports) {
112539
112554
  "use strict";
112540
- var require_dist10 = require_dist_D98erEr();
112555
+ var require_dist10 = require_dist_BVrPqzF0();
112541
112556
  var require_x509_es = require_x509_es_WtnRNyYS();
112542
112557
  var node_crypto = __require("crypto");
112543
112558
  var SYSTEM_SECRET_OWNER = "system";
@@ -112996,7 +113011,7 @@ var require_alerts_addon = __commonJS({
112996
113011
  [Symbol.toStringTag]: { value: "Module" }
112997
113012
  });
112998
113013
  require_chunk_Cek0wNdY();
112999
- var require_dist10 = require_dist_D98erEr();
113014
+ var require_dist10 = require_dist_BVrPqzF0();
113000
113015
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
113001
113016
  function nextEpisodeId(input) {
113002
113017
  const { previous } = input;
@@ -114464,7 +114479,7 @@ var require_calendars_addon = __commonJS({
114464
114479
  [Symbol.toStringTag]: { value: "Module" }
114465
114480
  });
114466
114481
  require_chunk_Cek0wNdY();
114467
- var require_dist10 = require_dist_D98erEr();
114482
+ var require_dist10 = require_dist_BVrPqzF0();
114468
114483
  var require_settle_sources = require_settle_sources_Bhsy57y();
114469
114484
  var zod = __require("zod");
114470
114485
  var CALENDAR_COLOR_PALETTE = [
@@ -114680,11 +114695,11 @@ var require_calendars = __commonJS({
114680
114695
  }
114681
114696
  });
114682
114697
 
114683
- // ../system/dist/composition-sources-9_KhSHJA.js
114684
- var require_composition_sources_9_KhSHJA = __commonJS({
114685
- "../system/dist/composition-sources-9_KhSHJA.js"(exports) {
114698
+ // ../system/dist/composition-sources-Ccap9mGu.js
114699
+ var require_composition_sources_Ccap9mGu = __commonJS({
114700
+ "../system/dist/composition-sources-Ccap9mGu.js"(exports) {
114686
114701
  "use strict";
114687
- var require_dist10 = require_dist_D98erEr();
114702
+ var require_dist10 = require_dist_BVrPqzF0();
114688
114703
  function normalizedNativeOwner(owner) {
114689
114704
  const trimmed = owner?.trim();
114690
114705
  return trimmed ? trimmed : null;
@@ -114792,11 +114807,11 @@ var require_composition_sources_9_KhSHJA = __commonJS({
114792
114807
  }
114793
114808
  });
114794
114809
 
114795
- // ../system/dist/retention-BAdXMSlp.js
114796
- var require_retention_BAdXMSlp = __commonJS({
114797
- "../system/dist/retention-BAdXMSlp.js"(exports) {
114810
+ // ../system/dist/retention-Br19kWJ2.js
114811
+ var require_retention_Br19kWJ2 = __commonJS({
114812
+ "../system/dist/retention-Br19kWJ2.js"(exports) {
114798
114813
  "use strict";
114799
- var require_dist10 = require_dist_D98erEr();
114814
+ var require_dist10 = require_dist_BVrPqzF0();
114800
114815
  function subjectIdOf(subject) {
114801
114816
  return subject.kind === "device" ? `d:${subject.deviceId}` : `n:${subject.nodeId}`;
114802
114817
  }
@@ -115734,9 +115749,9 @@ var require_composer_addon = __commonJS({
115734
115749
  [Symbol.toStringTag]: { value: "Module" }
115735
115750
  });
115736
115751
  require_chunk_Cek0wNdY();
115737
- var require_dist10 = require_dist_D98erEr();
115738
- var require_composition_sources = require_composition_sources_9_KhSHJA();
115739
- var require_retention = require_retention_BAdXMSlp();
115752
+ var require_dist10 = require_dist_BVrPqzF0();
115753
+ var require_composition_sources = require_composition_sources_Ccap9mGu();
115754
+ var require_retention = require_retention_Br19kWJ2();
115740
115755
  var zod = __require("zod");
115741
115756
  function isRecord$4(value) {
115742
115757
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -121450,7 +121465,7 @@ var require_console_logging = __commonJS({
121450
121465
  [Symbol.toStringTag]: { value: "Module" }
121451
121466
  });
121452
121467
  require_chunk_Cek0wNdY();
121453
- var require_dist10 = require_dist_D98erEr();
121468
+ var require_dist10 = require_dist_BVrPqzF0();
121454
121469
  var require_formatter = require_formatter_xbxeV4GD();
121455
121470
  var LEVEL_RANK = {
121456
121471
  debug: 0,
@@ -127504,9 +127519,9 @@ var require_core_blocks_addon = __commonJS({
127504
127519
  "use strict";
127505
127520
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
127506
127521
  var require_chunk = require_chunk_Cek0wNdY();
127507
- var require_dist10 = require_dist_D98erEr();
127522
+ var require_dist10 = require_dist_BVrPqzF0();
127508
127523
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
127509
- var require_composition_sources = require_composition_sources_9_KhSHJA();
127524
+ var require_composition_sources = require_composition_sources_Ccap9mGu();
127510
127525
  var require_prefix_range = require_prefix_range_D_KueXbQ();
127511
127526
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
127512
127527
  var zod = __require("zod");
@@ -142956,7 +142971,7 @@ var require_device_manager_addon = __commonJS({
142956
142971
  [Symbol.toStringTag]: { value: "Module" }
142957
142972
  });
142958
142973
  require_chunk_Cek0wNdY();
142959
- var require_dist10 = require_dist_D98erEr();
142974
+ var require_dist10 = require_dist_BVrPqzF0();
142960
142975
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
142961
142976
  var require_prefix_range = require_prefix_range_D_KueXbQ();
142962
142977
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
@@ -152224,7 +152239,7 @@ var require_hub_forwarder = __commonJS({
152224
152239
  [Symbol.toStringTag]: { value: "Module" }
152225
152240
  });
152226
152241
  require_chunk_Cek0wNdY();
152227
- var require_dist10 = require_dist_D98erEr();
152242
+ var require_dist10 = require_dist_BVrPqzF0();
152228
152243
  var require_formatter = require_formatter_xbxeV4GD();
152229
152244
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
152230
152245
  var HubForwarderDestination = class {
@@ -152358,7 +152373,7 @@ var require_liveness_monitor_addon = __commonJS({
152358
152373
  "use strict";
152359
152374
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
152360
152375
  var require_chunk = require_chunk_Cek0wNdY();
152361
- var require_dist10 = require_dist_D98erEr();
152376
+ var require_dist10 = require_dist_BVrPqzF0();
152362
152377
  var node_fs_promises = __require("fs/promises");
152363
152378
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
152364
152379
  var node_path = __require("path");
@@ -152877,10 +152892,10 @@ var require_local_auth_addon = __commonJS({
152877
152892
  [Symbol.toStringTag]: { value: "Module" }
152878
152893
  });
152879
152894
  var require_chunk = require_chunk_Cek0wNdY();
152880
- var require_dist10 = require_dist_D98erEr();
152895
+ var require_dist10 = require_dist_BVrPqzF0();
152881
152896
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
152882
152897
  var require_ms$1 = require_ms_LB3giGoU();
152883
- var require_device_ca = require_device_ca_Cf7XE3OX();
152898
+ var require_device_ca = require_device_ca_COyWgERw();
152884
152899
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
152885
152900
  var zod = __require("zod");
152886
152901
  var node_crypto = __require("crypto");
@@ -163275,7 +163290,7 @@ var require_loki_logging = __commonJS({
163275
163290
  [Symbol.toStringTag]: { value: "Module" }
163276
163291
  });
163277
163292
  require_chunk_Cek0wNdY();
163278
- var require_dist10 = require_dist_D98erEr();
163293
+ var require_dist10 = require_dist_BVrPqzF0();
163279
163294
  var require_log_read_filter = require_log_read_filter_Bi066jwU();
163280
163295
  var zod = __require("zod");
163281
163296
  function sanitizeLabelName(raw) {
@@ -164236,7 +164251,7 @@ var require_native_metrics_addon = __commonJS({
164236
164251
  [Symbol.toStringTag]: { value: "Module" }
164237
164252
  });
164238
164253
  var require_chunk = require_chunk_Cek0wNdY();
164239
- var require_dist10 = require_dist_D98erEr();
164254
+ var require_dist10 = require_dist_BVrPqzF0();
164240
164255
  var node_fs_promises = __require("fs/promises");
164241
164256
  var node_util = __require("util");
164242
164257
  var node_os = __require("os");
@@ -166856,8 +166871,8 @@ var require_device_history_addon = __commonJS({
166856
166871
  [Symbol.toStringTag]: { value: "Module" }
166857
166872
  });
166858
166873
  require_chunk_Cek0wNdY();
166859
- var require_dist10 = require_dist_D98erEr();
166860
- var require_retention = require_retention_BAdXMSlp();
166874
+ var require_dist10 = require_dist_BVrPqzF0();
166875
+ var require_retention = require_retention_Br19kWJ2();
166861
166876
  var FLUSH_INTERVAL_MS = 1e4;
166862
166877
  var MAX_BUFFER_AGE_MS = 6e4;
166863
166878
  var MAX_QUEUE_POINTS = 1e5;
@@ -169578,7 +169593,7 @@ var require_filesystem_storage_addon = __commonJS({
169578
169593
  [Symbol.toStringTag]: { value: "Module" }
169579
169594
  });
169580
169595
  var require_chunk = require_chunk_Cek0wNdY();
169581
- var require_dist10 = require_dist_D98erEr();
169596
+ var require_dist10 = require_dist_BVrPqzF0();
169582
169597
  var node_crypto = __require("crypto");
169583
169598
  var node_fs_promises = __require("fs/promises");
169584
169599
  var node_path = __require("path");
@@ -170688,12 +170703,12 @@ var require_lib = __commonJS({
170688
170703
  }
170689
170704
  });
170690
170705
 
170691
- // ../system/dist/wal-maintenance-yGa7IMrk.js
170692
- var require_wal_maintenance_yGa7IMrk = __commonJS({
170693
- "../system/dist/wal-maintenance-yGa7IMrk.js"(exports) {
170706
+ // ../system/dist/wal-maintenance-Cfc56oIs.js
170707
+ var require_wal_maintenance_Cfc56oIs = __commonJS({
170708
+ "../system/dist/wal-maintenance-Cfc56oIs.js"(exports) {
170694
170709
  "use strict";
170695
170710
  var require_chunk = require_chunk_Cek0wNdY();
170696
- var require_dist10 = require_dist_D98erEr();
170711
+ var require_dist10 = require_dist_BVrPqzF0();
170697
170712
  var require_prefix_range = require_prefix_range_D_KueXbQ();
170698
170713
  var node_crypto = __require("crypto");
170699
170714
  var node_module = __require("module");
@@ -174931,9 +174946,9 @@ var require_sqlite_settings_addon = __commonJS({
174931
174946
  [Symbol.toStringTag]: { value: "Module" }
174932
174947
  });
174933
174948
  var require_chunk = require_chunk_Cek0wNdY();
174934
- var require_dist10 = require_dist_D98erEr();
174949
+ var require_dist10 = require_dist_BVrPqzF0();
174935
174950
  var require_retired_settings_keys = require_retired_settings_keys_DorakcxV();
174936
- var require_wal_maintenance = require_wal_maintenance_yGa7IMrk();
174951
+ var require_wal_maintenance = require_wal_maintenance_Cfc56oIs();
174937
174952
  var require_sqlite_snapshot_protocol = require_sqlite_snapshot_protocol_B9OndqZv();
174938
174953
  var require_wal_checkpoint_policy = require_wal_checkpoint_policy_d2z2qPqx();
174939
174954
  var node_fs_promises = __require("fs/promises");
@@ -176903,7 +176918,7 @@ var require_sqlite_storage = __commonJS({
176903
176918
  });
176904
176919
  require_chunk_Cek0wNdY();
176905
176920
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
176906
- var require_wal_maintenance = require_wal_maintenance_yGa7IMrk();
176921
+ var require_wal_maintenance = require_wal_maintenance_Cfc56oIs();
176907
176922
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
176908
176923
  var ConfigStore = class {
176909
176924
  db;
@@ -176987,7 +177002,7 @@ var require_storage_orchestrator_addon = __commonJS({
176987
177002
  [Symbol.toStringTag]: { value: "Module" }
176988
177003
  });
176989
177004
  var require_chunk = require_chunk_Cek0wNdY();
176990
- var require_dist10 = require_dist_D98erEr();
177005
+ var require_dist10 = require_dist_BVrPqzF0();
176991
177006
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
176992
177007
  var zod = __require("zod");
176993
177008
  var node_crypto = __require("crypto");
@@ -181574,12 +181589,12 @@ var require_storage_orchestrator = __commonJS({
181574
181589
  }
181575
181590
  });
181576
181591
 
181577
- // ../system/dist/agent-https-D_yo1pzU.js
181578
- var require_agent_https_D_yo1pzU = __commonJS({
181579
- "../system/dist/agent-https-D_yo1pzU.js"(exports) {
181592
+ // ../system/dist/agent-https-BrFzCJ3T.js
181593
+ var require_agent_https_BrFzCJ3T = __commonJS({
181594
+ "../system/dist/agent-https-BrFzCJ3T.js"(exports) {
181580
181595
  "use strict";
181581
181596
  var require_chunk = require_chunk_Cek0wNdY();
181582
- var require_dist10 = require_dist_D98erEr();
181597
+ var require_dist10 = require_dist_BVrPqzF0();
181583
181598
  var require_x509_es = require_x509_es_WtnRNyYS();
181584
181599
  var zod = __require("zod");
181585
181600
  var node_crypto = __require("crypto");
@@ -182634,11 +182649,11 @@ var require_agent_https_D_yo1pzU = __commonJS({
182634
182649
  }
182635
182650
  });
182636
182651
 
182637
- // ../system/dist/constants-D-InfVew.js
182638
- var require_constants_D_InfVew = __commonJS({
182639
- "../system/dist/constants-D-InfVew.js"(exports) {
182652
+ // ../system/dist/constants-BA4NvR5U.js
182653
+ var require_constants_BA4NvR5U = __commonJS({
182654
+ "../system/dist/constants-BA4NvR5U.js"(exports) {
182640
182655
  "use strict";
182641
- var MESH_CA_KEY_REF = require_dist_D98erEr().buildSecretRef({
182656
+ var MESH_CA_KEY_REF = require_dist_BVrPqzF0().buildSecretRef({
182642
182657
  owner: "system",
182643
182658
  scope: { kind: "global" },
182644
182659
  fieldPath: "meshCaKey"
@@ -199735,11 +199750,11 @@ var require_system_config_addon = __commonJS({
199735
199750
  [Symbol.toStringTag]: { value: "Module" }
199736
199751
  });
199737
199752
  var require_chunk = require_chunk_Cek0wNdY();
199738
- var require_dist10 = require_dist_D98erEr();
199753
+ var require_dist10 = require_dist_BVrPqzF0();
199739
199754
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
199740
199755
  var require_x509_es = require_x509_es_WtnRNyYS();
199741
- var require_agent_https = require_agent_https_D_yo1pzU();
199742
- var require_constants$8 = require_constants_D_InfVew();
199756
+ var require_agent_https = require_agent_https_BrFzCJ3T();
199757
+ var require_constants$8 = require_constants_BA4NvR5U();
199743
199758
  var require_src$1 = require_src_CnAqG6EF();
199744
199759
  var zod = __require("zod");
199745
199760
  var node_crypto = __require("crypto");
@@ -281015,7 +281030,7 @@ var require_winston_logging = __commonJS({
281015
281030
  [Symbol.toStringTag]: { value: "Module" }
281016
281031
  });
281017
281032
  var require_chunk = require_chunk_Cek0wNdY();
281018
- var require_dist10 = require_dist_D98erEr();
281033
+ var require_dist10 = require_dist_BVrPqzF0();
281019
281034
  var require_formatter = require_formatter_xbxeV4GD();
281020
281035
  var require_log_file_read_protocol = require_log_file_read_protocol_D0WcLbIV();
281021
281036
  var node_path = __require("path");
@@ -288863,14 +288878,14 @@ var require_lib_C3U1yluo = __commonJS({
288863
288878
  }
288864
288879
  });
288865
288880
 
288866
- // ../system/dist/hub-mdns-ciao-D9zaAZDx.js
288867
- var require_hub_mdns_ciao_D9zaAZDx = __commonJS({
288868
- "../system/dist/hub-mdns-ciao-D9zaAZDx.js"(exports) {
288881
+ // ../system/dist/hub-mdns-ciao-UNHb4yo0.js
288882
+ var require_hub_mdns_ciao_UNHb4yo0 = __commonJS({
288883
+ "../system/dist/hub-mdns-ciao-UNHb4yo0.js"(exports) {
288869
288884
  "use strict";
288870
288885
  var require_chunk = require_chunk_Cek0wNdY();
288871
- var require_dist10 = require_dist_D98erEr();
288886
+ var require_dist10 = require_dist_BVrPqzF0();
288872
288887
  var require_x509_es = require_x509_es_WtnRNyYS();
288873
- var require_agent_https = require_agent_https_D_yo1pzU();
288888
+ var require_agent_https = require_agent_https_BrFzCJ3T();
288874
288889
  var node_crypto = __require("crypto");
288875
288890
  var _camstack_types_node = require_node();
288876
288891
  var node_fs_promises = __require("fs/promises");
@@ -289882,12 +289897,12 @@ var require_platform_key_source_Di7HrlGR = __commonJS({
289882
289897
  }
289883
289898
  });
289884
289899
 
289885
- // ../system/dist/restore-archive-TQEVsdCY.js
289886
- var require_restore_archive_TQEVsdCY = __commonJS({
289887
- "../system/dist/restore-archive-TQEVsdCY.js"(exports) {
289900
+ // ../system/dist/restore-archive-BS86n89m.js
289901
+ var require_restore_archive_BS86n89m = __commonJS({
289902
+ "../system/dist/restore-archive-BS86n89m.js"(exports) {
289888
289903
  "use strict";
289889
289904
  var require_chunk = require_chunk_Cek0wNdY();
289890
- var require_dist10 = require_dist_D98erEr();
289905
+ var require_dist10 = require_dist_BVrPqzF0();
289891
289906
  var zod = __require("zod");
289892
289907
  var node_crypto = __require("crypto");
289893
289908
  var buffer = __require("buffer");
@@ -301184,8 +301199,8 @@ var require_dist3 = __commonJS({
301184
301199
  "use strict";
301185
301200
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
301186
301201
  var require_chunk = require_chunk_Cek0wNdY();
301187
- var require_dist10 = require_dist_D98erEr();
301188
- var require_device_ca = require_device_ca_Cf7XE3OX();
301202
+ var require_dist10 = require_dist_BVrPqzF0();
301203
+ var require_device_ca = require_device_ca_COyWgERw();
301189
301204
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
301190
301205
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
301191
301206
  require_alerts();
@@ -301210,21 +301225,21 @@ var require_dist3 = __commonJS({
301210
301225
  var require_builtins_native_metrics_native_metrics_addon = require_native_metrics_addon();
301211
301226
  var require_builtins_device_history_device_history_addon = require_device_history_addon();
301212
301227
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
301213
- var require_wal_maintenance = require_wal_maintenance_yGa7IMrk();
301228
+ var require_wal_maintenance = require_wal_maintenance_Cfc56oIs();
301214
301229
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
301215
301230
  var require_builtins_sqlite_storage_index = require_sqlite_storage();
301216
301231
  var require_builtins_storage_orchestrator_storage_orchestrator_addon = require_storage_orchestrator_addon();
301217
301232
  require_storage_orchestrator();
301218
- var require_agent_https = require_agent_https_D_yo1pzU();
301219
- var require_constants15 = require_constants_D_InfVew();
301233
+ var require_agent_https = require_agent_https_BrFzCJ3T();
301234
+ var require_constants15 = require_constants_BA4NvR5U();
301220
301235
  var require_builtins_system_config_system_config_addon = require_system_config_addon();
301221
301236
  require_system_config();
301222
301237
  var require_builtins_winston_logging_index = require_winston_logging();
301223
301238
  var require_custom_action_registry = require_custom_action_registry_JuMHGKEc();
301224
- var require_hub_mdns_ciao = require_hub_mdns_ciao_D9zaAZDx();
301239
+ var require_hub_mdns_ciao = require_hub_mdns_ciao_UNHb4yo0();
301225
301240
  var require_platform_key_source = require_platform_key_source_Di7HrlGR();
301226
301241
  var require_resource_monitor = require_resource_monitor_mio9dSCT();
301227
- var require_restore_archive = require_restore_archive_TQEVsdCY();
301242
+ var require_restore_archive = require_restore_archive_BS86n89m();
301228
301243
  var require_coral_accelerators = require_coral_accelerators_BSVsBVzt();
301229
301244
  var zod = __require("zod");
301230
301245
  var node_crypto = __require("crypto");
@@ -305690,7 +305705,8 @@ var require_dist3 = __commonJS({
305690
305705
  isHub: zod.z.boolean(),
305691
305706
  localIps: zod.z.array(zod.z.string()).readonly(),
305692
305707
  addonIds: zod.z.array(zod.z.string()).readonly(),
305693
- packages: zod.z.array(NodePackageEntrySchema).readonly().optional()
305708
+ packages: zod.z.array(NodePackageEntrySchema).readonly().optional(),
305709
+ tombstones: zod.z.array(zod.z.string()).readonly().optional()
305694
305710
  });
305695
305711
  var ClusterNodeHistoryBodySchema = zod.z.object({ descriptor: _clusterNodeDescriptorBody });
305696
305712
  var clusterNodeHistoryKindSpec = {
@@ -313384,7 +313400,8 @@ var require_dist3 = __commonJS({
313384
313400
  "auth.tokenExpiry",
313385
313401
  "auth.accessTokenExpiry",
313386
313402
  "auth.trustedProxies",
313387
- "auth.trustedNetworks"
313403
+ "auth.trustedNetworks",
313404
+ "addons.convergence.dryRun"
313388
313405
  ];
313389
313406
  function isPreloadedSystemKey(key) {
313390
313407
  if (PRELOADED_SYSTEM_KEYS.includes(key)) return true;
@@ -379182,6 +379199,12 @@ ${eventLeaves.map(({ leaf, path }) => emitLeaf(leaf, path)).join("\n\n")}
379182
379199
  instanceId: zod.z.string(),
379183
379200
  cameraStableId: zod.z.string(),
379184
379201
  nodeId: zod.z.string(),
379202
+ /**
379203
+ * False while the node the instance is bound to is not in the cluster
379204
+ * topology (forgotten, renamed, re-paired). The instance is kept; the
379205
+ * operator re-points it with `updateInstance({ targetNodeId })`.
379206
+ */
379207
+ nodeInCluster: zod.z.boolean(),
379185
379208
  profileId: zod.z.string(),
379186
379209
  profileLabel: zod.z.string(),
379187
379210
  name: zod.z.string(),
@@ -379241,6 +379264,8 @@ ${eventLeaves.map(({ leaf, path }) => emitLeaf(leaf, path)).join("\n\n")}
379241
379264
  }),
379242
379265
  updateInstance: require_sleep.method(zod.z.object({
379243
379266
  instanceId: zod.z.string().min(1),
379267
+ /** Re-point the instance to another node; the profile must exist there. */
379268
+ targetNodeId: zod.z.string().min(1).max(256).optional(),
379244
379269
  name: zod.z.string().trim().min(1).max(160).optional(),
379245
379270
  executable: zod.z.string().max(1024).optional(),
379246
379271
  args: zod.z.array(zod.z.string().max(2048)).max(64).optional(),
@@ -382515,7 +382540,14 @@ ${eventLeaves.map(({ leaf, path }) => emitLeaf(leaf, path)).join("\n\n")}
382515
382540
  col: zod.z.number().int().min(0).max(24),
382516
382541
  rowSpan: zod.z.number().int().min(1).max(24),
382517
382542
  colSpan: zod.z.number().int().min(1).max(24),
382518
- title: zod.z.string().min(1).max(40).optional()
382543
+ title: zod.z.string().min(1).max(40).optional(),
382544
+ /**
382545
+ * How much room a divider takes: `compact` (the default) is a slim rule,
382546
+ * `normal` a full grid row. Other frames ignore it.
382547
+ */
382548
+ size: zod.z.enum(["compact", "normal"]).optional(),
382549
+ /** Keep the title in the layout but draw only the rule. */
382550
+ hideTitle: zod.z.boolean().optional()
382519
382551
  });
382520
382552
  function rectsOverlap(a, b) {
382521
382553
  return a.row < b.row + b.rowSpan && b.row < a.row + a.rowSpan && a.col < b.col + b.colSpan && b.col < a.col + a.colSpan;
@@ -438029,6 +438061,69 @@ var require_agent_mesh_gate = __commonJS({
438029
438061
  }
438030
438062
  });
438031
438063
 
438064
+ // ../../server/backend/dist/agent/agent-shutdown.js
438065
+ var require_agent_shutdown = __commonJS({
438066
+ "../../server/backend/dist/agent/agent-shutdown.js"(exports) {
438067
+ "use strict";
438068
+ Object.defineProperty(exports, "__esModule", { value: true });
438069
+ exports.SHUTDOWN_DEADLINE_MS = exports.SHUTDOWN_STEP_TIMEOUT_MS = void 0;
438070
+ exports.createAgentShutdown = createAgentShutdown;
438071
+ exports.SHUTDOWN_STEP_TIMEOUT_MS = 5e3;
438072
+ exports.SHUTDOWN_DEADLINE_MS = 15e3;
438073
+ function errMessage(err) {
438074
+ return err instanceof Error ? err.message : String(err);
438075
+ }
438076
+ async function runBounded(step, timeoutMs) {
438077
+ let timer;
438078
+ const timeout = new Promise((resolve) => {
438079
+ timer = setTimeout(() => resolve("timed-out"), timeoutMs);
438080
+ });
438081
+ try {
438082
+ return await Promise.race([step.run().then(() => "done"), timeout]);
438083
+ } finally {
438084
+ clearTimeout(timer);
438085
+ }
438086
+ }
438087
+ function createAgentShutdown(deps) {
438088
+ const stepTimeoutMs = deps.stepTimeoutMs ?? exports.SHUTDOWN_STEP_TIMEOUT_MS;
438089
+ const deadlineMs = deps.deadlineMs ?? exports.SHUTDOWN_DEADLINE_MS;
438090
+ const { logger } = deps;
438091
+ let startedAt = null;
438092
+ return async (reason) => {
438093
+ if (startedAt !== null) {
438094
+ logger.error(`shutdown already in progress for ${Date.now() - startedAt}ms; ${reason} forces exit`);
438095
+ deps.exit(1);
438096
+ return;
438097
+ }
438098
+ const began = Date.now();
438099
+ startedAt = began;
438100
+ logger.info(`shutdown started by ${reason}`);
438101
+ const deadline = setTimeout(() => {
438102
+ logger.error(`shutdown deadline of ${deadlineMs}ms exceeded (started by ${reason}); exiting 1`);
438103
+ deps.exit(1);
438104
+ }, deadlineMs);
438105
+ for (const step of deps.steps) {
438106
+ const stepStart = Date.now();
438107
+ try {
438108
+ const outcome = await runBounded(step, stepTimeoutMs);
438109
+ const took = `${Date.now() - stepStart}ms`;
438110
+ if (outcome === "timed-out") {
438111
+ logger.warn(`shutdown step ${step.name} timed out after ${took}; continuing`);
438112
+ } else {
438113
+ logger.info(`shutdown step ${step.name} done in ${took}`);
438114
+ }
438115
+ } catch (err) {
438116
+ logger.warn(`shutdown step ${step.name} failed after ${Date.now() - stepStart}ms: ${errMessage(err)}`);
438117
+ }
438118
+ }
438119
+ clearTimeout(deadline);
438120
+ logger.info(`shutdown finished in ${Date.now() - began}ms (started by ${reason})`);
438121
+ deps.exit(0);
438122
+ };
438123
+ }
438124
+ }
438125
+ });
438126
+
438032
438127
  // ../../server/backend/dist/agent/agent-pairing-local.js
438033
438128
  var require_agent_pairing_local = __commonJS({
438034
438129
  "../../server/backend/dist/agent/agent-pairing-local.js"(exports) {
@@ -574919,6 +575014,7 @@ var require_main2 = __commonJS({
574919
575014
  var agent_addon_updates_js_1 = require_agent_addon_updates();
574920
575015
  var agent_hub_registration_js_1 = require_agent_hub_registration();
574921
575016
  var agent_mesh_gate_js_1 = require_agent_mesh_gate();
575017
+ var agent_shutdown_js_1 = require_agent_shutdown();
574922
575018
  var agent_pairing_local_js_1 = require_agent_pairing_local();
574923
575019
  var agent_membership_watch_js_1 = require_agent_membership_watch();
574924
575020
  var agent_pairing_store_js_1 = require_agent_pairing_store();
@@ -575647,26 +575743,41 @@ var require_main2 = __commonJS({
575647
575743
  }).then((listener) => {
575648
575744
  agentHttpListener = listener;
575649
575745
  });
575650
- const shutdown = async () => {
575651
- console.log("[Agent] Shutting down...");
575652
- registerAbortController.abort();
575653
- udsEventBridgeDispose?.();
575654
- udsEventBridgeDispose = null;
575655
- for (const [, entry] of loadedAddons) {
575656
- if (entry.addon?.shutdown) {
575657
- try {
575658
- await entry.addon.shutdown();
575659
- } catch {
575746
+ const shutdown = (0, agent_shutdown_js_1.createAgentShutdown)({
575747
+ logger: consoleLogger,
575748
+ exit: (code) => process.exit(code),
575749
+ steps: [
575750
+ {
575751
+ name: "abort-registration",
575752
+ run: async () => {
575753
+ registerAbortController.abort();
575754
+ udsEventBridgeDispose?.();
575755
+ udsEventBridgeDispose = null;
575660
575756
  }
575661
- }
575662
- }
575663
- await agentMdns.stop().catch(() => void 0);
575664
- await agentHttpListener?.close().catch(() => void 0);
575665
- await broker.stop();
575666
- process.exit(0);
575667
- };
575668
- process.on("SIGTERM", shutdown);
575669
- process.on("SIGINT", shutdown);
575757
+ },
575758
+ {
575759
+ name: "addons",
575760
+ // Concurrent: one slow addon must not eat the budget of the others.
575761
+ run: async () => {
575762
+ const stops = [];
575763
+ for (const [addonId, entry] of loadedAddons) {
575764
+ if (!entry.addon?.shutdown)
575765
+ continue;
575766
+ const began = Date.now();
575767
+ stops.push(Promise.resolve().then(() => entry.addon?.shutdown?.()).then(() => void 0).catch((err) => consoleLogger.warn(`addon ${addonId} shutdown failed: ${(0, types_2.errMsg)(err)}`)).finally(() => consoleLogger.info(`addon ${addonId} shutdown took ${Date.now() - began}ms`)));
575768
+ }
575769
+ await Promise.all(stops);
575770
+ }
575771
+ },
575772
+ // Withdraw the mDNS advertisement, then stop accepting new HTTP/HTTPS connections before
575773
+ // the broker goes away.
575774
+ { name: "mdns", run: () => agentMdns.stop() },
575775
+ { name: "http-listener", run: async () => void await agentHttpListener?.close() },
575776
+ { name: "broker", run: () => broker.stop() }
575777
+ ]
575778
+ });
575779
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
575780
+ process.on("SIGINT", () => void shutdown("SIGINT"));
575670
575781
  if (!addonsBooted) {
575671
575782
  console.log(`[Agent] Node "${config.nodeId}" is unpaired - serving pairing only; addons boot after pairing`);
575672
575783
  return;
@@ -592486,6 +592597,14 @@ var require_addon_package_service = __commonJS({
592486
592597
  fs.rmSync(destDir, { recursive: true, force: true });
592487
592598
  }
592488
592599
  }
592600
+ /**
592601
+ * The version the registry serves as `latest` for a package — what the hub
592602
+ * follows for an agent-only package it tracks without installing (D982).
592603
+ * Throws when the registry cannot answer.
592604
+ */
592605
+ async resolveRegistryVersion(name) {
592606
+ return resolveNpmVersion(name, "latest", process.env["CAMSTACK_NPM_REGISTRY"]);
592607
+ }
592489
592608
  /**
592490
592609
  * Resolve `name@version` and `npm pack` it into a tarball buffer
592491
592610
  * WITHOUT installing. Used to push a package update to an agent: the
@@ -598291,7 +598410,7 @@ var require_addon_upload = __commonJS({
598291
598410
  bytes: staged.bytes
598292
598411
  };
598293
598412
  }
598294
- async function registerAddonUploadRoute(fastify, addonBridge, authService, moleculer, addonRegistry, addonPackageService, logger, resolveClusterBaseUrl, deliveryLedger) {
598413
+ async function registerAddonUploadRoute(fastify, addonBridge, authService, moleculer, addonRegistry, addonPackageService, logger, resolveClusterBaseUrl, deliveryLedger, convergence) {
598295
598414
  await fastify.register(Promise.resolve().then(() => __importStar(require_multipart2())), {
598296
598415
  limits: { fileSize: MAX_UPLOAD_BYTES }
598297
598416
  });
@@ -598322,10 +598441,10 @@ var require_addon_upload = __commonJS({
598322
598441
  });
598323
598442
  }
598324
598443
  if (!nodeId || nodeId === "hub") {
598325
- return installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, data.filename, buffer, resolveClusterBaseUrl, deliveryLedger);
598444
+ return installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, data.filename, buffer, resolveClusterBaseUrl, deliveryLedger, convergence);
598326
598445
  }
598327
598446
  const agentAddonId = addonIdHint ?? manifest.name;
598328
- return deployToAgent(reply, moleculer, logger, nodeId, agentAddonId, buffer, resolveClusterBaseUrl, deliveryLedger);
598447
+ return deployToAgent(reply, moleculer, logger, nodeId, agentAddonId, buffer, resolveClusterBaseUrl, deliveryLedger, convergence);
598329
598448
  });
598330
598449
  }
598331
598450
  function packageHasAgentDeployable(addonsDir, packageName) {
@@ -598378,7 +598497,7 @@ var require_addon_upload = __commonJS({
598378
598497
  }
598379
598498
  return results;
598380
598499
  }
598381
- async function installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, filename, buffer, resolveClusterBaseUrl, deliveryLedger) {
598500
+ async function installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, filename, buffer, resolveClusterBaseUrl, deliveryLedger, convergence) {
598382
598501
  const tmpDir = path.join(os.tmpdir(), `camstack-addon-upload-${Date.now()}`);
598383
598502
  fs.mkdirSync(tmpDir, { recursive: true });
598384
598503
  const tgzPath = path.join(tmpDir, filename);
@@ -598453,7 +598572,10 @@ var require_addon_upload = __commonJS({
598453
598572
  logger.info("propagation done", {
598454
598573
  meta: { packageName: result.name, agents: agentResults }
598455
598574
  });
598575
+ void convergence?.requestConvergence("hub-change");
598456
598576
  });
598577
+ } else {
598578
+ void convergence?.requestConvergence("hub-change");
598457
598579
  }
598458
598580
  return reply.send({
598459
598581
  success: true,
@@ -598469,7 +598591,7 @@ var require_addon_upload = __commonJS({
598469
598591
  fs.rmSync(tmpDir, { recursive: true, force: true });
598470
598592
  }
598471
598593
  }
598472
- async function deployToAgent(reply, moleculer, logger, nodeId, addonId, buffer, resolveClusterBaseUrl, deliveryLedger) {
598594
+ async function deployToAgent(reply, moleculer, logger, nodeId, addonId, buffer, resolveClusterBaseUrl, deliveryLedger, convergence) {
598473
598595
  const hubBaseUrl = await resolveBaseUrlOrReason(resolveClusterBaseUrl);
598474
598596
  if (!hubBaseUrl.ok) {
598475
598597
  logger.error("agent deploy: no hub address the agent can reach \u2014 nothing delivered", {
@@ -598488,6 +598610,7 @@ var require_addon_upload = __commonJS({
598488
598610
  if (!result.success) {
598489
598611
  return reply.status(502).send({ error: `Agent deploy failed: ${result.error ?? "unknown"}` });
598490
598612
  }
598613
+ await convergence?.noteExplicitDelivery(nodeId, addonId);
598491
598614
  return reply.send({
598492
598615
  success: true,
598493
598616
  target: nodeId,
@@ -599374,6 +599497,96 @@ var require_addon_uninstall = __commonJS({
599374
599497
  }
599375
599498
  });
599376
599499
 
599500
+ // ../../server/backend/dist/core/agent/agent-desired-set.js
599501
+ var require_agent_desired_set = __commonJS({
599502
+ "../../server/backend/dist/core/agent/agent-desired-set.js"(exports) {
599503
+ "use strict";
599504
+ Object.defineProperty(exports, "__esModule", { value: true });
599505
+ exports.AGENT_TRACKED_PACKAGES = void 0;
599506
+ exports.isAgentTrackedPackage = isAgentTrackedPackage;
599507
+ exports.computeDesiredSet = computeDesiredSet;
599508
+ exports.diffNodeAgainstDesired = diffNodeAgainstDesired;
599509
+ var system_1 = require_dist3();
599510
+ exports.AGENT_TRACKED_PACKAGES = ["@camstack/addon-agent-ui"];
599511
+ function isAgentTrackedPackage(name) {
599512
+ return exports.AGENT_TRACKED_PACKAGES.includes(name);
599513
+ }
599514
+ function computeDesiredSet(input) {
599515
+ const desired = /* @__PURE__ */ new Map();
599516
+ const hubOnly = /* @__PURE__ */ new Set();
599517
+ const excluded = [];
599518
+ for (const [name, fact] of input.hubPackages) {
599519
+ if (input.bootstrap.has(name))
599520
+ continue;
599521
+ if (fact.placements.length > 0 && fact.placements.every((p) => p === "hub-only")) {
599522
+ hubOnly.add(name);
599523
+ excluded.push({ name, reason: "hub-only placement" });
599524
+ continue;
599525
+ }
599526
+ if (fact.placements.includes("hub-only")) {
599527
+ excluded.push({ name, reason: "mixes a hub-only addon" });
599528
+ continue;
599529
+ }
599530
+ if (!fact.placements.includes("any-node")) {
599531
+ excluded.push({ name, reason: "placement does not allow any node" });
599532
+ continue;
599533
+ }
599534
+ if (input.tombstones.has(name)) {
599535
+ excluded.push({ name, reason: "tombstoned" });
599536
+ continue;
599537
+ }
599538
+ if (fact.version === null) {
599539
+ excluded.push({ name, reason: "hub version unknown" });
599540
+ continue;
599541
+ }
599542
+ desired.set(name, { name, version: fact.version, origin: "hub-any-node" });
599543
+ }
599544
+ for (const [name, version] of input.trackedForAgents) {
599545
+ if (input.bootstrap.has(name))
599546
+ continue;
599547
+ if (input.tombstones.has(name)) {
599548
+ excluded.push({ name, reason: "tombstoned" });
599549
+ continue;
599550
+ }
599551
+ desired.set(name, { name, version, origin: "agent-tracked" });
599552
+ }
599553
+ return { desired, hubOnly, tombstones: input.tombstones, excluded };
599554
+ }
599555
+ function diffNodeAgainstDesired(input) {
599556
+ const observed = new Map(input.observed.map((p) => [p.name, p.version]));
599557
+ const install = [];
599558
+ const update = [];
599559
+ const remove = [];
599560
+ const retired = [];
599561
+ for (const entry of input.desired.desired.values()) {
599562
+ const present = observed.has(entry.name);
599563
+ const from = present ? observed.get(entry.name) ?? null : null;
599564
+ const decision = (0, system_1.decideAddonVersionMove)(from, entry.version, system_1.ADDON_VERSION_REASON_HUB);
599565
+ if (!decision.move)
599566
+ continue;
599567
+ if (input.retired.has(entry.name)) {
599568
+ retired.push(entry.name);
599569
+ continue;
599570
+ }
599571
+ const target = { ...entry, from };
599572
+ if (present)
599573
+ update.push(target);
599574
+ else
599575
+ install.push(target);
599576
+ }
599577
+ for (const name of observed.keys()) {
599578
+ if (input.bootstrap.has(name))
599579
+ continue;
599580
+ if (input.desired.tombstones.has(name))
599581
+ remove.push({ name, reason: "tombstoned" });
599582
+ else if (input.desired.hubOnly.has(name))
599583
+ remove.push({ name, reason: "hub-only placement" });
599584
+ }
599585
+ return { install, update, remove, retired };
599586
+ }
599587
+ }
599588
+ });
599589
+
599377
599590
  // ../../server/backend/dist/core/updates/agent-installed-packages.js
599378
599591
  var require_agent_installed_packages = __commonJS({
599379
599592
  "../../server/backend/dist/core/updates/agent-installed-packages.js"(exports) {
@@ -601174,6 +601387,7 @@ var require_cap_providers = __commonJS({
601174
601387
  var integration_id_backfill_1 = require_integration_id_backfill();
601175
601388
  var addon_package_service_js_1 = require_addon_package_service();
601176
601389
  var addon_uninstall_js_1 = require_addon_uninstall();
601390
+ var agent_desired_set_1 = require_agent_desired_set();
601177
601391
  var lifecycle_runner_singleton_js_1 = require_lifecycle_runner_singleton();
601178
601392
  var agent_installed_packages_js_1 = require_agent_installed_packages();
601179
601393
  var http_request_census_singleton_js_1 = require_http_request_census_singleton();
@@ -602275,6 +602489,13 @@ var require_cap_providers = __commonJS({
602275
602489
  const broker = (0, system_1.asBrokerLike)(moleculer.broker);
602276
602490
  const uninstallSeam = buildUninstallSeam(ar, ps, broker, agentRegistry);
602277
602491
  const frameworkAllowSet = /* @__PURE__ */ new Set([addon_package_service_js_1.SYSTEM_PACKAGE]);
602492
+ const requestHubChangeConvergence = () => {
602493
+ void agentRegistry?.requestConvergence("hub-change");
602494
+ };
602495
+ const deployPackedToAgent = async (agentNodeId, packageName, bundle) => {
602496
+ await broker.call("$agent.deploy", { addonId: packageName, bundle }, { nodeID: agentNodeId, timeout: 3e5 });
602497
+ await broker.call("$agent.reload", {}, { nodeID: agentNodeId, timeout: 3e5 });
602498
+ };
602278
602499
  const lifecycleRunner = (0, lifecycle_runner_singleton_js_1.getLifecycleRunner)();
602279
602500
  const lifecycleCreatedBy = ctx.user?.username ?? ctx.user?.id ?? "system";
602280
602501
  return {
@@ -602325,13 +602546,23 @@ var require_cap_providers = __commonJS({
602325
602546
  level: input.level
602326
602547
  }),
602327
602548
  listPackages: async () => ps.listInstalled(),
602328
- installPackage: async (input) => ps.installAndLoad(input.packageName, input.version),
602329
- installFromWorkspace: async (input) => ps.installFromWorkspaceAndLoad(input.packageName),
602549
+ installPackage: async (input) => {
602550
+ const result = await ps.installAndLoad(input.packageName, input.version);
602551
+ requestHubChangeConvergence();
602552
+ return result;
602553
+ },
602554
+ installFromWorkspace: async (input) => {
602555
+ const result = await ps.installFromWorkspaceAndLoad(input.packageName);
602556
+ requestHubChangeConvergence();
602557
+ return result;
602558
+ },
602330
602559
  isWorkspaceAvailable: async () => ps.isWorkspaceAvailable(),
602331
602560
  listWorkspacePackages: async () => ps.listWorkspacePackages(),
602332
602561
  uninstallPackage: async (input) => {
602333
602562
  try {
602334
- return await (0, addon_uninstall_js_1.uninstallPackage)(uninstallSeam, input);
602563
+ const result = await (0, addon_uninstall_js_1.uninstallPackage)(uninstallSeam, input);
602564
+ requestHubChangeConvergence();
602565
+ return result;
602335
602566
  } catch (err) {
602336
602567
  if (err instanceof addon_uninstall_js_1.PackageProtectedError) {
602337
602568
  throw new server_1.TRPCError({ code: "FORBIDDEN", message: err.message });
@@ -602356,6 +602587,28 @@ var require_cap_providers = __commonJS({
602356
602587
  },
602357
602588
  updatePackage: async (input) => {
602358
602589
  const nodeId = input.nodeId;
602590
+ if ((0, agent_desired_set_1.isAgentTrackedPackage)(input.name) && (nodeId === void 0 || isHubNode(nodeId))) {
602591
+ if (agentRegistry === void 0) {
602592
+ return {
602593
+ success: false,
602594
+ error: `${input.name} is delivered by the agent registry, which is not wired`
602595
+ };
602596
+ }
602597
+ const packed2 = await ps.packPackage(input.name, input.version);
602598
+ const targets = agentRegistry.onlineAgentIds();
602599
+ const failures = [];
602600
+ for (const target of targets) {
602601
+ try {
602602
+ await deployPackedToAgent(target, input.name, packed2.buffer);
602603
+ await agentRegistry.noteExplicitDelivery(target, input.name);
602604
+ } catch (err) {
602605
+ failures.push(`${target}: ${err instanceof Error ? err.message : String(err)}`);
602606
+ }
602607
+ }
602608
+ if (failures.length > 0)
602609
+ return { success: false, error: failures.join("; ") };
602610
+ return { success: true, name: input.name, version: packed2.version };
602611
+ }
602359
602612
  if (nodeId === void 0 || isHubNode(nodeId)) {
602360
602613
  const version = input.version ?? "latest";
602361
602614
  const { jobId } = await lifecycleRunner.startJob({
@@ -602366,13 +602619,14 @@ var require_cap_providers = __commonJS({
602366
602619
  const job = lifecycleRunner.getJob(jobId);
602367
602620
  const task = job?.tasks[0];
602368
602621
  if (task?.phase === "done") {
602622
+ requestHubChangeConvergence();
602369
602623
  return { success: true, name: input.name, version, jobId };
602370
602624
  }
602371
602625
  return { success: false, error: task?.error ?? "update failed", jobId };
602372
602626
  }
602373
602627
  const packed = await ps.packPackage(input.name, input.version);
602374
- await broker.call("$agent.deploy", { addonId: input.name, bundle: packed.buffer }, { nodeID: nodeId, timeout: 3e5 });
602375
- await broker.call("$agent.reload", {}, { nodeID: nodeId, timeout: 3e5 });
602628
+ await deployPackedToAgent(nodeId, input.name, packed.buffer);
602629
+ await agentRegistry?.noteExplicitDelivery(nodeId, input.name);
602376
602630
  return { success: true, name: input.name, version: packed.version, nodeId };
602377
602631
  },
602378
602632
  rollbackPackage: async (input) => ps.rollbackPackage(input.name),
@@ -618889,92 +619143,18 @@ var require_agent_addon_backfill = __commonJS({
618889
619143
  Object.defineProperty(exports, "__esModule", { value: true });
618890
619144
  exports.BackfillAttemptTracker = exports.MAX_BACKFILL_ATTEMPTS = void 0;
618891
619145
  exports.triggerMayDeliver = triggerMayDeliver;
618892
- exports.freshNodeSkipReason = freshNodeSkipReason;
618893
- exports.computeBackfillPlan = computeBackfillPlan;
618894
619146
  exports.confirmBackfillPlan = confirmBackfillPlan;
618895
619147
  exports.executeBackfill = executeBackfill;
618896
- var system_1 = require_dist3();
618897
619148
  var DELIVERING_TRIGGERS = /* @__PURE__ */ new Set([
618898
619149
  "node-registration",
618899
619150
  "hub-boot",
618900
- "operator-repair"
619151
+ "operator-repair",
619152
+ "periodic",
619153
+ "hub-change"
618901
619154
  ]);
618902
619155
  function triggerMayDeliver(trigger) {
618903
619156
  return DELIVERING_TRIGGERS.has(trigger);
618904
619157
  }
618905
- function freshNodeSkipReason(placements) {
618906
- if (placements.includes("hub-only"))
618907
- return "hub-only placement";
618908
- if (!placements.includes("any-node"))
618909
- return "placement does not allow any node";
618910
- return null;
618911
- }
618912
- function computeBackfillPlan(input) {
618913
- if (input.freshNode !== void 0 && input.recorded.length === 0) {
618914
- return computeFreshNodePlan(input, input.freshNode);
618915
- }
618916
- const liveByName = new Map(input.live.map((p) => [p.name, p.version]));
618917
- const targets = [];
618918
- const retired = [];
618919
- const notOnHub = [];
618920
- for (const entry of input.recorded) {
618921
- if (input.bootstrap.has(entry.name))
618922
- continue;
618923
- if (input.undeployed.has(entry.name))
618924
- continue;
618925
- const hubVersion = input.hubVersions.get(entry.name);
618926
- const present = liveByName.has(entry.name);
618927
- const liveVersion = present ? liveByName.get(entry.name) ?? null : null;
618928
- if (hubVersion === void 0) {
618929
- if (!present && input.retired.has(entry.name))
618930
- retired.push(entry.name);
618931
- else if (!present)
618932
- notOnHub.push(entry.name);
618933
- continue;
618934
- }
618935
- const decision = (0, system_1.decideAddonVersionMove)(present ? liveVersion : null, hubVersion, system_1.ADDON_VERSION_REASON_HUB);
618936
- if (!decision.move)
618937
- continue;
618938
- if (input.retired.has(entry.name)) {
618939
- retired.push(entry.name);
618940
- continue;
618941
- }
618942
- targets.push({ name: entry.name, version: hubVersion, versionSource: "hub-installed" });
618943
- }
618944
- return { targets, retired, notOnHub, liveCount: liveByName.size, skipped: [] };
618945
- }
618946
- function computeFreshNodePlan(input, fresh) {
618947
- const liveByName = new Map(input.live.map((p) => [p.name, p.version]));
618948
- const targets = [];
618949
- const retired = [];
618950
- const skipped = [];
618951
- for (const [name, placements] of fresh.hubPackagePlacements) {
618952
- if (input.bootstrap.has(name))
618953
- continue;
618954
- if (input.undeployed.has(name))
618955
- continue;
618956
- const reason = freshNodeSkipReason(placements);
618957
- if (reason !== null) {
618958
- skipped.push({ name, reason });
618959
- continue;
618960
- }
618961
- const hubVersion = input.hubVersions.get(name);
618962
- if (hubVersion === void 0) {
618963
- skipped.push({ name, reason: "hub version unknown" });
618964
- continue;
618965
- }
618966
- const present = liveByName.has(name);
618967
- const decision = (0, system_1.decideAddonVersionMove)(present ? liveByName.get(name) ?? null : null, hubVersion, system_1.ADDON_VERSION_REASON_HUB);
618968
- if (!decision.move)
618969
- continue;
618970
- if (input.retired.has(name)) {
618971
- retired.push(name);
618972
- continue;
618973
- }
618974
- targets.push({ name, version: hubVersion, versionSource: "hub-installed" });
618975
- }
618976
- return { targets, retired, notOnHub: [], liveCount: liveByName.size, skipped };
618977
- }
618978
619158
  function confirmBackfillPlan(plan, secondRead) {
618979
619159
  const seen = new Map(secondRead.map((p) => [p.name, p.version]));
618980
619160
  const targets = [];
@@ -619061,13 +619241,18 @@ var require_agent_addon_backfill = __commonJS({
619061
619241
  } catch (err) {
619062
619242
  const error = err instanceof Error ? err.message : String(err);
619063
619243
  const { retired } = tracker.recordFailure(nodeId, target.name, target.version, error);
619064
- failed.push({ name: target.name, version: target.version, error, retired });
619244
+ failed.push({
619245
+ name: target.name,
619246
+ version: target.version,
619247
+ error,
619248
+ retired
619249
+ });
619065
619250
  }
619066
619251
  }
619067
619252
  if (delivered.length > 0) {
619068
619253
  await seam.reload(nodeId);
619069
619254
  }
619070
- return { delivered, failed, skipped: plan.retired, notOnHub: plan.notOnHub };
619255
+ return { delivered, failed, skipped: plan.retired };
619071
619256
  }
619072
619257
  }
619073
619258
  });
@@ -619311,7 +619496,7 @@ var require_agent_registry_service = __commonJS({
619311
619496
  };
619312
619497
  })();
619313
619498
  Object.defineProperty(exports, "__esModule", { value: true });
619314
- exports.AgentRegistryService = exports.AGENT_BACKFILL_RPC_TIMEOUT_MS = exports.FORGOTTEN_NODE_REVOCATION_REASON = void 0;
619499
+ exports.AgentRegistryService = exports.ADDON_CONVERGENCE_DRY_RUN_KEY = exports.ADDON_CONVERGENCE_INTERVAL_MS = exports.AGENT_BACKFILL_RPC_TIMEOUT_MS = exports.FORGOTTEN_NODE_REVOCATION_REASON = void 0;
619315
619500
  exports.toNodeLiveness = toNodeLiveness;
619316
619501
  exports.toDescriptor = toDescriptor;
619317
619502
  var node_crypto_1 = __require("crypto");
@@ -619319,6 +619504,7 @@ var require_agent_registry_service = __commonJS({
619319
619504
  var system_1 = require_dist3();
619320
619505
  var types_1 = require_dist4();
619321
619506
  var agent_addon_backfill_1 = require_agent_addon_backfill();
619507
+ var agent_desired_set_1 = require_agent_desired_set();
619322
619508
  var agent_delivery_ledger_1 = require_agent_delivery_ledger();
619323
619509
  var agent_liveness_event_1 = require_agent_liveness_event();
619324
619510
  var cluster_node_history_mirror_1 = require_cluster_node_history_mirror();
@@ -619358,6 +619544,10 @@ var require_agent_registry_service = __commonJS({
619358
619544
  "@camstack/system",
619359
619545
  "@camstack/addon-agent-ui"
619360
619546
  ]);
619547
+ var CONVERGENCE_BOOTSTRAP = /* @__PURE__ */ new Set(["@camstack/system"]);
619548
+ exports.ADDON_CONVERGENCE_INTERVAL_MS = 10 * 6e4;
619549
+ exports.ADDON_CONVERGENCE_DRY_RUN_KEY = "addons.convergence.dryRun";
619550
+ var TRACKED_VERSION_TTL_MS = 5 * 6e4;
619361
619551
  function rosterFromAddons(addons) {
619362
619552
  const byName = /* @__PURE__ */ new Map();
619363
619553
  for (const addon of addons) {
@@ -619470,6 +619660,14 @@ var require_agent_registry_service = __commonJS({
619470
619660
  * diff and each deliver it.
619471
619661
  */
619472
619662
  reconcilingNodes = /* @__PURE__ */ new Set();
619663
+ /** Resolves the version of an agent-tracked package (agent-ui); `null` = not wired. */
619664
+ trackedVersionResolver = null;
619665
+ trackedVersionCache = /* @__PURE__ */ new Map();
619666
+ /** Reads the dry-run switch per round, so flipping the setting needs no restart. */
619667
+ convergenceDryRun = () => true;
619668
+ convergenceTimer = null;
619669
+ convergenceRunning = false;
619670
+ convergenceRerun = null;
619473
619671
  constructor(eventBus, moleculer, capabilityService) {
619474
619672
  this.eventBus = eventBus;
619475
619673
  this.moleculer = moleculer;
@@ -619533,6 +619731,15 @@ var require_agent_registry_service = __commonJS({
619533
619731
  setAddonBackfill(seam) {
619534
619732
  this.addonBackfill = seam;
619535
619733
  }
619734
+ /** Wire how the hub resolves the version of an agent-only tracked package. */
619735
+ setTrackedVersionResolver(resolver) {
619736
+ this.trackedVersionResolver = resolver;
619737
+ this.trackedVersionCache.clear();
619738
+ }
619739
+ /** Wire the dry-run switch; unwired it is ON (D982: the first live run only logs). */
619740
+ setConvergenceDryRun(read) {
619741
+ this.convergenceDryRun = read;
619742
+ }
619536
619743
  /**
619537
619744
  * Delivery failures the breaker is tracking for a node — surfaced so a node
619538
619745
  * that is missing capabilities never merely *looks* healthy.
@@ -619553,9 +619760,18 @@ var require_agent_registry_service = __commonJS({
619553
619760
  const packageName = this.resolvePackageForAddon(addonId);
619554
619761
  if (packageName === null)
619555
619762
  return;
619556
- if (AGENT_BOOTSTRAP_PACKAGES.has(packageName))
619763
+ if (CONVERGENCE_BOOTSTRAP.has(packageName))
619557
619764
  return;
619558
619765
  await this.historyStore?.prunePackages(nodeId, [packageName]);
619766
+ await this.historyStore?.addTombstones(nodeId, [packageName]);
619767
+ }
619768
+ /**
619769
+ * The operator delivered a package to ONE node on purpose (`updatePackage` with
619770
+ * a `nodeId`, `camstack deploy -n`): that lifts its tombstone, so the desired
619771
+ * set wants it there again.
619772
+ */
619773
+ async noteExplicitDelivery(nodeId, packageName) {
619774
+ await this.historyStore?.clearTombstones(nodeId, [packageName]);
619559
619775
  }
619560
619776
  /**
619561
619777
  * The operator removed a whole PACKAGE from a node (`addons.uninstallPackage`
@@ -619566,9 +619782,10 @@ var require_agent_registry_service = __commonJS({
619566
619782
  * stale-addon reconcile does.
619567
619783
  */
619568
619784
  async notePackageUndeployed(nodeId, packageName, addonIds) {
619569
- if (AGENT_BOOTSTRAP_PACKAGES.has(packageName))
619785
+ if (CONVERGENCE_BOOTSTRAP.has(packageName))
619570
619786
  return;
619571
619787
  await this.historyStore?.prunePackages(nodeId, [packageName]);
619788
+ await this.historyStore?.addTombstones(nodeId, [packageName]);
619572
619789
  for (const addonId of addonIds) {
619573
619790
  this.eventBus.emit({
619574
619791
  id: (0, node_crypto_1.randomUUID)(),
@@ -619826,6 +620043,89 @@ var require_agent_registry_service = __commonJS({
619826
620043
  }
619827
620044
  } catch {
619828
620045
  }
620046
+ this.startConvergenceTimer();
620047
+ }
620048
+ /**
620049
+ * The fixed schedule of D982: every online agent is converged to its desired
620050
+ * set every {@link ADDON_CONVERGENCE_INTERVAL_MS}. A round never gates
620051
+ * registration (D3) and a failed delivery waits for the next round — the
620052
+ * per-`(node, package)` breaker bounds the retries, there is no tight loop.
620053
+ */
620054
+ startConvergenceTimer() {
620055
+ if (this.convergenceTimer !== null)
620056
+ return;
620057
+ const timer = setInterval(() => {
620058
+ void this.requestConvergence("periodic");
620059
+ }, exports.ADDON_CONVERGENCE_INTERVAL_MS);
620060
+ timer.unref();
620061
+ this.convergenceTimer = timer;
620062
+ }
620063
+ /** Stop the convergence schedule (shutdown, specs). */
620064
+ stopConvergenceTimer() {
620065
+ if (this.convergenceTimer === null)
620066
+ return;
620067
+ clearInterval(this.convergenceTimer);
620068
+ this.convergenceTimer = null;
620069
+ }
620070
+ /**
620071
+ * Converge every online agent to its desired set. Requests that arrive while a
620072
+ * round runs collapse into ONE more round after it — a burst of hub installs
620073
+ * cannot stack passes, and a round never re-triggers itself.
620074
+ */
620075
+ async requestConvergence(trigger) {
620076
+ if (this.convergenceRunning) {
620077
+ this.convergenceRerun = trigger;
620078
+ return;
620079
+ }
620080
+ this.convergenceRunning = true;
620081
+ try {
620082
+ let next = trigger;
620083
+ while (next !== null) {
620084
+ this.convergenceRerun = null;
620085
+ for (const agentId of this.onlineAgentIds()) {
620086
+ await this.reconcileAgentAddons(agentId, next);
620087
+ }
620088
+ next = this.convergenceRerun;
620089
+ }
620090
+ } finally {
620091
+ this.convergenceRunning = false;
620092
+ }
620093
+ }
620094
+ /** Bare agent ids currently online (workers and the hub excluded). */
620095
+ onlineAgentIds() {
620096
+ const registry = this.nodeCatalogRegistry();
620097
+ const nodes = registry?.getNodeList?.({ onlyAvailable: true }) ?? [];
620098
+ return nodes.map((n) => n.id).filter((id) => typeof id === "string" && id !== "hub" && !id.includes("/"));
620099
+ }
620100
+ /** The version the hub follows for an agent-only tracked package, or `null`. */
620101
+ async resolveTrackedVersions() {
620102
+ const resolver = this.trackedVersionResolver;
620103
+ const out = /* @__PURE__ */ new Map();
620104
+ if (resolver === null)
620105
+ return out;
620106
+ const now = Date.now();
620107
+ for (const name of agent_desired_set_1.AGENT_TRACKED_PACKAGES) {
620108
+ const cached = this.trackedVersionCache.get(name);
620109
+ if (cached !== void 0 && now - cached.at < TRACKED_VERSION_TTL_MS) {
620110
+ out.set(name, cached.version);
620111
+ continue;
620112
+ }
620113
+ try {
620114
+ const version = await resolver(name);
620115
+ if (version === null)
620116
+ continue;
620117
+ this.trackedVersionCache.set(name, { version, at: now });
620118
+ out.set(name, version);
620119
+ } catch (err) {
620120
+ this.logger?.warn("convergence: could not resolve a tracked package version", {
620121
+ meta: {
620122
+ package: name,
620123
+ error: err instanceof Error ? err.message : String(err)
620124
+ }
620125
+ });
620126
+ }
620127
+ }
620128
+ return out;
619829
620129
  }
619830
620130
  /** The durable history's display name for a node, when it knows a real one. */
619831
620131
  knownNodeName(nodeId) {
@@ -619842,9 +620142,7 @@ var require_agent_registry_service = __commonJS({
619842
620142
  * must not abort the pass or hub boot.
619843
620143
  */
619844
620144
  async reconcileConnectedAgents() {
619845
- const registry = this.nodeCatalogRegistry();
619846
- const nodes = registry?.getNodeList?.({ onlyAvailable: true }) ?? [];
619847
- const agentIds = nodes.map((n) => n.id).filter((id) => typeof id === "string" && id !== "hub" && !id.includes("/"));
620145
+ const agentIds = this.onlineAgentIds();
619848
620146
  if (agentIds.length === 0)
619849
620147
  return;
619850
620148
  console.log(`[agent-registry] Boot reconcile: ${agentIds.length} connected agent(s)`);
@@ -619945,7 +620243,10 @@ var require_agent_registry_service = __commonJS({
619945
620243
  }
619946
620244
  }
619947
620245
  }
620246
+ const sweepsStale = trigger !== "periodic" && trigger !== "hub-change";
619948
620247
  const stale = agentAddons.filter((addon) => {
620248
+ if (!sweepsStale)
620249
+ return false;
619949
620250
  if (addon.packageName !== void 0 && AGENT_BOOTSTRAP_PACKAGES.has(addon.packageName)) {
619950
620251
  return false;
619951
620252
  }
@@ -619957,10 +620258,11 @@ var require_agent_registry_service = __commonJS({
619957
620258
  }
619958
620259
  return placement === "hub-only";
619959
620260
  });
619960
- if (stale.length === 0) {
620261
+ if (stale.length === 0 && sweepsStale) {
619961
620262
  console.log(`[agent-registry] Reconcile ${agentId}: no stale addons (${agentAddons.length} checked)`);
619962
620263
  }
619963
620264
  const undeployedPackages = /* @__PURE__ */ new Set();
620265
+ const undeployedIds = /* @__PURE__ */ new Set();
619964
620266
  const survivingIds = new Set(agentAddons.filter((a) => !stale.includes(a)).map((a) => a.id));
619965
620267
  for (const addon of stale) {
619966
620268
  const reason = hubPlacements.has(addon.id) ? "placement is hub-only" : "not installed on hub";
@@ -619969,6 +620271,7 @@ var require_agent_registry_service = __commonJS({
619969
620271
  nodeID: agentId,
619970
620272
  timeout: AGENT_RECONCILE_RPC_TIMEOUT_MS
619971
620273
  });
620274
+ undeployedIds.add(addon.id);
619972
620275
  console.log(`[agent-registry] Reconcile ${agentId}: undeployed stale addon "${addon.id}" (${reason})`);
619973
620276
  const packageName = addon.packageName;
619974
620277
  if (packageName !== void 0 && packageName.length > 0) {
@@ -619987,7 +620290,14 @@ var require_agent_registry_service = __commonJS({
619987
620290
  console.error(`[agent-registry] Reconcile ${agentId}: failed to undeploy "${addon.id}":`, err instanceof Error ? err.message : String(err));
619988
620291
  }
619989
620292
  }
619990
- await this.backfillAgentAddons(agentId, agentAddons, undeployedPackages, hubVersions, hubPackagePlacements, trigger);
620293
+ const hubPackageFacts = /* @__PURE__ */ new Map();
620294
+ for (const [name, placements] of hubPackagePlacements) {
620295
+ hubPackageFacts.set(name, {
620296
+ version: hubVersions.get(name) ?? null,
620297
+ placements
620298
+ });
620299
+ }
620300
+ await this.convergeAgentAddons(agentId, agentAddons.filter((a) => !undeployedIds.has(a.id)), undeployedPackages, hubPackageFacts, trigger);
619991
620301
  } catch (err) {
619992
620302
  console.error(`[agent-registry] Reconcile failed for agent ${agentId}:`, err instanceof Error ? err.message : String(err));
619993
620303
  } finally {
@@ -619995,22 +620305,25 @@ var require_agent_registry_service = __commonJS({
619995
620305
  }
619996
620306
  }
619997
620307
  /**
619998
- * Converge a node UP to the roster it last declared.
620308
+ * Converge a node to ITS DESIRED SET ([D982](../../../../docs/decisions/adr-0982-the-hub-owns-every-nodes-addon-set.md)).
620309
+ *
620310
+ * The desired set comes from the hub (placements, versions, the agent-only
620311
+ * packages it tracks, the node's tombstones); the roster the node reports is
620312
+ * the observation it is diffed against and is recorded only as history.
619999
620313
  *
620000
620314
  * Order matters and is deliberate:
620001
- * 1. record what the node declares NOW (grow-only merge) — a wiped node
620002
- * reporting 4 packages must not erase the record of its 14;
620003
- * 2. prune what this pass undeployed — the intended set's only shrink;
620004
- * 3. read the resulting intended set and deliver the difference.
620315
+ * 1. record what the node declares NOW — history, never intent;
620316
+ * 2. prune what the stale sweep undeployed from that record;
620317
+ * 3. compute the plan, then apply it behind the gates (trigger allowlist,
620318
+ * delivery window, confirmation read, dry-run switch).
620005
620319
  *
620006
620320
  * Every leg is idempotent: run twice with no change in between and step 3
620007
620321
  * finds nothing to do.
620008
620322
  */
620009
- async backfillAgentAddons(agentId, agentAddons, undeployedPackages, hubVersions, hubPackagePlacements, trigger) {
620323
+ async convergeAgentAddons(agentId, agentAddons, undeployedPackages, hubPackages, trigger) {
620010
620324
  const store = this.historyStore;
620011
620325
  if (!store)
620012
620326
  return;
620013
- const priorRoster = await store.getPackages(agentId);
620014
620327
  const live = rosterFromAddons(agentAddons);
620015
620328
  await store.recordPackages(agentId, live);
620016
620329
  if (undeployedPackages.size > 0) {
@@ -620019,63 +620332,153 @@ var require_agent_registry_service = __commonJS({
620019
620332
  const seam = this.addonBackfill;
620020
620333
  if (!seam)
620021
620334
  return;
620022
- const recorded = await store.getPackages(agentId);
620023
- const planned = (0, agent_addon_backfill_1.computeBackfillPlan)({
620024
- recorded,
620025
- live,
620026
- bootstrap: AGENT_BOOTSTRAP_PACKAGES,
620027
- undeployed: undeployedPackages,
620335
+ const tombstones = await store.getTombstones(agentId);
620336
+ const desired = (0, agent_desired_set_1.computeDesiredSet)({
620337
+ hubPackages,
620338
+ trackedForAgents: await this.resolveTrackedVersions(),
620339
+ tombstones,
620340
+ bootstrap: CONVERGENCE_BOOTSTRAP
620341
+ });
620342
+ const planned = (0, agent_desired_set_1.diffNodeAgainstDesired)({
620343
+ desired,
620344
+ observed: live.filter((p) => !undeployedPackages.has(p.name)),
620028
620345
  retired: this.backfillTracker.retiredFor(agentId),
620029
- hubVersions,
620030
- freshNode: priorRoster.length === 0 ? { hubPackagePlacements } : void 0
620346
+ bootstrap: CONVERGENCE_BOOTSTRAP
620031
620347
  });
620032
- if (priorRoster.length === 0) {
620033
- this.logger?.info("back-fill: fresh node (no prior declared roster), plan from the hub any-node set", {
620034
- tags: { nodeId: agentId },
620035
- meta: {
620036
- planned: planned.targets.map((t) => `${t.name}@${t.version}`)
620037
- }
620038
- });
620039
- for (const skip of planned.skipped) {
620040
- this.logger?.info("back-fill: fresh node not given a hub package", {
620041
- tags: { nodeId: agentId },
620042
- meta: { package: skip.name, reason: skip.reason }
620043
- });
620044
- }
620045
- }
620046
- if (planned.notOnHub.length > 0) {
620047
- console.log(`[agent-registry] Back-fill ${agentId}: NOT delivering ${planned.notOnHub.join(", ")} \u2014 in the node's declared roster but not installed on the hub`);
620048
- }
620049
- if (planned.targets.length === 0 && planned.retired.length === 0) {
620348
+ const changes = planned.install.length + planned.update.length + planned.remove.length;
620349
+ if (changes === 0 && planned.retired.length === 0)
620050
620350
  return;
620051
- }
620351
+ const dryRun = this.readConvergenceDryRun(agentId);
620352
+ this.logger?.info(dryRun ? "convergence plan (DRY RUN, removals held back)" : "convergence plan", {
620353
+ tags: { nodeId: agentId },
620354
+ meta: {
620355
+ trigger,
620356
+ install: planned.install.map((t) => `${t.name}@${t.version}`),
620357
+ update: planned.update.map((t) => `${t.name} ${t.from ?? "absent"} -> ${t.version}`),
620358
+ remove: planned.remove.map((r) => `${r.name} (${r.reason})`),
620359
+ retired: planned.retired,
620360
+ observed: live.length
620361
+ }
620362
+ });
620052
620363
  if (!(0, agent_addon_backfill_1.triggerMayDeliver)(trigger)) {
620053
- console.log(`[agent-registry] Back-fill ${agentId}: SKIPPED \u2014 trigger "${trigger}" may not deliver (${planned.targets.length} package(s) would have been installed)`);
620364
+ console.log(`[agent-registry] Convergence ${agentId}: SKIPPED \u2014 trigger "${trigger}" may not deliver (${changes} change(s) planned)`);
620054
620365
  return;
620055
620366
  }
620056
620367
  const suppression = this.deliveryLedger.suppressionFor(agentId);
620057
620368
  if (suppression !== null) {
620058
- console.log(`[agent-registry] Back-fill ${agentId}: SKIPPED \u2014 a hub delivery to this node is in flight or just finished (inFlight=${suppression.inFlight}, msSinceLastDelivery=${suppression.msSinceLastDelivery ?? "n/a"}, packages=${suppression.packages.join(", ") || "none"}); the node reported ${planned.liveCount} package(s) and the roster gap is OURS, not the node's`);
620369
+ console.log(`[agent-registry] Convergence ${agentId}: SKIPPED \u2014 a hub delivery to this node is in flight or just finished (inFlight=${suppression.inFlight}, msSinceLastDelivery=${suppression.msSinceLastDelivery ?? "n/a"}, packages=${suppression.packages.join(", ") || "none"}); the roster gap is OURS, not the node's`);
620059
620370
  return;
620060
620371
  }
620061
- const plan = await this.confirmBackfillTargets(agentId, planned);
620062
- if (plan === null)
620372
+ if (!dryRun)
620373
+ await this.applyRemovals(agentId, agentAddons, planned);
620374
+ await this.applyDeliveries(agentId, live, planned, trigger, seam);
620375
+ }
620376
+ /**
620377
+ * The dry-run switch, read fail-safe: a read that cannot be answered counts
620378
+ * as ON (nothing is removed) and never fails the reconcile.
620379
+ */
620380
+ readConvergenceDryRun(agentId) {
620381
+ try {
620382
+ return this.convergenceDryRun();
620383
+ } catch (err) {
620384
+ this.logger?.warn("convergence: dry-run switch unreadable, treated as ON", {
620385
+ tags: { nodeId: agentId },
620386
+ meta: { error: err instanceof Error ? err.message : String(err) }
620387
+ });
620388
+ return true;
620389
+ }
620390
+ }
620391
+ /**
620392
+ * Remove tombstoned and hub-only leftovers. A removal DESTROYS work, so it
620393
+ * needs a second roster read to agree (D49) and fails closed when that read
620394
+ * cannot be taken. Every removal logs its reason.
620395
+ */
620396
+ async applyRemovals(agentId, agentAddons, plan) {
620397
+ if (plan.remove.length === 0)
620063
620398
  return;
620064
- if (plan.targets.length === 0 && plan.retired.length === 0) {
620399
+ let secondAddons = null;
620400
+ try {
620401
+ secondAddons = this.extractAgentAddons(await this.broker.call("$agent.status", {}, { nodeID: agentId, timeout: AGENT_RECONCILE_RPC_TIMEOUT_MS }));
620402
+ } catch (err) {
620403
+ this.logger?.warn("convergence: removals skipped, the confirmation read failed", {
620404
+ tags: { nodeId: agentId },
620405
+ meta: { error: err instanceof Error ? err.message : String(err) }
620406
+ });
620065
620407
  return;
620066
620408
  }
620067
- const closeWindow = this.deliveryLedger.begin(agentId, plan.targets.map((t) => t.name).join(", ") || "back-fill");
620409
+ if (secondAddons === null) {
620410
+ this.logger?.warn("convergence: removals skipped, the confirmation read had no addon list", {
620411
+ tags: { nodeId: agentId }
620412
+ });
620413
+ return;
620414
+ }
620415
+ const stillThere = new Set(secondAddons.flatMap((a) => a.packageName === void 0 ? [] : [a.packageName]));
620416
+ for (const removal of plan.remove) {
620417
+ if (!stillThere.has(removal.name))
620418
+ continue;
620419
+ const addonIds = agentAddons.filter((a) => a.packageName === removal.name).map((a) => a.id);
620420
+ try {
620421
+ await this.broker.call("$agent.undeploy", {
620422
+ packageName: removal.name,
620423
+ ...addonIds[0] === void 0 ? {} : { addonId: addonIds[0] }
620424
+ }, { nodeID: agentId, timeout: AGENT_RECONCILE_RPC_TIMEOUT_MS });
620425
+ this.logger?.info("convergence: removed a package from the node", {
620426
+ tags: { nodeId: agentId },
620427
+ meta: { package: removal.name, reason: removal.reason, addonIds }
620428
+ });
620429
+ this.eventBus.emit({
620430
+ id: (0, node_crypto_1.randomUUID)(),
620431
+ timestamp: /* @__PURE__ */ new Date(),
620432
+ source: { type: "core", id: "agent-registry" },
620433
+ category: types_1.EventCategory.AddonUninstalled,
620434
+ data: {
620435
+ agentId,
620436
+ packageName: removal.name,
620437
+ reason: `convergence: ${removal.reason}`
620438
+ }
620439
+ });
620440
+ } catch (err) {
620441
+ this.logger?.warn("convergence: removing a package failed, the next round retries", {
620442
+ tags: { nodeId: agentId },
620443
+ meta: {
620444
+ package: removal.name,
620445
+ reason: removal.reason,
620446
+ error: err instanceof Error ? err.message : String(err)
620447
+ }
620448
+ });
620449
+ }
620450
+ }
620451
+ }
620452
+ /** Install and update, behind the D49 confirmation read. */
620453
+ async applyDeliveries(agentId, live, plan, trigger, seam) {
620454
+ const planned = {
620455
+ targets: [...plan.install, ...plan.update].map((t) => ({
620456
+ name: t.name,
620457
+ version: t.version,
620458
+ versionSource: t.origin === "agent-tracked" ? "agent-tracked" : "hub-installed"
620459
+ })),
620460
+ retired: plan.retired,
620461
+ liveCount: live.length
620462
+ };
620463
+ if (planned.targets.length === 0 && planned.retired.length === 0)
620464
+ return;
620465
+ const confirmed = await this.confirmBackfillTargets(agentId, planned);
620466
+ if (confirmed === null)
620467
+ return;
620468
+ if (confirmed.targets.length === 0 && confirmed.retired.length === 0)
620469
+ return;
620470
+ const closeWindow = this.deliveryLedger.begin(agentId, confirmed.targets.map((t) => t.name).join(", ") || "convergence");
620068
620471
  let outcome;
620069
620472
  try {
620070
- outcome = await (0, agent_addon_backfill_1.executeBackfill)(seam, agentId, plan, this.backfillTracker);
620473
+ outcome = await (0, agent_addon_backfill_1.executeBackfill)(seam, agentId, confirmed, this.backfillTracker);
620071
620474
  } finally {
620072
620475
  closeWindow();
620073
620476
  }
620074
620477
  if (outcome.delivered.length > 0) {
620075
- console.log(`[agent-registry] Back-fill ${agentId}: delivered ${outcome.delivered.map((d) => {
620478
+ console.log(`[agent-registry] Convergence ${agentId}: delivered ${outcome.delivered.map((d) => {
620076
620479
  const from = live.find((p) => p.name === d.name)?.version ?? "absent";
620077
- return `${d.name} ${from} \u2192 ${d.version} (agent follows the hub installed version)`;
620078
- }).join(", ")} \u2014 trigger=${trigger}, node reported ${plan.liveCount} package(s) and its declared roster has ${recorded.length}`);
620480
+ return `${d.name} ${from} \u2192 ${d.version} (${d.versionSource})`;
620481
+ }).join(", ")} \u2014 trigger=${trigger}, node reported ${confirmed.liveCount} package(s)`);
620079
620482
  this.eventBus.emit({
620080
620483
  id: (0, node_crypto_1.randomUUID)(),
620081
620484
  timestamp: /* @__PURE__ */ new Date(),
@@ -620084,15 +620487,15 @@ var require_agent_registry_service = __commonJS({
620084
620487
  data: {
620085
620488
  agentId,
620086
620489
  packages: outcome.delivered.map((d) => `${d.name}@${d.version}`),
620087
- reason: "back-fill: node missing packages from its declared roster"
620490
+ reason: "convergence: the hub desired set differs from what the node runs"
620088
620491
  }
620089
620492
  });
620090
620493
  }
620091
620494
  for (const failure of outcome.failed) {
620092
- console.error(`[agent-registry] Back-fill ${agentId}: FAILED to deliver ${failure.name}@${failure.version} \u2014 ${failure.error}` + (failure.retired ? ` (retired after ${agent_addon_backfill_1.MAX_BACKFILL_ATTEMPTS} attempts; this node is running WITHOUT that package)` : ""));
620495
+ console.error(`[agent-registry] Convergence ${agentId}: FAILED to deliver ${failure.name}@${failure.version} \u2014 ${failure.error}` + (failure.retired ? ` (retired after ${agent_addon_backfill_1.MAX_BACKFILL_ATTEMPTS} attempts; this node is running WITHOUT that package)` : " (the next round retries)"));
620093
620496
  }
620094
620497
  for (const name of outcome.skipped) {
620095
- console.error(`[agent-registry] Back-fill ${agentId}: ${name} is still missing and RETIRED \u2014 the node is degraded until an operator intervenes`);
620498
+ console.error(`[agent-registry] Convergence ${agentId}: ${name} is still wrong and RETIRED \u2014 the node is degraded until an operator intervenes`);
620096
620499
  }
620097
620500
  }
620098
620501
  /**
@@ -623281,7 +623684,8 @@ var require_cluster_node_history_store = __commonJS({
623281
623684
  isHub: zod_1.z.boolean(),
623282
623685
  localIps: zod_1.z.array(zod_1.z.string()).readonly(),
623283
623686
  addonIds: zod_1.z.array(zod_1.z.string()).readonly(),
623284
- packages: zod_1.z.array(zod_1.z.object({ name: zod_1.z.string(), version: zod_1.z.string().nullable() })).readonly().optional()
623687
+ packages: zod_1.z.array(zod_1.z.object({ name: zod_1.z.string(), version: zod_1.z.string().nullable() })).readonly().optional(),
623688
+ tombstones: zod_1.z.array(zod_1.z.string()).readonly().optional()
623285
623689
  });
623286
623690
  var RowDataSchema = zod_1.z.object({
623287
623691
  lastActive: zod_1.z.number(),
@@ -623289,7 +623693,7 @@ var require_cluster_node_history_store = __commonJS({
623289
623693
  });
623290
623694
  var LAST_ACTIVE_HEARTBEAT_MS = 5 * 6e4;
623291
623695
  function descriptorsEqual(a, b) {
623292
- return a.id === b.id && a.name === b.name && a.hostname === b.hostname && a.platform === b.platform && a.arch === b.arch && a.cpuModel === b.cpuModel && a.cpuCores === b.cpuCores && a.memoryMB === b.memoryMB && a.isHub === b.isHub && sameStrings(a.engines, b.engines) && sameStrings(a.localIps, b.localIps) && sameStrings(a.addonIds, b.addonIds) && samePackages(a.packages, b.packages);
623696
+ return a.id === b.id && a.name === b.name && a.hostname === b.hostname && a.platform === b.platform && a.arch === b.arch && a.cpuModel === b.cpuModel && a.cpuCores === b.cpuCores && a.memoryMB === b.memoryMB && a.isHub === b.isHub && sameStrings(a.engines, b.engines) && sameStrings(a.localIps, b.localIps) && sameStrings(a.addonIds, b.addonIds) && samePackages(a.packages, b.packages) && sameStrings(a.tombstones ?? [], b.tombstones ?? []);
623293
623697
  }
623294
623698
  function sameStrings(a, b) {
623295
623699
  return a.length === b.length && a.every((v, i) => v === b[i]);
@@ -623335,7 +623739,12 @@ var require_cluster_node_history_store = __commonJS({
623335
623739
  try {
623336
623740
  const existing = await this.readStored(kind, descriptor.id);
623337
623741
  const packages = descriptor.packages ?? existing?.descriptor.packages;
623338
- const next = packages === void 0 ? descriptor : { ...descriptor, packages };
623742
+ const tombstones = descriptor.tombstones ?? existing?.descriptor.tombstones;
623743
+ const next = {
623744
+ ...descriptor,
623745
+ ...packages === void 0 ? {} : { packages },
623746
+ ...tombstones === void 0 ? {} : { tombstones }
623747
+ };
623339
623748
  if (existing !== null && !this.worthWriting(existing, next))
623340
623749
  return;
623341
623750
  await this.writeStored(kind, descriptor.id, Date.now(), next);
@@ -623477,6 +623886,61 @@ var require_cluster_node_history_store = __commonJS({
623477
623886
  });
623478
623887
  }
623479
623888
  }
623889
+ /** The packages tombstoned for a node (D982). Empty when none or unreadable. */
623890
+ async getTombstones(nodeId) {
623891
+ const kind = this.resolveKind();
623892
+ if (kind === null)
623893
+ return /* @__PURE__ */ new Set();
623894
+ try {
623895
+ return new Set((await this.readStored(kind, nodeId))?.descriptor.tombstones ?? []);
623896
+ } catch (err) {
623897
+ this.logger.warn("getTombstones failed (best-effort)", {
623898
+ tags: { nodeId },
623899
+ meta: { error: (0, types_1.errMsg)(err) }
623900
+ });
623901
+ return /* @__PURE__ */ new Set();
623902
+ }
623903
+ }
623904
+ /** Keep packages off a node: an operator undeploy / hub uninstall for it. */
623905
+ async addTombstones(nodeId, names) {
623906
+ await this.editTombstones(nodeId, names, "add");
623907
+ }
623908
+ /** An explicit delivery to the node lifts the tombstone. */
623909
+ async clearTombstones(nodeId, names) {
623910
+ await this.editTombstones(nodeId, names, "clear");
623911
+ }
623912
+ async editTombstones(nodeId, names, mode) {
623913
+ if (names.length === 0)
623914
+ return;
623915
+ const kind = this.resolveKind();
623916
+ if (kind === null)
623917
+ return;
623918
+ try {
623919
+ const existing = await this.readStored(kind, nodeId);
623920
+ if (existing === null && mode === "clear")
623921
+ return;
623922
+ const current = new Set(existing?.descriptor.tombstones ?? []);
623923
+ const before = current.size;
623924
+ for (const name of names) {
623925
+ if (mode === "add")
623926
+ current.add(name);
623927
+ else
623928
+ current.delete(name);
623929
+ }
623930
+ if (current.size === before)
623931
+ return;
623932
+ const base = existing?.descriptor ?? _ClusterNodeHistoryStore.placeholderDescriptor(nodeId);
623933
+ await this.writeStored(kind, nodeId, existing?.lastActive ?? Date.now(), {
623934
+ ...base,
623935
+ tombstones: [...current].sort()
623936
+ });
623937
+ } catch (err) {
623938
+ this.logger.warn("editTombstones failed (best-effort)", {
623939
+ tags: { nodeId },
623940
+ meta: { error: (0, types_1.errMsg)(err), mode }
623941
+ });
623942
+ }
623943
+ }
623480
623944
  /**
623481
623945
  * Touch `lastActive` for a node without rewriting its descriptor — used on
623482
623946
  * `$node.disconnected` so an offline node's "last seen" is the disconnect
@@ -623527,11 +623991,16 @@ var require_cluster_node_history_store = __commonJS({
623527
623991
  });
623528
623992
  continue;
623529
623993
  }
623530
- result.push({ ...parsed.data.descriptor, lastActive: parsed.data.lastActive });
623994
+ result.push({
623995
+ ...parsed.data.descriptor,
623996
+ lastActive: parsed.data.lastActive
623997
+ });
623531
623998
  }
623532
623999
  return result;
623533
624000
  } catch (err) {
623534
- this.logger.warn("history read failed (best-effort)", { meta: { error: (0, types_1.errMsg)(err) } });
624001
+ this.logger.warn("history read failed (best-effort)", {
624002
+ meta: { error: (0, types_1.errMsg)(err) }
624003
+ });
623535
624004
  return null;
623536
624005
  }
623537
624006
  }
@@ -623551,7 +624020,9 @@ var require_cluster_node_history_store = __commonJS({
623551
624020
  const existed = await this.readStored(kind, nodeId) !== null;
623552
624021
  await kind.delete(nodeId);
623553
624022
  if (!existed) {
623554
- this.logger.info("cluster node had no history row to forget", { tags: { nodeId } });
624023
+ this.logger.info("cluster node had no history row to forget", {
624024
+ tags: { nodeId }
624025
+ });
623555
624026
  return "not-found";
623556
624027
  }
623557
624028
  this.logger.info("forgot cluster node", { tags: { nodeId } });
@@ -624379,6 +624850,8 @@ var require_manual_boot = __commonJS({
624379
624850
  await moleculerService.broker.call("$agent.reload", {}, { nodeID: nodeId, timeout: agent_registry_service_1.AGENT_BACKFILL_RPC_TIMEOUT_MS });
624380
624851
  }
624381
624852
  });
624853
+ agentRegistryService.setTrackedVersionResolver((name) => addonPackageService.resolveRegistryVersion(name));
624854
+ agentRegistryService.setConvergenceDryRun(() => configService.get(agent_registry_service_1.ADDON_CONVERGENCE_DRY_RUN_KEY) !== false);
624382
624855
  const lifecycleDataDir = path.resolve(process.env["CAMSTACK_DATA"] ?? "camstack-data");
624383
624856
  (0, lifecycle_runner_singleton_1.initLifecycleRunner)({
624384
624857
  dataDir: lifecycleDataDir,
@@ -624925,7 +625398,7 @@ var require_main4 = __commonJS({
624925
625398
  const uploadAddonRegistry = app.get(addon_registry_service_1.AddonRegistryService);
624926
625399
  const uploadAddonPackage = app.get(addon_package_service_1.AddonPackageService);
624927
625400
  const uploadLogger = app.get(logging_service_1.LoggingService).createLogger("addon-upload");
624928
- await (0, addon_upload_1.registerAddonUploadRoute)(fastify, uploadAddonBridge, uploadAuthService, uploadMoleculer, uploadAddonRegistry, uploadAddonPackage, uploadLogger, (0, hub_cluster_url_1.clusterBaseUrlResolver)(() => uploadAddonRegistry.getCapabilityRegistry().getSingleton("local-network")), app.get(agent_registry_service_1.AgentRegistryService).deliveryLedger);
625401
+ await (0, addon_upload_1.registerAddonUploadRoute)(fastify, uploadAddonBridge, uploadAuthService, uploadMoleculer, uploadAddonRegistry, uploadAddonPackage, uploadLogger, (0, hub_cluster_url_1.clusterBaseUrlResolver)(() => uploadAddonRegistry.getCapabilityRegistry().getSingleton("local-network")), app.get(agent_registry_service_1.AgentRegistryService).deliveryLedger, app.get(agent_registry_service_1.AgentRegistryService));
624929
625402
  console.log("[bootstrap] Addon upload route registered at POST /api/addons/upload");
624930
625403
  (0, server_upload_1.registerServerUploadRoute)(fastify, {
624931
625404
  authService: uploadAuthService,