@camstack/addon-import-alexa 0.2.72 → 0.2.74

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
@@ -33728,6 +33728,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33728
33728
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33729
33729
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33730
33730
  }
33731
+ /**
33732
+ * A top-level persisted row with NO configuration is a leftover, not a
33733
+ * device — quarantine it instead of dialling it.
33734
+ *
33735
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33736
+ * already in flight (D376), and then dialled at every single boot: eight
33737
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33738
+ * each one the same Zod refusal on `host` and `password`. The bound was
33739
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33740
+ * about the row ever stopped costing attempts and log lines.
33741
+ *
33742
+ * No retry can add a configuration that is not there. The row is reported
33743
+ * once, with its `deviceId`, and set aside.
33744
+ *
33745
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33746
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33747
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33748
+ * genuinely create configuration-free top-level devices turn this off.
33749
+ */
33750
+ quarantineConfiglessTopLevelRestores = true;
33751
+ isUnrestorableShell(saved) {
33752
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33753
+ if (saved.parentDeviceId !== null) return false;
33754
+ const config = saved.config;
33755
+ if (config === void 0) return false;
33756
+ return Object.keys(config).length === 0;
33757
+ }
33758
+ quarantineShell(saved) {
33759
+ this._permanentRestoreFailures.set(saved.id, {
33760
+ deviceId: saved.id,
33761
+ stableId: saved.stableId,
33762
+ type: saved.type,
33763
+ attempts: 0,
33764
+ lastError: "persisted row has no configuration",
33765
+ failedAt: Date.now()
33766
+ });
33767
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33768
+ tags: {
33769
+ deviceId: saved.id,
33770
+ stableId: saved.stableId
33771
+ },
33772
+ meta: {
33773
+ type: saved.type,
33774
+ provider: this.providerName
33775
+ }
33776
+ });
33777
+ }
33731
33778
  cancelRestoreRetries() {
33732
33779
  this._restoreRetryScheduler?.cancel();
33733
33780
  this._restoreRetryScheduler = null;
@@ -33912,6 +33959,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33912
33959
  });
33913
33960
  return;
33914
33961
  }
33962
+ if (this.isUnrestorableShell(saved)) {
33963
+ this.quarantineShell(saved);
33964
+ return;
33965
+ }
33915
33966
  try {
33916
33967
  await attemptRestore(saved);
33917
33968
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -33728,6 +33728,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33728
33728
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33729
33729
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33730
33730
  }
33731
+ /**
33732
+ * A top-level persisted row with NO configuration is a leftover, not a
33733
+ * device — quarantine it instead of dialling it.
33734
+ *
33735
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33736
+ * already in flight (D376), and then dialled at every single boot: eight
33737
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33738
+ * each one the same Zod refusal on `host` and `password`. The bound was
33739
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33740
+ * about the row ever stopped costing attempts and log lines.
33741
+ *
33742
+ * No retry can add a configuration that is not there. The row is reported
33743
+ * once, with its `deviceId`, and set aside.
33744
+ *
33745
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33746
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33747
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33748
+ * genuinely create configuration-free top-level devices turn this off.
33749
+ */
33750
+ quarantineConfiglessTopLevelRestores = true;
33751
+ isUnrestorableShell(saved) {
33752
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33753
+ if (saved.parentDeviceId !== null) return false;
33754
+ const config = saved.config;
33755
+ if (config === void 0) return false;
33756
+ return Object.keys(config).length === 0;
33757
+ }
33758
+ quarantineShell(saved) {
33759
+ this._permanentRestoreFailures.set(saved.id, {
33760
+ deviceId: saved.id,
33761
+ stableId: saved.stableId,
33762
+ type: saved.type,
33763
+ attempts: 0,
33764
+ lastError: "persisted row has no configuration",
33765
+ failedAt: Date.now()
33766
+ });
33767
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33768
+ tags: {
33769
+ deviceId: saved.id,
33770
+ stableId: saved.stableId
33771
+ },
33772
+ meta: {
33773
+ type: saved.type,
33774
+ provider: this.providerName
33775
+ }
33776
+ });
33777
+ }
33731
33778
  cancelRestoreRetries() {
33732
33779
  this._restoreRetryScheduler?.cancel();
33733
33780
  this._restoreRetryScheduler = null;
@@ -33912,6 +33959,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33912
33959
  });
33913
33960
  return;
33914
33961
  }
33962
+ if (this.isUnrestorableShell(saved)) {
33963
+ this.quarantineShell(saved);
33964
+ return;
33965
+ }
33915
33966
  try {
33916
33967
  await attemptRestore(saved);
33917
33968
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-import-alexa",
3
- "version": "0.2.72",
3
+ "version": "0.2.74",
4
4
  "description": "Alexa device-import provider for CamStack — imports the smart-home devices in a user's Alexa account via the unofficial alexa-remote2 cookie/token client (the inverse of the Alexa exporter)",
5
5
  "keywords": [
6
6
  "camstack",