@camstack/addon-admin-ui 1.2.526 → 1.2.528
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/CHANGELOG.md +19 -0
- package/dist/assets/{AddonAssetIcon-Bk09xHqf.js → AddonAssetIcon-B1rn0DLw.js} +1 -1
- package/dist/assets/Addons-jOEeQSpu.js +5 -0
- package/dist/assets/{AdminPage-CYSYefaL.js → AdminPage-DmkuRREx.js} +1 -1
- package/dist/assets/{AdminSectionedPage-B4MRvqE8.js → AdminSectionedPage-DuWHEheV.js} +1 -1
- package/dist/assets/AiProfiles-BEeNwF2L.js +3 -0
- package/dist/assets/Build-Co5ZdxuK.js +40 -0
- package/dist/assets/Calendars-BtDHWxd-.js +1 -0
- package/dist/assets/{Cluster-Dkt_tVUy.js → Cluster-BIBo49N1.js} +1 -1
- package/dist/assets/{ComposedDeviceEditDialog-DJ5mqI6C.js → ComposedDeviceEditDialog-B4GFluaT.js} +1 -1
- package/dist/assets/CompositionBlockEditor-BpvjitmC.js +2 -0
- package/dist/assets/CompositionEditorPage-dKqEx-JR.js +1 -0
- package/dist/assets/Data-RjeL53U1.js +2 -0
- package/dist/assets/DeviceDetail-eIrghHfV.js +2 -0
- package/dist/assets/DeviceDoActionFields-NGMXRetO.js +1 -0
- package/dist/assets/Devices-BGUXCPk6.js +1 -0
- package/dist/assets/{EmbedPlayerPage-DlIQigBi.js → EmbedPlayerPage-D0lVyXps.js} +1 -1
- package/dist/assets/FormBuilder-C-ti9Mun.js +1 -0
- package/dist/assets/History-DFE__TwY.js +1 -0
- package/dist/assets/Identity-xGpm6dM2.js +1 -0
- package/dist/assets/IntegrationDetail-BV550NaL.js +1 -0
- package/dist/assets/IntegrationHub-zoj_qUVu.js +1 -0
- package/dist/assets/Integrations-BgOMB--y.js +1 -0
- package/dist/assets/Integrations-Dnahdxh6.js +1 -0
- package/dist/assets/Library-Dta_WXbr.js +1 -0
- package/dist/assets/Locations-qtdnIjEQ.js +3 -0
- package/dist/assets/{Logs-BxTfJ-wk.js → Logs-DY8msvrb.js} +1 -1
- package/dist/assets/{MaskShapeCanvas-DBEbXuH2.js → MaskShapeCanvas-HJuLPIJP.js} +1 -1
- package/dist/assets/{MotionZonesSettings-D3Xxjf5N.js → MotionZonesSettings-COMPKhfx.js} +1 -1
- package/dist/assets/MyAccess-ayWZOTHE.js +2 -0
- package/dist/assets/Network-DDRo1U4A.js +1 -0
- package/dist/assets/NodeAddonsSettingsPanel-Bin3whKR.js +1 -0
- package/dist/assets/Notifications-DxWf70ir.js +1 -0
- package/dist/assets/Pipeline-B1zEi2hO.js +1 -0
- package/dist/assets/{PrivacyMaskSettings-CswE6pks.js → PrivacyMaskSettings-C88JqmdE.js} +1 -1
- package/dist/assets/Recordings-BQZ9OaK2.js +1 -0
- package/dist/assets/{RemoteGrid-BjtzHkpg.js → RemoteGrid-CF92aN64.js} +1 -1
- package/dist/assets/ScanNetworkModal-DNtPeZ2B.js +1 -0
- package/dist/assets/{SceneMonitorEditor-C2X6jZvS.js → SceneMonitorEditor-mE7W6emU.js} +1 -1
- package/dist/assets/SessionsList-DqDv9LKK.js +2 -0
- package/dist/assets/Showroom-D8Shgmh7.js +1 -0
- package/dist/assets/System-Cl5e6KJW.js +1 -0
- package/dist/assets/{Templates-tilFwunF.js → Templates-D0V7IdcS.js} +1 -1
- package/dist/assets/{Terminal-DiMnsE8i.js → Terminal-BBAoUjfs.js} +5 -5
- package/dist/assets/Trans-DvLMqjNd.js +1 -0
- package/dist/assets/{Viewer-BwirleAM.js → Viewer-BTxcZ-BR.js} +1 -1
- package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-w6FCrjgB.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-CmowmspB.js} +1 -1
- package/dist/assets/{addon-icons-context-CHk3ePjj.js → addon-icons-context-DZZCjo9x.js} +1 -1
- package/dist/assets/{archive-BU_eXxQC.js → archive-Bu9T4BL0.js} +1 -1
- package/dist/assets/{assignment-candidates-B4bOK7k8.js → assignment-candidates-YAud75JK.js} +1 -1
- package/dist/assets/{authed-media-BYuQnqbF.js → authed-media-yW7W_8pm.js} +1 -1
- package/dist/assets/{bell-off-DCQedZIx.js → bell-off-E0Abr7jJ.js} +1 -1
- package/dist/assets/{board-model-t0EX_kXW.js → board-model-BgDnb_No.js} +1 -1
- package/dist/assets/{boxes-BZMO9uMf.js → boxes-BkzsIUkw.js} +1 -1
- package/dist/assets/circle-slash-pdaivo-U.js +1 -0
- package/dist/assets/client-BvflqtP1.js +711 -0
- package/dist/assets/{cloud-upload-CApwQ18P.js → cloud-upload-BIjzjy_K.js} +1 -1
- package/dist/assets/crop-CZJDrMcX.js +1 -0
- package/dist/assets/device-integration-icon-Dxf6i9YB.js +1 -0
- package/dist/assets/device-picker-model-2dkiqOI6.js +1 -0
- package/dist/assets/{dist-Hz1nnJDv.js → dist-DoqhrZoo.js} +1 -1
- package/dist/assets/dist-Gm_hHBN2.js +711 -0
- package/dist/assets/{file-up-cso0QIDs.js → file-up-Cyg6A2qN.js} +1 -1
- package/dist/assets/{format-Csy9sDox.js → format-D6gOZRRr.js} +1 -1
- package/dist/assets/{grip-vertical-DqjjXK1h.js → grip-vertical-CLVyc1HC.js} +1 -1
- package/dist/assets/{hard-drive-download-D-ykdsm5.js → hard-drive-download-Cd122TYb.js} +1 -1
- package/dist/assets/{hostInit-CTFssGi8.js → hostInit-DR1ykbVx.js} +1 -1
- package/dist/assets/i18n-C02pTNTJ.js +1 -0
- package/dist/assets/icons-BSJCF6qM.js +1 -0
- package/dist/assets/index-BrupW6X6.css +2 -0
- package/dist/assets/index-CoVcMsxX.js +4 -0
- package/dist/assets/{key-C_dbeH7F.js → key-D_zqd1r-.js} +1 -1
- package/dist/assets/{key-round-CPg0Y5qN.js → key-round-pNw827HU.js} +1 -1
- package/dist/assets/{layers-Cb-ro1vb.js → layers-CFNXRt4j.js} +1 -1
- package/dist/assets/{linked-devices-model-ZCUJsj_d.js → linked-devices-model-CPj1sFzr.js} +1 -1
- package/dist/assets/{list-ordered-V6P5uujF.js → list-ordered-Dy6tCUD8.js} +1 -1
- package/dist/assets/location-owned-usage-BhWswowa.js +1 -0
- package/dist/assets/{memory-stick-Mm_iVwSZ.js → memory-stick-Bt2KoB-0.js} +1 -1
- package/dist/assets/{message-square-plus-Bfam15lf.js → message-square-plus-rkd_X0vs.js} +1 -1
- package/dist/assets/{mf-entry-bootstrap-0-019f9c14.js → mf-entry-bootstrap-0-bba9c01e.js} +2 -2
- package/dist/assets/{mic-off-CeSZf8K2.js → mic-off-DWb36TX2.js} +1 -1
- package/dist/assets/{monitor-off-U-RK64kw.js → monitor-off-B2GWMMt_.js} +1 -1
- package/dist/assets/{monitor-smartphone-DezRerVw.js → monitor-smartphone-DJzvbVtx.js} +1 -1
- package/dist/assets/mount-2dkiqOI6.js +1 -0
- package/dist/assets/notification-schedule-field-XRy8SQoM.js +1 -0
- package/dist/assets/{player-overlays-DgedyCX9.js → player-overlays-DpZIxQHp.js} +1 -1
- package/dist/assets/{recycle-Dkb1d-SV.js → recycle-BWLoavmg.js} +1 -1
- package/dist/assets/remoteEntry-CD4F7fE2.js +1 -0
- package/dist/assets/{ruler-DzpBI-EP.js → ruler-C1SbVRl3.js} +1 -1
- package/dist/assets/{scan-eye-BmLTXsIf.js → scan-eye-Bv4PGTFl.js} +1 -1
- package/dist/assets/{scissors-lBRCylhQ.js → scissors-BsEkbrx_.js} +1 -1
- package/dist/assets/{search-x-BRhrXpKx.js → search-x-DE4DxdsF.js} +1 -1
- package/dist/assets/{send-DVJoNc0-.js → send-Dd0BBamd.js} +1 -1
- package/dist/assets/{settings-2-D6sp_FTm.js → settings-2-z3X6U2eo.js} +1 -1
- package/dist/assets/{shapes-CQn3wvpk.js → shapes-D-IaC6en.js} +1 -1
- package/dist/assets/{sigma-CKK8bLRa.js → sigma-CxBVWJ85.js} +1 -1
- package/dist/assets/{speaker-BYorOPbN.js → speaker-Dy79a571.js} +1 -1
- package/dist/assets/src-KDhMZ5Ob.js +67 -0
- package/dist/assets/sso-popup-BYo1cU_K.js +1 -0
- package/dist/assets/{step-inspector-model-DEfajBM3.js → step-inspector-model-DG2Cy9_m.js} +1 -1
- package/dist/assets/{table-2-BPB4V5Fg.js → table-2-IUuOvuT8.js} +1 -1
- package/dist/assets/{timer-reset-Cu_VSXbl.js → timer-reset-D1H0hoMf.js} +1 -1
- package/dist/assets/{unlink-BcrHT0zX.js → unlink-DTu1e00O.js} +1 -1
- package/dist/assets/{usb-DhXPqJtn.js → usb-CFK8z-TS.js} +1 -1
- package/dist/assets/{use-device-bound-caps-K47AcsQ5.js → use-device-bound-caps-DS4FtJtN.js} +1 -1
- package/dist/assets/{use-scenes-feed-DQrMGL5E.js → use-scenes-feed-CE-KFGxN.js} +1 -1
- package/dist/assets/{use-stationary-objects-yu-O0RPa.js → use-stationary-objects-CFLRkxjx.js} +1 -1
- package/dist/assets/useEventInvalidation-2dkiqOI6.js +1 -0
- package/dist/assets/useNodeUrlSelection-Pafi-8p7.js +1 -0
- package/dist/assets/user-round-BSVSJsjB.js +1 -0
- package/dist/assets/{user-x-DRSOzwTr.js → user-x-KWsBR0tG.js} +1 -1
- package/dist/assets/variable-BG_qKWYf.js +1 -0
- package/dist/assets/{video-5IaJhdjU.js → video-D0guHpd1.js} +1 -1
- package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-BtgDin-X.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-FCSZ5q7m.js} +2 -2
- package/dist/index.html +14 -10
- package/dist/server/addon.js +910 -116
- package/package.json +1 -1
- package/dist/assets/Addons-BwXNg54C.js +0 -5
- package/dist/assets/AiProfiles-CRZH6xR0.js +0 -3
- package/dist/assets/BrokerEditorDialog-D9rZCGl9.js +0 -1
- package/dist/assets/Build-BJaqr-_Y.js +0 -40
- package/dist/assets/Calendars-D4ed6Nqc.js +0 -1
- package/dist/assets/CompositionBlockEditor-BEondavu.js +0 -2
- package/dist/assets/CompositionEditorPage-4rHclFIA.js +0 -1
- package/dist/assets/Data-pZe3A7Va.js +0 -2
- package/dist/assets/DeviceDetail-3jKJ80IC.js +0 -2
- package/dist/assets/DeviceDoActionFields-1jte2ge2.js +0 -1
- package/dist/assets/Devices-Bq4yRli5.js +0 -1
- package/dist/assets/History-DTt-NAMR.js +0 -1
- package/dist/assets/Identity-C3GHYP7_.js +0 -1
- package/dist/assets/IntegrationDetail-EOwq3jVG.js +0 -1
- package/dist/assets/IntegrationHub-8GCewFBs.js +0 -1
- package/dist/assets/Integrations-BocurtE0.js +0 -1
- package/dist/assets/Integrations-Bt3Im7Mh.js +0 -1
- package/dist/assets/Library-DTn9Nkjs.js +0 -1
- package/dist/assets/Locations-chgnzdWz.js +0 -3
- package/dist/assets/MyAccess-BTEJNJtS.js +0 -2
- package/dist/assets/Network-plYS78UN.js +0 -1
- package/dist/assets/NodeAddonsSettingsPanel-Do_I93Mx.js +0 -1
- package/dist/assets/Notifications-mslnotOB.js +0 -1
- package/dist/assets/Pipeline-Cjj8XOuS.js +0 -1
- package/dist/assets/Recordings-BpB53ln_.js +0 -1
- package/dist/assets/ScanNetworkModal-BR3tzZcL.js +0 -1
- package/dist/assets/SessionsList-BOjlUhj5.js +0 -2
- package/dist/assets/Showroom-CHwcC6p6.js +0 -1
- package/dist/assets/System-CiZ0xcyP.js +0 -1
- package/dist/assets/circle-slash-DFl97Cbx.js +0 -1
- package/dist/assets/client-F3l12jEi.js +0 -674
- package/dist/assets/crop-gfwPOF8e.js +0 -1
- package/dist/assets/device-integration-icon-CXG5v8dp.js +0 -1
- package/dist/assets/device-picker-model-Fpx4y0MY.js +0 -1
- package/dist/assets/dist-B2m_6_7g.js +0 -674
- package/dist/assets/i18n-BH0PY6IL.js +0 -1
- package/dist/assets/icons-B9uR9qKs.js +0 -1
- package/dist/assets/index-CQ7TjSgs.js +0 -4
- package/dist/assets/index-DjwiZ7qT.css +0 -2
- package/dist/assets/location-owned-usage-C6DcC3IT.js +0 -1
- package/dist/assets/log-in-Cxvw8rxc.js +0 -1
- package/dist/assets/mount-Fpx4y0MY.js +0 -1
- package/dist/assets/remoteEntry-DpkkaSO3.js +0 -1
- package/dist/assets/src-B0EA_4Ed.js +0 -67
- package/dist/assets/useEventInvalidation-Fpx4y0MY.js +0 -1
- package/dist/assets/useNodeUrlSelection-BuraZwll.js +0 -1
- package/dist/assets/user-round-D6wDvac9.js +0 -1
- package/dist/assets/variable-cv0ONmJ-.js +0 -1
package/dist/server/addon.js
CHANGED
|
@@ -5456,7 +5456,7 @@ var ZodIssueCode = {
|
|
|
5456
5456
|
var ZodFirstPartyTypeKind;
|
|
5457
5457
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5458
5458
|
//#endregion
|
|
5459
|
-
//#region ../types/dist/sleep-
|
|
5459
|
+
//#region ../types/dist/sleep-CeyhrcOk.mjs
|
|
5460
5460
|
/**
|
|
5461
5461
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5462
5462
|
* window to float samples (D455).
|
|
@@ -7523,6 +7523,7 @@ var BaseAddon = class {
|
|
|
7523
7523
|
* a warning and falls back rather than crashing boot.
|
|
7524
7524
|
*/
|
|
7525
7525
|
state(key, schema, fallback) {
|
|
7526
|
+
this._declaredStateKeys.add(key);
|
|
7526
7527
|
return createDurableState({
|
|
7527
7528
|
key,
|
|
7528
7529
|
schema,
|
|
@@ -7538,6 +7539,96 @@ var BaseAddon = class {
|
|
|
7538
7539
|
} })
|
|
7539
7540
|
});
|
|
7540
7541
|
}
|
|
7542
|
+
/** Durable-state keys this process registered through {@link state}. */
|
|
7543
|
+
_declaredStateKeys = /* @__PURE__ */ new Set();
|
|
7544
|
+
/**
|
|
7545
|
+
* Stale-data audit: every global-settings key this addon still declares —
|
|
7546
|
+
* constructor defaults, settings-schema fields (layout containers included),
|
|
7547
|
+
* durable-state keys and the root of each `secretStatePaths` entry. Answered
|
|
7548
|
+
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
7549
|
+
*/
|
|
7550
|
+
async getDeclaredSettingsKeys() {
|
|
7551
|
+
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
7552
|
+
}
|
|
7553
|
+
declaredKeySet() {
|
|
7554
|
+
const keys = new Set(Object.keys(this.defaults));
|
|
7555
|
+
const schema = this.globalSettingsSchema();
|
|
7556
|
+
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
7557
|
+
for (const key of this._declaredStateKeys) keys.add(key);
|
|
7558
|
+
for (const path of this.secretStatePaths) {
|
|
7559
|
+
const root = path.split(/[.[]/, 1)[0];
|
|
7560
|
+
if (root) keys.add(root);
|
|
7561
|
+
}
|
|
7562
|
+
return keys;
|
|
7563
|
+
}
|
|
7564
|
+
/**
|
|
7565
|
+
* Stale-data audit: remove stored global-settings keys the addon no longer
|
|
7566
|
+
* declares, and clear the keychain values their markers held — except a value
|
|
7567
|
+
* a surviving key still points at. A declared or absent key is refused.
|
|
7568
|
+
*/
|
|
7569
|
+
async unsetStoredSettingsKeys(keys) {
|
|
7570
|
+
const settings = this._ctx?.settings;
|
|
7571
|
+
const raw = this.rawSettings();
|
|
7572
|
+
if (!settings || !raw) return {
|
|
7573
|
+
removed: [],
|
|
7574
|
+
refused: keys.map((key) => ({
|
|
7575
|
+
key,
|
|
7576
|
+
reason: "no settings store"
|
|
7577
|
+
})),
|
|
7578
|
+
secretsCleared: 0
|
|
7579
|
+
};
|
|
7580
|
+
let stored;
|
|
7581
|
+
try {
|
|
7582
|
+
stored = await raw.readAddonStore();
|
|
7583
|
+
} catch (error) {
|
|
7584
|
+
const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
|
|
7585
|
+
return {
|
|
7586
|
+
removed: [],
|
|
7587
|
+
refused: keys.map((key) => ({
|
|
7588
|
+
key,
|
|
7589
|
+
reason
|
|
7590
|
+
})),
|
|
7591
|
+
secretsCleared: 0
|
|
7592
|
+
};
|
|
7593
|
+
}
|
|
7594
|
+
const declared = this.declaredKeySet();
|
|
7595
|
+
const removed = [];
|
|
7596
|
+
const refused = [];
|
|
7597
|
+
for (const key of keys) {
|
|
7598
|
+
const base = key.split("@", 1)[0] ?? key;
|
|
7599
|
+
if (declared.has(base)) refused.push({
|
|
7600
|
+
key,
|
|
7601
|
+
reason: "still declared by the addon"
|
|
7602
|
+
});
|
|
7603
|
+
else if (!(key in stored)) refused.push({
|
|
7604
|
+
key,
|
|
7605
|
+
reason: "not stored"
|
|
7606
|
+
});
|
|
7607
|
+
else removed.push(key);
|
|
7608
|
+
}
|
|
7609
|
+
if (removed.length === 0) return {
|
|
7610
|
+
removed,
|
|
7611
|
+
refused,
|
|
7612
|
+
secretsCleared: 0
|
|
7613
|
+
};
|
|
7614
|
+
const kept = /* @__PURE__ */ new Set();
|
|
7615
|
+
const held = /* @__PURE__ */ new Set();
|
|
7616
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
7617
|
+
const target = removed.includes(key) ? held : kept;
|
|
7618
|
+
for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
|
|
7619
|
+
}
|
|
7620
|
+
const refsToClear = [...held].filter((ref) => !kept.has(ref));
|
|
7621
|
+
const patch = {};
|
|
7622
|
+
for (const key of removed) patch[key] = void 0;
|
|
7623
|
+
await settings.writeAddonStore(patch);
|
|
7624
|
+
const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
|
|
7625
|
+
await this.resolveConfig();
|
|
7626
|
+
return {
|
|
7627
|
+
removed,
|
|
7628
|
+
refused,
|
|
7629
|
+
secretsCleared
|
|
7630
|
+
};
|
|
7631
|
+
}
|
|
7541
7632
|
/** Per-device variant of {@link state}, backed by the per-device store. */
|
|
7542
7633
|
deviceState(deviceId, key, schema, fallback) {
|
|
7543
7634
|
return createDurableState({
|
|
@@ -7808,6 +7899,21 @@ function dropPerNodeFields(fields) {
|
|
|
7808
7899
|
}
|
|
7809
7900
|
return kept;
|
|
7810
7901
|
}
|
|
7902
|
+
function collectFieldKeys(fields) {
|
|
7903
|
+
const collected = [];
|
|
7904
|
+
for (const field of fields) {
|
|
7905
|
+
if (field.type === "group") {
|
|
7906
|
+
collected.push(...collectFieldKeys(field.fields));
|
|
7907
|
+
continue;
|
|
7908
|
+
}
|
|
7909
|
+
if (field.type === "sub-tabs") {
|
|
7910
|
+
for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
|
|
7911
|
+
continue;
|
|
7912
|
+
}
|
|
7913
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
7914
|
+
}
|
|
7915
|
+
return collected;
|
|
7916
|
+
}
|
|
7811
7917
|
function collectPerNodeFieldKeys(fields) {
|
|
7812
7918
|
const collected = [];
|
|
7813
7919
|
for (const field of fields) {
|
|
@@ -15934,8 +16040,172 @@ object({
|
|
|
15934
16040
|
quantity: ReadingQuantitySchema,
|
|
15935
16041
|
unit: string().min(1)
|
|
15936
16042
|
});
|
|
15937
|
-
|
|
15938
|
-
|
|
16043
|
+
new Set([
|
|
16044
|
+
{
|
|
16045
|
+
id: "person",
|
|
16046
|
+
name: "Person"
|
|
16047
|
+
},
|
|
16048
|
+
{
|
|
16049
|
+
id: "vehicle",
|
|
16050
|
+
name: "Vehicle"
|
|
16051
|
+
},
|
|
16052
|
+
{
|
|
16053
|
+
id: "animal",
|
|
16054
|
+
name: "Animal"
|
|
16055
|
+
},
|
|
16056
|
+
{
|
|
16057
|
+
id: "package",
|
|
16058
|
+
name: "Package"
|
|
16059
|
+
}
|
|
16060
|
+
].map((l) => l.id));
|
|
16061
|
+
var COCO_TO_MACRO = {
|
|
16062
|
+
mapping: {
|
|
16063
|
+
person: "person",
|
|
16064
|
+
bicycle: "vehicle",
|
|
16065
|
+
car: "vehicle",
|
|
16066
|
+
motorcycle: "vehicle",
|
|
16067
|
+
airplane: "vehicle",
|
|
16068
|
+
bus: "vehicle",
|
|
16069
|
+
train: "vehicle",
|
|
16070
|
+
truck: "vehicle",
|
|
16071
|
+
boat: "vehicle",
|
|
16072
|
+
bird: "animal",
|
|
16073
|
+
cat: "animal",
|
|
16074
|
+
dog: "animal",
|
|
16075
|
+
horse: "animal",
|
|
16076
|
+
sheep: "animal",
|
|
16077
|
+
cow: "animal",
|
|
16078
|
+
elephant: "animal",
|
|
16079
|
+
bear: "animal",
|
|
16080
|
+
zebra: "animal",
|
|
16081
|
+
giraffe: "animal",
|
|
16082
|
+
suitcase: "package",
|
|
16083
|
+
backpack: "package",
|
|
16084
|
+
handbag: "package"
|
|
16085
|
+
},
|
|
16086
|
+
preserveOriginal: false
|
|
16087
|
+
};
|
|
16088
|
+
/**
|
|
16089
|
+
* WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
|
|
16090
|
+
* animal, package) plus an OPTIONAL label that narrows it.
|
|
16091
|
+
*
|
|
16092
|
+
* The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
|
|
16093
|
+
* second list. The label that fits depends on the macro class:
|
|
16094
|
+
*
|
|
16095
|
+
* - `animal` → a sub-class (`dog`, `cat`, …);
|
|
16096
|
+
* - `person` → a known face (an identity of the `face-gallery`);
|
|
16097
|
+
* - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
|
|
16098
|
+
* - `package` → a sub-class.
|
|
16099
|
+
*
|
|
16100
|
+
* `other` exists only so a class the taxonomy does not know (a custom model's
|
|
16101
|
+
* `forklift`) survives the conversion of a stored recipe without widening.
|
|
16102
|
+
*
|
|
16103
|
+
* A label is decided LATE (D683): face recognition and LPR land after the
|
|
16104
|
+
* detection starts. The track-lifecycle `update` event carries the newly
|
|
16105
|
+
* resolved `label` / `plateText`, which is why a leaf with a label fires when
|
|
16106
|
+
* the subject GETS it — not at the first detection.
|
|
16107
|
+
*/
|
|
16108
|
+
var SubClassLabelSchema = object({
|
|
16109
|
+
kind: literal("sub-class"),
|
|
16110
|
+
className: string().min(1)
|
|
16111
|
+
});
|
|
16112
|
+
var IdentityLabelSchema = object({
|
|
16113
|
+
kind: literal("identity"),
|
|
16114
|
+
/** The `face-gallery` identity id (stable across a rename). */
|
|
16115
|
+
identityId: string().min(1),
|
|
16116
|
+
/** The identity's name — what a track-lifecycle event carries as `label`. */
|
|
16117
|
+
name: string().min(1)
|
|
16118
|
+
});
|
|
16119
|
+
var PlateLabelSchema = object({
|
|
16120
|
+
kind: literal("plate"),
|
|
16121
|
+
text: string().min(1)
|
|
16122
|
+
});
|
|
16123
|
+
function macroOf$1(className) {
|
|
16124
|
+
return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
|
|
16125
|
+
}
|
|
16126
|
+
/** A sub-class label must belong to the macro it is under. */
|
|
16127
|
+
function subClassBelongs(macro, label) {
|
|
16128
|
+
return label === void 0 || macroOf$1(label.className) === macro;
|
|
16129
|
+
}
|
|
16130
|
+
var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
|
|
16131
|
+
var DetectionSubjectSchema = discriminatedUnion("macro", [
|
|
16132
|
+
object({
|
|
16133
|
+
macro: literal("animal"),
|
|
16134
|
+
label: subClassOf("animal").optional()
|
|
16135
|
+
}),
|
|
16136
|
+
object({
|
|
16137
|
+
macro: literal("person"),
|
|
16138
|
+
label: IdentityLabelSchema.optional()
|
|
16139
|
+
}),
|
|
16140
|
+
object({
|
|
16141
|
+
macro: literal("vehicle"),
|
|
16142
|
+
label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
|
|
16143
|
+
}),
|
|
16144
|
+
object({
|
|
16145
|
+
macro: literal("package"),
|
|
16146
|
+
label: subClassOf("package").optional()
|
|
16147
|
+
}),
|
|
16148
|
+
object({
|
|
16149
|
+
macro: literal("other"),
|
|
16150
|
+
className: string().min(1)
|
|
16151
|
+
})
|
|
16152
|
+
]);
|
|
16153
|
+
/**
|
|
16154
|
+
* The subject of a PARKED object. The stationary registry's event carries a
|
|
16155
|
+
* class and a zone, no face and no plate — an identity or plate label there
|
|
16156
|
+
* could never match, so the schema refuses it instead of letting it sit dead.
|
|
16157
|
+
*/
|
|
16158
|
+
var StationarySubjectSchema = DetectionSubjectSchema.refine((s) => s.macro === "other" || s.label === void 0 || s.label.kind === "sub-class", { message: "a parked object carries no face or plate — only a class can narrow it" });
|
|
16159
|
+
/**
|
|
16160
|
+
* A stored class string as a subject: a macro name stays a macro; a raw class
|
|
16161
|
+
* the taxonomy files under a macro becomes that macro plus the sub-class label
|
|
16162
|
+
* (`dog` → animal / dog); anything else is `other`, kept verbatim.
|
|
16163
|
+
*/
|
|
16164
|
+
function subjectFromClassName(className) {
|
|
16165
|
+
const name = className.trim().toLowerCase();
|
|
16166
|
+
const macro = COCO_TO_MACRO.mapping[name];
|
|
16167
|
+
if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
|
|
16168
|
+
if (macro === void 0) return {
|
|
16169
|
+
macro: "other",
|
|
16170
|
+
className
|
|
16171
|
+
};
|
|
16172
|
+
const label = {
|
|
16173
|
+
kind: "sub-class",
|
|
16174
|
+
className: name
|
|
16175
|
+
};
|
|
16176
|
+
switch (macro) {
|
|
16177
|
+
case "animal": return {
|
|
16178
|
+
macro: "animal",
|
|
16179
|
+
label
|
|
16180
|
+
};
|
|
16181
|
+
case "vehicle": return {
|
|
16182
|
+
macro: "vehicle",
|
|
16183
|
+
label
|
|
16184
|
+
};
|
|
16185
|
+
case "package": return {
|
|
16186
|
+
macro: "package",
|
|
16187
|
+
label
|
|
16188
|
+
};
|
|
16189
|
+
default: return {
|
|
16190
|
+
macro: "other",
|
|
16191
|
+
className
|
|
16192
|
+
};
|
|
16193
|
+
}
|
|
16194
|
+
}
|
|
16195
|
+
function macroOnly(macro) {
|
|
16196
|
+
switch (macro) {
|
|
16197
|
+
case "animal": return { macro: "animal" };
|
|
16198
|
+
case "person": return { macro: "person" };
|
|
16199
|
+
case "vehicle": return { macro: "vehicle" };
|
|
16200
|
+
case "package": return { macro: "package" };
|
|
16201
|
+
default: return {
|
|
16202
|
+
macro: "other",
|
|
16203
|
+
className: macro
|
|
16204
|
+
};
|
|
16205
|
+
}
|
|
16206
|
+
}
|
|
16207
|
+
/** How a state leaf compares a device field to a value. Derived from the
|
|
16208
|
+
* field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
|
|
15939
16209
|
var AutomationConditionOperatorSchema = _enum([
|
|
15940
16210
|
"eq",
|
|
15941
16211
|
"ne",
|
|
@@ -15946,12 +16216,76 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
15946
16216
|
"contains",
|
|
15947
16217
|
"in"
|
|
15948
16218
|
]);
|
|
15949
|
-
|
|
15950
|
-
|
|
16219
|
+
/**
|
|
16220
|
+
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
16221
|
+
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
16222
|
+
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
16223
|
+
* fire; it does not lose it.
|
|
16224
|
+
*/
|
|
16225
|
+
var DeviceBecomesLeafSchema = object({
|
|
16226
|
+
kind: literal("device-becomes"),
|
|
16227
|
+
deviceId: number().int().nonnegative(),
|
|
16228
|
+
cap: string().min(1),
|
|
16229
|
+
fieldPath: string().min(1),
|
|
16230
|
+
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16231
|
+
becomes: union([
|
|
16232
|
+
string(),
|
|
16233
|
+
number(),
|
|
16234
|
+
boolean()
|
|
16235
|
+
]).optional(),
|
|
16236
|
+
/** Only on a CHANGE of value, not on every re-report. */
|
|
16237
|
+
edge: boolean().optional(),
|
|
16238
|
+
/** The new value must hold this long before it counts. */
|
|
16239
|
+
forMs: number().int().min(0).max(864e5).optional(),
|
|
16240
|
+
/** Collapse a burst into one occurrence. */
|
|
16241
|
+
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16242
|
+
});
|
|
16243
|
+
var DeviceEventLeafSchema = object({
|
|
16244
|
+
kind: literal("device-event"),
|
|
16245
|
+
/** An `EventCategory` value. */
|
|
16246
|
+
category: string().min(1),
|
|
16247
|
+
deviceId: number().int().nonnegative().optional()
|
|
16248
|
+
});
|
|
16249
|
+
var ScheduleLeafSchema = object({
|
|
16250
|
+
kind: literal("schedule"),
|
|
16251
|
+
cron: string().min(1).max(120)
|
|
16252
|
+
});
|
|
16253
|
+
var ManualLeafSchema = object({ kind: literal("manual") });
|
|
16254
|
+
var IntervalLeafSchema = object({
|
|
16255
|
+
kind: literal("interval"),
|
|
16256
|
+
minutes: number().int().min(1).max(1440)
|
|
16257
|
+
});
|
|
16258
|
+
var DetectionLeafSchema = object({
|
|
16259
|
+
kind: literal("detection"),
|
|
16260
|
+
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
16261
|
+
subjects: array(DetectionSubjectSchema).min(1),
|
|
16262
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16263
|
+
/** The track must have visited at least one of these zones. */
|
|
16264
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16265
|
+
/** Best-so-far confidence the track must reach. */
|
|
16266
|
+
minScore: number().min(0).max(1).optional()
|
|
16267
|
+
});
|
|
16268
|
+
var StationaryLeafSchema = object({
|
|
16269
|
+
kind: literal("stationary"),
|
|
16270
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16271
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16272
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16273
|
+
/** `appeared`: it became parked. `departed`: the registry retired it. */
|
|
16274
|
+
phase: _enum(["appeared", "departed"])
|
|
16275
|
+
});
|
|
16276
|
+
/** "is": one device field compared to one value, NOW. */
|
|
16277
|
+
var DeviceIsLeafSchema = object({
|
|
16278
|
+
kind: literal("device-is"),
|
|
15951
16279
|
deviceId: number().int().nonnegative(),
|
|
15952
16280
|
cap: string().min(1),
|
|
15953
16281
|
fieldPath: string().min(1),
|
|
15954
16282
|
operator: AutomationConditionOperatorSchema,
|
|
16283
|
+
/**
|
|
16284
|
+
* The value. A number on a quantity field is stored in the field's base unit
|
|
16285
|
+
* (D944); a quantity value is the WRITE form, which the save boundary
|
|
16286
|
+
* replaces with the converted number and `unit`. A leaf that still carries
|
|
16287
|
+
* one is never evaluated (it fails).
|
|
16288
|
+
*/
|
|
15955
16289
|
value: union([
|
|
15956
16290
|
string(),
|
|
15957
16291
|
number(),
|
|
@@ -15959,24 +16293,26 @@ var AutomationConditionLeafSchema = object({
|
|
|
15959
16293
|
array(union([string(), number()])),
|
|
15960
16294
|
QuantityValueSchema
|
|
15961
16295
|
]),
|
|
16296
|
+
/** The unit the person picked for `value` (display only; `value` is in the base unit). */
|
|
15962
16297
|
unit: string().min(1).optional()
|
|
15963
16298
|
});
|
|
15964
|
-
|
|
16299
|
+
/**
|
|
16300
|
+
* True when the camera currently has a PARKED object matching `subjects` in at
|
|
16301
|
+
* least one of `zones`. Reads the zone-analytics snapshot through the same
|
|
16302
|
+
* mirror as every other state leaf, so a dropped event never decides it (D49).
|
|
16303
|
+
*/
|
|
16304
|
+
var StationaryPresentLeafSchema = object({
|
|
15965
16305
|
kind: literal("stationary-present"),
|
|
15966
16306
|
deviceId: number().int().nonnegative(),
|
|
15967
|
-
|
|
15968
|
-
classes: array(string().min(1)).min(1).optional(),
|
|
15969
|
-
/** The parked object must be in at least one of these zones. */
|
|
16307
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
15970
16308
|
zones: array(string().min(1)).min(1).optional()
|
|
15971
16309
|
});
|
|
15972
16310
|
/**
|
|
15973
|
-
* The
|
|
15974
|
-
*
|
|
15975
|
-
*
|
|
15976
|
-
* validation is the SAME function `ExpressionSourceSchema` runs, so the two
|
|
15977
|
-
* cannot drift — an expression that one accepts, the other accepts.
|
|
16311
|
+
* The escape hatch: the bounded expression engine over named device bindings.
|
|
16312
|
+
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
16313
|
+
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
15978
16314
|
*/
|
|
15979
|
-
var
|
|
16315
|
+
var ExpressionLeafSchema = object({
|
|
15980
16316
|
kind: literal("expression"),
|
|
15981
16317
|
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
15982
16318
|
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
@@ -15988,55 +16324,62 @@ var AutomationConditionExpressionSchema = object({
|
|
|
15988
16324
|
path: ["expr"]
|
|
15989
16325
|
});
|
|
15990
16326
|
});
|
|
15991
|
-
var
|
|
16327
|
+
var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
15992
16328
|
object({
|
|
15993
16329
|
kind: literal("all"),
|
|
15994
|
-
children: array(
|
|
16330
|
+
children: array(AutomationWhenSchema)
|
|
15995
16331
|
}),
|
|
15996
16332
|
object({
|
|
15997
16333
|
kind: literal("any"),
|
|
15998
|
-
children: array(
|
|
16334
|
+
children: array(AutomationWhenSchema)
|
|
15999
16335
|
}),
|
|
16000
16336
|
object({
|
|
16001
16337
|
kind: literal("not"),
|
|
16002
|
-
child:
|
|
16338
|
+
child: AutomationWhenSchema
|
|
16003
16339
|
}),
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16340
|
+
DeviceBecomesLeafSchema,
|
|
16341
|
+
DeviceEventLeafSchema,
|
|
16342
|
+
ScheduleLeafSchema,
|
|
16343
|
+
ManualLeafSchema,
|
|
16344
|
+
IntervalLeafSchema,
|
|
16345
|
+
DetectionLeafSchema,
|
|
16346
|
+
StationaryLeafSchema,
|
|
16347
|
+
DeviceIsLeafSchema,
|
|
16348
|
+
StationaryPresentLeafSchema,
|
|
16349
|
+
ExpressionLeafSchema
|
|
16007
16350
|
]));
|
|
16008
16351
|
/**
|
|
16009
|
-
*
|
|
16010
|
-
*
|
|
16011
|
-
*
|
|
16012
|
-
*
|
|
16013
|
-
*
|
|
16014
|
-
*
|
|
16015
|
-
*
|
|
16016
|
-
*
|
|
16017
|
-
|
|
16018
|
-
|
|
16352
|
+
* The recipe shape before the single `when` tree: a flat list of triggers
|
|
16353
|
+
* (OR'd, each an instant) and an optional condition tree. Stored recipes —
|
|
16354
|
+
* the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
|
|
16355
|
+
* older client still posts — are in this shape, so it stays READABLE here, and
|
|
16356
|
+
* {@link convertLegacyRecipe} turns it into the new one, losslessly and one
|
|
16357
|
+
* way. Nothing writes this shape any more.
|
|
16358
|
+
*
|
|
16359
|
+
* `{ triggers: [t1…tn], conditions?: C }`
|
|
16360
|
+
* → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
|
|
16361
|
+
* a single trigger is not wrapped in `any`.
|
|
16362
|
+
* a `condition` leaf → a `device-is` leaf (the "is" verb);
|
|
16363
|
+
* a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
|
|
16364
|
+
* `classes: ['dog']` → the macro `animal` with the label `dog`.
|
|
16365
|
+
*/
|
|
16366
|
+
var LegacyTriggerSchema = discriminatedUnion("kind", [
|
|
16019
16367
|
object({
|
|
16020
16368
|
kind: literal("device-state"),
|
|
16021
16369
|
deviceId: number().int().nonnegative(),
|
|
16022
16370
|
cap: string().min(1),
|
|
16023
16371
|
fieldPath: string().min(1),
|
|
16024
|
-
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16025
16372
|
becomes: union([
|
|
16026
16373
|
string(),
|
|
16027
16374
|
number(),
|
|
16028
16375
|
boolean()
|
|
16029
16376
|
]).optional(),
|
|
16030
|
-
/** Only on a CHANGE of value, not on every re-report. */
|
|
16031
16377
|
edge: boolean().optional(),
|
|
16032
|
-
/** The condition must hold this long before the run starts. */
|
|
16033
16378
|
forMs: number().int().min(0).max(864e5).optional(),
|
|
16034
|
-
/** Collapse a burst into one run. */
|
|
16035
16379
|
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16036
16380
|
}),
|
|
16037
16381
|
object({
|
|
16038
16382
|
kind: literal("device-event"),
|
|
16039
|
-
/** An `EventCategory` value. */
|
|
16040
16383
|
category: string().min(1),
|
|
16041
16384
|
deviceId: number().int().nonnegative().optional()
|
|
16042
16385
|
}),
|
|
@@ -16047,25 +16390,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16047
16390
|
object({ kind: literal("manual") }),
|
|
16048
16391
|
object({
|
|
16049
16392
|
kind: literal("detection"),
|
|
16050
|
-
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16051
|
-
* matches every raw class that maps to it; any other name matches
|
|
16052
|
-
* exactly. */
|
|
16053
16393
|
classes: array(string().min(1)).min(1),
|
|
16054
16394
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16055
|
-
/** The track must have visited at least one of these zones. */
|
|
16056
16395
|
zones: array(string().min(1)).min(1).optional(),
|
|
16057
|
-
/** Best-so-far confidence the track must reach. */
|
|
16058
16396
|
minScore: number().min(0).max(1).optional()
|
|
16059
16397
|
}),
|
|
16060
16398
|
object({
|
|
16061
16399
|
kind: literal("stationary"),
|
|
16062
16400
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16063
|
-
/** Class names; a macro class matches like the detection trigger. */
|
|
16064
16401
|
classes: array(string().min(1)).min(1).optional(),
|
|
16065
|
-
/** The parked object must be in at least one of these zones. */
|
|
16066
16402
|
zones: array(string().min(1)).min(1).optional(),
|
|
16067
|
-
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16068
|
-
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16069
16403
|
phase: _enum(["appeared", "departed"])
|
|
16070
16404
|
}),
|
|
16071
16405
|
object({
|
|
@@ -16073,6 +16407,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16073
16407
|
minutes: number().int().min(1).max(1440)
|
|
16074
16408
|
})
|
|
16075
16409
|
]);
|
|
16410
|
+
var LegacyConditionLeafSchema = object({
|
|
16411
|
+
kind: literal("condition"),
|
|
16412
|
+
deviceId: number().int().nonnegative(),
|
|
16413
|
+
cap: string().min(1),
|
|
16414
|
+
fieldPath: string().min(1),
|
|
16415
|
+
operator: AutomationConditionOperatorSchema,
|
|
16416
|
+
value: union([
|
|
16417
|
+
string(),
|
|
16418
|
+
number(),
|
|
16419
|
+
boolean(),
|
|
16420
|
+
array(union([string(), number()])),
|
|
16421
|
+
QuantityValueSchema
|
|
16422
|
+
]),
|
|
16423
|
+
unit: string().min(1).optional()
|
|
16424
|
+
});
|
|
16425
|
+
var LegacyStationaryPresentSchema = object({
|
|
16426
|
+
kind: literal("stationary-present"),
|
|
16427
|
+
deviceId: number().int().nonnegative(),
|
|
16428
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16429
|
+
zones: array(string().min(1)).min(1).optional()
|
|
16430
|
+
});
|
|
16431
|
+
var LegacyExpressionSchema = object({
|
|
16432
|
+
kind: literal("expression"),
|
|
16433
|
+
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16434
|
+
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
16435
|
+
}).superRefine((src, ctx) => {
|
|
16436
|
+
const err = validateExpressionSource(src);
|
|
16437
|
+
if (err !== null) ctx.addIssue({
|
|
16438
|
+
code: "custom",
|
|
16439
|
+
message: err,
|
|
16440
|
+
path: ["expr"]
|
|
16441
|
+
});
|
|
16442
|
+
});
|
|
16443
|
+
var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
16444
|
+
object({
|
|
16445
|
+
kind: literal("all"),
|
|
16446
|
+
children: array(LegacyConditionSchema)
|
|
16447
|
+
}),
|
|
16448
|
+
object({
|
|
16449
|
+
kind: literal("any"),
|
|
16450
|
+
children: array(LegacyConditionSchema)
|
|
16451
|
+
}),
|
|
16452
|
+
object({
|
|
16453
|
+
kind: literal("not"),
|
|
16454
|
+
child: LegacyConditionSchema
|
|
16455
|
+
}),
|
|
16456
|
+
LegacyConditionLeafSchema,
|
|
16457
|
+
LegacyExpressionSchema,
|
|
16458
|
+
LegacyStationaryPresentSchema
|
|
16459
|
+
]));
|
|
16460
|
+
function convertTrigger(trigger) {
|
|
16461
|
+
switch (trigger.kind) {
|
|
16462
|
+
case "device-state": return {
|
|
16463
|
+
...trigger,
|
|
16464
|
+
kind: "device-becomes"
|
|
16465
|
+
};
|
|
16466
|
+
case "detection": {
|
|
16467
|
+
const { classes, ...rest } = trigger;
|
|
16468
|
+
return {
|
|
16469
|
+
...rest,
|
|
16470
|
+
subjects: classes.map(subjectFromClassName)
|
|
16471
|
+
};
|
|
16472
|
+
}
|
|
16473
|
+
case "stationary": {
|
|
16474
|
+
const { classes, ...rest } = trigger;
|
|
16475
|
+
return {
|
|
16476
|
+
...rest,
|
|
16477
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
16478
|
+
};
|
|
16479
|
+
}
|
|
16480
|
+
default: return trigger;
|
|
16481
|
+
}
|
|
16482
|
+
}
|
|
16483
|
+
/** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
|
|
16484
|
+
function convertLegacyCondition(condition) {
|
|
16485
|
+
switch (condition.kind) {
|
|
16486
|
+
case "condition": return {
|
|
16487
|
+
...condition,
|
|
16488
|
+
kind: "device-is"
|
|
16489
|
+
};
|
|
16490
|
+
case "stationary-present": {
|
|
16491
|
+
const { classes, ...rest } = condition;
|
|
16492
|
+
return {
|
|
16493
|
+
...rest,
|
|
16494
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
16495
|
+
};
|
|
16496
|
+
}
|
|
16497
|
+
case "expression": return condition;
|
|
16498
|
+
case "not": return {
|
|
16499
|
+
kind: "not",
|
|
16500
|
+
child: convertLegacyCondition(condition.child)
|
|
16501
|
+
};
|
|
16502
|
+
case "all":
|
|
16503
|
+
case "any": return {
|
|
16504
|
+
kind: condition.kind,
|
|
16505
|
+
children: condition.children.map(convertLegacyCondition)
|
|
16506
|
+
};
|
|
16507
|
+
}
|
|
16508
|
+
}
|
|
16509
|
+
/** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
|
|
16510
|
+
function convertLegacyWhen(half) {
|
|
16511
|
+
const leaves = half.triggers.map(convertTrigger);
|
|
16512
|
+
const first = leaves[0];
|
|
16513
|
+
const triggers = leaves.length === 1 && first !== void 0 ? first : {
|
|
16514
|
+
kind: "any",
|
|
16515
|
+
children: leaves
|
|
16516
|
+
};
|
|
16517
|
+
if (half.conditions === void 0) return triggers;
|
|
16518
|
+
return {
|
|
16519
|
+
kind: "all",
|
|
16520
|
+
children: [triggers, convertLegacyCondition(half.conditions)]
|
|
16521
|
+
};
|
|
16522
|
+
}
|
|
16523
|
+
/**
|
|
16524
|
+
* The automation RECIPE — what a visually-authored automation is, before it
|
|
16525
|
+
* becomes code.
|
|
16526
|
+
*
|
|
16527
|
+
* This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
|
|
16528
|
+
* Phase 6. A recipe is not an engine and never evaluates anything: it is a
|
|
16529
|
+
* declarative description that {@link generateAutomationBlock} compiles to the
|
|
16530
|
+
* TypeScript of an ordinary core block. There is therefore no second rule
|
|
16531
|
+
* engine, no second store and no second runtime — the block runner is the
|
|
16532
|
+
* runtime, `core_blocks` is the store (§2.1).
|
|
16533
|
+
*
|
|
16534
|
+
* Two parts:
|
|
16535
|
+
*
|
|
16536
|
+
* - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
|
|
16537
|
+
* (instants) and state leaves (durations). It replaces the flat trigger list
|
|
16538
|
+
* plus the separate condition tree; the firing rule is in that file and in
|
|
16539
|
+
* ADR "one tree".
|
|
16540
|
+
* - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
|
|
16541
|
+
* fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
|
|
16542
|
+
* this had to diverge from "verbatim", and why.
|
|
16543
|
+
*
|
|
16544
|
+
* A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
|
|
16545
|
+
* {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
|
|
16546
|
+
*/
|
|
16076
16547
|
/**
|
|
16077
16548
|
* One action step.
|
|
16078
16549
|
*
|
|
@@ -16175,69 +16646,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
|
16175
16646
|
minutes: number().int().min(1).max(1440)
|
|
16176
16647
|
})
|
|
16177
16648
|
]);
|
|
16178
|
-
var
|
|
16179
|
-
|
|
16180
|
-
conditions: AutomationConditionSchema.optional(),
|
|
16649
|
+
var CurrentAutomationRecipeSchema = object({
|
|
16650
|
+
when: AutomationWhenSchema,
|
|
16181
16651
|
actions: array(AutomationActionSchema),
|
|
16182
16652
|
runMode: AutomationRunModeSchema.optional(),
|
|
16183
16653
|
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16184
16654
|
keepTraces: number().int().min(1).max(100).optional()
|
|
16185
16655
|
});
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
|
|
16206
|
-
|
|
16207
|
-
|
|
16208
|
-
|
|
16209
|
-
|
|
16210
|
-
|
|
16211
|
-
bus: "vehicle",
|
|
16212
|
-
train: "vehicle",
|
|
16213
|
-
truck: "vehicle",
|
|
16214
|
-
boat: "vehicle",
|
|
16215
|
-
bird: "animal",
|
|
16216
|
-
cat: "animal",
|
|
16217
|
-
dog: "animal",
|
|
16218
|
-
horse: "animal",
|
|
16219
|
-
sheep: "animal",
|
|
16220
|
-
cow: "animal",
|
|
16221
|
-
elephant: "animal",
|
|
16222
|
-
bear: "animal",
|
|
16223
|
-
zebra: "animal",
|
|
16224
|
-
giraffe: "animal",
|
|
16225
|
-
suitcase: "package",
|
|
16226
|
-
backpack: "package",
|
|
16227
|
-
handbag: "package"
|
|
16228
|
-
},
|
|
16229
|
-
preserveOriginal: false
|
|
16230
|
-
};
|
|
16656
|
+
/** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
|
|
16657
|
+
var LegacyAutomationRecipeSchema = object({
|
|
16658
|
+
triggers: array(LegacyTriggerSchema),
|
|
16659
|
+
conditions: LegacyConditionSchema.optional(),
|
|
16660
|
+
actions: array(AutomationActionSchema),
|
|
16661
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
16662
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
16663
|
+
});
|
|
16664
|
+
/** The old shape as the new one, lossless. */
|
|
16665
|
+
function convertLegacyRecipe(legacy) {
|
|
16666
|
+
const { triggers, conditions, ...rest } = legacy;
|
|
16667
|
+
return {
|
|
16668
|
+
when: convertLegacyWhen({
|
|
16669
|
+
triggers,
|
|
16670
|
+
conditions
|
|
16671
|
+
}),
|
|
16672
|
+
...rest
|
|
16673
|
+
};
|
|
16674
|
+
}
|
|
16675
|
+
/**
|
|
16676
|
+
* Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
|
|
16677
|
+
* saved block carries): the new shape as it is, or the old shape converted to
|
|
16678
|
+
* it. The output type is always the new one.
|
|
16679
|
+
*/
|
|
16680
|
+
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
16231
16681
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16232
16682
|
/**
|
|
16233
16683
|
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16234
16684
|
* so the editor can paint every block green or red with its reason.
|
|
16235
16685
|
*
|
|
16236
|
-
* A trace is data, not a log: it covers the WHOLE tree — the
|
|
16237
|
-
* every
|
|
16238
|
-
* short-circuit never reached), the run-mode verdict and every action
|
|
16239
|
-
* each addressed by a stable path (`
|
|
16240
|
-
* for an Action run).
|
|
16686
|
+
* A trace is data, not a log: it covers the WHOLE tree — the event leaf that
|
|
16687
|
+
* fired, every node of the `when` tree (containers and leaves, including the
|
|
16688
|
+
* ones a short-circuit never reached), the run-mode verdict and every action
|
|
16689
|
+
* step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
|
|
16690
|
+
* for an Action run). Traces written before the single tree carry
|
|
16691
|
+
* `conditions.*` paths and still parse. Skipped
|
|
16241
16692
|
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16242
16693
|
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16243
16694
|
*
|
|
@@ -16269,6 +16720,8 @@ var TraceNodeOutcomeSchema = _enum([
|
|
|
16269
16720
|
]);
|
|
16270
16721
|
var AutomationTriggerTraceSchema = object({
|
|
16271
16722
|
kind: string().min(1),
|
|
16723
|
+
/** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
|
|
16724
|
+
path: string().min(1).optional(),
|
|
16272
16725
|
outcome: _enum(["pass", "fail"]),
|
|
16273
16726
|
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16274
16727
|
reason: string(),
|
|
@@ -16276,15 +16729,23 @@ var AutomationTriggerTraceSchema = object({
|
|
|
16276
16729
|
vars: record(string(), TraceValueSchema)
|
|
16277
16730
|
});
|
|
16278
16731
|
var AutomationConditionTraceNodeSchema = object({
|
|
16279
|
-
/** `
|
|
16732
|
+
/** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
|
|
16280
16733
|
path: string().min(1),
|
|
16281
16734
|
kind: _enum([
|
|
16282
16735
|
"all",
|
|
16283
16736
|
"any",
|
|
16284
16737
|
"not",
|
|
16285
16738
|
"condition",
|
|
16739
|
+
"device-is",
|
|
16286
16740
|
"expression",
|
|
16287
|
-
"stationary-present"
|
|
16741
|
+
"stationary-present",
|
|
16742
|
+
"device-becomes",
|
|
16743
|
+
"device-event",
|
|
16744
|
+
"schedule",
|
|
16745
|
+
"interval",
|
|
16746
|
+
"manual",
|
|
16747
|
+
"detection",
|
|
16748
|
+
"stationary"
|
|
16288
16749
|
]),
|
|
16289
16750
|
outcome: TraceNodeOutcomeSchema,
|
|
16290
16751
|
reason: string(),
|
|
@@ -16318,6 +16779,12 @@ var AutomationStepTraceSchema = object({
|
|
|
16318
16779
|
deviceId: number().int().optional(),
|
|
16319
16780
|
startedAt: number().optional(),
|
|
16320
16781
|
endedAt: number().optional(),
|
|
16782
|
+
/**
|
|
16783
|
+
* The parameters the step SENT, after `{{vars.x}}` substitution — what the
|
|
16784
|
+
* device was actually asked. Bounded like trigger `vars`; a secret parameter
|
|
16785
|
+
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
16786
|
+
*/
|
|
16787
|
+
args: record(string(), TraceValueSchema).optional(),
|
|
16321
16788
|
outcome: _enum([
|
|
16322
16789
|
"ok",
|
|
16323
16790
|
"error",
|
|
@@ -17067,6 +17534,23 @@ method(object({
|
|
|
17067
17534
|
addonId: string(),
|
|
17068
17535
|
nodeId: string().optional()
|
|
17069
17536
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
17537
|
+
addonId: string(),
|
|
17538
|
+
nodeId: string().optional()
|
|
17539
|
+
}), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
|
|
17540
|
+
addonId: string(),
|
|
17541
|
+
nodeId: string().optional(),
|
|
17542
|
+
keys: array(string())
|
|
17543
|
+
}), object({
|
|
17544
|
+
removed: array(string()),
|
|
17545
|
+
refused: array(object({
|
|
17546
|
+
key: string(),
|
|
17547
|
+
reason: string()
|
|
17548
|
+
})),
|
|
17549
|
+
secretsCleared: number()
|
|
17550
|
+
}), {
|
|
17551
|
+
kind: "mutation",
|
|
17552
|
+
auth: "admin"
|
|
17553
|
+
}), method(object({
|
|
17070
17554
|
addonId: string(),
|
|
17071
17555
|
deviceId: number(),
|
|
17072
17556
|
nodeId: string().optional()
|
|
@@ -20990,6 +21474,8 @@ var StorageKindSchema = object({
|
|
|
20990
21474
|
"orphaned",
|
|
20991
21475
|
"dropped"
|
|
20992
21476
|
]),
|
|
21477
|
+
/** An owner declared this kind since the engine started. */
|
|
21478
|
+
declaredThisBoot: boolean(),
|
|
20993
21479
|
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
20994
21480
|
rows: number().int().nonnegative().nullable(),
|
|
20995
21481
|
/** Rows x the average size of a sample; null for an empty kind. */
|
|
@@ -21001,6 +21487,51 @@ var StorageKindSchema = object({
|
|
|
21001
21487
|
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
21002
21488
|
declaration: RecordKindDeclarationSchema
|
|
21003
21489
|
});
|
|
21490
|
+
_enum([
|
|
21491
|
+
"orphaned",
|
|
21492
|
+
"stale",
|
|
21493
|
+
"unknown"
|
|
21494
|
+
]);
|
|
21495
|
+
/** A record kind nobody declared since its owner started, or whose owner is gone. */
|
|
21496
|
+
var StaleKindFindingSchema = object({
|
|
21497
|
+
owner: string(),
|
|
21498
|
+
kind: string(),
|
|
21499
|
+
/** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
|
|
21500
|
+
verdict: _enum(["orphaned", "unknown"]),
|
|
21501
|
+
reason: string(),
|
|
21502
|
+
rows: number().int().nonnegative().nullable(),
|
|
21503
|
+
/** local-auth's tables are never dropped by this audit. */
|
|
21504
|
+
protected: boolean()
|
|
21505
|
+
});
|
|
21506
|
+
/** A stored global-settings key the running addon no longer declares. */
|
|
21507
|
+
var StaleSettingsKeyFindingSchema = object({
|
|
21508
|
+
addonId: string(),
|
|
21509
|
+
/** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
|
|
21510
|
+
key: string(),
|
|
21511
|
+
/**
|
|
21512
|
+
* Keychain refs whose ONLY marker sits under this key: in use by stale data,
|
|
21513
|
+
* not orphans yet. Removing the key releases them.
|
|
21514
|
+
*/
|
|
21515
|
+
secretRefs: array(string())
|
|
21516
|
+
});
|
|
21517
|
+
/** An addon the audit could not judge, with the reason. Never reported as stale. */
|
|
21518
|
+
var StaleUnknownSchema = object({
|
|
21519
|
+
subject: string(),
|
|
21520
|
+
reason: string()
|
|
21521
|
+
});
|
|
21522
|
+
var StaleDataReportSchema = object({
|
|
21523
|
+
/** Epoch ms of the last finished scan; `null` before the first. */
|
|
21524
|
+
scannedAt: number().nullable(),
|
|
21525
|
+
scanning: boolean(),
|
|
21526
|
+
trigger: _enum([
|
|
21527
|
+
"boot",
|
|
21528
|
+
"interval",
|
|
21529
|
+
"manual"
|
|
21530
|
+
]).nullable(),
|
|
21531
|
+
kinds: array(StaleKindFindingSchema),
|
|
21532
|
+
settingsKeys: array(StaleSettingsKeyFindingSchema),
|
|
21533
|
+
unknown: array(StaleUnknownSchema)
|
|
21534
|
+
});
|
|
21004
21535
|
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
21005
21536
|
owner: string().min(1).optional(),
|
|
21006
21537
|
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
@@ -21008,7 +21539,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
|
|
|
21008
21539
|
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
21009
21540
|
owner: string().min(1),
|
|
21010
21541
|
kind: RecordKindNameSchema
|
|
21011
|
-
}), RecordQueryResultSchema, { auth: "admin" })
|
|
21542
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
|
|
21543
|
+
kind: "mutation",
|
|
21544
|
+
auth: "admin"
|
|
21545
|
+
}), method(object({
|
|
21546
|
+
owner: string().min(1),
|
|
21547
|
+
kind: RecordKindNameSchema
|
|
21548
|
+
}), object({
|
|
21549
|
+
deleted: number().int().nonnegative(),
|
|
21550
|
+
secretsReleased: number().int().nonnegative()
|
|
21551
|
+
}), {
|
|
21552
|
+
kind: "mutation",
|
|
21553
|
+
auth: "admin",
|
|
21554
|
+
caller: "required"
|
|
21555
|
+
}), method(object({
|
|
21556
|
+
addonId: string().min(1),
|
|
21557
|
+
key: string().min(1)
|
|
21558
|
+
}), object({
|
|
21559
|
+
removed: boolean(),
|
|
21560
|
+
secretsCleared: number().int().nonnegative()
|
|
21561
|
+
}), {
|
|
21562
|
+
kind: "mutation",
|
|
21563
|
+
auth: "admin",
|
|
21564
|
+
caller: "required"
|
|
21565
|
+
});
|
|
21012
21566
|
/**
|
|
21013
21567
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
21014
21568
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
@@ -21147,6 +21701,12 @@ method(object({
|
|
|
21147
21701
|
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
21148
21702
|
kind: "mutation",
|
|
21149
21703
|
auth: "admin"
|
|
21704
|
+
}), method(object({
|
|
21705
|
+
refs: RefListSchema,
|
|
21706
|
+
onBehalfOf: string().min(1)
|
|
21707
|
+
}), object({ released: number().int() }), {
|
|
21708
|
+
kind: "mutation",
|
|
21709
|
+
auth: "admin"
|
|
21150
21710
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
21151
21711
|
/**
|
|
21152
21712
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -21936,7 +22496,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
|
|
|
21936
22496
|
/** Upstream-system identity + rendering hints for the candidate. */
|
|
21937
22497
|
sourceInfo: SourceInfoSchema.optional(),
|
|
21938
22498
|
/** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
|
|
21939
|
-
children: array(DiscoveredChildDeviceSchema).readonly().optional()
|
|
22499
|
+
children: array(DiscoveredChildDeviceSchema).readonly().optional(),
|
|
22500
|
+
/** `childNativeId` of the gateway candidate in the same list this device sits behind. */
|
|
22501
|
+
parentStableId: string().optional()
|
|
21940
22502
|
}));
|
|
21941
22503
|
/**
|
|
21942
22504
|
* Status block — populated by the integration after each
|
|
@@ -42251,6 +42813,79 @@ object({
|
|
|
42251
42813
|
metadata: record(string(), unknown()).optional()
|
|
42252
42814
|
});
|
|
42253
42815
|
/**
|
|
42816
|
+
* HOW a provider can be found on the network. The provider only DECLARES the
|
|
42817
|
+
* mode; the `local-network` scan engine runs it once for every provider that
|
|
42818
|
+
* asked (one multicast browse, one host sweep) and hands the raw hits back to
|
|
42819
|
+
* `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
|
|
42820
|
+
* except through a `custom` mode.
|
|
42821
|
+
*/
|
|
42822
|
+
var DiscoveryModeSchema = discriminatedUnion("kind", [
|
|
42823
|
+
object({
|
|
42824
|
+
kind: literal("mdns"),
|
|
42825
|
+
/** DNS-SD service type, e.g. `_esphomelib._tcp`. */
|
|
42826
|
+
serviceType: string().min(1),
|
|
42827
|
+
/** Regular expression an instance name must match (case-insensitive). */
|
|
42828
|
+
instancePattern: string().optional()
|
|
42829
|
+
}),
|
|
42830
|
+
object({
|
|
42831
|
+
kind: literal("ws-discovery"),
|
|
42832
|
+
/** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
|
|
42833
|
+
types: array(string()).optional()
|
|
42834
|
+
}),
|
|
42835
|
+
object({
|
|
42836
|
+
kind: literal("ssdp"),
|
|
42837
|
+
searchTarget: string().min(1)
|
|
42838
|
+
}),
|
|
42839
|
+
object({
|
|
42840
|
+
kind: literal("udp-broadcast"),
|
|
42841
|
+
port: number().int().min(1).max(65535),
|
|
42842
|
+
payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
|
|
42843
|
+
/** Port replies arrive on, when the device answers somewhere other than the source port. */
|
|
42844
|
+
listenPort: number().int().min(1).max(65535).optional()
|
|
42845
|
+
}),
|
|
42846
|
+
object({
|
|
42847
|
+
kind: literal("http-probe"),
|
|
42848
|
+
port: number().int().min(1).max(65535),
|
|
42849
|
+
path: string().startsWith("/"),
|
|
42850
|
+
method: _enum(["GET", "POST"]).default("GET"),
|
|
42851
|
+
bodyText: string().optional(),
|
|
42852
|
+
contentType: string().optional(),
|
|
42853
|
+
tls: boolean().default(false),
|
|
42854
|
+
/** Status codes that count as a hit. Omitted = any HTTP answer. */
|
|
42855
|
+
acceptStatus: array(number().int()).optional()
|
|
42856
|
+
}),
|
|
42857
|
+
object({
|
|
42858
|
+
kind: literal("custom"),
|
|
42859
|
+
/** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
|
|
42860
|
+
reason: string().min(1)
|
|
42861
|
+
})
|
|
42862
|
+
]);
|
|
42863
|
+
/** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
|
|
42864
|
+
var DiscoveryHitSchema = object({
|
|
42865
|
+
/** Index into the provider's own `getDiscoveryModes()` array. */
|
|
42866
|
+
modeIndex: number().int().min(0),
|
|
42867
|
+
host: string(),
|
|
42868
|
+
port: number().int(),
|
|
42869
|
+
http: object({
|
|
42870
|
+
status: number().int(),
|
|
42871
|
+
headers: record(string(), string()),
|
|
42872
|
+
/** At most 16 KiB. */
|
|
42873
|
+
bodyText: string()
|
|
42874
|
+
}).optional(),
|
|
42875
|
+
mdns: object({
|
|
42876
|
+
instance: string(),
|
|
42877
|
+
host: string(),
|
|
42878
|
+
port: number().int(),
|
|
42879
|
+
txt: record(string(), string())
|
|
42880
|
+
}).optional(),
|
|
42881
|
+
udp: object({ payloadHex: string() }).optional(),
|
|
42882
|
+
wsd: object({
|
|
42883
|
+
xaddrs: array(string()),
|
|
42884
|
+
types: array(string()),
|
|
42885
|
+
scopes: array(string())
|
|
42886
|
+
}).optional()
|
|
42887
|
+
});
|
|
42888
|
+
/**
|
|
42254
42889
|
* Candidate handed back from discovery and accepted by
|
|
42255
42890
|
* `adoptDiscoveredDevice`. Shape mirrors the in-process
|
|
42256
42891
|
* `DiscoveredDevice` interface used by `DeviceDiscovery`.
|
|
@@ -42285,7 +42920,17 @@ var DiscoveryCandidateSchema = object({
|
|
|
42285
42920
|
/** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
|
|
42286
42921
|
onboardedDeviceId: number().optional(),
|
|
42287
42922
|
/** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
|
|
42288
|
-
onboardedName: string().optional()
|
|
42923
|
+
onboardedName: string().optional(),
|
|
42924
|
+
/**
|
|
42925
|
+
* This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
|
|
42926
|
+
* not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
|
|
42927
|
+
* which is still read for one release.
|
|
42928
|
+
*/
|
|
42929
|
+
setupIntegration: boolean().optional(),
|
|
42930
|
+
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
42931
|
+
parentStableId: string().optional(),
|
|
42932
|
+
/** `false` = shown for context but not pickable. Default true. */
|
|
42933
|
+
selectable: boolean().optional()
|
|
42289
42934
|
});
|
|
42290
42935
|
/**
|
|
42291
42936
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -42338,6 +42983,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
|
|
|
42338
42983
|
}), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
|
|
42339
42984
|
kind: "mutation",
|
|
42340
42985
|
auth: "admin"
|
|
42986
|
+
}), method(object({}), array(DiscoveryModeSchema)), method(object({
|
|
42987
|
+
hits: array(DiscoveryHitSchema),
|
|
42988
|
+
params: record(string(), unknown()).optional()
|
|
42989
|
+
}), array(DiscoveryCandidateSchema), {
|
|
42990
|
+
kind: "mutation",
|
|
42991
|
+
auth: "admin"
|
|
42341
42992
|
}), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
|
|
42342
42993
|
kind: "mutation",
|
|
42343
42994
|
auth: "admin"
|
|
@@ -43065,17 +43716,23 @@ method(object({
|
|
|
43065
43716
|
}), method(object({}), object({ providers: array(object({
|
|
43066
43717
|
addonId: string(),
|
|
43067
43718
|
label: string()
|
|
43068
|
-
})).readonly() }), { auth: "admin" }), method(
|
|
43069
|
-
|
|
43070
|
-
|
|
43071
|
-
|
|
43072
|
-
|
|
43073
|
-
|
|
43074
|
-
|
|
43075
|
-
|
|
43076
|
-
}),
|
|
43719
|
+
})).readonly() }), { auth: "admin" }), method(
|
|
43720
|
+
/** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
|
|
43721
|
+
object({ subnets: array(string()).optional() }),
|
|
43722
|
+
object({ groups: array(object({
|
|
43723
|
+
addonId: string(),
|
|
43724
|
+
label: string(),
|
|
43725
|
+
candidates: array(DiscoveryCandidateSchema).readonly(),
|
|
43726
|
+
error: string().nullable()
|
|
43727
|
+
})).readonly() }),
|
|
43728
|
+
{
|
|
43729
|
+
kind: "mutation",
|
|
43730
|
+
auth: "admin"
|
|
43731
|
+
}
|
|
43732
|
+
), method(object({
|
|
43077
43733
|
addonId: string(),
|
|
43078
|
-
params: record(string(), unknown()).optional()
|
|
43734
|
+
params: record(string(), unknown()).optional(),
|
|
43735
|
+
subnets: array(string()).optional()
|
|
43079
43736
|
}), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
|
|
43080
43737
|
kind: "mutation",
|
|
43081
43738
|
auth: "admin"
|
|
@@ -55621,6 +56278,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
55621
56278
|
instanceId: string(),
|
|
55622
56279
|
cameraStableId: string(),
|
|
55623
56280
|
nodeId: string(),
|
|
56281
|
+
/**
|
|
56282
|
+
* False while the node the instance is bound to is not in the cluster
|
|
56283
|
+
* topology (forgotten, renamed, re-paired). The instance is kept; the
|
|
56284
|
+
* operator re-points it with `updateInstance({ targetNodeId })`.
|
|
56285
|
+
*/
|
|
56286
|
+
nodeInCluster: boolean(),
|
|
55624
56287
|
profileId: string(),
|
|
55625
56288
|
profileLabel: string(),
|
|
55626
56289
|
name: string(),
|
|
@@ -55670,6 +56333,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
55670
56333
|
auth: "admin"
|
|
55671
56334
|
}), method(object({
|
|
55672
56335
|
instanceId: string().min(1),
|
|
56336
|
+
/** Re-point the instance to another node; the profile must exist there. */
|
|
56337
|
+
targetNodeId: string().min(1).max(256).optional(),
|
|
55673
56338
|
name: string().trim().min(1).max(160).optional(),
|
|
55674
56339
|
executable: string().max(1024).optional(),
|
|
55675
56340
|
args: array(string().max(2048)).max(64).optional(),
|
|
@@ -60695,7 +61360,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
60695
61360
|
id: string(),
|
|
60696
61361
|
settings: record(string(), unknown()),
|
|
60697
61362
|
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
60698
|
-
secretKeys: array(string()).optional()
|
|
61363
|
+
secretKeys: array(string()).optional(),
|
|
61364
|
+
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
61365
|
+
skipRestart: boolean().optional()
|
|
60699
61366
|
}), object({ success: boolean() }), {
|
|
60700
61367
|
kind: "mutation",
|
|
60701
61368
|
auth: "admin"
|
|
@@ -61172,6 +61839,39 @@ var TlsStatusSchema = object({
|
|
|
61172
61839
|
hasCa: boolean()
|
|
61173
61840
|
}).nullable()
|
|
61174
61841
|
});
|
|
61842
|
+
/** One provider's ask of the scan engine: the modes it declared. */
|
|
61843
|
+
var ScanRequestSchema = object({
|
|
61844
|
+
providerId: string().min(1),
|
|
61845
|
+
modes: array(DiscoveryModeSchema)
|
|
61846
|
+
});
|
|
61847
|
+
var ScanErrorSchema = object({
|
|
61848
|
+
/** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
|
|
61849
|
+
providerId: string().optional(),
|
|
61850
|
+
/** The mode family that failed (`mdns`, `http-probe`, …). */
|
|
61851
|
+
mode: string().optional(),
|
|
61852
|
+
message: string()
|
|
61853
|
+
});
|
|
61854
|
+
/** What one scan did, per mode family; the engine logs the same numbers once. */
|
|
61855
|
+
var ScanStatsSchema = object({
|
|
61856
|
+
hostsProbed: number().int(),
|
|
61857
|
+
hits: number().int(),
|
|
61858
|
+
timeouts: number().int(),
|
|
61859
|
+
byMode: record(string(), object({
|
|
61860
|
+
probed: number().int(),
|
|
61861
|
+
hits: number().int(),
|
|
61862
|
+
timeouts: number().int()
|
|
61863
|
+
}))
|
|
61864
|
+
});
|
|
61865
|
+
var ScanNetworkResultSchema = object({
|
|
61866
|
+
hits: array(object({
|
|
61867
|
+
providerId: string(),
|
|
61868
|
+
hit: DiscoveryHitSchema
|
|
61869
|
+
})),
|
|
61870
|
+
/** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
|
|
61871
|
+
subnetsScanned: array(string()),
|
|
61872
|
+
errors: array(ScanErrorSchema),
|
|
61873
|
+
stats: ScanStatsSchema
|
|
61874
|
+
});
|
|
61175
61875
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
61176
61876
|
/**
|
|
61177
61877
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -61226,6 +61926,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
61226
61926
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
61227
61927
|
kind: "mutation",
|
|
61228
61928
|
auth: "admin"
|
|
61929
|
+
}), method(object({
|
|
61930
|
+
requests: array(ScanRequestSchema),
|
|
61931
|
+
subnets: array(string()).optional()
|
|
61932
|
+
}), ScanNetworkResultSchema, {
|
|
61933
|
+
kind: "mutation",
|
|
61934
|
+
auth: "admin",
|
|
61935
|
+
timeoutMs: 9e4
|
|
61936
|
+
}), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
|
|
61937
|
+
kind: "mutation",
|
|
61938
|
+
auth: "admin"
|
|
61229
61939
|
}), method(_void(), TlsStatusSchema, {
|
|
61230
61940
|
kind: "mutation",
|
|
61231
61941
|
auth: "admin"
|
|
@@ -68067,6 +68777,13 @@ Object.freeze({
|
|
|
68067
68777
|
access: "create",
|
|
68068
68778
|
role: "admin"
|
|
68069
68779
|
},
|
|
68780
|
+
"addonSettings.getDeclaredSettingsKeys": {
|
|
68781
|
+
capName: "addon-settings",
|
|
68782
|
+
capScope: "system",
|
|
68783
|
+
addonId: null,
|
|
68784
|
+
access: "view",
|
|
68785
|
+
role: "admin"
|
|
68786
|
+
},
|
|
68070
68787
|
"addonSettings.getDeviceSettings": {
|
|
68071
68788
|
capName: "addon-settings",
|
|
68072
68789
|
capScope: "system",
|
|
@@ -68088,6 +68805,13 @@ Object.freeze({
|
|
|
68088
68805
|
access: "view",
|
|
68089
68806
|
role: "admin"
|
|
68090
68807
|
},
|
|
68808
|
+
"addonSettings.unsetStoredSettingsKeys": {
|
|
68809
|
+
capName: "addon-settings",
|
|
68810
|
+
capScope: "system",
|
|
68811
|
+
addonId: null,
|
|
68812
|
+
access: "create",
|
|
68813
|
+
role: "admin"
|
|
68814
|
+
},
|
|
68091
68815
|
"addonSettings.updateDeviceSettings": {
|
|
68092
68816
|
capName: "addon-settings",
|
|
68093
68817
|
capScope: "system",
|
|
@@ -70503,6 +71227,13 @@ Object.freeze({
|
|
|
70503
71227
|
access: "create",
|
|
70504
71228
|
role: "admin"
|
|
70505
71229
|
},
|
|
71230
|
+
"deviceProvider.classifyDiscoveryHits": {
|
|
71231
|
+
capName: "device-provider",
|
|
71232
|
+
capScope: "system",
|
|
71233
|
+
addonId: null,
|
|
71234
|
+
access: "create",
|
|
71235
|
+
role: "admin"
|
|
71236
|
+
},
|
|
70506
71237
|
"deviceProvider.createDevice": {
|
|
70507
71238
|
capName: "device-provider",
|
|
70508
71239
|
capScope: "system",
|
|
@@ -70531,6 +71262,13 @@ Object.freeze({
|
|
|
70531
71262
|
access: "view",
|
|
70532
71263
|
role: "admin"
|
|
70533
71264
|
},
|
|
71265
|
+
"deviceProvider.getDiscoveryModes": {
|
|
71266
|
+
capName: "device-provider",
|
|
71267
|
+
capScope: "system",
|
|
71268
|
+
addonId: null,
|
|
71269
|
+
access: "view",
|
|
71270
|
+
role: "admin"
|
|
71271
|
+
},
|
|
70534
71272
|
"deviceProvider.getDiscoveryParamsSchema": {
|
|
70535
71273
|
capName: "device-provider",
|
|
70536
71274
|
capScope: "system",
|
|
@@ -71308,6 +72046,13 @@ Object.freeze({
|
|
|
71308
72046
|
access: "create",
|
|
71309
72047
|
role: "admin"
|
|
71310
72048
|
},
|
|
72049
|
+
"keychain.releaseRecordRefs": {
|
|
72050
|
+
capName: "keychain",
|
|
72051
|
+
capScope: "system",
|
|
72052
|
+
addonId: null,
|
|
72053
|
+
access: "create",
|
|
72054
|
+
role: "admin"
|
|
72055
|
+
},
|
|
71311
72056
|
"keychain.resolve": {
|
|
71312
72057
|
capName: "keychain",
|
|
71313
72058
|
capScope: "system",
|
|
@@ -71728,6 +72473,13 @@ Object.freeze({
|
|
|
71728
72473
|
access: "view",
|
|
71729
72474
|
role: "admin"
|
|
71730
72475
|
},
|
|
72476
|
+
"localNetwork.getScanSubnets": {
|
|
72477
|
+
capName: "local-network",
|
|
72478
|
+
capScope: "system",
|
|
72479
|
+
addonId: null,
|
|
72480
|
+
access: "view",
|
|
72481
|
+
role: "admin"
|
|
72482
|
+
},
|
|
71731
72483
|
"localNetwork.getTlsStatus": {
|
|
71732
72484
|
capName: "local-network",
|
|
71733
72485
|
capScope: "system",
|
|
@@ -71777,6 +72529,13 @@ Object.freeze({
|
|
|
71777
72529
|
access: "create",
|
|
71778
72530
|
role: "admin"
|
|
71779
72531
|
},
|
|
72532
|
+
"localNetwork.scanNetwork": {
|
|
72533
|
+
capName: "local-network",
|
|
72534
|
+
capScope: "system",
|
|
72535
|
+
addonId: null,
|
|
72536
|
+
access: "create",
|
|
72537
|
+
role: "admin"
|
|
72538
|
+
},
|
|
71780
72539
|
"localNetwork.setAddressRole": {
|
|
71781
72540
|
capName: "local-network",
|
|
71782
72541
|
capScope: "system",
|
|
@@ -71798,6 +72557,13 @@ Object.freeze({
|
|
|
71798
72557
|
access: "create",
|
|
71799
72558
|
role: "admin"
|
|
71800
72559
|
},
|
|
72560
|
+
"localNetwork.setScanSubnets": {
|
|
72561
|
+
capName: "local-network",
|
|
72562
|
+
capScope: "system",
|
|
72563
|
+
addonId: null,
|
|
72564
|
+
access: "create",
|
|
72565
|
+
role: "admin"
|
|
72566
|
+
},
|
|
71801
72567
|
"localNetwork.setViewerEndpoints": {
|
|
71802
72568
|
capName: "local-network",
|
|
71803
72569
|
capScope: "system",
|
|
@@ -76019,6 +76785,13 @@ Object.freeze({
|
|
|
76019
76785
|
access: "create",
|
|
76020
76786
|
role: "admin"
|
|
76021
76787
|
},
|
|
76788
|
+
"storageAdmin.dropOrphanedKind": {
|
|
76789
|
+
capName: "storage-admin",
|
|
76790
|
+
capScope: "system",
|
|
76791
|
+
addonId: null,
|
|
76792
|
+
access: "delete",
|
|
76793
|
+
role: "admin"
|
|
76794
|
+
},
|
|
76022
76795
|
"storageAdmin.listKinds": {
|
|
76023
76796
|
capName: "storage-admin",
|
|
76024
76797
|
capScope: "system",
|
|
@@ -76040,6 +76813,27 @@ Object.freeze({
|
|
|
76040
76813
|
access: "view",
|
|
76041
76814
|
role: "admin"
|
|
76042
76815
|
},
|
|
76816
|
+
"storageAdmin.scanStaleData": {
|
|
76817
|
+
capName: "storage-admin",
|
|
76818
|
+
capScope: "system",
|
|
76819
|
+
addonId: null,
|
|
76820
|
+
access: "create",
|
|
76821
|
+
role: "admin"
|
|
76822
|
+
},
|
|
76823
|
+
"storageAdmin.staleDataReport": {
|
|
76824
|
+
capName: "storage-admin",
|
|
76825
|
+
capScope: "system",
|
|
76826
|
+
addonId: null,
|
|
76827
|
+
access: "view",
|
|
76828
|
+
role: "admin"
|
|
76829
|
+
},
|
|
76830
|
+
"storageAdmin.unsetStaleSettingsKey": {
|
|
76831
|
+
capName: "storage-admin",
|
|
76832
|
+
capScope: "system",
|
|
76833
|
+
addonId: null,
|
|
76834
|
+
access: "create",
|
|
76835
|
+
role: "admin"
|
|
76836
|
+
},
|
|
76043
76837
|
"storageEvictable.evict": {
|
|
76044
76838
|
capName: "storage-evictable",
|
|
76045
76839
|
capScope: "system",
|
|
@@ -85993,7 +86787,7 @@ var AdminUIAddon = class extends BaseAddon {
|
|
|
85993
86787
|
capability: adminUiCapability,
|
|
85994
86788
|
provider: {
|
|
85995
86789
|
getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
|
|
85996
|
-
getVersion: async () => ({ version: "1.2.
|
|
86790
|
+
getVersion: async () => ({ version: "1.2.528" })
|
|
85997
86791
|
}
|
|
85998
86792
|
}];
|
|
85999
86793
|
}
|