@camstack/addon-provider-amcrest 0.2.75 → 0.2.77

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 CHANGED
@@ -33873,6 +33873,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33873
33873
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33874
33874
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33875
33875
  }
33876
+ /**
33877
+ * A top-level persisted row with NO configuration is a leftover, not a
33878
+ * device — quarantine it instead of dialling it.
33879
+ *
33880
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33881
+ * already in flight (D376), and then dialled at every single boot: eight
33882
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33883
+ * each one the same Zod refusal on `host` and `password`. The bound was
33884
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33885
+ * about the row ever stopped costing attempts and log lines.
33886
+ *
33887
+ * No retry can add a configuration that is not there. The row is reported
33888
+ * once, with its `deviceId`, and set aside.
33889
+ *
33890
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33891
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33892
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33893
+ * genuinely create configuration-free top-level devices turn this off.
33894
+ */
33895
+ quarantineConfiglessTopLevelRestores = true;
33896
+ isUnrestorableShell(saved) {
33897
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33898
+ if (saved.parentDeviceId !== null) return false;
33899
+ const config = saved.config;
33900
+ if (config === void 0) return false;
33901
+ return Object.keys(config).length === 0;
33902
+ }
33903
+ quarantineShell(saved) {
33904
+ this._permanentRestoreFailures.set(saved.id, {
33905
+ deviceId: saved.id,
33906
+ stableId: saved.stableId,
33907
+ type: saved.type,
33908
+ attempts: 0,
33909
+ lastError: "persisted row has no configuration",
33910
+ failedAt: Date.now()
33911
+ });
33912
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33913
+ tags: {
33914
+ deviceId: saved.id,
33915
+ stableId: saved.stableId
33916
+ },
33917
+ meta: {
33918
+ type: saved.type,
33919
+ provider: this.providerName
33920
+ }
33921
+ });
33922
+ }
33876
33923
  cancelRestoreRetries() {
33877
33924
  this._restoreRetryScheduler?.cancel();
33878
33925
  this._restoreRetryScheduler = null;
@@ -34057,6 +34104,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34057
34104
  });
34058
34105
  return;
34059
34106
  }
34107
+ if (this.isUnrestorableShell(saved)) {
34108
+ this.quarantineShell(saved);
34109
+ return;
34110
+ }
34060
34111
  try {
34061
34112
  await attemptRestore(saved);
34062
34113
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -33874,6 +33874,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33874
33874
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33875
33875
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33876
33876
  }
33877
+ /**
33878
+ * A top-level persisted row with NO configuration is a leftover, not a
33879
+ * device — quarantine it instead of dialling it.
33880
+ *
33881
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33882
+ * already in flight (D376), and then dialled at every single boot: eight
33883
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33884
+ * each one the same Zod refusal on `host` and `password`. The bound was
33885
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33886
+ * about the row ever stopped costing attempts and log lines.
33887
+ *
33888
+ * No retry can add a configuration that is not there. The row is reported
33889
+ * once, with its `deviceId`, and set aside.
33890
+ *
33891
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33892
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33893
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33894
+ * genuinely create configuration-free top-level devices turn this off.
33895
+ */
33896
+ quarantineConfiglessTopLevelRestores = true;
33897
+ isUnrestorableShell(saved) {
33898
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33899
+ if (saved.parentDeviceId !== null) return false;
33900
+ const config = saved.config;
33901
+ if (config === void 0) return false;
33902
+ return Object.keys(config).length === 0;
33903
+ }
33904
+ quarantineShell(saved) {
33905
+ this._permanentRestoreFailures.set(saved.id, {
33906
+ deviceId: saved.id,
33907
+ stableId: saved.stableId,
33908
+ type: saved.type,
33909
+ attempts: 0,
33910
+ lastError: "persisted row has no configuration",
33911
+ failedAt: Date.now()
33912
+ });
33913
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33914
+ tags: {
33915
+ deviceId: saved.id,
33916
+ stableId: saved.stableId
33917
+ },
33918
+ meta: {
33919
+ type: saved.type,
33920
+ provider: this.providerName
33921
+ }
33922
+ });
33923
+ }
33877
33924
  cancelRestoreRetries() {
33878
33925
  this._restoreRetryScheduler?.cancel();
33879
33926
  this._restoreRetryScheduler = null;
@@ -34058,6 +34105,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34058
34105
  });
34059
34106
  return;
34060
34107
  }
34108
+ if (this.isUnrestorableShell(saved)) {
34109
+ this.quarantineShell(saved);
34110
+ return;
34111
+ }
34061
34112
  try {
34062
34113
  await attemptRestore(saved);
34063
34114
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-amcrest",
3
- "version": "0.2.75",
3
+ "version": "0.2.77",
4
4
  "description": "Amcrest/Dahua camera device provider addon for CamStack — Dahua CGI over HTTP(S) with digest auth (snapshot, RTSP catalog, PTZ, image/day-night config)",
5
5
  "keywords": [
6
6
  "camstack",