@camstack/types 1.2.156 → 1.2.158
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/device/base-device-provider.d.ts +22 -0
- package/dist/index.js +51 -0
- package/dist/index.mjs +51 -0
- package/dist/types/detection.d.ts +38 -0
- package/package.json +1 -1
|
@@ -190,6 +190,28 @@ export declare abstract class BaseDeviceProvider<TConfig extends object = Record
|
|
|
190
190
|
/** One-line operator-facing summary for `getStatus().error`, or
|
|
191
191
|
* `null` when every device restored. */
|
|
192
192
|
protected restoreFailureSummary(): string | null;
|
|
193
|
+
/**
|
|
194
|
+
* A top-level persisted row with NO configuration is a leftover, not a
|
|
195
|
+
* device — quarantine it instead of dialling it.
|
|
196
|
+
*
|
|
197
|
+
* Device 614 was deleted, resurrected as a config-less shell by a write
|
|
198
|
+
* already in flight (D376), and then dialled at every single boot: eight
|
|
199
|
+
* `Failed to restore device — bounded retry scheduled` lines in one day,
|
|
200
|
+
* each one the same Zod refusal on `host` and `password`. The bound was
|
|
201
|
+
* real, but it is a bound PER PROCESS, and the runner restarts — so nothing
|
|
202
|
+
* about the row ever stopped costing attempts and log lines.
|
|
203
|
+
*
|
|
204
|
+
* No retry can add a configuration that is not there. The row is reported
|
|
205
|
+
* once, with its `deviceId`, and set aside.
|
|
206
|
+
*
|
|
207
|
+
* Top-level only: an accessory child legitimately carries an empty blob (it
|
|
208
|
+
* lives off its parent), and a hub-adopted child with an emptied blob is
|
|
209
|
+
* repaired by `healSavedConfig` from its parent instead. Providers that
|
|
210
|
+
* genuinely create configuration-free top-level devices turn this off.
|
|
211
|
+
*/
|
|
212
|
+
protected readonly quarantineConfiglessTopLevelRestores: boolean;
|
|
213
|
+
private isUnrestorableShell;
|
|
214
|
+
private quarantineShell;
|
|
193
215
|
private cancelRestoreRetries;
|
|
194
216
|
private recordPermanentRestoreFailure;
|
|
195
217
|
private scheduleRestoreRetries;
|
package/dist/index.js
CHANGED
|
@@ -37567,6 +37567,53 @@ var BaseDeviceProvider = class extends require_sleep.BaseAddon {
|
|
|
37567
37567
|
const ids = [...this._permanentRestoreFailures.keys()].join(", ");
|
|
37568
37568
|
return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
|
|
37569
37569
|
}
|
|
37570
|
+
/**
|
|
37571
|
+
* A top-level persisted row with NO configuration is a leftover, not a
|
|
37572
|
+
* device — quarantine it instead of dialling it.
|
|
37573
|
+
*
|
|
37574
|
+
* Device 614 was deleted, resurrected as a config-less shell by a write
|
|
37575
|
+
* already in flight (D376), and then dialled at every single boot: eight
|
|
37576
|
+
* `Failed to restore device — bounded retry scheduled` lines in one day,
|
|
37577
|
+
* each one the same Zod refusal on `host` and `password`. The bound was
|
|
37578
|
+
* real, but it is a bound PER PROCESS, and the runner restarts — so nothing
|
|
37579
|
+
* about the row ever stopped costing attempts and log lines.
|
|
37580
|
+
*
|
|
37581
|
+
* No retry can add a configuration that is not there. The row is reported
|
|
37582
|
+
* once, with its `deviceId`, and set aside.
|
|
37583
|
+
*
|
|
37584
|
+
* Top-level only: an accessory child legitimately carries an empty blob (it
|
|
37585
|
+
* lives off its parent), and a hub-adopted child with an emptied blob is
|
|
37586
|
+
* repaired by `healSavedConfig` from its parent instead. Providers that
|
|
37587
|
+
* genuinely create configuration-free top-level devices turn this off.
|
|
37588
|
+
*/
|
|
37589
|
+
quarantineConfiglessTopLevelRestores = true;
|
|
37590
|
+
isUnrestorableShell(saved) {
|
|
37591
|
+
if (!this.quarantineConfiglessTopLevelRestores) return false;
|
|
37592
|
+
if (saved.parentDeviceId !== null) return false;
|
|
37593
|
+
const config = saved.config;
|
|
37594
|
+
if (config === void 0) return false;
|
|
37595
|
+
return Object.keys(config).length === 0;
|
|
37596
|
+
}
|
|
37597
|
+
quarantineShell(saved) {
|
|
37598
|
+
this._permanentRestoreFailures.set(saved.id, {
|
|
37599
|
+
deviceId: saved.id,
|
|
37600
|
+
stableId: saved.stableId,
|
|
37601
|
+
type: saved.type,
|
|
37602
|
+
attempts: 0,
|
|
37603
|
+
lastError: "persisted row has no configuration",
|
|
37604
|
+
failedAt: Date.now()
|
|
37605
|
+
});
|
|
37606
|
+
this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
|
|
37607
|
+
tags: {
|
|
37608
|
+
deviceId: saved.id,
|
|
37609
|
+
stableId: saved.stableId
|
|
37610
|
+
},
|
|
37611
|
+
meta: {
|
|
37612
|
+
type: saved.type,
|
|
37613
|
+
provider: this.providerName
|
|
37614
|
+
}
|
|
37615
|
+
});
|
|
37616
|
+
}
|
|
37570
37617
|
cancelRestoreRetries() {
|
|
37571
37618
|
this._restoreRetryScheduler?.cancel();
|
|
37572
37619
|
this._restoreRetryScheduler = null;
|
|
@@ -37751,6 +37798,10 @@ var BaseDeviceProvider = class extends require_sleep.BaseAddon {
|
|
|
37751
37798
|
});
|
|
37752
37799
|
return;
|
|
37753
37800
|
}
|
|
37801
|
+
if (this.isUnrestorableShell(saved)) {
|
|
37802
|
+
this.quarantineShell(saved);
|
|
37803
|
+
return;
|
|
37804
|
+
}
|
|
37754
37805
|
try {
|
|
37755
37806
|
await attemptRestore(saved);
|
|
37756
37807
|
} catch (err) {
|
package/dist/index.mjs
CHANGED
|
@@ -37559,6 +37559,53 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
37559
37559
|
const ids = [...this._permanentRestoreFailures.keys()].join(", ");
|
|
37560
37560
|
return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
|
|
37561
37561
|
}
|
|
37562
|
+
/**
|
|
37563
|
+
* A top-level persisted row with NO configuration is a leftover, not a
|
|
37564
|
+
* device — quarantine it instead of dialling it.
|
|
37565
|
+
*
|
|
37566
|
+
* Device 614 was deleted, resurrected as a config-less shell by a write
|
|
37567
|
+
* already in flight (D376), and then dialled at every single boot: eight
|
|
37568
|
+
* `Failed to restore device — bounded retry scheduled` lines in one day,
|
|
37569
|
+
* each one the same Zod refusal on `host` and `password`. The bound was
|
|
37570
|
+
* real, but it is a bound PER PROCESS, and the runner restarts — so nothing
|
|
37571
|
+
* about the row ever stopped costing attempts and log lines.
|
|
37572
|
+
*
|
|
37573
|
+
* No retry can add a configuration that is not there. The row is reported
|
|
37574
|
+
* once, with its `deviceId`, and set aside.
|
|
37575
|
+
*
|
|
37576
|
+
* Top-level only: an accessory child legitimately carries an empty blob (it
|
|
37577
|
+
* lives off its parent), and a hub-adopted child with an emptied blob is
|
|
37578
|
+
* repaired by `healSavedConfig` from its parent instead. Providers that
|
|
37579
|
+
* genuinely create configuration-free top-level devices turn this off.
|
|
37580
|
+
*/
|
|
37581
|
+
quarantineConfiglessTopLevelRestores = true;
|
|
37582
|
+
isUnrestorableShell(saved) {
|
|
37583
|
+
if (!this.quarantineConfiglessTopLevelRestores) return false;
|
|
37584
|
+
if (saved.parentDeviceId !== null) return false;
|
|
37585
|
+
const config = saved.config;
|
|
37586
|
+
if (config === void 0) return false;
|
|
37587
|
+
return Object.keys(config).length === 0;
|
|
37588
|
+
}
|
|
37589
|
+
quarantineShell(saved) {
|
|
37590
|
+
this._permanentRestoreFailures.set(saved.id, {
|
|
37591
|
+
deviceId: saved.id,
|
|
37592
|
+
stableId: saved.stableId,
|
|
37593
|
+
type: saved.type,
|
|
37594
|
+
attempts: 0,
|
|
37595
|
+
lastError: "persisted row has no configuration",
|
|
37596
|
+
failedAt: Date.now()
|
|
37597
|
+
});
|
|
37598
|
+
this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
|
|
37599
|
+
tags: {
|
|
37600
|
+
deviceId: saved.id,
|
|
37601
|
+
stableId: saved.stableId
|
|
37602
|
+
},
|
|
37603
|
+
meta: {
|
|
37604
|
+
type: saved.type,
|
|
37605
|
+
provider: this.providerName
|
|
37606
|
+
}
|
|
37607
|
+
});
|
|
37608
|
+
}
|
|
37562
37609
|
cancelRestoreRetries() {
|
|
37563
37610
|
this._restoreRetryScheduler?.cancel();
|
|
37564
37611
|
this._restoreRetryScheduler = null;
|
|
@@ -37743,6 +37790,10 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
37743
37790
|
});
|
|
37744
37791
|
return;
|
|
37745
37792
|
}
|
|
37793
|
+
if (this.isUnrestorableShell(saved)) {
|
|
37794
|
+
this.quarantineShell(saved);
|
|
37795
|
+
return;
|
|
37796
|
+
}
|
|
37746
37797
|
try {
|
|
37747
37798
|
await attemptRestore(saved);
|
|
37748
37799
|
} catch (err) {
|
|
@@ -9,6 +9,13 @@ export interface SpatialDetection {
|
|
|
9
9
|
readonly mask?: Uint8Array;
|
|
10
10
|
readonly maskWidth?: number;
|
|
11
11
|
readonly maskHeight?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Mirrored from {@link ObjectDetection.nativeRecovery} when this detection
|
|
14
|
+
* enters the tracker. The tracker's SPAWN gates (per-class score floor,
|
|
15
|
+
* person-over-animal dedup) are tuned for the coarse full-frame pass; a box
|
|
16
|
+
* measured at native resolution is not the thing they exist to kill.
|
|
17
|
+
*/
|
|
18
|
+
readonly nativeRecovery?: NativeRecoveryEvidence;
|
|
12
19
|
}
|
|
13
20
|
export interface CropInput {
|
|
14
21
|
readonly frame: FrameInput;
|
|
@@ -216,6 +223,37 @@ export interface DetectionBase {
|
|
|
216
223
|
readonly labels: readonly ScoredLabel[];
|
|
217
224
|
readonly track?: TrackInfo;
|
|
218
225
|
readonly debug?: DetectionDebug;
|
|
226
|
+
/**
|
|
227
|
+
* Present when this box did NOT come from the full-frame pass but from a
|
|
228
|
+
* SECOND detector pass on a native-resolution crop of a motion region
|
|
229
|
+
* (`small-subject-recovery.ts` in the runner). See
|
|
230
|
+
* {@link NativeRecoveryEvidence}.
|
|
231
|
+
*/
|
|
232
|
+
readonly nativeRecovery?: NativeRecoveryEvidence;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* What a native-resolution second pass ALREADY measured about a box.
|
|
236
|
+
*
|
|
237
|
+
* The first-level detector sees a 4K frame at 320 px: a 100 px dog is 8 px and
|
|
238
|
+
* is not emitted. The runner's recovery pass re-runs THE SAME detector on a
|
|
239
|
+
* 640–800 px native square around the moving region and finds it at 0.82–0.91
|
|
240
|
+
* (`docs/benchmarks/pipeline-frame-model-eval.md`, case `miss-dog-615`). That
|
|
241
|
+
* pass is already a second look at better pixels — so every downstream gate
|
|
242
|
+
* whose job is to ask "did the coarse pass see a real thing?" has its answer,
|
|
243
|
+
* and re-deriving a THIRD, tighter crop of the same subject can only lose
|
|
244
|
+
* information (D377).
|
|
245
|
+
*
|
|
246
|
+
* Carried from the runner to post-analysis on the emitted frame result and
|
|
247
|
+
* mirrored onto the tracker's {@link SpatialDetection}. Plain data: no pixels,
|
|
248
|
+
* no handle — the crop it names is long gone, and its VERDICT is what travels.
|
|
249
|
+
*/
|
|
250
|
+
export interface NativeRecoveryEvidence {
|
|
251
|
+
/** Score the second pass measured, on the native crop, with the same model. */
|
|
252
|
+
readonly score: number;
|
|
253
|
+
/** Side of the native square the crop was cut from, in native pixels. */
|
|
254
|
+
readonly cropSidePx: number;
|
|
255
|
+
/** Side of the compressed view the model actually saw, in pixels. */
|
|
256
|
+
readonly viewPx: number;
|
|
219
257
|
}
|
|
220
258
|
/** Object detection (first-level person/vehicle/animal OR detail face/plate). */
|
|
221
259
|
export interface ObjectDetection extends DetectionBase {
|