@camstack/system 1.2.307 → 1.2.309
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/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composed-device.d.ts +26 -0
- package/dist/builtins/composer/composer.addon.d.ts +12 -0
- package/dist/builtins/composer/composer.addon.js +1682 -0
- package/dist/builtins/composer/composer.addon.mjs +1677 -0
- package/dist/builtins/composer/composer.d.ts +103 -0
- package/dist/builtins/composer/composition-declarations.d.ts +53 -0
- package/dist/builtins/composer/composition-runtime.d.ts +88 -0
- package/dist/builtins/composer/deadline-timer.d.ts +18 -0
- package/dist/builtins/composer/index.d.ts +1 -0
- package/dist/builtins/composer/index.js +8 -0
- package/dist/builtins/composer/index.mjs +2 -0
- package/dist/builtins/composer/source-tracker.d.ts +78 -0
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/block-supervisor.d.ts +32 -1
- package/dist/builtins/core-blocks/blocks-integration.d.ts +5 -0
- package/dist/builtins/core-blocks/composition-api.d.ts +25 -0
- package/dist/builtins/core-blocks/composition-sources.d.ts +10 -0
- package/dist/builtins/core-blocks/core-block-store.d.ts +34 -2
- package/dist/builtins/core-blocks/core-blocks.addon.d.ts +6 -0
- package/dist/builtins/core-blocks/core-blocks.addon.js +504 -11
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +504 -11
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +10 -31
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +9 -30
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/shared/device-state-changed.d.ts +17 -0
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/device-state-changed-Bk7cvTnz.js +31 -0
- package/dist/device-state-changed-DoBbkyEI.mjs +20 -0
- package/dist/{dist-DdfJa8Zn.mjs → dist-BEuQPDk_.mjs} +1333 -83
- package/dist/{dist-D3ch99WD.js → dist-BhO9eYKO.js} +1422 -82
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/index.mjs +4 -2
- package/dist/{retired-settings-keys-BJNhhXap.mjs → retired-settings-keys-CQvG6B-Q.mjs} +1 -1
- package/dist/{retired-settings-keys-DG_LEPqC.js → retired-settings-keys-DHgnJqS3.js} +1 -1
- package/package.json +20 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { K as buildCapabilityLookup, X as composedDeviceRef, at as coreBlockSourceKind, d as CORE_BLOCK_ADDON_PREFIX, et as compositionSourceKey, f as CoreBlockSchema, g as DeclaredDevices, it as coreBlockIdFromAddonId, jn as BaseAddon, kn as errMsg, nt as coreBlockAddonId, ot as coreBlocksCapability, rt as coreBlockComposition, t as ALL_CAPABILITY_DEFINITIONS, tt as compositionSourceRefs, u as CORE_BLOCKS_ADDON_ID, wn as validateComposition } from "../../dist-BEuQPDk_.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -152,6 +152,27 @@ async function removeBlockPackage(root, blockId) {
|
|
|
152
152
|
}
|
|
153
153
|
//#endregion
|
|
154
154
|
//#region src/builtins/core-blocks/block-supervisor.ts
|
|
155
|
+
/**
|
|
156
|
+
* Converging the running block processes onto the stored blocks.
|
|
157
|
+
*
|
|
158
|
+
* Reconcile against the registry, not one-shot reactions to events: a spawn
|
|
159
|
+
* that was missed because an event was dropped (D8 — events are telemetry) has
|
|
160
|
+
* to be fixed by the next pass, and an operator restarting the hub must end up
|
|
161
|
+
* with exactly the blocks the store says are enabled. So `converge()` is
|
|
162
|
+
* idempotent and safe to call from a timer, from a write, and from boot.
|
|
163
|
+
*
|
|
164
|
+
* **The supervision itself is not in this file, and that is deliberate.** A
|
|
165
|
+
* block that never returns is bounded by `process-service`'s `CrashSupervisor`
|
|
166
|
+
* (D6) — the same circuit-breaker that bounds every addon. This class only
|
|
167
|
+
* decides which runners should exist and translates their state back into
|
|
168
|
+
* something the author can read. Re-implementing backoff here would be a second
|
|
169
|
+
* supervisor to keep in step with the first.
|
|
170
|
+
*
|
|
171
|
+
* The invariant it exists to hold: **a block that is not running says why.**
|
|
172
|
+
* Every path out of `converge()` that does not end in a live process writes a
|
|
173
|
+
* status the operator can see, with the reason attached. A block that is
|
|
174
|
+
* silently absent is the failure mode this whole feature has to avoid.
|
|
175
|
+
*/
|
|
155
176
|
/** Runner state → what the author should read. `crashed` is the window between
|
|
156
177
|
* an exit and the scheduled respawn, so it reads as `starting` with the reason
|
|
157
178
|
* attached rather than as a terminal failure it is not. */
|
|
@@ -176,6 +197,16 @@ var BlockSupervisor = class {
|
|
|
176
197
|
/** Serialises passes so a timer tick cannot interleave with a write-driven
|
|
177
198
|
* converge and spawn the same runner twice. */
|
|
178
199
|
inFlight = Promise.resolve();
|
|
200
|
+
/** Composition restarts not yet delivered to the composer. Stores nothing durable (D62). */
|
|
201
|
+
pendingCompositionRestarts = /* @__PURE__ */ new Set();
|
|
202
|
+
/**
|
|
203
|
+
* Empty applies still owed: the composer withdraws only after TWO agree
|
|
204
|
+
* (D49), and a boot sends one. Only an apply the composer says it ACTED on
|
|
205
|
+
* (`result.acted`) pays one off — it can refuse with the store loaded.
|
|
206
|
+
*/
|
|
207
|
+
emptyAppliesOwed = 2;
|
|
208
|
+
/** Consecutive applies that could not reach the composer: warned once per run. */
|
|
209
|
+
failedApplies = 0;
|
|
179
210
|
constructor(deps) {
|
|
180
211
|
this.deps = deps;
|
|
181
212
|
}
|
|
@@ -212,6 +243,11 @@ var BlockSupervisor = class {
|
|
|
212
243
|
* then never re-launch it.
|
|
213
244
|
*/
|
|
214
245
|
restart(blockId) {
|
|
246
|
+
const block = this.deps.store.get(blockId);
|
|
247
|
+
if (block !== null && coreBlockSourceKind(block) === "composition") {
|
|
248
|
+
this.pendingCompositionRestarts = new Set([...this.pendingCompositionRestarts, blockId]);
|
|
249
|
+
return this.converge();
|
|
250
|
+
}
|
|
215
251
|
this.inFlight = this.inFlight.then(() => this.runRestart(blockId), () => this.runRestart(blockId));
|
|
216
252
|
return this.inFlight;
|
|
217
253
|
}
|
|
@@ -236,9 +272,11 @@ var BlockSupervisor = class {
|
|
|
236
272
|
}
|
|
237
273
|
}
|
|
238
274
|
async reconcile() {
|
|
275
|
+
if (!this.deps.storeLoaded()) await this.deps.loadStore();
|
|
239
276
|
const blocks = this.deps.store.list();
|
|
240
|
-
const mine = blocks.filter((b) => b.enabled && this.isPlacedHere(b));
|
|
241
|
-
const
|
|
277
|
+
const mine = blocks.filter((b) => b.enabled && this.isPlacedHere(b) && coreBlockSourceKind(b) === "code");
|
|
278
|
+
const snapshots = await this.deps.host.list();
|
|
279
|
+
const runners = new Map(snapshots.map((r) => [blockIdFromAddonId(r.name), r]).filter((pair) => pair[0] !== null));
|
|
242
280
|
for (const block of blocks) {
|
|
243
281
|
if (!block.enabled || this.isPlacedHere(block)) continue;
|
|
244
282
|
await this.report(block.id, "failed", `no core-block supervisor runs on "${block.placement}" — this block is not running anywhere. Move it to "${this.deps.nodeId}" to run it.`);
|
|
@@ -256,6 +294,7 @@ var BlockSupervisor = class {
|
|
|
256
294
|
if (this.deps.store.get(blockId) !== null) await this.report(blockId, "stopped");
|
|
257
295
|
else await removeBlockPackage(this.deps.root, blockId);
|
|
258
296
|
}
|
|
297
|
+
for (const block of blocks) if (block.enabled && coreBlockSourceKind(block) === "recipe") await this.report(block.id, "failed", "a recipe block cannot run yet — recipes are stored, not executed, in this release");
|
|
259
298
|
for (const block of mine) {
|
|
260
299
|
const runner = runners.get(block.id);
|
|
261
300
|
const stale = runner !== void 0 && this.materialized.get(block.id) !== block.updatedAt;
|
|
@@ -273,6 +312,71 @@ var BlockSupervisor = class {
|
|
|
273
312
|
}
|
|
274
313
|
await this.launch(block);
|
|
275
314
|
}
|
|
315
|
+
await this.convergeCompositions(blocks, snapshots);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Send every composition placed here to the composer and mirror its verdicts
|
|
319
|
+
* into the block status, reason verbatim. A `starting` verdict (the composer
|
|
320
|
+
* does not trust its store or seed yet) is mirrored as `starting`; the next
|
|
321
|
+
* pass of this same converge is the retry — no timer of its own (D3).
|
|
322
|
+
*/
|
|
323
|
+
async convergeCompositions(blocks, runners) {
|
|
324
|
+
const compositions = blocks.flatMap((block) => {
|
|
325
|
+
const composition = coreBlockComposition(block);
|
|
326
|
+
return composition !== null && this.isPlacedHere(block) ? [{
|
|
327
|
+
block,
|
|
328
|
+
composition
|
|
329
|
+
}] : [];
|
|
330
|
+
});
|
|
331
|
+
const unreadable = this.deps.store.unreadableCompositions();
|
|
332
|
+
const nothingToSay = compositions.length === 0 && unreadable.length === 0;
|
|
333
|
+
if (nothingToSay && this.emptyAppliesOwed === 0) return;
|
|
334
|
+
const composer = this.deps.composer;
|
|
335
|
+
if (composer === null) {
|
|
336
|
+
for (const { block } of compositions) await this.report(block.id, "failed", "no composer is available on this node — a composition cannot run here");
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (runners.find((r) => r.name === "composer")?.state === "failed") {
|
|
340
|
+
for (const { block } of compositions) await this.report(block.id, "failed", "the composer runner's crash circuit-breaker tripped — no composition runs until it is restarted");
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const restart = this.pendingCompositionRestarts;
|
|
344
|
+
const integrationId = this.deps.integrationId();
|
|
345
|
+
const storeLoaded = this.deps.storeLoaded();
|
|
346
|
+
try {
|
|
347
|
+
const result = await composer.apply({
|
|
348
|
+
integrationId,
|
|
349
|
+
storeLoaded,
|
|
350
|
+
blocks: compositions.map(({ block, composition }) => ({
|
|
351
|
+
id: block.id,
|
|
352
|
+
name: block.name,
|
|
353
|
+
enabled: block.enabled,
|
|
354
|
+
updatedAt: block.updatedAt,
|
|
355
|
+
composition
|
|
356
|
+
})),
|
|
357
|
+
restartBlockIds: [...restart],
|
|
358
|
+
unreadableBlocks: [...unreadable]
|
|
359
|
+
});
|
|
360
|
+
if (this.failedApplies > 0) {
|
|
361
|
+
this.deps.logger.info("the composer answered again", { meta: { failedApplies: this.failedApplies } });
|
|
362
|
+
this.failedApplies = 0;
|
|
363
|
+
}
|
|
364
|
+
this.pendingCompositionRestarts = new Set([...this.pendingCompositionRestarts].filter((id) => !restart.has(id)));
|
|
365
|
+
this.emptyAppliesOwed = !nothingToSay || !result.acted ? 2 : Math.max(0, this.emptyAppliesOwed - 1);
|
|
366
|
+
const verdicts = new Map(result.blocks.map((v) => [v.blockId, v]));
|
|
367
|
+
for (const { block } of compositions) {
|
|
368
|
+
const verdict = verdicts.get(block.id);
|
|
369
|
+
if (verdict === void 0) await this.report(block.id, "starting", "the composer did not answer for this block");
|
|
370
|
+
else await this.report(block.id, verdict.status, verdict.reason);
|
|
371
|
+
}
|
|
372
|
+
} catch (err) {
|
|
373
|
+
this.failedApplies += 1;
|
|
374
|
+
if (this.failedApplies === 1) this.deps.logger.warn("the composer could not be reached — compositions keep their last status", { meta: {
|
|
375
|
+
error: errMsg(err),
|
|
376
|
+
compositions: compositions.length
|
|
377
|
+
} });
|
|
378
|
+
for (const { block } of compositions) if (block.status === "stopped" && block.enabled) await this.report(block.id, "starting", `the composer is not reachable yet: ${errMsg(err)}`);
|
|
379
|
+
}
|
|
276
380
|
}
|
|
277
381
|
/** Compile, write the package, spawn. Each failure is reported ON the block
|
|
278
382
|
* — the author asked for this to run, so they are owed the reason it does
|
|
@@ -543,12 +647,42 @@ var CORE_BLOCKS_INDEXES = [{
|
|
|
543
647
|
name: "idx_core_blocks_enabled",
|
|
544
648
|
columns: ["enabled"]
|
|
545
649
|
}];
|
|
650
|
+
/**
|
|
651
|
+
* Rows one read asks for. A read that ANSWERS this many is not a loaded table:
|
|
652
|
+
* rows past the limit would be absent, and an absent composition reads as a
|
|
653
|
+
* deleted one (D49).
|
|
654
|
+
*/
|
|
655
|
+
var CORE_BLOCKS_LOAD_LIMIT = 1e3;
|
|
656
|
+
/**
|
|
657
|
+
* What an unparseable row's `source.kind` says, or null when it cannot be told.
|
|
658
|
+
* Absent means `code`, as it does for a readable row.
|
|
659
|
+
*/
|
|
660
|
+
function rawSourceKind(raw) {
|
|
661
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
662
|
+
if (!("source" in raw) || raw.source === void 0) return "code";
|
|
663
|
+
const source = raw.source;
|
|
664
|
+
if (typeof source !== "object" || source === null || !("kind" in source)) return null;
|
|
665
|
+
return typeof source.kind === "string" ? source.kind : null;
|
|
666
|
+
}
|
|
667
|
+
/** An unreadable row that may be a composition: one whose kind says so, or cannot be told. */
|
|
668
|
+
function mayBeComposition(raw) {
|
|
669
|
+
const kind = rawSourceKind(raw);
|
|
670
|
+
return kind !== "code" && kind !== "recipe";
|
|
671
|
+
}
|
|
672
|
+
function parseErrorText(issues) {
|
|
673
|
+
return issues.map((i) => `${i.path.map(String).join(".")}: ${i.message}`).join("; ");
|
|
674
|
+
}
|
|
546
675
|
var CoreBlockStore = class {
|
|
547
676
|
byId = /* @__PURE__ */ new Map();
|
|
677
|
+
/** Rows that exist but do not parse and may be compositions — held for the composer, never dropped (D49). */
|
|
678
|
+
unreadable = [];
|
|
548
679
|
store;
|
|
549
680
|
logger;
|
|
550
681
|
now;
|
|
551
682
|
newId;
|
|
683
|
+
loaded = false;
|
|
684
|
+
/** Consecutive failed reads — the warn is once per run of them, not once per retry. */
|
|
685
|
+
failedLoads = 0;
|
|
552
686
|
constructor(deps) {
|
|
553
687
|
this.store = deps.store;
|
|
554
688
|
this.logger = deps.logger;
|
|
@@ -562,32 +696,70 @@ var CoreBlockStore = class {
|
|
|
562
696
|
indexes: [...CORE_BLOCKS_INDEXES]
|
|
563
697
|
});
|
|
564
698
|
}
|
|
699
|
+
/** True only after a `load()` that READ the table. The composer withdraws nothing otherwise (D49). */
|
|
700
|
+
isLoaded() {
|
|
701
|
+
return this.loaded;
|
|
702
|
+
}
|
|
703
|
+
/** Stored composition rows that do not parse, by id — the composer holds their devices. */
|
|
704
|
+
unreadableCompositions() {
|
|
705
|
+
return this.unreadable;
|
|
706
|
+
}
|
|
565
707
|
/**
|
|
566
708
|
* Hydrate every block.
|
|
567
709
|
*
|
|
568
710
|
* A row whose JSON no longer validates is SKIPPED with a warning rather than
|
|
569
711
|
* dropped or repaired: it is somebody's code, and the honest answer to "I
|
|
570
|
-
* cannot read this" is to leave it on disk and say so.
|
|
712
|
+
* cannot read this" is to leave it on disk and say so. A skipped row that may
|
|
713
|
+
* be a COMPOSITION is also kept in `unreadableCompositions()`: absent from the
|
|
714
|
+
* composer's input it would read as deleted, and its device would go (D49).
|
|
715
|
+
*
|
|
716
|
+
* `source` is parsed here, and a build older than D659 strips `source` and
|
|
717
|
+
* rewrites the row — rolling back below D659 erases compositions.
|
|
571
718
|
*/
|
|
572
719
|
async load() {
|
|
573
720
|
try {
|
|
574
721
|
const rows = await this.store.query.query({
|
|
575
722
|
collection: CORE_BLOCKS_COLLECTION,
|
|
576
|
-
filter: { limit:
|
|
723
|
+
filter: { limit: CORE_BLOCKS_LOAD_LIMIT }
|
|
577
724
|
});
|
|
725
|
+
if (rows.length >= 1e3) throw new Error(`core_blocks answered ${rows.length} rows, the read limit — rows past it would read as deleted`);
|
|
578
726
|
this.byId.clear();
|
|
727
|
+
this.loaded = true;
|
|
728
|
+
if (this.failedLoads > 0) {
|
|
729
|
+
this.logger.info("core blocks loaded after failed reads", { meta: { failedAttempts: this.failedLoads } });
|
|
730
|
+
this.failedLoads = 0;
|
|
731
|
+
}
|
|
579
732
|
let skipped = 0;
|
|
733
|
+
const unreadable = [];
|
|
580
734
|
for (const row of rows) {
|
|
581
|
-
const
|
|
735
|
+
const raw = row.data["block"];
|
|
736
|
+
const parsed = CoreBlockSchema.safeParse(raw);
|
|
582
737
|
if (!parsed.success) {
|
|
583
738
|
skipped += 1;
|
|
739
|
+
if (mayBeComposition(raw)) {
|
|
740
|
+
const name = row.data["name"];
|
|
741
|
+
unreadable.push({
|
|
742
|
+
id: row.id,
|
|
743
|
+
name: typeof name === "string" ? name : null,
|
|
744
|
+
error: parseErrorText(parsed.error.issues)
|
|
745
|
+
});
|
|
746
|
+
}
|
|
584
747
|
continue;
|
|
585
748
|
}
|
|
586
749
|
this.byId.set(parsed.data.id, parsed.data);
|
|
587
750
|
}
|
|
588
|
-
|
|
751
|
+
this.unreadable = unreadable;
|
|
752
|
+
if (skipped > 0) this.logger.warn("core blocks skipped as unreadable — left on disk, not repaired", { meta: {
|
|
753
|
+
skipped,
|
|
754
|
+
heldForComposer: unreadable.map((u) => u.id)
|
|
755
|
+
} });
|
|
589
756
|
} catch (err) {
|
|
590
|
-
this.
|
|
757
|
+
this.failedLoads += 1;
|
|
758
|
+
if (this.failedLoads === 1) this.logger.warn("core block load failed — retried on every converge until it reads", { meta: { error: String(err) } });
|
|
759
|
+
else this.logger.debug("core block load failed again", { meta: {
|
|
760
|
+
error: String(err),
|
|
761
|
+
failedAttempts: this.failedLoads
|
|
762
|
+
} });
|
|
591
763
|
}
|
|
592
764
|
}
|
|
593
765
|
list() {
|
|
@@ -601,6 +773,7 @@ var CoreBlockStore = class {
|
|
|
601
773
|
return this.list().filter((b) => b.enabled && b.placement === nodeId);
|
|
602
774
|
}
|
|
603
775
|
async create(input, createdBy) {
|
|
776
|
+
this.requireLoaded();
|
|
604
777
|
const now = this.now();
|
|
605
778
|
const block = CoreBlockSchema.parse({
|
|
606
779
|
...input,
|
|
@@ -619,13 +792,61 @@ var CoreBlockStore = class {
|
|
|
619
792
|
async update(blockId, patch) {
|
|
620
793
|
const existing = this.byId.get(blockId);
|
|
621
794
|
if (!existing) throw new Error(`core block not found: ${blockId}`);
|
|
795
|
+
if ((patch.code !== void 0 && patch.code !== existing.code || patch.placement !== void 0 && patch.placement !== existing.placement || patch.integrationId !== void 0 && patch.integrationId !== existing.integrationId) && coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — its code, placement and integration are fixed; edit it with updateComposition`);
|
|
796
|
+
if (patch.name !== void 0 && patch.name !== existing.name && coreBlockSourceKind(existing) !== "code") throw new Error(`core block "${existing.name}" is a composition — rename it with updateComposition, which checks expectedUpdatedAt`);
|
|
622
797
|
const updated = CoreBlockSchema.parse({
|
|
623
798
|
...existing,
|
|
624
799
|
...patch,
|
|
625
800
|
id: existing.id,
|
|
626
801
|
createdBy: existing.createdBy,
|
|
627
802
|
createdAt: existing.createdAt,
|
|
628
|
-
updatedAt: this.now()
|
|
803
|
+
updatedAt: Math.max(this.now(), existing.updatedAt + 1)
|
|
804
|
+
});
|
|
805
|
+
await this.persist(updated);
|
|
806
|
+
this.byId.set(updated.id, updated);
|
|
807
|
+
return updated;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Store a composition block (D659): hub-placed, no code, never `running` on
|
|
811
|
+
* creation — the composer's verdict, mirrored by the supervisor, says that.
|
|
812
|
+
*/
|
|
813
|
+
async createComposition(input, createdBy) {
|
|
814
|
+
this.requireLoaded();
|
|
815
|
+
const now = this.now();
|
|
816
|
+
const block = CoreBlockSchema.parse({
|
|
817
|
+
name: input.name,
|
|
818
|
+
enabled: input.enabled,
|
|
819
|
+
placement: "hub",
|
|
820
|
+
code: "",
|
|
821
|
+
source: {
|
|
822
|
+
kind: "composition",
|
|
823
|
+
composition: input.composition
|
|
824
|
+
},
|
|
825
|
+
id: this.newId(),
|
|
826
|
+
createdBy,
|
|
827
|
+
createdAt: now,
|
|
828
|
+
updatedAt: now,
|
|
829
|
+
status: "stopped",
|
|
830
|
+
lastChangedAt: now
|
|
831
|
+
});
|
|
832
|
+
await this.persist(block);
|
|
833
|
+
this.byId.set(block.id, block);
|
|
834
|
+
return block;
|
|
835
|
+
}
|
|
836
|
+
/** Patch a composition block. Immutable: returns the NEW block. Refuses a code block by name. */
|
|
837
|
+
async updateComposition(blockId, patch) {
|
|
838
|
+
const existing = this.byId.get(blockId);
|
|
839
|
+
if (!existing) throw new Error(`core block not found: ${blockId}`);
|
|
840
|
+
if (coreBlockSourceKind(existing) !== "composition") throw new Error(`core block "${existing.name}" is a code block — edit it with update`);
|
|
841
|
+
const updated = CoreBlockSchema.parse({
|
|
842
|
+
...existing,
|
|
843
|
+
...patch.name === void 0 ? {} : { name: patch.name },
|
|
844
|
+
...patch.enabled === void 0 ? {} : { enabled: patch.enabled },
|
|
845
|
+
...patch.composition === void 0 ? {} : { source: {
|
|
846
|
+
kind: "composition",
|
|
847
|
+
composition: patch.composition
|
|
848
|
+
} },
|
|
849
|
+
updatedAt: Math.max(this.now(), existing.updatedAt + 1)
|
|
629
850
|
});
|
|
630
851
|
await this.persist(updated);
|
|
631
852
|
this.byId.set(updated.id, updated);
|
|
@@ -651,9 +872,17 @@ var CoreBlockStore = class {
|
|
|
651
872
|
await this.persist(next);
|
|
652
873
|
this.byId.set(blockId, next);
|
|
653
874
|
}
|
|
875
|
+
/**
|
|
876
|
+
* A write before the table has been read would be wiped from memory by the
|
|
877
|
+
* load that follows (it clears and refills), so it is refused by name.
|
|
878
|
+
*/
|
|
879
|
+
requireLoaded() {
|
|
880
|
+
if (!this.loaded) throw new Error("core_blocks has not loaded yet — nothing can be created until it has; retry shortly");
|
|
881
|
+
}
|
|
654
882
|
/** Idempotent delete — an unknown id is a no-op. */
|
|
655
883
|
async delete(blockId) {
|
|
656
884
|
this.byId.delete(blockId);
|
|
885
|
+
this.unreadable = this.unreadable.filter((u) => u.id !== blockId);
|
|
657
886
|
await this.store.delete.mutate({
|
|
658
887
|
collection: CORE_BLOCKS_COLLECTION,
|
|
659
888
|
key: blockId
|
|
@@ -718,6 +947,11 @@ var BLOCKS_INTEGRATION_NAME = "Blocks";
|
|
|
718
947
|
* with every device on the cluster, and the sweep would delete the blocks'), or
|
|
719
948
|
* make `core-blocks` declare a device of its own. Neither is worth the
|
|
720
949
|
* convenience; the spec test pins both.
|
|
950
|
+
*
|
|
951
|
+
* A SECOND owner hangs devices here since D659: `composer` (every composed
|
|
952
|
+
* device). It is equally safe for the same reason — its rows are owned by
|
|
953
|
+
* `composer`, never by `core-blocks`, and its own withdrawal sweep is scoped to
|
|
954
|
+
* `composer`.
|
|
721
955
|
*/
|
|
722
956
|
async function reconcileBlocksIntegration(ports) {
|
|
723
957
|
return (await new DeclaredDevices({
|
|
@@ -775,6 +1009,229 @@ function armBlocksIntegration(options) {
|
|
|
775
1009
|
})() };
|
|
776
1010
|
}
|
|
777
1011
|
//#endregion
|
|
1012
|
+
//#region src/builtins/core-blocks/composition-api.ts
|
|
1013
|
+
/**
|
|
1014
|
+
* The composition half of the `core-blocks` provider (D659). Flat methods,
|
|
1015
|
+
* spread into the provider: no nested sub-object.
|
|
1016
|
+
*
|
|
1017
|
+
* Every save is VALIDATED against the live sources and every other stored
|
|
1018
|
+
* composition first; a refusal names each problem by path and reason (D391)
|
|
1019
|
+
* and stores nothing. The store's own refusals (not loaded, unknown block, a
|
|
1020
|
+
* code block) propagate unchanged — this layer never swallows them.
|
|
1021
|
+
*/
|
|
1022
|
+
var NOT_LOADED_REASON = "core_blocks has not loaded yet";
|
|
1023
|
+
function writeQueue() {
|
|
1024
|
+
let tail = Promise.resolve();
|
|
1025
|
+
return (write) => {
|
|
1026
|
+
const run = tail.then(write, write);
|
|
1027
|
+
tail = run.then(() => void 0, () => void 0);
|
|
1028
|
+
return run;
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Optimistic concurrency: an update names the `updatedAt` it read. A block
|
|
1033
|
+
* changed since is refused by name — last-write-wins would drop the other
|
|
1034
|
+
* admin's edit without a word. An unknown block falls through to the store,
|
|
1035
|
+
* whose own refusal names it.
|
|
1036
|
+
*/
|
|
1037
|
+
function refuseIfChanged(deps, blockId, expectedUpdatedAt) {
|
|
1038
|
+
const stored = deps.store.get(blockId);
|
|
1039
|
+
if (stored === null || stored.updatedAt === expectedUpdatedAt) return;
|
|
1040
|
+
deps.logger.warn("composition update refused: changed since read", { meta: {
|
|
1041
|
+
blockId,
|
|
1042
|
+
storedUpdatedAt: stored.updatedAt,
|
|
1043
|
+
expectedUpdatedAt
|
|
1044
|
+
} });
|
|
1045
|
+
throw new Error(`core-blocks: the composition "${stored.name}" changed since you read it: stored updatedAt ${stored.updatedAt}, yours ${expectedUpdatedAt} — reload and re-apply your edit`);
|
|
1046
|
+
}
|
|
1047
|
+
async function validateAgainstStore(deps, composition, blockId) {
|
|
1048
|
+
if (!deps.store.isLoaded()) throw new Error(`${NOT_LOADED_REASON} — a composition cannot be judged against the stored ones until it has; retry shortly`);
|
|
1049
|
+
const self = blockId === null ? null : composedDeviceRef(blockId);
|
|
1050
|
+
const peers = deps.store.list().flatMap((block) => {
|
|
1051
|
+
const peer = coreBlockComposition(block);
|
|
1052
|
+
return peer === null || block.id === blockId ? [] : [{
|
|
1053
|
+
target: composedDeviceRef(block.id),
|
|
1054
|
+
composition: peer
|
|
1055
|
+
}];
|
|
1056
|
+
});
|
|
1057
|
+
const selfKey = self === null ? null : compositionSourceKey(self);
|
|
1058
|
+
const refs = compositionSourceRefs(composition).filter((r) => compositionSourceKey(r) !== selfKey);
|
|
1059
|
+
const sources = await deps.describeSources(refs);
|
|
1060
|
+
return validateComposition(composition, {
|
|
1061
|
+
lookupCap: deps.lookupCap,
|
|
1062
|
+
sources,
|
|
1063
|
+
self,
|
|
1064
|
+
peers
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
function describeProblems(validation) {
|
|
1068
|
+
return validation.problems.map((p) => `${p.path} [${p.code}]: ${p.message}`);
|
|
1069
|
+
}
|
|
1070
|
+
function refuse(deps, subject, validation) {
|
|
1071
|
+
const problems = describeProblems(validation);
|
|
1072
|
+
deps.logger.warn("composition save refused", { meta: {
|
|
1073
|
+
name: subject.name,
|
|
1074
|
+
blockId: subject.blockId,
|
|
1075
|
+
problems
|
|
1076
|
+
} });
|
|
1077
|
+
throw new Error(`core-blocks: the composition "${subject.name}" was refused — ${problems.join("; ")}`);
|
|
1078
|
+
}
|
|
1079
|
+
async function readStates(deps, blockIds) {
|
|
1080
|
+
try {
|
|
1081
|
+
const states = await deps.composerStates(blockIds);
|
|
1082
|
+
const byId = new Map(states.map((s) => [s.blockId, s]));
|
|
1083
|
+
return new Map(blockIds.map((id) => [id, {
|
|
1084
|
+
kind: "answered",
|
|
1085
|
+
state: byId.get(id) ?? null
|
|
1086
|
+
}]));
|
|
1087
|
+
} catch (err) {
|
|
1088
|
+
const error = errMsg(err);
|
|
1089
|
+
deps.logger.warn("composer state unreachable", { meta: {
|
|
1090
|
+
blocks: blockIds.length,
|
|
1091
|
+
error
|
|
1092
|
+
} });
|
|
1093
|
+
return new Map(blockIds.map((id) => [id, {
|
|
1094
|
+
kind: "unreachable",
|
|
1095
|
+
error
|
|
1096
|
+
}]));
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
function toViews(blocks, states) {
|
|
1100
|
+
return blocks.flatMap((block) => {
|
|
1101
|
+
const state = states.get(block.id);
|
|
1102
|
+
return state === void 0 ? [] : [{
|
|
1103
|
+
block,
|
|
1104
|
+
state
|
|
1105
|
+
}];
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Every write runs validate-then-persist on ONE chain. Validation awaits a
|
|
1110
|
+
* source read before the save lands; two unserialized saves (A reads B, B
|
|
1111
|
+
* reads A) would each be judged against a store without the other and BOTH
|
|
1112
|
+
* pass — a stored cycle nothing downstream catches.
|
|
1113
|
+
*/
|
|
1114
|
+
function buildCompositionApi(deps) {
|
|
1115
|
+
const serialized = writeQueue();
|
|
1116
|
+
return {
|
|
1117
|
+
createComposition: ({ block, caller }) => serialized(async () => {
|
|
1118
|
+
const validation = await validateAgainstStore(deps, block.composition, null);
|
|
1119
|
+
if (!validation.ok) refuse(deps, {
|
|
1120
|
+
name: block.name,
|
|
1121
|
+
blockId: null
|
|
1122
|
+
}, validation);
|
|
1123
|
+
const created = await deps.store.createComposition(block, caller.userId);
|
|
1124
|
+
deps.logger.info("composition created", { meta: {
|
|
1125
|
+
blockId: created.id,
|
|
1126
|
+
name: created.name,
|
|
1127
|
+
by: caller.userId
|
|
1128
|
+
} });
|
|
1129
|
+
deps.convergeSoon();
|
|
1130
|
+
return { block: created };
|
|
1131
|
+
}),
|
|
1132
|
+
updateComposition: ({ blockId, expectedUpdatedAt, block, caller }) => serialized(async () => {
|
|
1133
|
+
refuseIfChanged(deps, blockId, expectedUpdatedAt);
|
|
1134
|
+
if (block.composition !== void 0) {
|
|
1135
|
+
const validation = await validateAgainstStore(deps, block.composition, blockId);
|
|
1136
|
+
if (!validation.ok) refuse(deps, {
|
|
1137
|
+
name: block.name ?? blockId,
|
|
1138
|
+
blockId
|
|
1139
|
+
}, validation);
|
|
1140
|
+
}
|
|
1141
|
+
const updated = await deps.store.updateComposition(blockId, block);
|
|
1142
|
+
deps.logger.info("composition updated", { meta: {
|
|
1143
|
+
blockId,
|
|
1144
|
+
by: caller.userId
|
|
1145
|
+
} });
|
|
1146
|
+
deps.convergeSoon();
|
|
1147
|
+
return { block: updated };
|
|
1148
|
+
}),
|
|
1149
|
+
getComposition: async ({ blockId }) => {
|
|
1150
|
+
if (!deps.store.isLoaded()) return {
|
|
1151
|
+
state: "not-loaded",
|
|
1152
|
+
reason: NOT_LOADED_REASON
|
|
1153
|
+
};
|
|
1154
|
+
const block = deps.store.get(blockId);
|
|
1155
|
+
if (block === null || coreBlockComposition(block) === null) return {
|
|
1156
|
+
state: "loaded",
|
|
1157
|
+
view: null
|
|
1158
|
+
};
|
|
1159
|
+
const [view] = toViews([block], await readStates(deps, [blockId]));
|
|
1160
|
+
return {
|
|
1161
|
+
state: "loaded",
|
|
1162
|
+
view: view ?? null
|
|
1163
|
+
};
|
|
1164
|
+
},
|
|
1165
|
+
listCompositions: async () => {
|
|
1166
|
+
if (!deps.store.isLoaded()) return {
|
|
1167
|
+
state: "not-loaded",
|
|
1168
|
+
reason: NOT_LOADED_REASON
|
|
1169
|
+
};
|
|
1170
|
+
const blocks = deps.store.list().filter((b) => coreBlockComposition(b) !== null);
|
|
1171
|
+
if (blocks.length === 0) return {
|
|
1172
|
+
state: "loaded",
|
|
1173
|
+
views: []
|
|
1174
|
+
};
|
|
1175
|
+
return {
|
|
1176
|
+
state: "loaded",
|
|
1177
|
+
views: toViews(blocks, await readStates(deps, blocks.map((b) => b.id)))
|
|
1178
|
+
};
|
|
1179
|
+
},
|
|
1180
|
+
validateComposition: async ({ composition, blockId }) => validateAgainstStore(deps, composition, blockId ?? null)
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
//#endregion
|
|
1184
|
+
//#region src/builtins/core-blocks/composition-sources.ts
|
|
1185
|
+
/**
|
|
1186
|
+
* Describe a composition's sources for SAVE-time validation (D659): one
|
|
1187
|
+
* `listAll` per owning addon, one `getBindings` per found device. A read that
|
|
1188
|
+
* fails makes the source `unreadable`, and the save is REFUSED rather than
|
|
1189
|
+
* stored unchecked. Refusing a save destroys nothing (D49).
|
|
1190
|
+
*
|
|
1191
|
+
* Sources are keyed by `{addonId, stableId}` — never a numeric device id,
|
|
1192
|
+
* which is this node's allocation and not the composition's identity.
|
|
1193
|
+
*/
|
|
1194
|
+
async function listOwners(ports, refs) {
|
|
1195
|
+
const addons = [...new Set(refs.map((r) => r.addonId))];
|
|
1196
|
+
const listed = await Promise.all(addons.map(async (addonId) => {
|
|
1197
|
+
try {
|
|
1198
|
+
return [addonId, await ports.listByAddon(addonId)];
|
|
1199
|
+
} catch (err) {
|
|
1200
|
+
return [addonId, { error: errMsg(err) }];
|
|
1201
|
+
}
|
|
1202
|
+
}));
|
|
1203
|
+
return new Map(listed);
|
|
1204
|
+
}
|
|
1205
|
+
async function describeOne(ports, ref, listing) {
|
|
1206
|
+
if (listing === void 0) return {
|
|
1207
|
+
kind: "unreadable",
|
|
1208
|
+
error: `\`${ref.addonId}\` was not listed`
|
|
1209
|
+
};
|
|
1210
|
+
if ("error" in listing) return {
|
|
1211
|
+
kind: "unreadable",
|
|
1212
|
+
error: listing.error
|
|
1213
|
+
};
|
|
1214
|
+
const row = listing.find((r) => r.stableId === ref.stableId);
|
|
1215
|
+
if (row === void 0) return { kind: "absent" };
|
|
1216
|
+
try {
|
|
1217
|
+
return {
|
|
1218
|
+
kind: "found",
|
|
1219
|
+
deviceId: row.id,
|
|
1220
|
+
caps: new Set(await ports.boundCaps(row.id))
|
|
1221
|
+
};
|
|
1222
|
+
} catch (err) {
|
|
1223
|
+
return {
|
|
1224
|
+
kind: "unreadable",
|
|
1225
|
+
error: errMsg(err)
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
async function describeCompositionSources(ports, refs) {
|
|
1230
|
+
const owners = await listOwners(ports, refs);
|
|
1231
|
+
const described = await Promise.all(refs.map(async (ref) => [compositionSourceKey(ref), await describeOne(ports, ref, owners.get(ref.addonId))]));
|
|
1232
|
+
return new Map(described);
|
|
1233
|
+
}
|
|
1234
|
+
//#endregion
|
|
778
1235
|
//#region src/builtins/core-blocks/core-blocks.addon.ts
|
|
779
1236
|
/**
|
|
780
1237
|
* The `core-blocks` provider — CRUD over user-authored TypeScript, with the
|
|
@@ -886,6 +1343,7 @@ var CoreBlocksAddon = class extends BaseAddon {
|
|
|
886
1343
|
store: {
|
|
887
1344
|
list: () => store.list(),
|
|
888
1345
|
get: (blockId) => store.get(blockId),
|
|
1346
|
+
unreadableCompositions: () => store.unreadableCompositions(),
|
|
889
1347
|
setStatus: (blockId, status, error) => store.setStatus(blockId, status, error)
|
|
890
1348
|
},
|
|
891
1349
|
host: createBrokerProcessHost(broker),
|
|
@@ -896,7 +1354,15 @@ var CoreBlocksAddon = class extends BaseAddon {
|
|
|
896
1354
|
};
|
|
897
1355
|
return compileBlock(this.transpiler, code);
|
|
898
1356
|
},
|
|
899
|
-
logger: this.ctx.logger.child("supervisor")
|
|
1357
|
+
logger: this.ctx.logger.child("supervisor"),
|
|
1358
|
+
composer: { apply: async (input) => {
|
|
1359
|
+
const api = this.ctx.api;
|
|
1360
|
+
if (!api) throw new Error("no ctx.api on this node — the composer cannot be reached");
|
|
1361
|
+
return api.composer.applyBlocks.mutate(input);
|
|
1362
|
+
} },
|
|
1363
|
+
integrationId: () => this.blocksIntegrationId,
|
|
1364
|
+
storeLoaded: () => store.isLoaded(),
|
|
1365
|
+
loadStore: () => store.load()
|
|
900
1366
|
});
|
|
901
1367
|
}
|
|
902
1368
|
/**
|
|
@@ -1032,8 +1498,35 @@ var CoreBlocksAddon = class extends BaseAddon {
|
|
|
1032
1498
|
filePath: l.filePath,
|
|
1033
1499
|
content: l.content
|
|
1034
1500
|
})) };
|
|
1035
|
-
}
|
|
1501
|
+
},
|
|
1502
|
+
...this.buildCompositionApi()
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* The composition methods (D659), flat. Sources are read over `ctx.api` at
|
|
1507
|
+
* SAVE time — one listing per owning addon, one binding read per found
|
|
1508
|
+
* device — and a read that fails refuses the save rather than skipping it.
|
|
1509
|
+
*/
|
|
1510
|
+
buildCompositionApi() {
|
|
1511
|
+
const requireApi = () => {
|
|
1512
|
+
const api = this.ctx.api;
|
|
1513
|
+
if (!api) throw new Error("core-blocks: no ctx.api on this node — compositions cannot be read");
|
|
1514
|
+
return api;
|
|
1036
1515
|
};
|
|
1516
|
+
return buildCompositionApi({
|
|
1517
|
+
store: this.requireStore(),
|
|
1518
|
+
describeSources: (refs) => describeCompositionSources({
|
|
1519
|
+
listByAddon: async (addonId) => requireApi().deviceManager.listAll.query({
|
|
1520
|
+
addonId,
|
|
1521
|
+
projection: "slim"
|
|
1522
|
+
}),
|
|
1523
|
+
boundCaps: async (deviceId) => (await requireApi().deviceManager.getBindings.query({ deviceId })).entries.map((e) => e.capName)
|
|
1524
|
+
}, refs),
|
|
1525
|
+
lookupCap: buildCapabilityLookup(ALL_CAPABILITY_DEFINITIONS),
|
|
1526
|
+
composerStates: async (blockIds) => (await requireApi().composer.getStates.query({ blockIds: [...blockIds] })).states,
|
|
1527
|
+
convergeSoon: () => this.convergeSoon(),
|
|
1528
|
+
logger: this.ctx.logger.child("compositions")
|
|
1529
|
+
});
|
|
1037
1530
|
}
|
|
1038
1531
|
};
|
|
1039
1532
|
/**
|
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
7
|
-
const require_retired_settings_keys = require("../../retired-settings-keys-
|
|
6
|
+
const require_dist = require("../../dist-BhO9eYKO.js");
|
|
7
|
+
const require_retired_settings_keys = require("../../retired-settings-keys-DHgnJqS3.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let _camstack_types_node = require("@camstack/types/node");
|
|
10
10
|
//#region src/builtins/device-manager/adoption-job-engine.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-
|
|
1
|
+
import { Bn as WELL_KNOWN_TAB_MAP, C as MINUTES_PER_DAY, Fn as DeviceFeature, Ht as locationIconKey, In as DeviceRole, Ln as DeviceType, Ot as formatMinutes, Rt as legacyAutoLinkIds, Yn as isDeviceConfigCap, Zt as normalizeUnit, _ as DeviceStatusSchema, bt as enumerateSchemaFields, dt as defineExtensionSlotProvider, en as parseStreamParamsFormPatch, gt as deviceStatusCapability, ht as deviceStateCapability, ir as EventCategory, j as STREAM_PROFILE_META, jn as BaseAddon, kn as errMsg, ln as runtimeStatePolicyFor, mn as siteZoneKey, mt as deviceManagerCapability, o as CAMERA_SWITCH_ORDER, p as DAY_NAMES, pt as deviceExtensionCapability, q as buildStreamParamsConfigSchema, rr as sleep$1, s as CAP_NAMES_WITH_STATUS, st as createDeviceExtensionProvider, t as ALL_CAPABILITY_DEFINITIONS, wt as expandContainer, x as LocationIconIdSchema, yt as enumerateItemArrayFields } from "../../dist-BEuQPDk_.mjs";
|
|
2
|
+
import { n as purgeRetiredSettingsRows } from "../../retired-settings-keys-CQvG6B-Q.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { canonicalDeviceFingerprint } from "@camstack/types/node";
|
|
5
5
|
//#region src/builtins/device-manager/adoption-job-engine.ts
|