@camstack/addon-provider-rtsp 1.2.150 → 1.2.152
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 +12 -0
- package/dist/addon.js +856 -661
- package/dist/addon.mjs +856 -661
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -6005,7 +6005,7 @@ var ZodIssueCode = {
|
|
|
6005
6005
|
var ZodFirstPartyTypeKind;
|
|
6006
6006
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6007
6007
|
//#endregion
|
|
6008
|
-
//#region ../types/dist/sleep-
|
|
6008
|
+
//#region ../types/dist/sleep-BX_MqzUe.mjs
|
|
6009
6009
|
/**
|
|
6010
6010
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6011
6011
|
* window to float samples (D455).
|
|
@@ -6814,6 +6814,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6814
6814
|
}
|
|
6815
6815
|
return out;
|
|
6816
6816
|
}
|
|
6817
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6818
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6819
|
+
"cluster",
|
|
6820
|
+
"device-defaults",
|
|
6821
|
+
"node"
|
|
6822
|
+
];
|
|
6817
6823
|
/**
|
|
6818
6824
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6819
6825
|
*
|
|
@@ -7628,6 +7634,28 @@ var SECRET_RETRY_MAX_MS = 5 * 6e4;
|
|
|
7628
7634
|
/** Coalesces a burst of `keychain.changed` events into one re-resolve. */
|
|
7629
7635
|
var SECRET_CHANGE_DEBOUNCE_MS = 250;
|
|
7630
7636
|
/**
|
|
7637
|
+
* Pair a catalogue with the function that builds its ports FROM THE MIGRATION API. The one
|
|
7638
|
+
* place a catalogue is bound: a migration reaches nothing a ports binder cannot reach
|
|
7639
|
+
* through `api` (guard `check-migration-ports-surface`).
|
|
7640
|
+
*/
|
|
7641
|
+
function migrationSet(catalogue, bindPorts) {
|
|
7642
|
+
return { bind: (api) => defineAddonMigrations(catalogue, bindPorts(api)) };
|
|
7643
|
+
}
|
|
7644
|
+
/** A mixed array is a mistake: the other kind would be dropped without a word. */
|
|
7645
|
+
function refuseMixed(form, kept, dropped) {
|
|
7646
|
+
if (dropped > 0) throw new Error(`runMigrations(${form}): ${dropped} of ${kept + dropped} entries are of the other kind and would be dropped; pass one kind per call`);
|
|
7647
|
+
}
|
|
7648
|
+
function catalogueOf(list) {
|
|
7649
|
+
const kept = list.filter((entry) => !("execute" in entry));
|
|
7650
|
+
refuseMixed("catalogue, (api) => ports", kept.length, list.length - kept.length);
|
|
7651
|
+
return kept;
|
|
7652
|
+
}
|
|
7653
|
+
function boundOf(list) {
|
|
7654
|
+
const kept = list.filter((entry) => "execute" in entry);
|
|
7655
|
+
refuseMixed("bound migrations", kept.length, list.length - kept.length);
|
|
7656
|
+
return kept;
|
|
7657
|
+
}
|
|
7658
|
+
/**
|
|
7631
7659
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
7632
7660
|
*
|
|
7633
7661
|
* - Typed `config` property with automatic resolution from store + defaults
|
|
@@ -7705,6 +7733,13 @@ var BaseAddon = class {
|
|
|
7705
7733
|
*/
|
|
7706
7734
|
secretStatePaths = [];
|
|
7707
7735
|
_addonSecretTree = null;
|
|
7736
|
+
/**
|
|
7737
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7738
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7739
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7740
|
+
*/
|
|
7741
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7742
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7708
7743
|
_deviceSecretTree = null;
|
|
7709
7744
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7710
7745
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7774,6 +7809,7 @@ var BaseAddon = class {
|
|
|
7774
7809
|
this._ctx = context;
|
|
7775
7810
|
this._secretSettings = null;
|
|
7776
7811
|
}
|
|
7812
|
+
await this.loadScopedSchemas();
|
|
7777
7813
|
await this.resolveConfig();
|
|
7778
7814
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7779
7815
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7783,25 +7819,24 @@ var BaseAddon = class {
|
|
|
7783
7819
|
const normalized = normalizeAddonInitResult(result);
|
|
7784
7820
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7785
7821
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7822
|
+
await this.loadScopedSchemas();
|
|
7786
7823
|
return normalized;
|
|
7787
7824
|
}
|
|
7825
|
+
async runMigrations(first, second, third) {
|
|
7826
|
+
if (typeof second === "function") return this.runMigrationSets([migrationSet(catalogueOf(first), second)], third);
|
|
7827
|
+
return this.runBound(boundOf(first), second);
|
|
7828
|
+
}
|
|
7788
7829
|
/**
|
|
7789
|
-
*
|
|
7790
|
-
*
|
|
7791
|
-
*
|
|
7792
|
-
* restored devices — with the catalogue bound to the addon's ports
|
|
7793
|
-
* (`defineAddonMigrations(ADDON_MIGRATIONS, ports)`).
|
|
7794
|
-
*
|
|
7795
|
-
* An engine that cannot be reached or refuses the catalogue is logged and runs
|
|
7796
|
-
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7797
|
-
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7798
|
-
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7799
|
-
*
|
|
7800
|
-
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7801
|
-
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7802
|
-
* omitted applied ids "newer").
|
|
7830
|
+
* Several catalogues of ONE owner in one declaration (the engine replaces an owner's
|
|
7831
|
+
* catalogue on every declare, so two calls would each judge the other's ids "newer").
|
|
7832
|
+
* Build each entry with `migrationSet(catalogue, (api) => ports)`.
|
|
7803
7833
|
*/
|
|
7804
|
-
async
|
|
7834
|
+
async runMigrationSets(sets, options) {
|
|
7835
|
+
const api = this.ctx.migrationApi;
|
|
7836
|
+
if (api === void 0) throw new Error(`${this.ctx.id}: the migration API is not available on this context`);
|
|
7837
|
+
return this.runBound(sets.flatMap((set) => set.bind(api)), options);
|
|
7838
|
+
}
|
|
7839
|
+
async runBound(migrations, options) {
|
|
7805
7840
|
const api = this.ctx.api.migrations;
|
|
7806
7841
|
const report = await runAddonMigrations({
|
|
7807
7842
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
@@ -7871,6 +7906,16 @@ var BaseAddon = class {
|
|
|
7871
7906
|
schema(schema) {
|
|
7872
7907
|
return schema;
|
|
7873
7908
|
}
|
|
7909
|
+
/**
|
|
7910
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7911
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7912
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7913
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7914
|
+
* once known.
|
|
7915
|
+
*/
|
|
7916
|
+
scopedSettingsCapabilities() {
|
|
7917
|
+
return [];
|
|
7918
|
+
}
|
|
7874
7919
|
/** Override to provide global-level settings UI schema. */
|
|
7875
7920
|
globalSettingsSchema(_cap) {
|
|
7876
7921
|
return null;
|
|
@@ -7914,7 +7959,8 @@ var BaseAddon = class {
|
|
|
7914
7959
|
return [];
|
|
7915
7960
|
}
|
|
7916
7961
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7917
|
-
|
|
7962
|
+
await this.loadScopedSchemas(cap);
|
|
7963
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7918
7964
|
if (!schema) return { sections: [] };
|
|
7919
7965
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7920
7966
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7949,10 +7995,9 @@ var BaseAddon = class {
|
|
|
7949
7995
|
*
|
|
7950
7996
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7951
7997
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7952
|
-
* SHAPE of the form only — the values live
|
|
7953
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
7998
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7954
7999
|
*/
|
|
7955
|
-
async
|
|
8000
|
+
async integrationFormSchema(_instance) {
|
|
7956
8001
|
return null;
|
|
7957
8002
|
}
|
|
7958
8003
|
/**
|
|
@@ -7998,7 +8043,7 @@ var BaseAddon = class {
|
|
|
7998
8043
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
7999
8044
|
*/
|
|
8000
8045
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8001
|
-
const scoped = await this.
|
|
8046
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
8002
8047
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
8003
8048
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8004
8049
|
}
|
|
@@ -8046,6 +8091,7 @@ var BaseAddon = class {
|
|
|
8046
8091
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8047
8092
|
*/
|
|
8048
8093
|
async resolveGlobalStore(nodeId, cap) {
|
|
8094
|
+
await this.loadScopedSchemas(cap);
|
|
8049
8095
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8050
8096
|
return this.projectForNode(raw, nodeId, cap);
|
|
8051
8097
|
}
|
|
@@ -8056,6 +8102,7 @@ var BaseAddon = class {
|
|
|
8056
8102
|
}
|
|
8057
8103
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8058
8104
|
async readRawProjectedStore(nodeId, cap) {
|
|
8105
|
+
await this.loadScopedSchemas(cap);
|
|
8059
8106
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8060
8107
|
return this.projectForNode(raw, nodeId, cap);
|
|
8061
8108
|
}
|
|
@@ -8158,6 +8205,7 @@ var BaseAddon = class {
|
|
|
8158
8205
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8159
8206
|
}
|
|
8160
8207
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8208
|
+
await this.loadScopedSchemas();
|
|
8161
8209
|
const keys = this.perNodeKeys();
|
|
8162
8210
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8163
8211
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8171,7 +8219,8 @@ var BaseAddon = class {
|
|
|
8171
8219
|
await this.resolveConfig();
|
|
8172
8220
|
await this.onConfigChanged();
|
|
8173
8221
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8174
|
-
|
|
8222
|
+
const legacy = this.globalSettingsSchema();
|
|
8223
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8175
8224
|
}
|
|
8176
8225
|
/**
|
|
8177
8226
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8179,14 +8228,67 @@ var BaseAddon = class {
|
|
|
8179
8228
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8180
8229
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8181
8230
|
*/
|
|
8182
|
-
|
|
8231
|
+
/**
|
|
8232
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8233
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8234
|
+
* derived from the previous set. Idempotent.
|
|
8235
|
+
*/
|
|
8236
|
+
async loadScopedSchemas(extraCap) {
|
|
8237
|
+
const caps = new Set([
|
|
8238
|
+
...this.scopedSettingsCapabilities(),
|
|
8239
|
+
...this._registeredCapNames,
|
|
8240
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8241
|
+
]);
|
|
8242
|
+
let added = false;
|
|
8243
|
+
for (const cap of caps) {
|
|
8244
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8245
|
+
const loaded = [];
|
|
8246
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8247
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8248
|
+
if (schema !== null) loaded.push({
|
|
8249
|
+
scope,
|
|
8250
|
+
schema
|
|
8251
|
+
});
|
|
8252
|
+
}
|
|
8253
|
+
this._scopedByCap.set(cap, loaded);
|
|
8254
|
+
added = true;
|
|
8255
|
+
}
|
|
8256
|
+
if (!added) return;
|
|
8257
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8258
|
+
this._addonSecretTree = null;
|
|
8259
|
+
}
|
|
8260
|
+
/**
|
|
8261
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8262
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8263
|
+
*/
|
|
8264
|
+
scopedFormSchema(cap) {
|
|
8265
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8266
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8267
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8268
|
+
}
|
|
8269
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8270
|
+
loadedScopedSchemas(cap) {
|
|
8271
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8272
|
+
return [...this._scopedByCap.values()].flat();
|
|
8273
|
+
}
|
|
8274
|
+
/**
|
|
8275
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8276
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8277
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8278
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8279
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8280
|
+
*/
|
|
8183
8281
|
perNodeKeys(cap) {
|
|
8184
8282
|
const cacheKey = cap ?? "";
|
|
8185
|
-
const cached = this.
|
|
8283
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8186
8284
|
if (cached) return cached;
|
|
8187
|
-
const
|
|
8188
|
-
const keys = new Set(
|
|
8189
|
-
this.
|
|
8285
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8286
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8287
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8288
|
+
if (scope !== "node") continue;
|
|
8289
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8290
|
+
}
|
|
8291
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8190
8292
|
return keys;
|
|
8191
8293
|
}
|
|
8192
8294
|
/**
|
|
@@ -8196,10 +8298,9 @@ var BaseAddon = class {
|
|
|
8196
8298
|
* time to return its response before the addon is torn down and
|
|
8197
8299
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8198
8300
|
*/
|
|
8199
|
-
maybeAutoRestart(patchKeys,
|
|
8200
|
-
if (!schema) return;
|
|
8301
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8201
8302
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8202
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8303
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8203
8304
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8204
8305
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8205
8306
|
}
|
|
@@ -8395,12 +8496,14 @@ var BaseAddon = class {
|
|
|
8395
8496
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8396
8497
|
*/
|
|
8397
8498
|
async getDeclaredSettingsKeys() {
|
|
8499
|
+
await this.loadScopedSchemas();
|
|
8398
8500
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8399
8501
|
}
|
|
8400
8502
|
declaredKeySet() {
|
|
8401
8503
|
const keys = new Set(Object.keys(this.defaults));
|
|
8402
8504
|
const schema = this.globalSettingsSchema();
|
|
8403
8505
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8506
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8404
8507
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8405
8508
|
for (const path of this.secretStatePaths) {
|
|
8406
8509
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8496,7 +8599,11 @@ var BaseAddon = class {
|
|
|
8496
8599
|
}
|
|
8497
8600
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8498
8601
|
addonSecretTree() {
|
|
8499
|
-
this._addonSecretTree
|
|
8602
|
+
if (this._addonSecretTree === null) {
|
|
8603
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8604
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8605
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8606
|
+
}
|
|
8500
8607
|
return this._addonSecretTree;
|
|
8501
8608
|
}
|
|
8502
8609
|
deviceSecretTree() {
|
|
@@ -8746,6 +8853,23 @@ function dropPerNodeFields(fields) {
|
|
|
8746
8853
|
}
|
|
8747
8854
|
return kept;
|
|
8748
8855
|
}
|
|
8856
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8857
|
+
function collectValueFieldKeys(fields) {
|
|
8858
|
+
const collected = [];
|
|
8859
|
+
for (const field of fields) {
|
|
8860
|
+
if (field.type === "group") {
|
|
8861
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8862
|
+
continue;
|
|
8863
|
+
}
|
|
8864
|
+
if (field.type === "sub-tabs") {
|
|
8865
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8866
|
+
continue;
|
|
8867
|
+
}
|
|
8868
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8869
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8870
|
+
}
|
|
8871
|
+
return collected;
|
|
8872
|
+
}
|
|
8749
8873
|
function collectFieldKeys(fields) {
|
|
8750
8874
|
const collected = [];
|
|
8751
8875
|
for (const field of fields) {
|
|
@@ -9288,6 +9412,7 @@ function defineEntryKind(name, spec) {
|
|
|
9288
9412
|
class: "config",
|
|
9289
9413
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9290
9414
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9415
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9291
9416
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9292
9417
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9293
9418
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
@@ -12046,7 +12171,7 @@ function maskUrlCredentials(rawUrl) {
|
|
|
12046
12171
|
}
|
|
12047
12172
|
}
|
|
12048
12173
|
//#endregion
|
|
12049
|
-
//#region ../types/dist/composition-
|
|
12174
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
12050
12175
|
/**
|
|
12051
12176
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
12052
12177
|
*
|
|
@@ -13527,7 +13652,7 @@ var RecordIntegrationInstances = class {
|
|
|
13527
13652
|
return record === null ? null : this.viewOf(record);
|
|
13528
13653
|
};
|
|
13529
13654
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
13530
|
-
createInstance = async ({ id, name, kind }) => {
|
|
13655
|
+
createInstance = async ({ id, name, kind, values }) => {
|
|
13531
13656
|
const minted = id ?? this.hooks.mintId?.({
|
|
13532
13657
|
name,
|
|
13533
13658
|
...kind !== void 0 ? { kind } : {}
|
|
@@ -13537,10 +13662,20 @@ var RecordIntegrationInstances = class {
|
|
|
13537
13662
|
id: minted,
|
|
13538
13663
|
data: this.hooks.initialBody(name, kind)
|
|
13539
13664
|
}, { ifRev: 0 });
|
|
13665
|
+
const patch = this.patchOf(values ?? {});
|
|
13666
|
+
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
13540
13667
|
await this.hooks.onChanged?.(minted, "created");
|
|
13541
13668
|
return { id: minted };
|
|
13542
13669
|
};
|
|
13543
13670
|
updateInstance = async ({ id, name, values }) => {
|
|
13671
|
+
const patch = this.patchOf(values);
|
|
13672
|
+
if (name !== void 0) patch[this.nameField] = name;
|
|
13673
|
+
await this.handle.patch(id, patch);
|
|
13674
|
+
await this.hooks.onChanged?.(id, "updated");
|
|
13675
|
+
return { ok: true };
|
|
13676
|
+
};
|
|
13677
|
+
/** The record patch a form `values` object means: secrets kept, cleared or replaced. */
|
|
13678
|
+
patchOf(values) {
|
|
13544
13679
|
const patch = {};
|
|
13545
13680
|
for (const [key, value] of Object.entries(values)) {
|
|
13546
13681
|
if (value === void 0) continue;
|
|
@@ -13551,11 +13686,8 @@ var RecordIntegrationInstances = class {
|
|
|
13551
13686
|
else if (isSecretClear(value)) patch[key] = null;
|
|
13552
13687
|
else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
|
|
13553
13688
|
}
|
|
13554
|
-
|
|
13555
|
-
|
|
13556
|
-
await this.hooks.onChanged?.(id, "updated");
|
|
13557
|
-
return { ok: true };
|
|
13558
|
-
};
|
|
13689
|
+
return patch;
|
|
13690
|
+
}
|
|
13559
13691
|
/** The switch lives in the record body; the addon is told so it applies it (D996). */
|
|
13560
13692
|
setInstanceEnabled = async ({ id, enabled }) => {
|
|
13561
13693
|
if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
|
|
@@ -14754,20 +14886,28 @@ async function moveIntegrationRowsToRecords(ports, spec) {
|
|
|
14754
14886
|
};
|
|
14755
14887
|
}
|
|
14756
14888
|
/**
|
|
14757
|
-
* The addon-side ports of `moveIntegrationRowsToRecords` (planning#416)
|
|
14758
|
-
* from its own process, so the
|
|
14759
|
-
* `
|
|
14760
|
-
*
|
|
14761
|
-
*
|
|
14762
|
-
|
|
14889
|
+
* The addon-side ports of `moveIntegrationRowsToRecords` (planning#416). An addon runs the move
|
|
14890
|
+
* from its own process, so the leftover rows are reached through the migration-only door
|
|
14891
|
+
* (`migrations.readLegacyIntegrationRows` / `clearLegacyIntegrationRows`) on the hub's connection.
|
|
14892
|
+
* The door returns markers. This process is the owner, so it resolves and releases them with
|
|
14893
|
+
* `resolveSecretMarkers` — the same call the old settings door used — and only then asks the
|
|
14894
|
+
* door to delete the rows. The addon's own record kind is its records handle.
|
|
14895
|
+
*/
|
|
14896
|
+
async function resolvedRows(stored, secrets) {
|
|
14897
|
+
if (secrets === null) return stored;
|
|
14898
|
+
const { value, unresolved } = await resolveSecretMarkers(stored, secrets);
|
|
14899
|
+
if (unresolved.length > 0) throw new Error(`${unresolved.length} secret(s) cannot be read from the keychain`);
|
|
14900
|
+
return value;
|
|
14901
|
+
}
|
|
14763
14902
|
function integrationRowsPortsOverApi(deps) {
|
|
14903
|
+
const secrets = deps.secrets ?? null;
|
|
14764
14904
|
return {
|
|
14765
14905
|
warn: (message) => deps.log.warn(message),
|
|
14766
14906
|
listIntegrations: async (addonId) => (await deps.api.list()).filter((i) => i.addonId === addonId).map((i) => ({
|
|
14767
14907
|
id: i.id,
|
|
14768
14908
|
name: i.name
|
|
14769
14909
|
})),
|
|
14770
|
-
readRows: (integrationId) => deps.api.
|
|
14910
|
+
readRows: async (integrationId) => resolvedRows(await deps.api.readLegacyIntegrationRows({ integrationId }), secrets),
|
|
14771
14911
|
putRecord: async (id, body) => {
|
|
14772
14912
|
if (await deps.records.get(id) !== null) return "exists";
|
|
14773
14913
|
try {
|
|
@@ -14786,7 +14926,9 @@ function integrationRowsPortsOverApi(deps) {
|
|
|
14786
14926
|
await deps.records.delete(id);
|
|
14787
14927
|
},
|
|
14788
14928
|
clearRows: async (integrationId) => {
|
|
14789
|
-
await deps.api.
|
|
14929
|
+
const refs = collectSecretMarkerRefs(await deps.api.readLegacyIntegrationRows({ integrationId }));
|
|
14930
|
+
if (refs.length > 0 && secrets !== null) await secrets.delete(refs);
|
|
14931
|
+
await deps.api.clearLegacyIntegrationRows({ integrationId });
|
|
14790
14932
|
}
|
|
14791
14933
|
};
|
|
14792
14934
|
}
|
|
@@ -14848,13 +14990,13 @@ async function claimDevicesToDefaultIntegration(ports, spec) {
|
|
|
14848
14990
|
};
|
|
14849
14991
|
if (orphans.some((d) => (d.parentDeviceId ?? null) === null) && fallback === null) throw new Error(`${orphans.length} device(s) of '${spec.addonId}' carry no integration and the addon has no integration to claim them to`);
|
|
14850
14992
|
for (const device of orphans) if (resolve(device, new Set([device.id])) === null) unresolved.add(device.id);
|
|
14851
|
-
for (const id of unresolved) ports.warnDevice(`device ${id} of '${spec.addonId}' carries no integration and its owner chain does not resolve to one: left unstamped`, id);
|
|
14993
|
+
for (const id of unresolved) await ports.warnDevice(`device ${id} of '${spec.addonId}' carries no integration and its owner chain does not resolve to one: left unstamped`, id);
|
|
14852
14994
|
for (const [deviceId, integrationId] of plan) await ports.setIntegrationId(deviceId, integrationId);
|
|
14853
14995
|
const wrong = [];
|
|
14854
14996
|
for (const [deviceId, integrationId] of plan) if (await ports.readIntegrationId(deviceId) !== integrationId) wrong.push(deviceId);
|
|
14855
14997
|
if (wrong.length > 0) throw new Error(`device(s) ${wrong.join(", ")} did not read back their integration after the claim`);
|
|
14856
14998
|
const claimed = plan.size;
|
|
14857
|
-
ports.info(`${claimed} device(s) of '${spec.addonId}' claimed to their owner's integration or the default; ${unresolved.size} unresolved, ${reservations.size} reservation(s) skipped`);
|
|
14999
|
+
await ports.info(`${claimed} device(s) of '${spec.addonId}' claimed to their owner's integration or the default; ${unresolved.size} unresolved, ${reservations.size} reservation(s) skipped`);
|
|
14858
15000
|
return {
|
|
14859
15001
|
outcome: claimed > 0 ? "done" : "already-done",
|
|
14860
15002
|
summary: {
|
|
@@ -14876,14 +15018,14 @@ async function claimDevicesToDefaultIntegration(ports, spec) {
|
|
|
14876
15018
|
* 2. `claim-devices` (tolerate): `claimDevicesToDefaultIntegration`; with no integration to claim
|
|
14877
15019
|
* to it fails and is retried on the next boot.
|
|
14878
15020
|
*
|
|
14879
|
-
* Both run in the addon process through the hub API (`ctx.api`)
|
|
14880
|
-
*
|
|
14881
|
-
* from the record, not from the rows.
|
|
15021
|
+
* Both run in the addon process through the hub API (`ctx.api`). The leftover rows are read
|
|
15022
|
+
* through the migration-only door, which answers empty once the store is gone.
|
|
14882
15023
|
*/
|
|
14883
15024
|
function defaultIntegrationPortsOver(deps) {
|
|
14884
15025
|
return {
|
|
14885
15026
|
...integrationRowsPortsOverApi({
|
|
14886
15027
|
api: deps.api,
|
|
15028
|
+
...deps.secrets !== void 0 ? { secrets: deps.secrets } : {},
|
|
14887
15029
|
records: deps.records.kind({
|
|
14888
15030
|
name: deps.kind.name,
|
|
14889
15031
|
declaration: deps.kind.declaration,
|
|
@@ -14980,25 +15122,6 @@ function defaultIntegrationMigrations(spec) {
|
|
|
14980
15122
|
...spec.eligible !== void 0 ? { eligible: spec.eligible } : {}
|
|
14981
15123
|
})];
|
|
14982
15124
|
}
|
|
14983
|
-
function defaultIntegrationApiOver(api) {
|
|
14984
|
-
return {
|
|
14985
|
-
list: () => api.integrations.list.query(),
|
|
14986
|
-
getSettings: (input) => api.integrations.getSettings.query(input),
|
|
14987
|
-
clearSettings: (input) => api.integrations.clearSettings.mutate(input),
|
|
14988
|
-
listDevices: (addonId) => api.deviceManager.listAll.query({
|
|
14989
|
-
addonId,
|
|
14990
|
-
projection: "slim"
|
|
14991
|
-
}),
|
|
14992
|
-
setDeviceIntegrationId: async (deviceId, integrationId) => {
|
|
14993
|
-
await api.deviceManager.setIntegrationId.mutate({
|
|
14994
|
-
deviceId,
|
|
14995
|
-
integrationId
|
|
14996
|
-
});
|
|
14997
|
-
},
|
|
14998
|
-
readDeviceHasConfig: async (deviceId) => Object.keys((await api.deviceManager.getDevice.query({ deviceId }))?.config ?? {}).length > 0,
|
|
14999
|
-
readDeviceIntegrationId: async (deviceId) => (await api.deviceManager.getDevice.query({ deviceId }))?.integrationId ?? null
|
|
15000
|
-
};
|
|
15001
|
-
}
|
|
15002
15125
|
/**
|
|
15003
15126
|
* The trainer contract — what the hub hands the cloud trainer and what it
|
|
15004
15127
|
* gets back (cloud training, D805+).
|
|
@@ -16119,6 +16242,39 @@ var UnstampedEventMediaCountSchema = object({
|
|
|
16119
16242
|
/** Sum across both, or `null` when either lane could not be counted. */
|
|
16120
16243
|
total: number().int().nonnegative().nullable()
|
|
16121
16244
|
}).nullable();
|
|
16245
|
+
/**
|
|
16246
|
+
* Where the stored event media sits, per storage location: how many media rows
|
|
16247
|
+
* and how many bytes carry each location id, how many carry none (legacy,
|
|
16248
|
+
* resolved to the default `eventMedia` location), and how many name a location
|
|
16249
|
+
* that was not asked about (a deleted or never-listed one).
|
|
16250
|
+
*
|
|
16251
|
+
* One covering-index aggregate per asked location plus the NULL one, and one
|
|
16252
|
+
* unfiltered total that scans the covering index: O(rows), not a walk of the
|
|
16253
|
+
* rows themselves. `unlisted` is total minus the ASKED locations minus NULL, so
|
|
16254
|
+
* it is a deleted location only when every location id was asked.
|
|
16255
|
+
* `null` is "could not be measured", never zero.
|
|
16256
|
+
*/
|
|
16257
|
+
var MediaLocationCensusInputSchema = object({ locationIds: array(string().min(1)).max(50).readonly() });
|
|
16258
|
+
var MediaLocationCensusSchema = object({
|
|
16259
|
+
locations: array(object({
|
|
16260
|
+
locationId: string(),
|
|
16261
|
+
rows: number().int().nonnegative(),
|
|
16262
|
+
bytes: number().nonnegative()
|
|
16263
|
+
})).readonly(),
|
|
16264
|
+
unstamped: object({
|
|
16265
|
+
rows: number().int().nonnegative(),
|
|
16266
|
+
bytes: number().nonnegative()
|
|
16267
|
+
}),
|
|
16268
|
+
/** Rows stamped with a location id that was not in the asked list. */
|
|
16269
|
+
unlisted: object({
|
|
16270
|
+
rows: number().int().nonnegative(),
|
|
16271
|
+
bytes: number().nonnegative()
|
|
16272
|
+
}),
|
|
16273
|
+
total: object({
|
|
16274
|
+
rows: number().int().nonnegative(),
|
|
16275
|
+
bytes: number().nonnegative()
|
|
16276
|
+
})
|
|
16277
|
+
});
|
|
16122
16278
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
16123
16279
|
/** The independently selectable logical storage classes — every class
|
|
16124
16280
|
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
@@ -47155,52 +47311,7 @@ onBrightnessChanged: { data: object({
|
|
|
47155
47311
|
minIntervalMs: 0
|
|
47156
47312
|
} } }
|
|
47157
47313
|
};
|
|
47158
|
-
|
|
47159
|
-
* `broker` — unified pub/sub broker registry, system-scoped collection.
|
|
47160
|
-
*
|
|
47161
|
-
* The cap models any kind-tagged message broker the user wants to
|
|
47162
|
-
* register and that other addons might consume. The first two kinds
|
|
47163
|
-
* are MQTT (mosquitto / aedes embedded / cloud bridge) and
|
|
47164
|
-
* Home Assistant (HA WebSocket — subscribe_entities + call_service).
|
|
47165
|
-
* Future kinds (Zigbee2MQTT bridge, ZHA, KNX, Telegram, …) plug in
|
|
47166
|
-
* the same surface.
|
|
47167
|
-
*
|
|
47168
|
-
* Why one cap, not one cap per kind:
|
|
47169
|
-
* - The integrations page wants a single table of "every broker"
|
|
47170
|
-
* across kinds; a unified cap drives it without join logic.
|
|
47171
|
-
* - Generic admin operations (add / remove / test / status) are
|
|
47172
|
-
* identical across kinds — duplicating them per cap was busywork.
|
|
47173
|
-
* - The `network-access` cap precedent: same interface, many
|
|
47174
|
-
* `providerKind: 'ingress'` implementations (Tailscale, ngrok, …).
|
|
47175
|
-
*
|
|
47176
|
-
* What stays kind-specific:
|
|
47177
|
-
* - `getBrokerConfig` payload (kind decides whether it returns
|
|
47178
|
-
* mqtt URL+creds, HA baseUrl+token, …) — typed as
|
|
47179
|
-
* `Record<string, unknown>` at the wire; consumers narrow.
|
|
47180
|
-
* - `publish` / `subscribe` / `getState` arguments (target / filter /
|
|
47181
|
-
* key) — also typed as `Record<string, unknown>` so each kind can
|
|
47182
|
-
* evolve its surface (MQTT topic+qos vs HA entity_id+domain)
|
|
47183
|
-
* without rebreaking the cap signature.
|
|
47184
|
-
* - `add` `settings` payload (different fields per kind, validated by
|
|
47185
|
-
* the kind-specific provider).
|
|
47186
|
-
*
|
|
47187
|
-
* Bidirectionality:
|
|
47188
|
-
* - `publish` is RPC — synchronous result (provider-defined).
|
|
47189
|
-
* - `subscribe` returns a subscription handle; the actual message
|
|
47190
|
-
* stream flows over the typed event-bus as `broker.message` events
|
|
47191
|
-
* keyed by `(brokerId, subscriptionId)`. Consumers filter in their
|
|
47192
|
-
* event handler. Subscriptions persist across reconnects — the
|
|
47193
|
-
* provider re-subscribes upstream after a transport drop.
|
|
47194
|
-
*
|
|
47195
|
-
* Why event-bus push (not a subscription RPC stream):
|
|
47196
|
-
* - Matches the rest of CamStack's D8 contract — events for telemetry,
|
|
47197
|
-
* RPC for loss-is-a-bug. Broker messages are telemetry (a single
|
|
47198
|
-
* drop is recoverable via `getState({key})`).
|
|
47199
|
-
* - Frees `broker.subscribe` from holding a long-lived RPC channel.
|
|
47200
|
-
* - Cross-process: events already route through `$event-bus`; no
|
|
47201
|
-
* per-broker wire-shaping needed.
|
|
47202
|
-
*/
|
|
47203
|
-
var BrokerStatusEnum = _enum([
|
|
47314
|
+
_enum([
|
|
47204
47315
|
"connected",
|
|
47205
47316
|
"disconnected",
|
|
47206
47317
|
"connecting",
|
|
@@ -47208,141 +47319,12 @@ var BrokerStatusEnum = _enum([
|
|
|
47208
47319
|
"unreachable",
|
|
47209
47320
|
"error"
|
|
47210
47321
|
]);
|
|
47211
|
-
var BrokerInfoSchema = object({
|
|
47212
|
-
/** Stable broker id. Persisted; survives addon restarts. */
|
|
47213
|
-
id: string(),
|
|
47214
|
-
/** Addon id of the provider that OWNS this broker.
|
|
47215
|
-
*
|
|
47216
|
-
* The `broker` cap is a system-scoped collection: several addons
|
|
47217
|
-
* register a `broker` provider, each owning a DISJOINT set of
|
|
47218
|
-
* brokers (mqtt-broker owns `mqtt_*`, provider-homeassistant owns
|
|
47219
|
-
* `ha_*`). A broker therefore belongs to exactly one addon — like a
|
|
47220
|
-
* device belongs to one integration. The admin UI threads this id
|
|
47221
|
-
* back as the `{ addonId }` system-collection selector on every
|
|
47222
|
-
* id-keyed call (`get` / `getSettings` / `setSettings` / `remove` /
|
|
47223
|
-
* `testConnection`), so the call routes to the OWNING provider
|
|
47224
|
-
* instead of defaulting to the first-registered one. */
|
|
47225
|
-
addonId: string(),
|
|
47226
|
-
/** Human-readable name (operator-chosen at add-time). */
|
|
47227
|
-
name: string(),
|
|
47228
|
-
/** Provider-defined kind tag — `mqtt` / `home-assistant` / future. */
|
|
47229
|
-
kind: string(),
|
|
47230
|
-
status: BrokerStatusEnum,
|
|
47231
|
-
/** Free-form provider-specific info (HA version, MQTT broker
|
|
47232
|
-
* flavour, latency, mTLS active, …). */
|
|
47233
|
-
info: record(string(), unknown()),
|
|
47234
|
-
/** Ms epoch of the last connection probe / status update. */
|
|
47235
|
-
lastCheckedAt: number().nullable(),
|
|
47236
|
-
/** Last error message, when `status` indicates failure. */
|
|
47237
|
-
error: string().nullable()
|
|
47238
|
-
});
|
|
47239
47322
|
var RegistryStatusSchema = object({
|
|
47240
47323
|
brokerCount: number().int().nonnegative(),
|
|
47241
47324
|
connectedCount: number().int().nonnegative()
|
|
47242
47325
|
});
|
|
47243
|
-
/**
|
|
47244
|
-
* One entry per `broker` provider — which addon provides which broker
|
|
47245
|
-
* kind(s). The unified create picker reads this to know, for each
|
|
47246
|
-
* (addonId, kind) pair the operator can pick, where to route the
|
|
47247
|
-
* follow-up `getSettingsSchema` / `testSettings` / `add` calls (via the
|
|
47248
|
-
* `{ addonId }` selector). A provider only advertises a kind here when
|
|
47249
|
-
* it implements `getSettingsSchema` for that kind (so the picker never
|
|
47250
|
-
* offers a kind whose creation form can't be rendered).
|
|
47251
|
-
*/
|
|
47252
|
-
var BrokerProviderInfoSchema = object({
|
|
47253
|
-
/** Addon id of the `broker` provider this entry describes. */
|
|
47254
|
-
addonId: string(),
|
|
47255
|
-
/** Broker kinds this provider can create, with a display label. */
|
|
47256
|
-
kinds: array(object({
|
|
47257
|
-
kind: string(),
|
|
47258
|
-
label: string(),
|
|
47259
|
-
/**
|
|
47260
|
-
* Relative hub path to start an interactive account link (OAuth popup).
|
|
47261
|
-
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
47262
|
-
*/
|
|
47263
|
-
interactiveConnectPath: string().min(1).optional(),
|
|
47264
|
-
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
47265
|
-
interactiveDisconnectPath: string().min(1).optional(),
|
|
47266
|
-
/**
|
|
47267
|
-
* The kind signs in through a server-held interactive session instead of
|
|
47268
|
-
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
47269
|
-
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
47270
|
-
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
47271
|
-
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
47272
|
-
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
47273
|
-
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
47274
|
-
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
47275
|
-
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
47276
|
-
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
47277
|
-
* offers it as the default path; the fields the session fills are
|
|
47278
|
-
* marked `filledBySignIn` in the kind's settings schema.
|
|
47279
|
-
*
|
|
47280
|
-
* `methods` lists the ways the session can sign in, recommended first:
|
|
47281
|
-
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
47282
|
-
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
47283
|
-
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
47284
|
-
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
47285
|
-
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
47286
|
-
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
47287
|
-
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
47288
|
-
* `['proxy']`.
|
|
47289
|
-
*/
|
|
47290
|
-
signIn: object({
|
|
47291
|
-
basePath: string().min(1),
|
|
47292
|
-
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
47293
|
-
}).optional()
|
|
47294
|
-
}))
|
|
47295
|
-
});
|
|
47296
|
-
var ListInputSchema = object({
|
|
47297
|
-
/** Optional kind filter — `list({kind:'home-assistant'})` returns
|
|
47298
|
-
* only HA brokers. Omit to list every broker. */
|
|
47299
|
-
kind: string().optional() });
|
|
47300
47326
|
var GetInputSchema = object({ id: string() });
|
|
47301
|
-
var AddInputSchema = object({
|
|
47302
|
-
kind: string().min(1),
|
|
47303
|
-
name: string().min(1),
|
|
47304
|
-
/**
|
|
47305
|
-
* ADOPT an existing id instead of minting a new one.
|
|
47306
|
-
*
|
|
47307
|
-
* Written the day this cost an outage. A broker's id is not a detail: HA
|
|
47308
|
-
* devices carry it inside their `stableId` (`ha:ha_004:dev:…`), so a broker
|
|
47309
|
-
* lost from config and re-added as `ha_001` leaves every one of its devices
|
|
47310
|
-
* bound to a broker that no longer exists. Re-entering the password under the
|
|
47311
|
-
* ORIGINAL id turns a multi-step device migration back into re-entering a
|
|
47312
|
-
* password.
|
|
47313
|
-
*
|
|
47314
|
-
* A provider MUST refuse an id that is already in use — adopting a live
|
|
47315
|
-
* broker's id would silently take it over.
|
|
47316
|
-
*/
|
|
47317
|
-
id: string().min(1).optional(),
|
|
47318
|
-
/** Kind-specific settings (e.g. MQTT `{url,username,password}` or HA
|
|
47319
|
-
* `{baseUrl,accessToken}`). Validated by the kind-specific provider
|
|
47320
|
-
* branch on receipt — invalid shape rejects the add. */
|
|
47321
|
-
settings: record(string(), unknown())
|
|
47322
|
-
});
|
|
47323
|
-
var AddResultSchema = object({ id: string() });
|
|
47324
|
-
var RemoveInputSchema = object({ id: string() });
|
|
47325
|
-
var TestConnectionResultSchema = discriminatedUnion("ok", [object({
|
|
47326
|
-
ok: literal(true),
|
|
47327
|
-
latencyMs: number().nonnegative()
|
|
47328
|
-
}), object({
|
|
47329
|
-
ok: literal(false),
|
|
47330
|
-
error: string()
|
|
47331
|
-
})]);
|
|
47332
47327
|
var SettingsRecordSchema$1 = record(string(), unknown());
|
|
47333
|
-
var SettingsSchemaInputSchema = object({ kind: string() });
|
|
47334
|
-
var TestSettingsInputSchema = object({
|
|
47335
|
-
kind: string(),
|
|
47336
|
-
settings: SettingsRecordSchema$1
|
|
47337
|
-
});
|
|
47338
|
-
var TestSettingsResultSchema = discriminatedUnion("ok", [object({
|
|
47339
|
-
ok: literal(true),
|
|
47340
|
-
latencyMs: number().nonnegative().optional()
|
|
47341
|
-
}).strict(), object({
|
|
47342
|
-
ok: literal(false),
|
|
47343
|
-
error: string()
|
|
47344
|
-
})]);
|
|
47345
|
-
var SettingsSchemaResultSchema = unknown().nullable();
|
|
47346
47328
|
var PublishInputSchema = object({
|
|
47347
47329
|
brokerId: string(),
|
|
47348
47330
|
/** Kind-specific routing target.
|
|
@@ -47376,25 +47358,7 @@ var GetStateInputSchema = object({
|
|
|
47376
47358
|
* HA: entity_id (returns the cached entity state). */
|
|
47377
47359
|
key: string()
|
|
47378
47360
|
});
|
|
47379
|
-
method(
|
|
47380
|
-
kind: "mutation",
|
|
47381
|
-
auth: "admin"
|
|
47382
|
-
}), method(RemoveInputSchema, _void(), {
|
|
47383
|
-
kind: "mutation",
|
|
47384
|
-
auth: "admin"
|
|
47385
|
-
}), method(GetInputSchema, TestConnectionResultSchema, {
|
|
47386
|
-
kind: "mutation",
|
|
47387
|
-
auth: "admin"
|
|
47388
|
-
}), method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }), method(object({
|
|
47389
|
-
id: string(),
|
|
47390
|
-
settings: SettingsRecordSchema$1
|
|
47391
|
-
}), _void(), {
|
|
47392
|
-
kind: "mutation",
|
|
47393
|
-
auth: "admin"
|
|
47394
|
-
}), method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }), method(SettingsSchemaInputSchema, SettingsSchemaResultSchema, { auth: "admin" }), method(TestSettingsInputSchema, TestSettingsResultSchema, {
|
|
47395
|
-
kind: "mutation",
|
|
47396
|
-
auth: "admin"
|
|
47397
|
-
}), method(PublishInputSchema, unknown(), {
|
|
47361
|
+
method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }), method(PublishInputSchema, unknown(), {
|
|
47398
47362
|
kind: "mutation",
|
|
47399
47363
|
auth: "admin"
|
|
47400
47364
|
}), method(SubscribeInputSchema, SubscribeResultSchema, {
|
|
@@ -50623,6 +50587,9 @@ var RunningProposalEvidenceSchema = object({
|
|
|
50623
50587
|
runsFound: number().int().nonnegative(),
|
|
50624
50588
|
pausesFound: number().int().nonnegative(),
|
|
50625
50589
|
longestPauseMs: number().nonnegative(),
|
|
50590
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
50591
|
+
pulsesFound: number().int().nonnegative(),
|
|
50592
|
+
longestPulseMs: number().nonnegative(),
|
|
50626
50593
|
idleFloorW: number(),
|
|
50627
50594
|
noiseCeilingW: number(),
|
|
50628
50595
|
plateauW: number(),
|
|
@@ -52199,7 +52166,14 @@ var DiscoveryCandidateSchema = object({
|
|
|
52199
52166
|
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
52200
52167
|
parentStableId: string().optional(),
|
|
52201
52168
|
/** `false` = shown for context but not pickable. Default true. */
|
|
52202
|
-
selectable: boolean().optional()
|
|
52169
|
+
selectable: boolean().optional(),
|
|
52170
|
+
/**
|
|
52171
|
+
* Keys of the provider's creation form (`getChildCreationSchema`) this candidate
|
|
52172
|
+
* cannot be adopted without — a key for an encrypted board, a pairing code. The
|
|
52173
|
+
* admin asks for exactly these, on the creation form prefilled with
|
|
52174
|
+
* `prefilledConfig`, before it adopts. Absent or empty = adopt as it is.
|
|
52175
|
+
*/
|
|
52176
|
+
needsParams: array(string()).optional()
|
|
52203
52177
|
});
|
|
52204
52178
|
/**
|
|
52205
52179
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -52326,8 +52300,14 @@ var deviceProviderCapability = {
|
|
|
52326
52300
|
* provider does not support manual creation. Lets the Add-Device dialog
|
|
52327
52301
|
* pick the right type instead of assuming Camera.
|
|
52328
52302
|
*/
|
|
52329
|
-
getManualCreationType: method(object({
|
|
52330
|
-
|
|
52303
|
+
getManualCreationType: method(object({
|
|
52304
|
+
/** The integration asked about; a provider whose integrations differ answers per one. */
|
|
52305
|
+
integrationId: string().optional() }), object({ deviceType: _enum(DeviceType).nullable() })),
|
|
52306
|
+
adoptDiscoveredDevice: method(object({
|
|
52307
|
+
candidate: DiscoveryCandidateSchema,
|
|
52308
|
+
/** The integration the operator adopts into; a provider with several instances needs it. */
|
|
52309
|
+
integrationId: string().optional()
|
|
52310
|
+
}), DeviceSummarySchema, {
|
|
52331
52311
|
kind: "mutation",
|
|
52332
52312
|
auth: "admin"
|
|
52333
52313
|
}),
|
|
@@ -52338,10 +52318,16 @@ var deviceProviderCapability = {
|
|
|
52338
52318
|
* devices of that type. The output is a `ConfigUISchema` — the
|
|
52339
52319
|
* router type-asserts it at the boundary.
|
|
52340
52320
|
*/
|
|
52341
|
-
getChildCreationSchema: method(object({
|
|
52321
|
+
getChildCreationSchema: method(object({
|
|
52322
|
+
type: _enum(DeviceType),
|
|
52323
|
+
/** The integration asked about; a provider whose integrations differ answers per one. */
|
|
52324
|
+
integrationId: string().optional()
|
|
52325
|
+
}), CreationSchemaOutputSchema),
|
|
52342
52326
|
createDevice: method(object({
|
|
52343
52327
|
type: _enum(DeviceType),
|
|
52344
|
-
config: record(string(), unknown())
|
|
52328
|
+
config: record(string(), unknown()),
|
|
52329
|
+
/** The integration the operator adds the device to; a provider with several instances needs it. */
|
|
52330
|
+
integrationId: string().optional()
|
|
52345
52331
|
}), DeviceSummarySchema, {
|
|
52346
52332
|
kind: "mutation",
|
|
52347
52333
|
auth: "admin"
|
|
@@ -53066,7 +53052,10 @@ method(object({
|
|
|
53066
53052
|
auth: "admin"
|
|
53067
53053
|
}), method(object({
|
|
53068
53054
|
addonId: string(),
|
|
53069
|
-
type: _enum(DeviceType)
|
|
53055
|
+
type: _enum(DeviceType),
|
|
53056
|
+
/** The integration the form is for; a provider whose integrations differ (a hub
|
|
53057
|
+
* integration takes no manual add) answers per integration. Absent = the addon's own. */
|
|
53058
|
+
integrationId: string().optional()
|
|
53070
53059
|
}), unknown().nullable()), method(object({
|
|
53071
53060
|
addonId: string(),
|
|
53072
53061
|
type: _enum(DeviceType),
|
|
@@ -53150,7 +53139,11 @@ method(object({
|
|
|
53150
53139
|
kind: "mutation",
|
|
53151
53140
|
auth: "admin",
|
|
53152
53141
|
timeoutMs: 12e4
|
|
53153
|
-
}), method(object({
|
|
53142
|
+
}), method(object({
|
|
53143
|
+
addonId: string(),
|
|
53144
|
+
/** The integration the Add-Device dialog is open on; absent = the addon's own. */
|
|
53145
|
+
integrationId: string().optional()
|
|
53146
|
+
}), object({ deviceType: _enum(DeviceType).nullable() }), { auth: "admin" }), method(object({ addonId: string() }), unknown(), { auth: "admin" }), method(object({
|
|
53154
53147
|
deviceId: number(),
|
|
53155
53148
|
key: string(),
|
|
53156
53149
|
value: unknown()
|
|
@@ -56430,7 +56423,7 @@ var TestConnectionStatusEnum = _enum([
|
|
|
56430
56423
|
"inconclusive",
|
|
56431
56424
|
"unsupported"
|
|
56432
56425
|
]);
|
|
56433
|
-
var TestConnectionResultSchema
|
|
56426
|
+
var TestConnectionResultSchema = object({
|
|
56434
56427
|
/** True ONLY for `validated`. Never true for a test that did not run. */
|
|
56435
56428
|
success: boolean(),
|
|
56436
56429
|
error: string().optional(),
|
|
@@ -56451,11 +56444,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
56451
56444
|
info: record(string(), unknown()).optional(),
|
|
56452
56445
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
56453
56446
|
originStableId: string().optional(),
|
|
56454
|
-
settings: record(string(), unknown()).optional()
|
|
56455
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
56456
|
-
* the keychain, not the table (D787). */
|
|
56457
|
-
secretKeys: array(string()).optional(),
|
|
56458
|
-
skipRestart: boolean().optional()
|
|
56447
|
+
settings: record(string(), unknown()).optional()
|
|
56459
56448
|
});
|
|
56460
56449
|
var UpdateIntegrationInputSchema = object({
|
|
56461
56450
|
id: string(),
|
|
@@ -56476,26 +56465,10 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
56476
56465
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
56477
56466
|
kind: "mutation",
|
|
56478
56467
|
auth: "admin"
|
|
56479
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
56480
|
-
id: string(),
|
|
56481
|
-
settings: record(string(), unknown()),
|
|
56482
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
56483
|
-
secretKeys: array(string()).optional(),
|
|
56484
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
56485
|
-
skipRestart: boolean().optional()
|
|
56486
|
-
}), object({ success: boolean() }), {
|
|
56487
|
-
kind: "mutation",
|
|
56488
|
-
auth: "admin"
|
|
56489
|
-
}), method(object({ id: string() }), object({
|
|
56490
|
-
rows: number().int().nonnegative(),
|
|
56491
|
-
secrets: number().int().nonnegative()
|
|
56492
|
-
}), {
|
|
56493
|
-
kind: "mutation",
|
|
56494
|
-
auth: "admin"
|
|
56495
56468
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
56496
56469
|
addonId: string(),
|
|
56497
56470
|
settings: record(string(), unknown())
|
|
56498
|
-
}), TestConnectionResultSchema
|
|
56471
|
+
}), TestConnectionResultSchema, {
|
|
56499
56472
|
kind: "mutation",
|
|
56500
56473
|
auth: "admin"
|
|
56501
56474
|
});
|
|
@@ -56522,6 +56495,14 @@ var ConfigSchemaPassthrough$3 = custom();
|
|
|
56522
56495
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
56523
56496
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
56524
56497
|
/**
|
|
56498
|
+
* What the addon's instances ARE for the Integrations page (planning#416). `devices`: each
|
|
56499
|
+
* instance is an integration that brings devices (the registry owns its on/off switch);
|
|
56500
|
+
* `services`: a connection configured from the addon's own function page, never listed on
|
|
56501
|
+
* Integrations.
|
|
56502
|
+
* It is the same vocabulary as the manifest's `integrationRole`, which stays as an override.
|
|
56503
|
+
*/
|
|
56504
|
+
var IntegrationInstanceRoleSchema = _enum(["devices", "services"]);
|
|
56505
|
+
/**
|
|
56525
56506
|
* One kind of instance a provider can create (an adapter, an account type). The sign-in and
|
|
56526
56507
|
* connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
|
|
56527
56508
|
* hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
|
|
@@ -56532,6 +56513,22 @@ var IntegrationInstanceKindSchema = object({
|
|
|
56532
56513
|
/** Stable id of the kind, sent back as `createInstance.kind`. */
|
|
56533
56514
|
id: string().min(1),
|
|
56534
56515
|
label: string().min(1),
|
|
56516
|
+
/**
|
|
56517
|
+
* The function capability this kind is configured for (`broker`, `smtp-provider`,
|
|
56518
|
+
* `device-provider`, ...). A page section is the kinds whose `functionCap` it names (D1013); a
|
|
56519
|
+
* provider with more than one section must name it on every kind.
|
|
56520
|
+
*/
|
|
56521
|
+
functionCap: string().min(1),
|
|
56522
|
+
/**
|
|
56523
|
+
* For `functionCap: 'broker'`: the registry kind of the broker an instance of this kind is
|
|
56524
|
+
* (`matter`, `home-assistant`, ...). A caller that needs a broker of one kind (a link field)
|
|
56525
|
+
* finds the instances whose kind names it; absent takes the kind `id`.
|
|
56526
|
+
*/
|
|
56527
|
+
brokerKind: string().min(1).optional(),
|
|
56528
|
+
/** What instances of this kind are for the Integrations page; absent takes the provider's `role`. */
|
|
56529
|
+
role: IntegrationInstanceRoleSchema.optional(),
|
|
56530
|
+
/** `testInstance` really probes the remote for this kind; absent takes the provider's `canTest`. */
|
|
56531
|
+
canTest: boolean().optional(),
|
|
56535
56532
|
/** The kind signs in through a server-held interactive session. */
|
|
56536
56533
|
signIn: object({
|
|
56537
56534
|
basePath: string().min(1),
|
|
@@ -56544,11 +56541,7 @@ var IntegrationInstanceKindSchema = object({
|
|
|
56544
56541
|
});
|
|
56545
56542
|
var IntegrationInstanceDescriptionSchema = object({
|
|
56546
56543
|
/** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
|
|
56547
|
-
role:
|
|
56548
|
-
"devices",
|
|
56549
|
-
"services",
|
|
56550
|
-
"both"
|
|
56551
|
-
]),
|
|
56544
|
+
role: IntegrationInstanceRoleSchema,
|
|
56552
56545
|
/** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
|
|
56553
56546
|
discovery: object({
|
|
56554
56547
|
mode: _enum([
|
|
@@ -56623,7 +56616,11 @@ var integrationInstancesCapability = {
|
|
|
56623
56616
|
methods: {
|
|
56624
56617
|
describe: method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }),
|
|
56625
56618
|
/** The form; `id: null` is the create form. */
|
|
56626
|
-
instanceFormSchema: method(object({
|
|
56619
|
+
instanceFormSchema: method(object({
|
|
56620
|
+
id: IntegrationInstanceIdSchema.nullable(),
|
|
56621
|
+
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
56622
|
+
kind: string().min(1).optional()
|
|
56623
|
+
}), ConfigSchemaPassthrough$3, { auth: "admin" }),
|
|
56627
56624
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
56628
56625
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
56629
56626
|
/**
|
|
@@ -56633,7 +56630,9 @@ var integrationInstancesCapability = {
|
|
|
56633
56630
|
createInstance: method(object({
|
|
56634
56631
|
id: IntegrationInstanceIdSchema.optional(),
|
|
56635
56632
|
name: string().min(1),
|
|
56636
|
-
kind: string().min(1).optional()
|
|
56633
|
+
kind: string().min(1).optional(),
|
|
56634
|
+
/** Form values written with the record (a link picked in the create dialog). */
|
|
56635
|
+
values: record(string(), unknown()).optional()
|
|
56637
56636
|
}), object({ id: string() }), {
|
|
56638
56637
|
kind: "mutation",
|
|
56639
56638
|
auth: "admin"
|
|
@@ -56660,8 +56659,10 @@ var integrationInstancesCapability = {
|
|
|
56660
56659
|
}),
|
|
56661
56660
|
testInstance: optionalMethod(object({
|
|
56662
56661
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
56663
|
-
values: record(string(), unknown())
|
|
56664
|
-
|
|
56662
|
+
values: record(string(), unknown()),
|
|
56663
|
+
/** The kind to test when `id` is null and the provider has several. */
|
|
56664
|
+
kind: string().min(1).optional()
|
|
56665
|
+
}), TestConnectionResultSchema, {
|
|
56665
56666
|
kind: "mutation",
|
|
56666
56667
|
auth: "admin"
|
|
56667
56668
|
})
|
|
@@ -59418,6 +59419,380 @@ var SystemMetricsSchema = object({
|
|
|
59418
59419
|
gpuMemoryPercent: number().optional()
|
|
59419
59420
|
});
|
|
59420
59421
|
method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema);
|
|
59422
|
+
/**
|
|
59423
|
+
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
59424
|
+
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
59425
|
+
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
59426
|
+
*
|
|
59427
|
+
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
59428
|
+
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
59429
|
+
* that with the addon id closed over). It is deliberately NOT in any input
|
|
59430
|
+
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
59431
|
+
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
59432
|
+
* addons go through the owner's capability.
|
|
59433
|
+
*
|
|
59434
|
+
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
59435
|
+
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
59436
|
+
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
59437
|
+
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
59438
|
+
* row's own ref.
|
|
59439
|
+
*
|
|
59440
|
+
* Internal: addons reach it through `ctx.records`, never directly.
|
|
59441
|
+
*/
|
|
59442
|
+
var RecordIdSchema = string().min(1).max(256);
|
|
59443
|
+
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
59444
|
+
var RecordBodySchema = record(string(), unknown());
|
|
59445
|
+
var RecordInputSchema = object({
|
|
59446
|
+
id: RecordIdSchema,
|
|
59447
|
+
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
59448
|
+
deviceId: number().int().optional(),
|
|
59449
|
+
/** The domain clock, ms: observed / created / opened. */
|
|
59450
|
+
at: number().int().optional(),
|
|
59451
|
+
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
59452
|
+
expiresAt: number().int().optional(),
|
|
59453
|
+
data: RecordBodySchema
|
|
59454
|
+
});
|
|
59455
|
+
var RecordViewSchema = object({
|
|
59456
|
+
id: string(),
|
|
59457
|
+
deviceId: number().int().nullable(),
|
|
59458
|
+
at: number().nullable(),
|
|
59459
|
+
expiresAt: number().nullable(),
|
|
59460
|
+
rev: number().int(),
|
|
59461
|
+
updatedAt: number(),
|
|
59462
|
+
data: RecordBodySchema
|
|
59463
|
+
});
|
|
59464
|
+
/** Declared index fields only. Anything else does not compile, by design. */
|
|
59465
|
+
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
59466
|
+
string(),
|
|
59467
|
+
number(),
|
|
59468
|
+
boolean(),
|
|
59469
|
+
object({ in: array(union([
|
|
59470
|
+
string(),
|
|
59471
|
+
number(),
|
|
59472
|
+
boolean()
|
|
59473
|
+
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
59474
|
+
object({
|
|
59475
|
+
gt: union([string(), number()]).optional(),
|
|
59476
|
+
gte: union([string(), number()]).optional(),
|
|
59477
|
+
lt: union([string(), number()]).optional(),
|
|
59478
|
+
lte: union([string(), number()]).optional()
|
|
59479
|
+
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
59480
|
+
]));
|
|
59481
|
+
var KindShape = { kind: RecordKindNameSchema };
|
|
59482
|
+
var TimeWindowShape = {
|
|
59483
|
+
deviceId: number().int().optional(),
|
|
59484
|
+
atFrom: number().optional(),
|
|
59485
|
+
atTo: number().optional()
|
|
59486
|
+
};
|
|
59487
|
+
var RecordQueryInputSchema = object({
|
|
59488
|
+
...KindShape,
|
|
59489
|
+
...TimeWindowShape,
|
|
59490
|
+
where: RecordWhereSchema.optional(),
|
|
59491
|
+
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
59492
|
+
order: _enum(["at", "id"]).optional(),
|
|
59493
|
+
desc: boolean().optional(),
|
|
59494
|
+
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
59495
|
+
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
59496
|
+
cursor: string().min(1).optional()
|
|
59497
|
+
});
|
|
59498
|
+
var RecordQueryResultSchema = object({
|
|
59499
|
+
records: array(RecordViewSchema),
|
|
59500
|
+
nextCursor: string().nullable()
|
|
59501
|
+
});
|
|
59502
|
+
var RecordKindStateSchema = _enum([
|
|
59503
|
+
"created",
|
|
59504
|
+
"unchanged",
|
|
59505
|
+
"updated"
|
|
59506
|
+
]);
|
|
59507
|
+
var DeclareKindResultSchema = object({
|
|
59508
|
+
kindId: number().int(),
|
|
59509
|
+
state: RecordKindStateSchema,
|
|
59510
|
+
createdIndexes: array(string())
|
|
59511
|
+
});
|
|
59512
|
+
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
59513
|
+
var SecretRefsList = array(string());
|
|
59514
|
+
/**
|
|
59515
|
+
* One op of a `transact`: the single-row writes, each on a kind of the caller.
|
|
59516
|
+
* All ops of one call share ONE database file and ONE `transactFamily`.
|
|
59517
|
+
*/
|
|
59518
|
+
var RecordTransactOpSchema = discriminatedUnion("op", [
|
|
59519
|
+
object({
|
|
59520
|
+
op: literal("put"),
|
|
59521
|
+
kind: RecordKindNameSchema,
|
|
59522
|
+
record: RecordInputSchema,
|
|
59523
|
+
ifRev: number().int().min(0).optional()
|
|
59524
|
+
}),
|
|
59525
|
+
object({
|
|
59526
|
+
op: literal("patch"),
|
|
59527
|
+
kind: RecordKindNameSchema,
|
|
59528
|
+
id: RecordIdSchema,
|
|
59529
|
+
data: RecordBodySchema,
|
|
59530
|
+
at: number().int().nullable().optional(),
|
|
59531
|
+
expiresAt: number().int().nullable().optional(),
|
|
59532
|
+
ifRev: number().int().min(1).optional()
|
|
59533
|
+
}),
|
|
59534
|
+
object({
|
|
59535
|
+
op: literal("delete"),
|
|
59536
|
+
kind: RecordKindNameSchema,
|
|
59537
|
+
id: RecordIdSchema
|
|
59538
|
+
})
|
|
59539
|
+
]);
|
|
59540
|
+
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
59541
|
+
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
59542
|
+
kind: "mutation",
|
|
59543
|
+
auth: "admin"
|
|
59544
|
+
}), method(object({
|
|
59545
|
+
...KindShape,
|
|
59546
|
+
id: RecordIdSchema
|
|
59547
|
+
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
59548
|
+
...KindShape,
|
|
59549
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59550
|
+
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
59551
|
+
...KindShape,
|
|
59552
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59553
|
+
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
59554
|
+
...KindShape,
|
|
59555
|
+
record: RecordInputSchema,
|
|
59556
|
+
/** Compare-and-set. `0` means "the row must not exist". */
|
|
59557
|
+
ifRev: number().int().min(0).optional()
|
|
59558
|
+
}), object({
|
|
59559
|
+
rev: number().int(),
|
|
59560
|
+
releasedRefs: SecretRefsList
|
|
59561
|
+
}), {
|
|
59562
|
+
kind: "mutation",
|
|
59563
|
+
auth: "admin"
|
|
59564
|
+
}), method(object({
|
|
59565
|
+
...KindShape,
|
|
59566
|
+
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59567
|
+
}), object({
|
|
59568
|
+
count: number().int(),
|
|
59569
|
+
releasedRefs: SecretRefsList
|
|
59570
|
+
}), {
|
|
59571
|
+
kind: "mutation",
|
|
59572
|
+
auth: "admin"
|
|
59573
|
+
}), method(object({
|
|
59574
|
+
...KindShape,
|
|
59575
|
+
id: RecordIdSchema,
|
|
59576
|
+
data: RecordBodySchema,
|
|
59577
|
+
at: number().int().nullable().optional(),
|
|
59578
|
+
expiresAt: number().int().nullable().optional(),
|
|
59579
|
+
ifRev: number().int().min(1).optional()
|
|
59580
|
+
}), object({
|
|
59581
|
+
rev: number().int(),
|
|
59582
|
+
releasedRefs: SecretRefsList
|
|
59583
|
+
}), {
|
|
59584
|
+
kind: "mutation",
|
|
59585
|
+
auth: "admin"
|
|
59586
|
+
}), method(object({
|
|
59587
|
+
...KindShape,
|
|
59588
|
+
id: RecordIdSchema
|
|
59589
|
+
}), object({
|
|
59590
|
+
deleted: boolean(),
|
|
59591
|
+
secretRefs: SecretRefsList
|
|
59592
|
+
}), {
|
|
59593
|
+
kind: "mutation",
|
|
59594
|
+
auth: "admin"
|
|
59595
|
+
}), method(object({
|
|
59596
|
+
...KindShape,
|
|
59597
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59598
|
+
}), object({
|
|
59599
|
+
deleted: number().int(),
|
|
59600
|
+
secretRefs: SecretRefsList
|
|
59601
|
+
}), {
|
|
59602
|
+
kind: "mutation",
|
|
59603
|
+
auth: "admin"
|
|
59604
|
+
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
59605
|
+
...KindShape,
|
|
59606
|
+
...TimeWindowShape,
|
|
59607
|
+
where: RecordWhereSchema.optional()
|
|
59608
|
+
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
59609
|
+
...KindShape,
|
|
59610
|
+
deviceId: number().int().optional(),
|
|
59611
|
+
from: number(),
|
|
59612
|
+
to: number(),
|
|
59613
|
+
bucketMs: number().int().positive()
|
|
59614
|
+
}), object({ buckets: array(object({
|
|
59615
|
+
bucketStart: number(),
|
|
59616
|
+
count: number().int()
|
|
59617
|
+
})) }), { auth: "admin" }), method(object({
|
|
59618
|
+
...KindShape,
|
|
59619
|
+
op: _enum([
|
|
59620
|
+
"sum",
|
|
59621
|
+
"min",
|
|
59622
|
+
"max",
|
|
59623
|
+
"count"
|
|
59624
|
+
]),
|
|
59625
|
+
field: RecordFieldPathSchema.optional(),
|
|
59626
|
+
groupBy: RecordFieldPathSchema.optional(),
|
|
59627
|
+
where: RecordWhereSchema.optional()
|
|
59628
|
+
}), object({ rows: array(object({
|
|
59629
|
+
group: union([
|
|
59630
|
+
string(),
|
|
59631
|
+
number(),
|
|
59632
|
+
_null()
|
|
59633
|
+
]),
|
|
59634
|
+
value: number().nullable()
|
|
59635
|
+
})) }), { auth: "admin" }), method(object({
|
|
59636
|
+
...KindShape,
|
|
59637
|
+
deviceId: number().int().optional(),
|
|
59638
|
+
atBefore: number().optional(),
|
|
59639
|
+
where: RecordWhereSchema.optional()
|
|
59640
|
+
}), object({
|
|
59641
|
+
deleted: number().int(),
|
|
59642
|
+
secretRefs: SecretRefsList
|
|
59643
|
+
}), {
|
|
59644
|
+
kind: "mutation",
|
|
59645
|
+
auth: "admin"
|
|
59646
|
+
}), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
|
|
59647
|
+
results: array(object({
|
|
59648
|
+
rev: number().int().nullable(),
|
|
59649
|
+
deleted: boolean()
|
|
59650
|
+
})),
|
|
59651
|
+
releasedRefs: SecretRefsList,
|
|
59652
|
+
secretRefs: SecretRefsList
|
|
59653
|
+
}), {
|
|
59654
|
+
kind: "mutation",
|
|
59655
|
+
auth: "admin"
|
|
59656
|
+
}), method(object({
|
|
59657
|
+
deviceId: number().int(),
|
|
59658
|
+
kinds: array(RecordKindNameSchema).optional()
|
|
59659
|
+
}), object({
|
|
59660
|
+
deleted: record(string(), number().int()),
|
|
59661
|
+
secretRefs: SecretRefsList
|
|
59662
|
+
}), {
|
|
59663
|
+
kind: "mutation",
|
|
59664
|
+
auth: "admin"
|
|
59665
|
+
}), method(object(KindShape), object({ id: number().int() }), {
|
|
59666
|
+
kind: "mutation",
|
|
59667
|
+
auth: "admin"
|
|
59668
|
+
});
|
|
59669
|
+
/**
|
|
59670
|
+
* The wire shapes of the migration API, version 1 (planning#447).
|
|
59671
|
+
*
|
|
59672
|
+
* A migration is code that outlives the server it was written against: an addon at
|
|
59673
|
+
* version 1.4 that is brought to 1.9 in one jump still runs its 1.4 catalogue, against
|
|
59674
|
+
* the 1.9 kernel. The only thing that code may call is {@link MigrationApiV1}; this file
|
|
59675
|
+
* is the contract of every method of it, one Zod pair per method.
|
|
59676
|
+
*
|
|
59677
|
+
* APPEND-ONLY. A released method is never removed and never changes meaning: an input
|
|
59678
|
+
* field is never removed or made required, an enum never narrows, an output field is
|
|
59679
|
+
* never removed. A change that cannot be expressed that way is `migration-api.v2`, next
|
|
59680
|
+
* to this file. `scripts/check-migration-api-append-only.ts` holds the line against
|
|
59681
|
+
* `scripts/migration-api-contract.json`, and every `behaviours` id below must have an
|
|
59682
|
+
* `it('<id>'` in `migration-api.contract.spec.ts`.
|
|
59683
|
+
*/
|
|
59684
|
+
var Rows = record(string(), unknown());
|
|
59685
|
+
var IntegrationId = object({ integrationId: string().min(1) });
|
|
59686
|
+
var RecordId = object({ id: string().min(1) });
|
|
59687
|
+
var RecordRowSchema = object({
|
|
59688
|
+
id: string(),
|
|
59689
|
+
deviceId: number().int().nullable(),
|
|
59690
|
+
at: number().nullable(),
|
|
59691
|
+
expiresAt: number().nullable(),
|
|
59692
|
+
rev: number().int(),
|
|
59693
|
+
updatedAt: number(),
|
|
59694
|
+
data: Rows
|
|
59695
|
+
});
|
|
59696
|
+
var RecordPutShape = {
|
|
59697
|
+
id: string().min(1),
|
|
59698
|
+
deviceId: number().int().optional(),
|
|
59699
|
+
at: number().int().optional(),
|
|
59700
|
+
expiresAt: number().int().optional(),
|
|
59701
|
+
data: Rows
|
|
59702
|
+
};
|
|
59703
|
+
var MigrationIntegrationSchema = object({
|
|
59704
|
+
id: string(),
|
|
59705
|
+
addonId: string(),
|
|
59706
|
+
name: string(),
|
|
59707
|
+
info: Rows,
|
|
59708
|
+
originStableId: string().optional()
|
|
59709
|
+
});
|
|
59710
|
+
var MigrationDeviceSchema = object({
|
|
59711
|
+
id: number().int(),
|
|
59712
|
+
integrationId: string().nullable().optional(),
|
|
59713
|
+
/** The owning device; null or absent for a root. */
|
|
59714
|
+
parentDeviceId: number().int().nullable().optional(),
|
|
59715
|
+
type: string().optional(),
|
|
59716
|
+
name: string().optional(),
|
|
59717
|
+
stableId: string().optional()
|
|
59718
|
+
});
|
|
59719
|
+
/** What the engine says about the migration API it serves: lets an addon notice an older server. */
|
|
59720
|
+
var MigrationApiInfoSchema = object({
|
|
59721
|
+
version: number().int(),
|
|
59722
|
+
methods: array(string())
|
|
59723
|
+
});
|
|
59724
|
+
object({
|
|
59725
|
+
message: string(),
|
|
59726
|
+
meta: Rows.optional(),
|
|
59727
|
+
/** Grouping tags of the line; a line about a device carries `{ deviceId }`. */
|
|
59728
|
+
tags: object({ deviceId: number().int().optional() }).optional()
|
|
59729
|
+
});
|
|
59730
|
+
object({
|
|
59731
|
+
owner: string().min(1),
|
|
59732
|
+
metas: array(MigrationMetaSchema)
|
|
59733
|
+
}), object({
|
|
59734
|
+
bootId: string(),
|
|
59735
|
+
snapshot: _enum([
|
|
59736
|
+
"none",
|
|
59737
|
+
"written",
|
|
59738
|
+
"refused"
|
|
59739
|
+
]),
|
|
59740
|
+
steps: array(MigrationOwnerStepSchema),
|
|
59741
|
+
/** The migration API the engine serves; absent from a server that predates it. */
|
|
59742
|
+
migrationApi: MigrationApiInfoSchema.optional()
|
|
59743
|
+
}), object({ id: string().min(1) }), object({
|
|
59744
|
+
proceed: boolean(),
|
|
59745
|
+
state: union([literal("pending"), MigrationStatusSchema]),
|
|
59746
|
+
reason: string().nullable()
|
|
59747
|
+
}), object({
|
|
59748
|
+
id: string().min(1),
|
|
59749
|
+
result: MigrationOwnerResultSchema
|
|
59750
|
+
}), object({
|
|
59751
|
+
status: MigrationStatusSchema,
|
|
59752
|
+
error: string().nullable(),
|
|
59753
|
+
rowsChanged: number().int().nonnegative()
|
|
59754
|
+
}), object({
|
|
59755
|
+
rows: number().int().nonnegative(),
|
|
59756
|
+
secrets: number().int().nonnegative()
|
|
59757
|
+
}), IntegrationId.extend({ patch: Rows }), object({ written: boolean() }), IntegrationId.extend({ keys: array(string().min(1)) }), object({
|
|
59758
|
+
present: boolean(),
|
|
59759
|
+
removed: Rows
|
|
59760
|
+
}), RecordRowSchema.nullable(), RecordRowSchema.nullable(), object({
|
|
59761
|
+
...RecordPutShape,
|
|
59762
|
+
ifRev: number().int().optional()
|
|
59763
|
+
}), object({ rev: number().int() }), object({ records: array(object(RecordPutShape)) }), object({ count: number().int().nonnegative() }), RecordId.extend({
|
|
59764
|
+
partial: Rows,
|
|
59765
|
+
ifRev: number().int().optional()
|
|
59766
|
+
}), object({ rev: number().int() }), object({ deleted: boolean() }), object({
|
|
59767
|
+
deviceId: number().int().optional(),
|
|
59768
|
+
atFrom: number().optional(),
|
|
59769
|
+
atTo: number().optional(),
|
|
59770
|
+
where: RecordWhereSchema.optional(),
|
|
59771
|
+
limit: number().int().min(1).optional(),
|
|
59772
|
+
cursor: string().min(1).optional(),
|
|
59773
|
+
resolveSecrets: boolean().optional()
|
|
59774
|
+
}), object({
|
|
59775
|
+
records: array(RecordRowSchema),
|
|
59776
|
+
nextCursor: string().nullable()
|
|
59777
|
+
}), object({
|
|
59778
|
+
deviceId: number().int().optional(),
|
|
59779
|
+
atBefore: number().optional(),
|
|
59780
|
+
where: RecordWhereSchema.optional()
|
|
59781
|
+
}), object({ count: number().int().nonnegative() }), object({}), object({ store: Rows }), object({ patch: Rows }), object({}), object({ keys: array(string().min(1)) }), object({ removed: array(string()) }), object({ refs: array(string()) }), object({ values: record(string(), string().nullable()) }), object({
|
|
59782
|
+
ref: string().min(1),
|
|
59783
|
+
value: string()
|
|
59784
|
+
}), object({}), object({ refs: array(string()) }), object({ deleted: number().int().nonnegative() }), object({}), object({ integrations: array(MigrationIntegrationSchema) }), object({ id: string().min(1) }), MigrationIntegrationSchema.nullable(), object({
|
|
59785
|
+
addonId: string().min(1),
|
|
59786
|
+
integrationId: string().min(1).optional()
|
|
59787
|
+
}), object({ devices: array(MigrationDeviceSchema) }), object({ deviceId: number().int() }), object({
|
|
59788
|
+
id: number().int(),
|
|
59789
|
+
integrationId: string().nullable(),
|
|
59790
|
+
/** Whether the device carries any stored config; its content is not offered. */
|
|
59791
|
+
hasConfig: boolean()
|
|
59792
|
+
}).nullable(), object({
|
|
59793
|
+
deviceId: number().int(),
|
|
59794
|
+
integrationId: string().min(1)
|
|
59795
|
+
}), object({}), object({}), object({ nodeId: string() }), object({}), object({});
|
|
59421
59796
|
method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { auth: "admin" }), method(object({ owner: string().optional() }), object({ pending: array(MigrationPendingEntrySchema) }), { auth: "admin" }), method(object({ id: string().min(1) }), MigrationDryRunResultSchema, { auth: "admin" }), method(object({
|
|
59422
59797
|
id: string().min(1),
|
|
59423
59798
|
error: string().min(1)
|
|
@@ -59447,7 +59822,9 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
59447
59822
|
"written",
|
|
59448
59823
|
"refused"
|
|
59449
59824
|
]),
|
|
59450
|
-
steps: array(MigrationOwnerStepSchema)
|
|
59825
|
+
steps: array(MigrationOwnerStepSchema),
|
|
59826
|
+
/** The migration API this engine serves (additive: a server that predates it omits it). */
|
|
59827
|
+
migrationApi: MigrationApiInfoSchema.optional()
|
|
59451
59828
|
}), {
|
|
59452
59829
|
kind: "mutation",
|
|
59453
59830
|
auth: "admin"
|
|
@@ -59468,6 +59845,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
59468
59845
|
}), {
|
|
59469
59846
|
kind: "mutation",
|
|
59470
59847
|
auth: "admin"
|
|
59848
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
59849
|
+
rows: number().int().nonnegative(),
|
|
59850
|
+
secrets: number().int().nonnegative()
|
|
59851
|
+
}), {
|
|
59852
|
+
kind: "mutation",
|
|
59853
|
+
auth: "admin"
|
|
59854
|
+
}), method(object({
|
|
59855
|
+
integrationId: string().min(1),
|
|
59856
|
+
patch: record(string(), unknown())
|
|
59857
|
+
}), object({ written: boolean() }), {
|
|
59858
|
+
kind: "mutation",
|
|
59859
|
+
auth: "admin"
|
|
59860
|
+
}), method(object({
|
|
59861
|
+
integrationId: string().min(1),
|
|
59862
|
+
keys: array(string().min(1))
|
|
59863
|
+
}), object({
|
|
59864
|
+
present: boolean(),
|
|
59865
|
+
removed: record(string(), unknown())
|
|
59866
|
+
}), {
|
|
59867
|
+
kind: "mutation",
|
|
59868
|
+
auth: "admin"
|
|
59471
59869
|
});
|
|
59472
59870
|
method(object({
|
|
59473
59871
|
sourceUrl: string(),
|
|
@@ -62093,7 +62491,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
62093
62491
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
62094
62492
|
kind: "mutation",
|
|
62095
62493
|
auth: "admin"
|
|
62096
|
-
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(
|
|
62494
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(MediaLocationCensusInputSchema, MediaLocationCensusSchema, {
|
|
62495
|
+
kind: "query",
|
|
62496
|
+
auth: "admin"
|
|
62497
|
+
}), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
|
|
62097
62498
|
kind: "query",
|
|
62098
62499
|
auth: "admin"
|
|
62099
62500
|
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
@@ -64748,7 +65149,7 @@ var BrokerStatusSchema$1 = _enum([
|
|
|
64748
65149
|
"unreachable",
|
|
64749
65150
|
"tls-error"
|
|
64750
65151
|
]);
|
|
64751
|
-
var BrokerInfoSchema
|
|
65152
|
+
var BrokerInfoSchema = object({
|
|
64752
65153
|
id: string(),
|
|
64753
65154
|
name: string(),
|
|
64754
65155
|
url: string(),
|
|
@@ -64811,7 +65212,7 @@ var StatusSchema = object({
|
|
|
64811
65212
|
brokerCount: number(),
|
|
64812
65213
|
embeddedRunning: boolean()
|
|
64813
65214
|
});
|
|
64814
|
-
method(_void(), array(BrokerInfoSchema
|
|
65215
|
+
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
64815
65216
|
kind: "mutation",
|
|
64816
65217
|
auth: "admin"
|
|
64817
65218
|
}), method(IdInputSchema, _void(), {
|
|
@@ -71993,253 +72394,6 @@ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, met
|
|
|
71993
72394
|
auth: "admin"
|
|
71994
72395
|
});
|
|
71995
72396
|
/**
|
|
71996
|
-
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
71997
|
-
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
71998
|
-
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
71999
|
-
*
|
|
72000
|
-
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
72001
|
-
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
72002
|
-
* that with the addon id closed over). It is deliberately NOT in any input
|
|
72003
|
-
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
72004
|
-
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
72005
|
-
* addons go through the owner's capability.
|
|
72006
|
-
*
|
|
72007
|
-
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
72008
|
-
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
72009
|
-
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
72010
|
-
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
72011
|
-
* row's own ref.
|
|
72012
|
-
*
|
|
72013
|
-
* Internal: addons reach it through `ctx.records`, never directly.
|
|
72014
|
-
*/
|
|
72015
|
-
var RecordIdSchema = string().min(1).max(256);
|
|
72016
|
-
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
72017
|
-
var RecordBodySchema = record(string(), unknown());
|
|
72018
|
-
var RecordInputSchema = object({
|
|
72019
|
-
id: RecordIdSchema,
|
|
72020
|
-
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
72021
|
-
deviceId: number().int().optional(),
|
|
72022
|
-
/** The domain clock, ms: observed / created / opened. */
|
|
72023
|
-
at: number().int().optional(),
|
|
72024
|
-
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
72025
|
-
expiresAt: number().int().optional(),
|
|
72026
|
-
data: RecordBodySchema
|
|
72027
|
-
});
|
|
72028
|
-
var RecordViewSchema = object({
|
|
72029
|
-
id: string(),
|
|
72030
|
-
deviceId: number().int().nullable(),
|
|
72031
|
-
at: number().nullable(),
|
|
72032
|
-
expiresAt: number().nullable(),
|
|
72033
|
-
rev: number().int(),
|
|
72034
|
-
updatedAt: number(),
|
|
72035
|
-
data: RecordBodySchema
|
|
72036
|
-
});
|
|
72037
|
-
/** Declared index fields only. Anything else does not compile, by design. */
|
|
72038
|
-
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
72039
|
-
string(),
|
|
72040
|
-
number(),
|
|
72041
|
-
boolean(),
|
|
72042
|
-
object({ in: array(union([
|
|
72043
|
-
string(),
|
|
72044
|
-
number(),
|
|
72045
|
-
boolean()
|
|
72046
|
-
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
72047
|
-
object({
|
|
72048
|
-
gt: union([string(), number()]).optional(),
|
|
72049
|
-
gte: union([string(), number()]).optional(),
|
|
72050
|
-
lt: union([string(), number()]).optional(),
|
|
72051
|
-
lte: union([string(), number()]).optional()
|
|
72052
|
-
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
72053
|
-
]));
|
|
72054
|
-
var KindShape = { kind: RecordKindNameSchema };
|
|
72055
|
-
var TimeWindowShape = {
|
|
72056
|
-
deviceId: number().int().optional(),
|
|
72057
|
-
atFrom: number().optional(),
|
|
72058
|
-
atTo: number().optional()
|
|
72059
|
-
};
|
|
72060
|
-
var RecordQueryInputSchema = object({
|
|
72061
|
-
...KindShape,
|
|
72062
|
-
...TimeWindowShape,
|
|
72063
|
-
where: RecordWhereSchema.optional(),
|
|
72064
|
-
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
72065
|
-
order: _enum(["at", "id"]).optional(),
|
|
72066
|
-
desc: boolean().optional(),
|
|
72067
|
-
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
72068
|
-
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
72069
|
-
cursor: string().min(1).optional()
|
|
72070
|
-
});
|
|
72071
|
-
var RecordQueryResultSchema = object({
|
|
72072
|
-
records: array(RecordViewSchema),
|
|
72073
|
-
nextCursor: string().nullable()
|
|
72074
|
-
});
|
|
72075
|
-
var RecordKindStateSchema = _enum([
|
|
72076
|
-
"created",
|
|
72077
|
-
"unchanged",
|
|
72078
|
-
"updated"
|
|
72079
|
-
]);
|
|
72080
|
-
var DeclareKindResultSchema = object({
|
|
72081
|
-
kindId: number().int(),
|
|
72082
|
-
state: RecordKindStateSchema,
|
|
72083
|
-
createdIndexes: array(string())
|
|
72084
|
-
});
|
|
72085
|
-
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
72086
|
-
var SecretRefsList = array(string());
|
|
72087
|
-
/**
|
|
72088
|
-
* One op of a `transact`: the single-row writes, each on a kind of the caller.
|
|
72089
|
-
* All ops of one call share ONE database file and ONE `transactFamily`.
|
|
72090
|
-
*/
|
|
72091
|
-
var RecordTransactOpSchema = discriminatedUnion("op", [
|
|
72092
|
-
object({
|
|
72093
|
-
op: literal("put"),
|
|
72094
|
-
kind: RecordKindNameSchema,
|
|
72095
|
-
record: RecordInputSchema,
|
|
72096
|
-
ifRev: number().int().min(0).optional()
|
|
72097
|
-
}),
|
|
72098
|
-
object({
|
|
72099
|
-
op: literal("patch"),
|
|
72100
|
-
kind: RecordKindNameSchema,
|
|
72101
|
-
id: RecordIdSchema,
|
|
72102
|
-
data: RecordBodySchema,
|
|
72103
|
-
at: number().int().nullable().optional(),
|
|
72104
|
-
expiresAt: number().int().nullable().optional(),
|
|
72105
|
-
ifRev: number().int().min(1).optional()
|
|
72106
|
-
}),
|
|
72107
|
-
object({
|
|
72108
|
-
op: literal("delete"),
|
|
72109
|
-
kind: RecordKindNameSchema,
|
|
72110
|
-
id: RecordIdSchema
|
|
72111
|
-
})
|
|
72112
|
-
]);
|
|
72113
|
-
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
72114
|
-
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
72115
|
-
kind: "mutation",
|
|
72116
|
-
auth: "admin"
|
|
72117
|
-
}), method(object({
|
|
72118
|
-
...KindShape,
|
|
72119
|
-
id: RecordIdSchema
|
|
72120
|
-
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
72121
|
-
...KindShape,
|
|
72122
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72123
|
-
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
72124
|
-
...KindShape,
|
|
72125
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72126
|
-
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
72127
|
-
...KindShape,
|
|
72128
|
-
record: RecordInputSchema,
|
|
72129
|
-
/** Compare-and-set. `0` means "the row must not exist". */
|
|
72130
|
-
ifRev: number().int().min(0).optional()
|
|
72131
|
-
}), object({
|
|
72132
|
-
rev: number().int(),
|
|
72133
|
-
releasedRefs: SecretRefsList
|
|
72134
|
-
}), {
|
|
72135
|
-
kind: "mutation",
|
|
72136
|
-
auth: "admin"
|
|
72137
|
-
}), method(object({
|
|
72138
|
-
...KindShape,
|
|
72139
|
-
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72140
|
-
}), object({
|
|
72141
|
-
count: number().int(),
|
|
72142
|
-
releasedRefs: SecretRefsList
|
|
72143
|
-
}), {
|
|
72144
|
-
kind: "mutation",
|
|
72145
|
-
auth: "admin"
|
|
72146
|
-
}), method(object({
|
|
72147
|
-
...KindShape,
|
|
72148
|
-
id: RecordIdSchema,
|
|
72149
|
-
data: RecordBodySchema,
|
|
72150
|
-
at: number().int().nullable().optional(),
|
|
72151
|
-
expiresAt: number().int().nullable().optional(),
|
|
72152
|
-
ifRev: number().int().min(1).optional()
|
|
72153
|
-
}), object({
|
|
72154
|
-
rev: number().int(),
|
|
72155
|
-
releasedRefs: SecretRefsList
|
|
72156
|
-
}), {
|
|
72157
|
-
kind: "mutation",
|
|
72158
|
-
auth: "admin"
|
|
72159
|
-
}), method(object({
|
|
72160
|
-
...KindShape,
|
|
72161
|
-
id: RecordIdSchema
|
|
72162
|
-
}), object({
|
|
72163
|
-
deleted: boolean(),
|
|
72164
|
-
secretRefs: SecretRefsList
|
|
72165
|
-
}), {
|
|
72166
|
-
kind: "mutation",
|
|
72167
|
-
auth: "admin"
|
|
72168
|
-
}), method(object({
|
|
72169
|
-
...KindShape,
|
|
72170
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72171
|
-
}), object({
|
|
72172
|
-
deleted: number().int(),
|
|
72173
|
-
secretRefs: SecretRefsList
|
|
72174
|
-
}), {
|
|
72175
|
-
kind: "mutation",
|
|
72176
|
-
auth: "admin"
|
|
72177
|
-
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
72178
|
-
...KindShape,
|
|
72179
|
-
...TimeWindowShape,
|
|
72180
|
-
where: RecordWhereSchema.optional()
|
|
72181
|
-
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
72182
|
-
...KindShape,
|
|
72183
|
-
deviceId: number().int().optional(),
|
|
72184
|
-
from: number(),
|
|
72185
|
-
to: number(),
|
|
72186
|
-
bucketMs: number().int().positive()
|
|
72187
|
-
}), object({ buckets: array(object({
|
|
72188
|
-
bucketStart: number(),
|
|
72189
|
-
count: number().int()
|
|
72190
|
-
})) }), { auth: "admin" }), method(object({
|
|
72191
|
-
...KindShape,
|
|
72192
|
-
op: _enum([
|
|
72193
|
-
"sum",
|
|
72194
|
-
"min",
|
|
72195
|
-
"max",
|
|
72196
|
-
"count"
|
|
72197
|
-
]),
|
|
72198
|
-
field: RecordFieldPathSchema.optional(),
|
|
72199
|
-
groupBy: RecordFieldPathSchema.optional(),
|
|
72200
|
-
where: RecordWhereSchema.optional()
|
|
72201
|
-
}), object({ rows: array(object({
|
|
72202
|
-
group: union([
|
|
72203
|
-
string(),
|
|
72204
|
-
number(),
|
|
72205
|
-
_null()
|
|
72206
|
-
]),
|
|
72207
|
-
value: number().nullable()
|
|
72208
|
-
})) }), { auth: "admin" }), method(object({
|
|
72209
|
-
...KindShape,
|
|
72210
|
-
deviceId: number().int().optional(),
|
|
72211
|
-
atBefore: number().optional(),
|
|
72212
|
-
where: RecordWhereSchema.optional()
|
|
72213
|
-
}), object({
|
|
72214
|
-
deleted: number().int(),
|
|
72215
|
-
secretRefs: SecretRefsList
|
|
72216
|
-
}), {
|
|
72217
|
-
kind: "mutation",
|
|
72218
|
-
auth: "admin"
|
|
72219
|
-
}), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
|
|
72220
|
-
results: array(object({
|
|
72221
|
-
rev: number().int().nullable(),
|
|
72222
|
-
deleted: boolean()
|
|
72223
|
-
})),
|
|
72224
|
-
releasedRefs: SecretRefsList,
|
|
72225
|
-
secretRefs: SecretRefsList
|
|
72226
|
-
}), {
|
|
72227
|
-
kind: "mutation",
|
|
72228
|
-
auth: "admin"
|
|
72229
|
-
}), method(object({
|
|
72230
|
-
deviceId: number().int(),
|
|
72231
|
-
kinds: array(RecordKindNameSchema).optional()
|
|
72232
|
-
}), object({
|
|
72233
|
-
deleted: record(string(), number().int()),
|
|
72234
|
-
secretRefs: SecretRefsList
|
|
72235
|
-
}), {
|
|
72236
|
-
kind: "mutation",
|
|
72237
|
-
auth: "admin"
|
|
72238
|
-
}), method(object(KindShape), object({ id: number().int() }), {
|
|
72239
|
-
kind: "mutation",
|
|
72240
|
-
auth: "admin"
|
|
72241
|
-
});
|
|
72242
|
-
/**
|
|
72243
72397
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
72244
72398
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
72245
72399
|
* declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
|
|
@@ -80898,11 +81052,15 @@ discriminatedUnion("kind", [object({
|
|
|
80898
81052
|
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
80899
81053
|
* near F. A device that is not near one level most of the time has no floor.
|
|
80900
81054
|
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
80901
|
-
* 3.
|
|
80902
|
-
*
|
|
81055
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
81056
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
81057
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
81058
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
81059
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
81060
|
+
* is no run.
|
|
80903
81061
|
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
80904
|
-
* grid a person would type; stop dwell covers the longest pause
|
|
80905
|
-
* covers the longest standby blip above T.
|
|
81062
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
81063
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
80906
81064
|
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
80907
81065
|
* run must hold exactly one true interval and nothing may flap outside.
|
|
80908
81066
|
*/
|
|
@@ -80913,6 +81071,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
|
80913
81071
|
30 * SECOND_MS;
|
|
80914
81072
|
60 * SECOND_MS;
|
|
80915
81073
|
20 * MINUTE_MS$1;
|
|
81074
|
+
90 * SECOND_MS;
|
|
81075
|
+
30 * MINUTE_MS$1;
|
|
81076
|
+
10 * MINUTE_MS$1;
|
|
81077
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
80916
81078
|
10 * SECOND_MS;
|
|
80917
81079
|
10 * SECOND_MS;
|
|
80918
81080
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
@@ -82063,7 +82225,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
82063
82225
|
* `deviceClasses` map (first registered type). `null` when manual creation is
|
|
82064
82226
|
* unsupported. Lets the Add-Device dialog pick the right type per provider.
|
|
82065
82227
|
*/
|
|
82066
|
-
async getManualCreationType() {
|
|
82228
|
+
async getManualCreationType(_input) {
|
|
82067
82229
|
if (!await this.supportsManualCreation()) return { deviceType: null };
|
|
82068
82230
|
return { deviceType: Object.values(DeviceType).find((t) => this.deviceClasses[t] !== void 0) ?? null };
|
|
82069
82231
|
}
|
|
@@ -82074,7 +82236,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
82074
82236
|
return true;
|
|
82075
82237
|
}
|
|
82076
82238
|
async getChildCreationSchema(input) {
|
|
82077
|
-
return this.onGetCreationSchema(input.type);
|
|
82239
|
+
return this.onGetCreationSchema(input.type, { integrationId: input.integrationId });
|
|
82078
82240
|
}
|
|
82079
82241
|
/**
|
|
82080
82242
|
* Default kernel-orchestrated `createDevice` implementation. The
|
|
@@ -82086,7 +82248,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
82086
82248
|
* `deviceClasses` instead.
|
|
82087
82249
|
*/
|
|
82088
82250
|
async createDevice(input) {
|
|
82089
|
-
const spec = await this.onCreateDevice(input.type, input.config);
|
|
82251
|
+
const spec = await this.onCreateDevice(input.type, input.config, { ...input.integrationId !== void 0 ? { integrationId: input.integrationId } : {} });
|
|
82090
82252
|
const Class = this.deviceClasses[spec.meta.type];
|
|
82091
82253
|
if (!Class) throw new Error(`${this.providerName} provider: no device class registered for type "${spec.meta.type}" — add it to the deviceClasses map`);
|
|
82092
82254
|
const stableId = this.generateStableId(spec.meta.type, spec.config);
|
|
@@ -83495,20 +83657,6 @@ Object.freeze({
|
|
|
83495
83657
|
access: "create",
|
|
83496
83658
|
role: "viewer"
|
|
83497
83659
|
},
|
|
83498
|
-
"broker.add": {
|
|
83499
|
-
capName: "broker",
|
|
83500
|
-
capScope: "system",
|
|
83501
|
-
addonId: null,
|
|
83502
|
-
access: "create",
|
|
83503
|
-
role: "admin"
|
|
83504
|
-
},
|
|
83505
|
-
"broker.get": {
|
|
83506
|
-
capName: "broker",
|
|
83507
|
-
capScope: "system",
|
|
83508
|
-
addonId: null,
|
|
83509
|
-
access: "view",
|
|
83510
|
-
role: "admin"
|
|
83511
|
-
},
|
|
83512
83660
|
"broker.getBrokerConfig": {
|
|
83513
83661
|
capName: "broker",
|
|
83514
83662
|
capScope: "system",
|
|
@@ -83516,20 +83664,6 @@ Object.freeze({
|
|
|
83516
83664
|
access: "view",
|
|
83517
83665
|
role: "admin"
|
|
83518
83666
|
},
|
|
83519
|
-
"broker.getSettings": {
|
|
83520
|
-
capName: "broker",
|
|
83521
|
-
capScope: "system",
|
|
83522
|
-
addonId: null,
|
|
83523
|
-
access: "view",
|
|
83524
|
-
role: "admin"
|
|
83525
|
-
},
|
|
83526
|
-
"broker.getSettingsSchema": {
|
|
83527
|
-
capName: "broker",
|
|
83528
|
-
capScope: "system",
|
|
83529
|
-
addonId: null,
|
|
83530
|
-
access: "view",
|
|
83531
|
-
role: "admin"
|
|
83532
|
-
},
|
|
83533
83667
|
"broker.getState": {
|
|
83534
83668
|
capName: "broker",
|
|
83535
83669
|
capScope: "system",
|
|
@@ -83544,20 +83678,6 @@ Object.freeze({
|
|
|
83544
83678
|
access: "view",
|
|
83545
83679
|
role: "admin"
|
|
83546
83680
|
},
|
|
83547
|
-
"broker.list": {
|
|
83548
|
-
capName: "broker",
|
|
83549
|
-
capScope: "system",
|
|
83550
|
-
addonId: null,
|
|
83551
|
-
access: "view",
|
|
83552
|
-
role: "admin"
|
|
83553
|
-
},
|
|
83554
|
-
"broker.listProviders": {
|
|
83555
|
-
capName: "broker",
|
|
83556
|
-
capScope: "system",
|
|
83557
|
-
addonId: null,
|
|
83558
|
-
access: "view",
|
|
83559
|
-
role: "admin"
|
|
83560
|
-
},
|
|
83561
83681
|
"broker.publish": {
|
|
83562
83682
|
capName: "broker",
|
|
83563
83683
|
capScope: "system",
|
|
@@ -83565,20 +83685,6 @@ Object.freeze({
|
|
|
83565
83685
|
access: "create",
|
|
83566
83686
|
role: "admin"
|
|
83567
83687
|
},
|
|
83568
|
-
"broker.remove": {
|
|
83569
|
-
capName: "broker",
|
|
83570
|
-
capScope: "system",
|
|
83571
|
-
addonId: null,
|
|
83572
|
-
access: "delete",
|
|
83573
|
-
role: "admin"
|
|
83574
|
-
},
|
|
83575
|
-
"broker.setSettings": {
|
|
83576
|
-
capName: "broker",
|
|
83577
|
-
capScope: "system",
|
|
83578
|
-
addonId: null,
|
|
83579
|
-
access: "create",
|
|
83580
|
-
role: "admin"
|
|
83581
|
-
},
|
|
83582
83688
|
"broker.subscribe": {
|
|
83583
83689
|
capName: "broker",
|
|
83584
83690
|
capScope: "system",
|
|
@@ -83586,20 +83692,6 @@ Object.freeze({
|
|
|
83586
83692
|
access: "create",
|
|
83587
83693
|
role: "admin"
|
|
83588
83694
|
},
|
|
83589
|
-
"broker.testConnection": {
|
|
83590
|
-
capName: "broker",
|
|
83591
|
-
capScope: "system",
|
|
83592
|
-
addonId: null,
|
|
83593
|
-
access: "create",
|
|
83594
|
-
role: "admin"
|
|
83595
|
-
},
|
|
83596
|
-
"broker.testSettings": {
|
|
83597
|
-
capName: "broker",
|
|
83598
|
-
capScope: "system",
|
|
83599
|
-
addonId: null,
|
|
83600
|
-
access: "create",
|
|
83601
|
-
role: "admin"
|
|
83602
|
-
},
|
|
83603
83695
|
"broker.unsubscribe": {
|
|
83604
83696
|
capName: "broker",
|
|
83605
83697
|
capScope: "system",
|
|
@@ -86197,13 +86289,6 @@ Object.freeze({
|
|
|
86197
86289
|
access: "create",
|
|
86198
86290
|
role: "admin"
|
|
86199
86291
|
},
|
|
86200
|
-
"integrations.clearSettings": {
|
|
86201
|
-
capName: "integrations",
|
|
86202
|
-
capScope: "system",
|
|
86203
|
-
addonId: null,
|
|
86204
|
-
access: "delete",
|
|
86205
|
-
role: "admin"
|
|
86206
|
-
},
|
|
86207
86292
|
"integrations.create": {
|
|
86208
86293
|
capName: "integrations",
|
|
86209
86294
|
capScope: "system",
|
|
@@ -86239,13 +86324,6 @@ Object.freeze({
|
|
|
86239
86324
|
access: "view",
|
|
86240
86325
|
role: "admin"
|
|
86241
86326
|
},
|
|
86242
|
-
"integrations.getSettings": {
|
|
86243
|
-
capName: "integrations",
|
|
86244
|
-
capScope: "system",
|
|
86245
|
-
addonId: null,
|
|
86246
|
-
access: "view",
|
|
86247
|
-
role: "admin"
|
|
86248
|
-
},
|
|
86249
86327
|
"integrations.list": {
|
|
86250
86328
|
capName: "integrations",
|
|
86251
86329
|
capScope: "system",
|
|
@@ -86253,13 +86331,6 @@ Object.freeze({
|
|
|
86253
86331
|
access: "view",
|
|
86254
86332
|
role: "admin"
|
|
86255
86333
|
},
|
|
86256
|
-
"integrations.setSettings": {
|
|
86257
|
-
capName: "integrations",
|
|
86258
|
-
capScope: "system",
|
|
86259
|
-
addonId: null,
|
|
86260
|
-
access: "create",
|
|
86261
|
-
role: "admin"
|
|
86262
|
-
},
|
|
86263
86334
|
"integrations.testConnection": {
|
|
86264
86335
|
capName: "integrations",
|
|
86265
86336
|
capScope: "system",
|
|
@@ -87254,6 +87325,13 @@ Object.freeze({
|
|
|
87254
87325
|
access: "view",
|
|
87255
87326
|
role: "admin"
|
|
87256
87327
|
},
|
|
87328
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
87329
|
+
capName: "migrations",
|
|
87330
|
+
capScope: "system",
|
|
87331
|
+
addonId: null,
|
|
87332
|
+
access: "delete",
|
|
87333
|
+
role: "admin"
|
|
87334
|
+
},
|
|
87257
87335
|
"migrations.declareOwner": {
|
|
87258
87336
|
capName: "migrations",
|
|
87259
87337
|
capScope: "system",
|
|
@@ -87261,6 +87339,13 @@ Object.freeze({
|
|
|
87261
87339
|
access: "create",
|
|
87262
87340
|
role: "admin"
|
|
87263
87341
|
},
|
|
87342
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
87343
|
+
capName: "migrations",
|
|
87344
|
+
capScope: "system",
|
|
87345
|
+
addonId: null,
|
|
87346
|
+
access: "delete",
|
|
87347
|
+
role: "admin"
|
|
87348
|
+
},
|
|
87264
87349
|
"migrations.dryRun": {
|
|
87265
87350
|
capName: "migrations",
|
|
87266
87351
|
capScope: "system",
|
|
@@ -87296,6 +87381,20 @@ Object.freeze({
|
|
|
87296
87381
|
access: "create",
|
|
87297
87382
|
role: "admin"
|
|
87298
87383
|
},
|
|
87384
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
87385
|
+
capName: "migrations",
|
|
87386
|
+
capScope: "system",
|
|
87387
|
+
addonId: null,
|
|
87388
|
+
access: "create",
|
|
87389
|
+
role: "admin"
|
|
87390
|
+
},
|
|
87391
|
+
"migrations.readLegacyIntegrationRows": {
|
|
87392
|
+
capName: "migrations",
|
|
87393
|
+
capScope: "system",
|
|
87394
|
+
addonId: null,
|
|
87395
|
+
access: "view",
|
|
87396
|
+
role: "admin"
|
|
87397
|
+
},
|
|
87299
87398
|
"migrations.restorePreMigration": {
|
|
87300
87399
|
capName: "migrations",
|
|
87301
87400
|
capScope: "system",
|
|
@@ -88556,6 +88655,13 @@ Object.freeze({
|
|
|
88556
88655
|
access: "view",
|
|
88557
88656
|
role: "viewer"
|
|
88558
88657
|
},
|
|
88658
|
+
"pipelineAnalytics.getMediaLocationCensus": {
|
|
88659
|
+
capName: "pipeline-analytics",
|
|
88660
|
+
capScope: "device",
|
|
88661
|
+
addonId: null,
|
|
88662
|
+
access: "view",
|
|
88663
|
+
role: "admin"
|
|
88664
|
+
},
|
|
88559
88665
|
"pipelineAnalytics.getMediaReclaimStatus": {
|
|
88560
88666
|
capName: "pipeline-analytics",
|
|
88561
88667
|
capScope: "device",
|
|
@@ -97563,6 +97669,100 @@ object({
|
|
|
97563
97669
|
delay: number().nonnegative().optional(),
|
|
97564
97670
|
commands: record(string(), unknown())
|
|
97565
97671
|
});
|
|
97672
|
+
entryBody({ brokerId: string() });
|
|
97673
|
+
function secretsOverMigrationApi(api) {
|
|
97674
|
+
return {
|
|
97675
|
+
resolve: async (refs) => {
|
|
97676
|
+
const { values } = await api.secrets.resolve({ refs: [...refs] });
|
|
97677
|
+
const out = {};
|
|
97678
|
+
for (const ref of refs) {
|
|
97679
|
+
const value = values[ref];
|
|
97680
|
+
out[ref] = value === null || value === void 0 ? { kind: "missing" } : {
|
|
97681
|
+
kind: "value",
|
|
97682
|
+
value,
|
|
97683
|
+
version: 0
|
|
97684
|
+
};
|
|
97685
|
+
}
|
|
97686
|
+
return out;
|
|
97687
|
+
},
|
|
97688
|
+
delete: async (refs) => (await api.secrets.delete({ refs: [...refs] })).deleted
|
|
97689
|
+
};
|
|
97690
|
+
}
|
|
97691
|
+
/** `ctx.records` as the migration API offers it: bind a kind, read and write by id. */
|
|
97692
|
+
function recordsOverMigrationApi(api) {
|
|
97693
|
+
return { kind: (definition) => {
|
|
97694
|
+
const handle = api.records.kind(definition);
|
|
97695
|
+
return {
|
|
97696
|
+
get: (id) => handle.get({ id }),
|
|
97697
|
+
getResolved: (id) => handle.getResolved({ id }),
|
|
97698
|
+
put: (input, opts) => handle.put({
|
|
97699
|
+
...input,
|
|
97700
|
+
...opts?.ifRev === void 0 ? {} : { ifRev: opts.ifRev }
|
|
97701
|
+
}),
|
|
97702
|
+
delete: (id) => handle.delete({ id })
|
|
97703
|
+
};
|
|
97704
|
+
} };
|
|
97705
|
+
}
|
|
97706
|
+
function integrationRowsApiOver(api) {
|
|
97707
|
+
return {
|
|
97708
|
+
list: async () => (await api.integrations.list({})).integrations,
|
|
97709
|
+
readLegacyIntegrationRows: (input) => api.legacyRows.read(input),
|
|
97710
|
+
clearLegacyIntegrationRows: (input) => api.legacyRows.clear(input)
|
|
97711
|
+
};
|
|
97712
|
+
}
|
|
97713
|
+
/**
|
|
97714
|
+
* One serial lane per migration API: a log line is delivered after the line before it, so the
|
|
97715
|
+
* order the steps wrote them in is the order the hub reads them in. A delivery that fails
|
|
97716
|
+
* is reported once through the same door; if that fails too nothing is left to tell, and the
|
|
97717
|
+
* line is dropped rather than thrown into a migration step or left as an unhandled rejection.
|
|
97718
|
+
*/
|
|
97719
|
+
var lanes = /* @__PURE__ */ new WeakMap();
|
|
97720
|
+
function deliver(api, send) {
|
|
97721
|
+
const next = (lanes.get(api) ?? Promise.resolve()).then(send).then(() => void 0).catch(async (error) => {
|
|
97722
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
97723
|
+
await api.log.warn({ message: `a migration log line could not be delivered: ${reason}` }).catch(() => void 0);
|
|
97724
|
+
});
|
|
97725
|
+
lanes.set(api, next);
|
|
97726
|
+
return next;
|
|
97727
|
+
}
|
|
97728
|
+
function warnOver(api) {
|
|
97729
|
+
return (message) => deliver(api, () => api.log.warn({ message }));
|
|
97730
|
+
}
|
|
97731
|
+
/** What the rows-to-records move needs, over the migration API. */
|
|
97732
|
+
function rowsDepsOver(api) {
|
|
97733
|
+
return {
|
|
97734
|
+
api: integrationRowsApiOver(api),
|
|
97735
|
+
records: recordsOverMigrationApi(api),
|
|
97736
|
+
secrets: secretsOverMigrationApi(api),
|
|
97737
|
+
warn: warnOver(api)
|
|
97738
|
+
};
|
|
97739
|
+
}
|
|
97740
|
+
function defaultIntegrationApiOver$1(api) {
|
|
97741
|
+
return {
|
|
97742
|
+
...integrationRowsApiOver(api),
|
|
97743
|
+
listDevices: async (addonId) => (await api.devices.list({ addonId })).devices,
|
|
97744
|
+
readDeviceHasConfig: async (deviceId) => (await api.devices.get({ deviceId }))?.hasConfig ?? false,
|
|
97745
|
+
setDeviceIntegrationId: async (deviceId, integrationId) => {
|
|
97746
|
+
await api.devices.setIntegrationId({
|
|
97747
|
+
deviceId,
|
|
97748
|
+
integrationId
|
|
97749
|
+
});
|
|
97750
|
+
},
|
|
97751
|
+
readDeviceIntegrationId: async (deviceId) => (await api.devices.get({ deviceId }))?.integrationId ?? null
|
|
97752
|
+
};
|
|
97753
|
+
}
|
|
97754
|
+
/** What the rows-to-records move and the device claim need, over the migration API. */
|
|
97755
|
+
function defaultIntegrationDepsOver(api) {
|
|
97756
|
+
return {
|
|
97757
|
+
...rowsDepsOver(api),
|
|
97758
|
+
api: defaultIntegrationApiOver$1(api),
|
|
97759
|
+
info: (message) => deliver(api, () => api.log.info({ message })),
|
|
97760
|
+
warnDevice: (message, deviceId) => deliver(api, () => api.log.warn({
|
|
97761
|
+
message,
|
|
97762
|
+
tags: { deviceId }
|
|
97763
|
+
}))
|
|
97764
|
+
};
|
|
97765
|
+
}
|
|
97566
97766
|
//#endregion
|
|
97567
97767
|
//#region src/rtsp-instances.ts
|
|
97568
97768
|
/**
|
|
@@ -98210,18 +98410,13 @@ var RtspProviderAddon = class extends BaseDeviceProvider {
|
|
|
98210
98410
|
const records = this.ctx.records;
|
|
98211
98411
|
if (records === void 0) throw new Error("provider-rtsp: ctx.records is required (hub-only addon)");
|
|
98212
98412
|
await records.declare(rtspRecordKinds);
|
|
98213
|
-
|
|
98413
|
+
await this.runMigrations(defaultIntegrationMigrations({
|
|
98214
98414
|
addonId: this.addonId,
|
|
98215
98415
|
date: "2026-11-04"
|
|
98216
|
-
}), defaultIntegrationPortsOver({
|
|
98217
|
-
|
|
98218
|
-
|
|
98219
|
-
kind: rtspConnectionsEntryKind,
|
|
98220
|
-
info: (message) => this.ctx.logger.info(message),
|
|
98221
|
-
warn: (message) => this.ctx.logger.warn(message),
|
|
98222
|
-
warnDevice: (message, deviceId) => this.ctx.logger.warn(message, { tags: { deviceId } })
|
|
98416
|
+
}), (api) => defaultIntegrationPortsOver({
|
|
98417
|
+
...defaultIntegrationDepsOver(api),
|
|
98418
|
+
kind: rtspConnectionsEntryKind
|
|
98223
98419
|
}));
|
|
98224
|
-
await this.runMigrations(defaultIntegration);
|
|
98225
98420
|
return { providers: [
|
|
98226
98421
|
...regs,
|
|
98227
98422
|
{
|