@camstack/addon-provider-homeassistant 1.2.648 → 1.2.649
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 +6 -0
- package/dist/addon.js +1 -1
- package/dist/addon.mjs +1 -1
- package/dist/{ha-broker-source-Ba20mhmb.js → ha-broker-source-CE3nHkyW.js} +262 -99
- package/dist/{ha-broker-source-iLA_DsW8.mjs → ha-broker-source-D3FBA6uG.mjs} +262 -99
- package/dist/ha-export/ha-export.addon.js +17 -6
- package/dist/ha-export/ha-export.addon.mjs +17 -6
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.649] - 2026-11-02
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The retired media base URL setting is now removed from the settings table where it is actually stored
|
|
15
|
+
|
|
10
16
|
## [1.2.648] - 2026-11-02
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/addon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_ha_broker_source = require("./ha-broker-source-
|
|
2
|
+
const require_ha_broker_source = require("./ha-broker-source-CE3nHkyW.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_zlib = require("node:zlib");
|
|
5
5
|
//#region src/ha-admin-link.ts
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as humiditySensorCapability, $t as discriminatedUnion, A as brokerCapability, At as tamperCapability, C as alarmPanelCapability, Ct as runningFromMediaPlayer, D as bestLocationMatch, E as batteryCapability, Et as scriptRunnerCapability, F as carbonMonoxideCapability, Ft as vacuumControlCapability, G as deviceAdoptionCapability, Gt as defineEntryKind, H as controlCapability, Ht as baseUnitOf, I as climateControlCapability, It as valveCapability, J as eventEmitterCapability, Kt as defineRecordKinds, L as colorCapability, Lt as vibrationCapability, M as buildDeviceAdminUrl, Mt as tryConvertDelta, N as buttonCapability, Nt as tryConvertUnit, O as binaryCapability, Ot as smokeCapability, P as calendarSourceCapability, Pt as updateCapability, Q as humidifierCapability, Qt as boolean, R as connectivityCapability, Rt as waterHeaterCapability, S as airQualitySensorCapability, St as runningFromLawnMower, T as automationControlCapability, Tt as runningStateCapability, U as coverCapability, Ut as createEvent, V as contactCapability, Vt as RecordSecretUnavailableError, W as deviceAdminLinkCapability, X as floodCapability, Xt as _enum, Y as fanControlCapability, Z as gasCapability, Zt as array, a as BaseDevice, an as union, at as motionCapability, b as accessoriesCapability, bt as resolveAttachmentUrls, cn as DeviceFeature, ct as notifierCapability, dn as errMsg, dt as parseAdminBase, en as literal, et as imageCapability, fn as EventCategory, ft as powerMeterCapability, gt as pressureSensorCapability, ht as presenceCapability, in as string, it as mediaPlayerCapability, j as buildAddonRouteProvider, jt as temperatureSensorCapability, k as brightnessCapability, kt as switchCapability, ln as DeviceRole, lt as numericSensorCapability, mt as prepareNotification, nn as object, nt as lawnMowerControlCapability, o as BaseDeviceProvider, on as unknown, ot as normalizeUnit, p as EnumSensorDateTimeFormatSchema, q as enumSensorCapability, qt as entryBody, rn as record, rt as lockControlCapability, sn as url, st as notificationOutputCapability, t as HA_BROKER_KIND, tn as number, tt as ingestReading, un as DeviceType, v as RUNNING_STATE_COLD, vt as publicEndpointReaderFrom, w as ambientLightSensorCapability, wt as runningFromVacuumState, x as addonRoutesCapability, y as TargetSchema, yt as quantityFromUnit, zt as weatherCapability } from "./ha-broker-source-
|
|
1
|
+
import { $ as humiditySensorCapability, $t as discriminatedUnion, A as brokerCapability, At as tamperCapability, C as alarmPanelCapability, Ct as runningFromMediaPlayer, D as bestLocationMatch, E as batteryCapability, Et as scriptRunnerCapability, F as carbonMonoxideCapability, Ft as vacuumControlCapability, G as deviceAdoptionCapability, Gt as defineEntryKind, H as controlCapability, Ht as baseUnitOf, I as climateControlCapability, It as valveCapability, J as eventEmitterCapability, Kt as defineRecordKinds, L as colorCapability, Lt as vibrationCapability, M as buildDeviceAdminUrl, Mt as tryConvertDelta, N as buttonCapability, Nt as tryConvertUnit, O as binaryCapability, Ot as smokeCapability, P as calendarSourceCapability, Pt as updateCapability, Q as humidifierCapability, Qt as boolean, R as connectivityCapability, Rt as waterHeaterCapability, S as airQualitySensorCapability, St as runningFromLawnMower, T as automationControlCapability, Tt as runningStateCapability, U as coverCapability, Ut as createEvent, V as contactCapability, Vt as RecordSecretUnavailableError, W as deviceAdminLinkCapability, X as floodCapability, Xt as _enum, Y as fanControlCapability, Z as gasCapability, Zt as array, a as BaseDevice, an as union, at as motionCapability, b as accessoriesCapability, bt as resolveAttachmentUrls, cn as DeviceFeature, ct as notifierCapability, dn as errMsg, dt as parseAdminBase, en as literal, et as imageCapability, fn as EventCategory, ft as powerMeterCapability, gt as pressureSensorCapability, ht as presenceCapability, in as string, it as mediaPlayerCapability, j as buildAddonRouteProvider, jt as temperatureSensorCapability, k as brightnessCapability, kt as switchCapability, ln as DeviceRole, lt as numericSensorCapability, mt as prepareNotification, nn as object, nt as lawnMowerControlCapability, o as BaseDeviceProvider, on as unknown, ot as normalizeUnit, p as EnumSensorDateTimeFormatSchema, q as enumSensorCapability, qt as entryBody, rn as record, rt as lockControlCapability, sn as url, st as notificationOutputCapability, t as HA_BROKER_KIND, tn as number, tt as ingestReading, un as DeviceType, v as RUNNING_STATE_COLD, vt as publicEndpointReaderFrom, w as ambientLightSensorCapability, wt as runningFromVacuumState, x as addonRoutesCapability, y as TargetSchema, yt as quantityFromUnit, zt as weatherCapability } from "./ha-broker-source-D3FBA6uG.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
4
4
|
//#region src/ha-admin-link.ts
|
|
@@ -5972,7 +5972,7 @@ var ZodIssueCode = {
|
|
|
5972
5972
|
var ZodFirstPartyTypeKind;
|
|
5973
5973
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5974
5974
|
//#endregion
|
|
5975
|
-
//#region ../types/dist/sleep-
|
|
5975
|
+
//#region ../types/dist/sleep-CdJHPm-k.mjs
|
|
5976
5976
|
/**
|
|
5977
5977
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5978
5978
|
* window to float samples (D455).
|
|
@@ -6089,7 +6089,7 @@ function isKeychainStoredField(field) {
|
|
|
6089
6089
|
var SECRET_CLEAR = { $secretClear: true };
|
|
6090
6090
|
/** The `'***'` token D634 redaction writes for non-admins. */
|
|
6091
6091
|
var LEGACY_DEVICE_REDACTION = "***";
|
|
6092
|
-
function isRecord$
|
|
6092
|
+
function isRecord$7(value) {
|
|
6093
6093
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6094
6094
|
}
|
|
6095
6095
|
function makeSecretMarker(ref) {
|
|
@@ -6100,12 +6100,12 @@ function makeSecretMarker(ref) {
|
|
|
6100
6100
|
}
|
|
6101
6101
|
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
6102
6102
|
function isSecretMarker(value) {
|
|
6103
|
-
if (!isRecord$
|
|
6103
|
+
if (!isRecord$7(value)) return false;
|
|
6104
6104
|
const ref = value["$secret"];
|
|
6105
6105
|
return typeof ref === "string" && ref.length > 0;
|
|
6106
6106
|
}
|
|
6107
6107
|
function isSecretClear(value) {
|
|
6108
|
-
return isRecord$
|
|
6108
|
+
return isRecord$7(value) && value["$secretClear"] === true;
|
|
6109
6109
|
}
|
|
6110
6110
|
/**
|
|
6111
6111
|
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
@@ -6858,7 +6858,7 @@ var KeychainWriteRefusedError = class extends Error {
|
|
|
6858
6858
|
}
|
|
6859
6859
|
};
|
|
6860
6860
|
var KEEP = { kind: "keep" };
|
|
6861
|
-
function isRecord$
|
|
6861
|
+
function isRecord$6(value) {
|
|
6862
6862
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6863
6863
|
}
|
|
6864
6864
|
function defaultMintId() {
|
|
@@ -6871,7 +6871,7 @@ function collectSecretMarkerRefs(value, out = []) {
|
|
|
6871
6871
|
return out;
|
|
6872
6872
|
}
|
|
6873
6873
|
if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
|
|
6874
|
-
else if (isRecord$
|
|
6874
|
+
else if (isRecord$6(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
6875
6875
|
return out;
|
|
6876
6876
|
}
|
|
6877
6877
|
async function storedValueEquals(secrets, marker, candidate) {
|
|
@@ -6926,7 +6926,7 @@ function rowId(row, idKey) {
|
|
|
6926
6926
|
return null;
|
|
6927
6927
|
}
|
|
6928
6928
|
async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
6929
|
-
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$
|
|
6929
|
+
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$6) : [];
|
|
6930
6930
|
if (!Array.isArray(incoming)) {
|
|
6931
6931
|
ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
|
|
6932
6932
|
return {
|
|
@@ -6942,7 +6942,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
|
6942
6942
|
const kept = /* @__PURE__ */ new Set();
|
|
6943
6943
|
const rows = [];
|
|
6944
6944
|
for (const row of incoming) {
|
|
6945
|
-
if (!isRecord$
|
|
6945
|
+
if (!isRecord$6(row)) {
|
|
6946
6946
|
rows.push(row);
|
|
6947
6947
|
continue;
|
|
6948
6948
|
}
|
|
@@ -6967,7 +6967,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
6967
6967
|
case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
|
|
6968
6968
|
case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
|
|
6969
6969
|
case "object": {
|
|
6970
|
-
if (!isRecord$
|
|
6970
|
+
if (!isRecord$6(incoming)) {
|
|
6971
6971
|
if (incoming === void 0) return KEEP;
|
|
6972
6972
|
ctx.deletes.push(...collectSecretMarkerRefs(stored));
|
|
6973
6973
|
return {
|
|
@@ -6975,7 +6975,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
6975
6975
|
value: incoming
|
|
6976
6976
|
};
|
|
6977
6977
|
}
|
|
6978
|
-
const storedObj = isRecord$
|
|
6978
|
+
const storedObj = isRecord$6(stored) ? stored : {};
|
|
6979
6979
|
return {
|
|
6980
6980
|
kind: "value",
|
|
6981
6981
|
value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
|
|
@@ -7098,7 +7098,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
|
|
|
7098
7098
|
value: items
|
|
7099
7099
|
};
|
|
7100
7100
|
}
|
|
7101
|
-
if (isRecord$
|
|
7101
|
+
if (isRecord$6(v)) {
|
|
7102
7102
|
const out = {};
|
|
7103
7103
|
for (const [k, child] of Object.entries(v)) {
|
|
7104
7104
|
const s = substitute(child);
|
|
@@ -7130,7 +7130,7 @@ var SecretOverwriteRefusedError = class extends Error {
|
|
|
7130
7130
|
this.name = "SecretOverwriteRefusedError";
|
|
7131
7131
|
}
|
|
7132
7132
|
};
|
|
7133
|
-
function isRecord$
|
|
7133
|
+
function isRecord$5(value) {
|
|
7134
7134
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7135
7135
|
}
|
|
7136
7136
|
function resolvedValue(pass, marker) {
|
|
@@ -7159,7 +7159,7 @@ function inArray(pass, incoming, stored) {
|
|
|
7159
7159
|
return incoming.map((item, index) => {
|
|
7160
7160
|
const twin = stored[index];
|
|
7161
7161
|
if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
|
|
7162
|
-
if (isRecord$
|
|
7162
|
+
if (isRecord$5(item) && isRecord$5(twin)) return inArrayRow(pass, item, twin);
|
|
7163
7163
|
if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
|
|
7164
7164
|
return item;
|
|
7165
7165
|
});
|
|
@@ -7170,7 +7170,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
7170
7170
|
const twin = stored[key];
|
|
7171
7171
|
if (isSecretMarker(twin)) {
|
|
7172
7172
|
if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
|
|
7173
|
-
} else if (isRecord$
|
|
7173
|
+
} else if (isRecord$5(value) && isRecord$5(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
7174
7174
|
else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
|
|
7175
7175
|
}
|
|
7176
7176
|
return out;
|
|
@@ -7178,7 +7178,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
7178
7178
|
async function walk(pass, incoming, stored) {
|
|
7179
7179
|
if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
|
|
7180
7180
|
if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
|
|
7181
|
-
if (!isRecord$
|
|
7181
|
+
if (!isRecord$5(incoming) || !isRecord$5(stored)) return incoming;
|
|
7182
7182
|
const out = { ...incoming };
|
|
7183
7183
|
for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
|
|
7184
7184
|
return out;
|
|
@@ -7209,7 +7209,7 @@ async function restoreUnchangedSecrets(input) {
|
|
|
7209
7209
|
function isJsonEqual(a, b) {
|
|
7210
7210
|
if (Object.is(a, b)) return true;
|
|
7211
7211
|
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
|
|
7212
|
-
if (isRecord$
|
|
7212
|
+
if (isRecord$5(a) && isRecord$5(b)) {
|
|
7213
7213
|
const keys = Object.keys(a);
|
|
7214
7214
|
if (keys.length !== Object.keys(b).length) return false;
|
|
7215
7215
|
return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
|
|
@@ -7233,7 +7233,7 @@ function markerKeysOf(blob, owner) {
|
|
|
7233
7233
|
}
|
|
7234
7234
|
function nodeFromStored(value, owner) {
|
|
7235
7235
|
if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
|
|
7236
|
-
if (!isRecord$
|
|
7236
|
+
if (!isRecord$5(value)) return null;
|
|
7237
7237
|
const children = /* @__PURE__ */ new Map();
|
|
7238
7238
|
for (const [key, child] of Object.entries(value)) {
|
|
7239
7239
|
const node = nodeFromStored(child, owner);
|
|
@@ -7852,6 +7852,44 @@ var BaseAddon = class {
|
|
|
7852
7852
|
return null;
|
|
7853
7853
|
}
|
|
7854
7854
|
/**
|
|
7855
|
+
* D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
|
|
7856
|
+
* provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
|
|
7857
|
+
* declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
|
|
7858
|
+
* {@link nodeSettingsSchema} and of {@link deviceDefaultsSchema} are disjoint, and a key never
|
|
7859
|
+
* contains `@` (guard `check-scoped-settings-declared`). SHAPE only: values are read by scope.
|
|
7860
|
+
*/
|
|
7861
|
+
async clusterSettingsSchema(_cap) {
|
|
7862
|
+
return null;
|
|
7863
|
+
}
|
|
7864
|
+
/**
|
|
7865
|
+
* D986: the form shape of the settings one capability owns PER NODE, or `null`. Stored as
|
|
7866
|
+
* `addon-settings` rows `<addonId>.<field>@<nodeId>` carrying `nodeId` in the row data, so a
|
|
7867
|
+
* forgotten node's rows go in one central delete.
|
|
7868
|
+
*/
|
|
7869
|
+
async nodeSettingsSchema(_cap) {
|
|
7870
|
+
return null;
|
|
7871
|
+
}
|
|
7872
|
+
/**
|
|
7873
|
+
* D986: the form shape of the FLEET DEFAULTS of the per-device settings one capability owns,
|
|
7874
|
+
* or `null`. Stored as `addon-settings` rows `<addonId>.<field>`; a device resolves its value
|
|
7875
|
+
* as device row, then this default, then the schema default (`resolveDeviceSettings`).
|
|
7876
|
+
*/
|
|
7877
|
+
async deviceDefaultsSchema(_cap) {
|
|
7878
|
+
return null;
|
|
7879
|
+
}
|
|
7880
|
+
/**
|
|
7881
|
+
* The host's read of the scoped declarations: the schema one capability declares for `scope`,
|
|
7882
|
+
* or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
|
|
7883
|
+
* host asks this method.
|
|
7884
|
+
*/
|
|
7885
|
+
async getScopedSettingsSchema(scope, cap) {
|
|
7886
|
+
switch (scope) {
|
|
7887
|
+
case "cluster": return this.clusterSettingsSchema(cap);
|
|
7888
|
+
case "node": return this.nodeSettingsSchema(cap);
|
|
7889
|
+
case "device-defaults": return this.deviceDefaultsSchema(cap);
|
|
7890
|
+
}
|
|
7891
|
+
}
|
|
7892
|
+
/**
|
|
7855
7893
|
* What the create wizard and the integration edit page render for one instance: the addon's
|
|
7856
7894
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
7857
7895
|
*/
|
|
@@ -8645,47 +8683,6 @@ function normalizeAddonInitResult(result) {
|
|
|
8645
8683
|
if (Array.isArray(result)) return { providers: result };
|
|
8646
8684
|
return result;
|
|
8647
8685
|
}
|
|
8648
|
-
/**
|
|
8649
|
-
* The share-view scope kinds a share-link token can carry. The hub mints
|
|
8650
|
-
* and validates them (`ShareViewGrantSchema` builds its
|
|
8651
|
-
* enum from this list); a data plane names the kinds it ACCEPTS in `share`.
|
|
8652
|
-
*/
|
|
8653
|
-
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
8654
|
-
/** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
|
|
8655
|
-
var PeerBytesTicketSchema = object({
|
|
8656
|
-
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
8657
|
-
url: string().min(1),
|
|
8658
|
-
/**
|
|
8659
|
-
* The HOST node this URL means something on — the hub or a named agent,
|
|
8660
|
-
* never a runner. {@link AddonPeerBytes.open} compares it to its own and
|
|
8661
|
-
* refuses `cross-node` by name when they differ, without dialling.
|
|
8662
|
-
*/
|
|
8663
|
-
hostNodeId: string().min(1),
|
|
8664
|
-
expiresAtMs: number().int().nonnegative(),
|
|
8665
|
-
/**
|
|
8666
|
-
* What the producer DECLARED the body to be, when it knows — `null` when it
|
|
8667
|
-
* does not. Never `0` for unknown (D393): a consumer sizing a bound off this
|
|
8668
|
-
* must be able to tell "the producer did not say" from "the body is empty".
|
|
8669
|
-
*/
|
|
8670
|
-
declaredBytes: number().int().nonnegative().nullable()
|
|
8671
|
-
});
|
|
8672
|
-
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
8673
|
-
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
8674
|
-
var RECORD_DATABASES = ["settings", "events"];
|
|
8675
|
-
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
8676
|
-
var RECORD_CLASSES = [
|
|
8677
|
-
"config",
|
|
8678
|
-
"ledger",
|
|
8679
|
-
"audit",
|
|
8680
|
-
"mirror",
|
|
8681
|
-
"ephemeral"
|
|
8682
|
-
];
|
|
8683
|
-
var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
|
|
8684
|
-
var RECORD_MAX_DATA_BYTES = 1024 * 1024;
|
|
8685
|
-
var RECORD_BATCH_MAX = 1e3;
|
|
8686
|
-
var RECORD_QUERY_MAX = 1e3;
|
|
8687
|
-
/** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
|
|
8688
|
-
var RECORD_SETTINGS_MAX_ROWS = 5e4;
|
|
8689
8686
|
var PREFIX = "records/";
|
|
8690
8687
|
/** The base of every records failure. `detail` is the human part of the message. */
|
|
8691
8688
|
var RecordsError = class extends Error {
|
|
@@ -8718,6 +8715,83 @@ var DeclarationRefusedError = class extends RecordsError {
|
|
|
8718
8715
|
this.name = "DeclarationRefusedError";
|
|
8719
8716
|
}
|
|
8720
8717
|
};
|
|
8718
|
+
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
8719
|
+
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
8720
|
+
var RECORD_DATABASES = ["settings", "events"];
|
|
8721
|
+
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
8722
|
+
var RECORD_CLASSES = [
|
|
8723
|
+
"config",
|
|
8724
|
+
"ledger",
|
|
8725
|
+
"audit",
|
|
8726
|
+
"mirror",
|
|
8727
|
+
"ephemeral"
|
|
8728
|
+
];
|
|
8729
|
+
var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
|
|
8730
|
+
var RECORD_MAX_DATA_BYTES = 1024 * 1024;
|
|
8731
|
+
var RECORD_BATCH_MAX = 1e3;
|
|
8732
|
+
var RECORD_QUERY_MAX = 1e3;
|
|
8733
|
+
/** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
|
|
8734
|
+
var RECORD_SETTINGS_MAX_ROWS = 5e4;
|
|
8735
|
+
/**
|
|
8736
|
+
* Secret fields of a record, addressed the way a Zod schema declares them
|
|
8737
|
+
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8738
|
+
* (`auth.accessToken`, `headers[].value`).
|
|
8739
|
+
*
|
|
8740
|
+
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8741
|
+
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8742
|
+
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
8743
|
+
* leaf names its element: `headers[X-Api].value`.
|
|
8744
|
+
*
|
|
8745
|
+
* Pure and browser-safe. Nothing here mutates: every transform returns a new value.
|
|
8746
|
+
*/
|
|
8747
|
+
/** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
|
|
8748
|
+
function parseSecretPath(path) {
|
|
8749
|
+
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8750
|
+
return path.split(".").map((raw) => {
|
|
8751
|
+
const each = raw.endsWith("[]");
|
|
8752
|
+
const key = each ? raw.slice(0, -2) : raw;
|
|
8753
|
+
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8754
|
+
return {
|
|
8755
|
+
key,
|
|
8756
|
+
each
|
|
8757
|
+
};
|
|
8758
|
+
});
|
|
8759
|
+
}
|
|
8760
|
+
/** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
|
|
8761
|
+
function secretArrayPrefixes(path) {
|
|
8762
|
+
const segments = parseSecretPath(path);
|
|
8763
|
+
const prefixes = [];
|
|
8764
|
+
const walked = [];
|
|
8765
|
+
for (const segment of segments) {
|
|
8766
|
+
walked.push(segment.key);
|
|
8767
|
+
if (segment.each) prefixes.push(walked.join("."));
|
|
8768
|
+
}
|
|
8769
|
+
return prefixes;
|
|
8770
|
+
}
|
|
8771
|
+
/**
|
|
8772
|
+
* The share-view scope kinds a share-link token can carry. The hub mints
|
|
8773
|
+
* and validates them (`ShareViewGrantSchema` builds its
|
|
8774
|
+
* enum from this list); a data plane names the kinds it ACCEPTS in `share`.
|
|
8775
|
+
*/
|
|
8776
|
+
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
8777
|
+
/** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
|
|
8778
|
+
var PeerBytesTicketSchema = object({
|
|
8779
|
+
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
8780
|
+
url: string().min(1),
|
|
8781
|
+
/**
|
|
8782
|
+
* The HOST node this URL means something on — the hub or a named agent,
|
|
8783
|
+
* never a runner. {@link AddonPeerBytes.open} compares it to its own and
|
|
8784
|
+
* refuses `cross-node` by name when they differ, without dialling.
|
|
8785
|
+
*/
|
|
8786
|
+
hostNodeId: string().min(1),
|
|
8787
|
+
expiresAtMs: number().int().nonnegative(),
|
|
8788
|
+
/**
|
|
8789
|
+
* What the producer DECLARED the body to be, when it knows — `null` when it
|
|
8790
|
+
* does not. Never `0` for unknown (D393): a consumer sizing a bound off this
|
|
8791
|
+
* must be able to tell "the producer did not say" from "the body is empty".
|
|
8792
|
+
*/
|
|
8793
|
+
declaredBytes: number().int().nonnegative().nullable()
|
|
8794
|
+
});
|
|
8721
8795
|
function defOf(schema) {
|
|
8722
8796
|
const zodBag = Reflect.get(schema, "_zod");
|
|
8723
8797
|
if (typeof zodBag !== "object" || zodBag === null) return {};
|
|
@@ -8786,42 +8860,6 @@ function collectZodSecretPaths(schema, prefix = "") {
|
|
|
8786
8860
|
return [...new Set(paths)];
|
|
8787
8861
|
}
|
|
8788
8862
|
/**
|
|
8789
|
-
* Secret fields of a record, addressed the way a Zod schema declares them
|
|
8790
|
-
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8791
|
-
* (`auth.accessToken`, `headers[].value`).
|
|
8792
|
-
*
|
|
8793
|
-
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8794
|
-
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8795
|
-
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
8796
|
-
* leaf names its element: `headers[X-Api].value`.
|
|
8797
|
-
*
|
|
8798
|
-
* Pure and browser-safe. Nothing here mutates: every transform returns a new value.
|
|
8799
|
-
*/
|
|
8800
|
-
/** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
|
|
8801
|
-
function parseSecretPath(path) {
|
|
8802
|
-
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8803
|
-
return path.split(".").map((raw) => {
|
|
8804
|
-
const each = raw.endsWith("[]");
|
|
8805
|
-
const key = each ? raw.slice(0, -2) : raw;
|
|
8806
|
-
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8807
|
-
return {
|
|
8808
|
-
key,
|
|
8809
|
-
each
|
|
8810
|
-
};
|
|
8811
|
-
});
|
|
8812
|
-
}
|
|
8813
|
-
/** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
|
|
8814
|
-
function secretArrayPrefixes(path) {
|
|
8815
|
-
const segments = parseSecretPath(path);
|
|
8816
|
-
const prefixes = [];
|
|
8817
|
-
const walked = [];
|
|
8818
|
-
for (const segment of segments) {
|
|
8819
|
-
walked.push(segment.key);
|
|
8820
|
-
if (segment.each) prefixes.push(walked.join("."));
|
|
8821
|
-
}
|
|
8822
|
-
return prefixes;
|
|
8823
|
-
}
|
|
8824
|
-
/**
|
|
8825
8863
|
* Record kinds (D881): the declaration an addon makes of one kind of fact, the
|
|
8826
8864
|
* Zod row schema that describes it, and the helper that turns the two into the
|
|
8827
8865
|
* JSON the engine stores.
|
|
@@ -9786,6 +9824,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9786
9824
|
"humidity-sensor",
|
|
9787
9825
|
"image",
|
|
9788
9826
|
"image-settings",
|
|
9827
|
+
"integration-instances",
|
|
9789
9828
|
"integrations",
|
|
9790
9829
|
"intercom",
|
|
9791
9830
|
"ir-transmitter",
|
|
@@ -11719,7 +11758,7 @@ object({
|
|
|
11719
11758
|
params: record(string(), union([string(), number()])).optional()
|
|
11720
11759
|
});
|
|
11721
11760
|
//#endregion
|
|
11722
|
-
//#region ../types/dist/composition-
|
|
11761
|
+
//#region ../types/dist/composition-BDUoQsw9.mjs
|
|
11723
11762
|
/**
|
|
11724
11763
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11725
11764
|
*
|
|
@@ -21356,6 +21395,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
21356
21395
|
"load-contribution": system(INTERNAL),
|
|
21357
21396
|
"local-network": system(NETWORK),
|
|
21358
21397
|
"log-channels": system(INTERNAL),
|
|
21398
|
+
"integration-instances": system(INTERNAL),
|
|
21359
21399
|
"log-destination": system(INTERNAL),
|
|
21360
21400
|
"login-method": system(IDENTITY),
|
|
21361
21401
|
"mesh-network": system(NETWORK),
|
|
@@ -43656,7 +43696,7 @@ var TargetKindCapsSchema = object({
|
|
|
43656
43696
|
* the union is large and not meant for runtime validation here; the exported
|
|
43657
43697
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
43658
43698
|
*/
|
|
43659
|
-
var ConfigSchemaPassthrough$
|
|
43699
|
+
var ConfigSchemaPassthrough$4 = unknown();
|
|
43660
43700
|
var TargetKindSchema = object({
|
|
43661
43701
|
kind: string(),
|
|
43662
43702
|
label: string(),
|
|
@@ -43693,7 +43733,7 @@ var TargetKindSchema = object({
|
|
|
43693
43733
|
* its raster path, which is the safe default for an unknown image.
|
|
43694
43734
|
*/
|
|
43695
43735
|
iconMediaType: string().optional(),
|
|
43696
|
-
configSchema: ConfigSchemaPassthrough$
|
|
43736
|
+
configSchema: ConfigSchemaPassthrough$4,
|
|
43697
43737
|
/**
|
|
43698
43738
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
43699
43739
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -55701,9 +55741,15 @@ var AvailableIntegrationTypeSchema = object({
|
|
|
55701
55741
|
* branch on CAP — never on addon name. `device-adoption` integrations
|
|
55702
55742
|
* (Home Assistant, …) route through the broker step (Approach A);
|
|
55703
55743
|
* `device-provider` integrations (Reolink/ONVIF) keep the
|
|
55704
|
-
* legacy config → discovery flow.
|
|
55744
|
+
* legacy config → discovery flow. `connection` is an addon that only holds a
|
|
55745
|
+
* connection as typed records (`integration-instances`, D985): no devices,
|
|
55746
|
+
* no discovery.
|
|
55705
55747
|
*/
|
|
55706
|
-
kind: _enum([
|
|
55748
|
+
kind: _enum([
|
|
55749
|
+
"device-adoption",
|
|
55750
|
+
"device-provider",
|
|
55751
|
+
"connection"
|
|
55752
|
+
]),
|
|
55707
55753
|
/**
|
|
55708
55754
|
* For `device-adoption` addons: the broker kind to create/link
|
|
55709
55755
|
* (e.g. `home-assistant`), declared in the addon manifest. `null` for
|
|
@@ -55816,6 +55862,67 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
55816
55862
|
auth: "admin"
|
|
55817
55863
|
});
|
|
55818
55864
|
/**
|
|
55865
|
+
* `integration-instances` - the connection of an addon that holds ONE or SEVERAL
|
|
55866
|
+
* (an account, a relay, a destination) as typed records, shown on the
|
|
55867
|
+
* Integrations page like every other integration (D985, D986).
|
|
55868
|
+
*
|
|
55869
|
+
* A collection of providers: each addon that holds connections registers one
|
|
55870
|
+
* and the hub aggregator (`integrations.*`) reaches a provider with the pinned
|
|
55871
|
+
* `addonId`. It is internal and has no route: the operator-facing surface is
|
|
55872
|
+
* the `integrations` cap, which keeps ONE list.
|
|
55873
|
+
*
|
|
55874
|
+
* Identity: the instance id IS the `integrations` registry id (`int_NNNN`,
|
|
55875
|
+
* immutable, already in the `/integrations/<id>` URL). A second operator-chosen
|
|
55876
|
+
* slug would be a second authority over one identity (D224). `name` is the
|
|
55877
|
+
* editable label.
|
|
55878
|
+
*
|
|
55879
|
+
* Secrets: every field the instance kind marks `.meta({ secret: true })` reaches
|
|
55880
|
+
* the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
|
|
55881
|
+
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
55882
|
+
* clears it.
|
|
55883
|
+
*/
|
|
55884
|
+
var ConfigSchemaPassthrough$3 = custom();
|
|
55885
|
+
var IntegrationInstanceIdSchema = string().min(1);
|
|
55886
|
+
var IntegrationInstanceDescriptionSchema = object({
|
|
55887
|
+
/** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
|
|
55888
|
+
instanceMode: _enum(["single", "multiple"]),
|
|
55889
|
+
/** What the instance is, for the UI (`destination`, `relay`, `account`). */
|
|
55890
|
+
kind: string(),
|
|
55891
|
+
/** True when `testInstance` really probes the remote. */
|
|
55892
|
+
canTest: boolean()
|
|
55893
|
+
});
|
|
55894
|
+
var IntegrationInstanceStatusSchema = object({
|
|
55895
|
+
state: _enum([
|
|
55896
|
+
"ok",
|
|
55897
|
+
"off",
|
|
55898
|
+
"error",
|
|
55899
|
+
"unknown"
|
|
55900
|
+
]),
|
|
55901
|
+
detail: string().optional()
|
|
55902
|
+
});
|
|
55903
|
+
var IntegrationInstanceSchema = object({
|
|
55904
|
+
id: IntegrationInstanceIdSchema,
|
|
55905
|
+
name: string(),
|
|
55906
|
+
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
55907
|
+
configured: boolean(),
|
|
55908
|
+
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
55909
|
+
formValues: record(string(), unknown()),
|
|
55910
|
+
status: IntegrationInstanceStatusSchema.optional()
|
|
55911
|
+
});
|
|
55912
|
+
var IntegrationInstancePatchSchema = object({
|
|
55913
|
+
id: IntegrationInstanceIdSchema,
|
|
55914
|
+
name: string().min(1).optional(),
|
|
55915
|
+
/** Omitted / '' / redaction token keeps a secret; `{ $secretClear: true }` clears it. */
|
|
55916
|
+
values: record(string(), unknown())
|
|
55917
|
+
});
|
|
55918
|
+
method(_void(), IntegrationInstanceDescriptionSchema), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3), method(_void(), array(IntegrationInstanceSchema).readonly()), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable()), method(object({
|
|
55919
|
+
id: IntegrationInstanceIdSchema,
|
|
55920
|
+
name: string().min(1)
|
|
55921
|
+
}), object({ id: string() }), { kind: "mutation" }), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), { kind: "mutation" }), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), { kind: "mutation" }), optionalMethod(object({
|
|
55922
|
+
id: IntegrationInstanceIdSchema.nullable(),
|
|
55923
|
+
values: record(string(), unknown())
|
|
55924
|
+
}), TestConnectionResultSchema$1, { kind: "mutation" });
|
|
55925
|
+
/**
|
|
55819
55926
|
* Two-way audio / talk-back cap. Clients open a WebRTC audio-only
|
|
55820
55927
|
* session (server-initiated offer pattern, identical to the main
|
|
55821
55928
|
* video WebRTC flow) and the browser captures the mic; the server
|
|
@@ -85272,6 +85379,62 @@ Object.freeze({
|
|
|
85272
85379
|
access: "create",
|
|
85273
85380
|
role: "admin"
|
|
85274
85381
|
},
|
|
85382
|
+
"integrationInstances.createInstance": {
|
|
85383
|
+
capName: "integration-instances",
|
|
85384
|
+
capScope: "system",
|
|
85385
|
+
addonId: null,
|
|
85386
|
+
access: "create",
|
|
85387
|
+
role: "admin"
|
|
85388
|
+
},
|
|
85389
|
+
"integrationInstances.deleteInstance": {
|
|
85390
|
+
capName: "integration-instances",
|
|
85391
|
+
capScope: "system",
|
|
85392
|
+
addonId: null,
|
|
85393
|
+
access: "delete",
|
|
85394
|
+
role: "admin"
|
|
85395
|
+
},
|
|
85396
|
+
"integrationInstances.describe": {
|
|
85397
|
+
capName: "integration-instances",
|
|
85398
|
+
capScope: "system",
|
|
85399
|
+
addonId: null,
|
|
85400
|
+
access: "view",
|
|
85401
|
+
role: "admin"
|
|
85402
|
+
},
|
|
85403
|
+
"integrationInstances.getInstance": {
|
|
85404
|
+
capName: "integration-instances",
|
|
85405
|
+
capScope: "system",
|
|
85406
|
+
addonId: null,
|
|
85407
|
+
access: "view",
|
|
85408
|
+
role: "admin"
|
|
85409
|
+
},
|
|
85410
|
+
"integrationInstances.instanceFormSchema": {
|
|
85411
|
+
capName: "integration-instances",
|
|
85412
|
+
capScope: "system",
|
|
85413
|
+
addonId: null,
|
|
85414
|
+
access: "view",
|
|
85415
|
+
role: "admin"
|
|
85416
|
+
},
|
|
85417
|
+
"integrationInstances.listInstances": {
|
|
85418
|
+
capName: "integration-instances",
|
|
85419
|
+
capScope: "system",
|
|
85420
|
+
addonId: null,
|
|
85421
|
+
access: "view",
|
|
85422
|
+
role: "admin"
|
|
85423
|
+
},
|
|
85424
|
+
"integrationInstances.testInstance": {
|
|
85425
|
+
capName: "integration-instances",
|
|
85426
|
+
capScope: "system",
|
|
85427
|
+
addonId: null,
|
|
85428
|
+
access: "create",
|
|
85429
|
+
role: "admin"
|
|
85430
|
+
},
|
|
85431
|
+
"integrationInstances.updateInstance": {
|
|
85432
|
+
capName: "integration-instances",
|
|
85433
|
+
capScope: "system",
|
|
85434
|
+
addonId: null,
|
|
85435
|
+
access: "create",
|
|
85436
|
+
role: "admin"
|
|
85437
|
+
},
|
|
85275
85438
|
"integrations.create": {
|
|
85276
85439
|
capName: "integrations",
|
|
85277
85440
|
capScope: "system",
|
|
@@ -5971,7 +5971,7 @@ var ZodIssueCode = {
|
|
|
5971
5971
|
var ZodFirstPartyTypeKind;
|
|
5972
5972
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5973
5973
|
//#endregion
|
|
5974
|
-
//#region ../types/dist/sleep-
|
|
5974
|
+
//#region ../types/dist/sleep-CdJHPm-k.mjs
|
|
5975
5975
|
/**
|
|
5976
5976
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5977
5977
|
* window to float samples (D455).
|
|
@@ -6088,7 +6088,7 @@ function isKeychainStoredField(field) {
|
|
|
6088
6088
|
var SECRET_CLEAR = { $secretClear: true };
|
|
6089
6089
|
/** The `'***'` token D634 redaction writes for non-admins. */
|
|
6090
6090
|
var LEGACY_DEVICE_REDACTION = "***";
|
|
6091
|
-
function isRecord$
|
|
6091
|
+
function isRecord$7(value) {
|
|
6092
6092
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6093
6093
|
}
|
|
6094
6094
|
function makeSecretMarker(ref) {
|
|
@@ -6099,12 +6099,12 @@ function makeSecretMarker(ref) {
|
|
|
6099
6099
|
}
|
|
6100
6100
|
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
6101
6101
|
function isSecretMarker(value) {
|
|
6102
|
-
if (!isRecord$
|
|
6102
|
+
if (!isRecord$7(value)) return false;
|
|
6103
6103
|
const ref = value["$secret"];
|
|
6104
6104
|
return typeof ref === "string" && ref.length > 0;
|
|
6105
6105
|
}
|
|
6106
6106
|
function isSecretClear(value) {
|
|
6107
|
-
return isRecord$
|
|
6107
|
+
return isRecord$7(value) && value["$secretClear"] === true;
|
|
6108
6108
|
}
|
|
6109
6109
|
/**
|
|
6110
6110
|
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
@@ -6857,7 +6857,7 @@ var KeychainWriteRefusedError = class extends Error {
|
|
|
6857
6857
|
}
|
|
6858
6858
|
};
|
|
6859
6859
|
var KEEP = { kind: "keep" };
|
|
6860
|
-
function isRecord$
|
|
6860
|
+
function isRecord$6(value) {
|
|
6861
6861
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6862
6862
|
}
|
|
6863
6863
|
function defaultMintId() {
|
|
@@ -6870,7 +6870,7 @@ function collectSecretMarkerRefs(value, out = []) {
|
|
|
6870
6870
|
return out;
|
|
6871
6871
|
}
|
|
6872
6872
|
if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
|
|
6873
|
-
else if (isRecord$
|
|
6873
|
+
else if (isRecord$6(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
6874
6874
|
return out;
|
|
6875
6875
|
}
|
|
6876
6876
|
async function storedValueEquals(secrets, marker, candidate) {
|
|
@@ -6925,7 +6925,7 @@ function rowId(row, idKey) {
|
|
|
6925
6925
|
return null;
|
|
6926
6926
|
}
|
|
6927
6927
|
async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
6928
|
-
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$
|
|
6928
|
+
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$6) : [];
|
|
6929
6929
|
if (!Array.isArray(incoming)) {
|
|
6930
6930
|
ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
|
|
6931
6931
|
return {
|
|
@@ -6941,7 +6941,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
|
6941
6941
|
const kept = /* @__PURE__ */ new Set();
|
|
6942
6942
|
const rows = [];
|
|
6943
6943
|
for (const row of incoming) {
|
|
6944
|
-
if (!isRecord$
|
|
6944
|
+
if (!isRecord$6(row)) {
|
|
6945
6945
|
rows.push(row);
|
|
6946
6946
|
continue;
|
|
6947
6947
|
}
|
|
@@ -6966,7 +6966,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
6966
6966
|
case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
|
|
6967
6967
|
case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
|
|
6968
6968
|
case "object": {
|
|
6969
|
-
if (!isRecord$
|
|
6969
|
+
if (!isRecord$6(incoming)) {
|
|
6970
6970
|
if (incoming === void 0) return KEEP;
|
|
6971
6971
|
ctx.deletes.push(...collectSecretMarkerRefs(stored));
|
|
6972
6972
|
return {
|
|
@@ -6974,7 +6974,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
6974
6974
|
value: incoming
|
|
6975
6975
|
};
|
|
6976
6976
|
}
|
|
6977
|
-
const storedObj = isRecord$
|
|
6977
|
+
const storedObj = isRecord$6(stored) ? stored : {};
|
|
6978
6978
|
return {
|
|
6979
6979
|
kind: "value",
|
|
6980
6980
|
value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
|
|
@@ -7097,7 +7097,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
|
|
|
7097
7097
|
value: items
|
|
7098
7098
|
};
|
|
7099
7099
|
}
|
|
7100
|
-
if (isRecord$
|
|
7100
|
+
if (isRecord$6(v)) {
|
|
7101
7101
|
const out = {};
|
|
7102
7102
|
for (const [k, child] of Object.entries(v)) {
|
|
7103
7103
|
const s = substitute(child);
|
|
@@ -7129,7 +7129,7 @@ var SecretOverwriteRefusedError = class extends Error {
|
|
|
7129
7129
|
this.name = "SecretOverwriteRefusedError";
|
|
7130
7130
|
}
|
|
7131
7131
|
};
|
|
7132
|
-
function isRecord$
|
|
7132
|
+
function isRecord$5(value) {
|
|
7133
7133
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7134
7134
|
}
|
|
7135
7135
|
function resolvedValue(pass, marker) {
|
|
@@ -7158,7 +7158,7 @@ function inArray(pass, incoming, stored) {
|
|
|
7158
7158
|
return incoming.map((item, index) => {
|
|
7159
7159
|
const twin = stored[index];
|
|
7160
7160
|
if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
|
|
7161
|
-
if (isRecord$
|
|
7161
|
+
if (isRecord$5(item) && isRecord$5(twin)) return inArrayRow(pass, item, twin);
|
|
7162
7162
|
if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
|
|
7163
7163
|
return item;
|
|
7164
7164
|
});
|
|
@@ -7169,7 +7169,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
7169
7169
|
const twin = stored[key];
|
|
7170
7170
|
if (isSecretMarker(twin)) {
|
|
7171
7171
|
if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
|
|
7172
|
-
} else if (isRecord$
|
|
7172
|
+
} else if (isRecord$5(value) && isRecord$5(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
7173
7173
|
else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
|
|
7174
7174
|
}
|
|
7175
7175
|
return out;
|
|
@@ -7177,7 +7177,7 @@ function inArrayRow(pass, incoming, stored) {
|
|
|
7177
7177
|
async function walk(pass, incoming, stored) {
|
|
7178
7178
|
if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
|
|
7179
7179
|
if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
|
|
7180
|
-
if (!isRecord$
|
|
7180
|
+
if (!isRecord$5(incoming) || !isRecord$5(stored)) return incoming;
|
|
7181
7181
|
const out = { ...incoming };
|
|
7182
7182
|
for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
|
|
7183
7183
|
return out;
|
|
@@ -7208,7 +7208,7 @@ async function restoreUnchangedSecrets(input) {
|
|
|
7208
7208
|
function isJsonEqual(a, b) {
|
|
7209
7209
|
if (Object.is(a, b)) return true;
|
|
7210
7210
|
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
|
|
7211
|
-
if (isRecord$
|
|
7211
|
+
if (isRecord$5(a) && isRecord$5(b)) {
|
|
7212
7212
|
const keys = Object.keys(a);
|
|
7213
7213
|
if (keys.length !== Object.keys(b).length) return false;
|
|
7214
7214
|
return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
|
|
@@ -7232,7 +7232,7 @@ function markerKeysOf(blob, owner) {
|
|
|
7232
7232
|
}
|
|
7233
7233
|
function nodeFromStored(value, owner) {
|
|
7234
7234
|
if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
|
|
7235
|
-
if (!isRecord$
|
|
7235
|
+
if (!isRecord$5(value)) return null;
|
|
7236
7236
|
const children = /* @__PURE__ */ new Map();
|
|
7237
7237
|
for (const [key, child] of Object.entries(value)) {
|
|
7238
7238
|
const node = nodeFromStored(child, owner);
|
|
@@ -7851,6 +7851,44 @@ var BaseAddon = class {
|
|
|
7851
7851
|
return null;
|
|
7852
7852
|
}
|
|
7853
7853
|
/**
|
|
7854
|
+
* D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
|
|
7855
|
+
* provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
|
|
7856
|
+
* declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
|
|
7857
|
+
* {@link nodeSettingsSchema} and of {@link deviceDefaultsSchema} are disjoint, and a key never
|
|
7858
|
+
* contains `@` (guard `check-scoped-settings-declared`). SHAPE only: values are read by scope.
|
|
7859
|
+
*/
|
|
7860
|
+
async clusterSettingsSchema(_cap) {
|
|
7861
|
+
return null;
|
|
7862
|
+
}
|
|
7863
|
+
/**
|
|
7864
|
+
* D986: the form shape of the settings one capability owns PER NODE, or `null`. Stored as
|
|
7865
|
+
* `addon-settings` rows `<addonId>.<field>@<nodeId>` carrying `nodeId` in the row data, so a
|
|
7866
|
+
* forgotten node's rows go in one central delete.
|
|
7867
|
+
*/
|
|
7868
|
+
async nodeSettingsSchema(_cap) {
|
|
7869
|
+
return null;
|
|
7870
|
+
}
|
|
7871
|
+
/**
|
|
7872
|
+
* D986: the form shape of the FLEET DEFAULTS of the per-device settings one capability owns,
|
|
7873
|
+
* or `null`. Stored as `addon-settings` rows `<addonId>.<field>`; a device resolves its value
|
|
7874
|
+
* as device row, then this default, then the schema default (`resolveDeviceSettings`).
|
|
7875
|
+
*/
|
|
7876
|
+
async deviceDefaultsSchema(_cap) {
|
|
7877
|
+
return null;
|
|
7878
|
+
}
|
|
7879
|
+
/**
|
|
7880
|
+
* The host's read of the scoped declarations: the schema one capability declares for `scope`,
|
|
7881
|
+
* or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
|
|
7882
|
+
* host asks this method.
|
|
7883
|
+
*/
|
|
7884
|
+
async getScopedSettingsSchema(scope, cap) {
|
|
7885
|
+
switch (scope) {
|
|
7886
|
+
case "cluster": return this.clusterSettingsSchema(cap);
|
|
7887
|
+
case "node": return this.nodeSettingsSchema(cap);
|
|
7888
|
+
case "device-defaults": return this.deviceDefaultsSchema(cap);
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
/**
|
|
7854
7892
|
* What the create wizard and the integration edit page render for one instance: the addon's
|
|
7855
7893
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
7856
7894
|
*/
|
|
@@ -8644,47 +8682,6 @@ function normalizeAddonInitResult(result) {
|
|
|
8644
8682
|
if (Array.isArray(result)) return { providers: result };
|
|
8645
8683
|
return result;
|
|
8646
8684
|
}
|
|
8647
|
-
/**
|
|
8648
|
-
* The share-view scope kinds a share-link token can carry. The hub mints
|
|
8649
|
-
* and validates them (`ShareViewGrantSchema` builds its
|
|
8650
|
-
* enum from this list); a data plane names the kinds it ACCEPTS in `share`.
|
|
8651
|
-
*/
|
|
8652
|
-
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
8653
|
-
/** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
|
|
8654
|
-
var PeerBytesTicketSchema = object({
|
|
8655
|
-
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
8656
|
-
url: string().min(1),
|
|
8657
|
-
/**
|
|
8658
|
-
* The HOST node this URL means something on — the hub or a named agent,
|
|
8659
|
-
* never a runner. {@link AddonPeerBytes.open} compares it to its own and
|
|
8660
|
-
* refuses `cross-node` by name when they differ, without dialling.
|
|
8661
|
-
*/
|
|
8662
|
-
hostNodeId: string().min(1),
|
|
8663
|
-
expiresAtMs: number().int().nonnegative(),
|
|
8664
|
-
/**
|
|
8665
|
-
* What the producer DECLARED the body to be, when it knows — `null` when it
|
|
8666
|
-
* does not. Never `0` for unknown (D393): a consumer sizing a bound off this
|
|
8667
|
-
* must be able to tell "the producer did not say" from "the body is empty".
|
|
8668
|
-
*/
|
|
8669
|
-
declaredBytes: number().int().nonnegative().nullable()
|
|
8670
|
-
});
|
|
8671
|
-
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
8672
|
-
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
8673
|
-
var RECORD_DATABASES = ["settings", "events"];
|
|
8674
|
-
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
8675
|
-
var RECORD_CLASSES = [
|
|
8676
|
-
"config",
|
|
8677
|
-
"ledger",
|
|
8678
|
-
"audit",
|
|
8679
|
-
"mirror",
|
|
8680
|
-
"ephemeral"
|
|
8681
|
-
];
|
|
8682
|
-
var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
|
|
8683
|
-
var RECORD_MAX_DATA_BYTES = 1024 * 1024;
|
|
8684
|
-
var RECORD_BATCH_MAX = 1e3;
|
|
8685
|
-
var RECORD_QUERY_MAX = 1e3;
|
|
8686
|
-
/** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
|
|
8687
|
-
var RECORD_SETTINGS_MAX_ROWS = 5e4;
|
|
8688
8685
|
var PREFIX = "records/";
|
|
8689
8686
|
/** The base of every records failure. `detail` is the human part of the message. */
|
|
8690
8687
|
var RecordsError = class extends Error {
|
|
@@ -8717,6 +8714,83 @@ var DeclarationRefusedError = class extends RecordsError {
|
|
|
8717
8714
|
this.name = "DeclarationRefusedError";
|
|
8718
8715
|
}
|
|
8719
8716
|
};
|
|
8717
|
+
/** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
|
|
8718
|
+
/** The files a kind may live in. `history` is device-history's (D850), never records. */
|
|
8719
|
+
var RECORD_DATABASES = ["settings", "events"];
|
|
8720
|
+
/** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
|
|
8721
|
+
var RECORD_CLASSES = [
|
|
8722
|
+
"config",
|
|
8723
|
+
"ledger",
|
|
8724
|
+
"audit",
|
|
8725
|
+
"mirror",
|
|
8726
|
+
"ephemeral"
|
|
8727
|
+
];
|
|
8728
|
+
var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
|
|
8729
|
+
var RECORD_MAX_DATA_BYTES = 1024 * 1024;
|
|
8730
|
+
var RECORD_BATCH_MAX = 1e3;
|
|
8731
|
+
var RECORD_QUERY_MAX = 1e3;
|
|
8732
|
+
/** A kind in the `settings` file may not bound itself above this (D447: high volume is `events`). */
|
|
8733
|
+
var RECORD_SETTINGS_MAX_ROWS = 5e4;
|
|
8734
|
+
/**
|
|
8735
|
+
* Secret fields of a record, addressed the way a Zod schema declares them
|
|
8736
|
+
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8737
|
+
* (`auth.accessToken`, `headers[].value`).
|
|
8738
|
+
*
|
|
8739
|
+
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8740
|
+
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8741
|
+
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
8742
|
+
* leaf names its element: `headers[X-Api].value`.
|
|
8743
|
+
*
|
|
8744
|
+
* Pure and browser-safe. Nothing here mutates: every transform returns a new value.
|
|
8745
|
+
*/
|
|
8746
|
+
/** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
|
|
8747
|
+
function parseSecretPath(path) {
|
|
8748
|
+
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8749
|
+
return path.split(".").map((raw) => {
|
|
8750
|
+
const each = raw.endsWith("[]");
|
|
8751
|
+
const key = each ? raw.slice(0, -2) : raw;
|
|
8752
|
+
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8753
|
+
return {
|
|
8754
|
+
key,
|
|
8755
|
+
each
|
|
8756
|
+
};
|
|
8757
|
+
});
|
|
8758
|
+
}
|
|
8759
|
+
/** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
|
|
8760
|
+
function secretArrayPrefixes(path) {
|
|
8761
|
+
const segments = parseSecretPath(path);
|
|
8762
|
+
const prefixes = [];
|
|
8763
|
+
const walked = [];
|
|
8764
|
+
for (const segment of segments) {
|
|
8765
|
+
walked.push(segment.key);
|
|
8766
|
+
if (segment.each) prefixes.push(walked.join("."));
|
|
8767
|
+
}
|
|
8768
|
+
return prefixes;
|
|
8769
|
+
}
|
|
8770
|
+
/**
|
|
8771
|
+
* The share-view scope kinds a share-link token can carry. The hub mints
|
|
8772
|
+
* and validates them (`ShareViewGrantSchema` builds its
|
|
8773
|
+
* enum from this list); a data plane names the kinds it ACCEPTS in `share`.
|
|
8774
|
+
*/
|
|
8775
|
+
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
8776
|
+
/** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
|
|
8777
|
+
var PeerBytesTicketSchema = object({
|
|
8778
|
+
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
8779
|
+
url: string().min(1),
|
|
8780
|
+
/**
|
|
8781
|
+
* The HOST node this URL means something on — the hub or a named agent,
|
|
8782
|
+
* never a runner. {@link AddonPeerBytes.open} compares it to its own and
|
|
8783
|
+
* refuses `cross-node` by name when they differ, without dialling.
|
|
8784
|
+
*/
|
|
8785
|
+
hostNodeId: string().min(1),
|
|
8786
|
+
expiresAtMs: number().int().nonnegative(),
|
|
8787
|
+
/**
|
|
8788
|
+
* What the producer DECLARED the body to be, when it knows — `null` when it
|
|
8789
|
+
* does not. Never `0` for unknown (D393): a consumer sizing a bound off this
|
|
8790
|
+
* must be able to tell "the producer did not say" from "the body is empty".
|
|
8791
|
+
*/
|
|
8792
|
+
declaredBytes: number().int().nonnegative().nullable()
|
|
8793
|
+
});
|
|
8720
8794
|
function defOf(schema) {
|
|
8721
8795
|
const zodBag = Reflect.get(schema, "_zod");
|
|
8722
8796
|
if (typeof zodBag !== "object" || zodBag === null) return {};
|
|
@@ -8785,42 +8859,6 @@ function collectZodSecretPaths(schema, prefix = "") {
|
|
|
8785
8859
|
return [...new Set(paths)];
|
|
8786
8860
|
}
|
|
8787
8861
|
/**
|
|
8788
|
-
* Secret fields of a record, addressed the way a Zod schema declares them
|
|
8789
|
-
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8790
|
-
* (`auth.accessToken`, `headers[].value`).
|
|
8791
|
-
*
|
|
8792
|
-
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8793
|
-
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8794
|
-
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
8795
|
-
* leaf names its element: `headers[X-Api].value`.
|
|
8796
|
-
*
|
|
8797
|
-
* Pure and browser-safe. Nothing here mutates: every transform returns a new value.
|
|
8798
|
-
*/
|
|
8799
|
-
/** The array prefixes (declared form, e.g. `headers` or `a.headers`) of a path. */
|
|
8800
|
-
function parseSecretPath(path) {
|
|
8801
|
-
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8802
|
-
return path.split(".").map((raw) => {
|
|
8803
|
-
const each = raw.endsWith("[]");
|
|
8804
|
-
const key = each ? raw.slice(0, -2) : raw;
|
|
8805
|
-
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8806
|
-
return {
|
|
8807
|
-
key,
|
|
8808
|
-
each
|
|
8809
|
-
};
|
|
8810
|
-
});
|
|
8811
|
-
}
|
|
8812
|
-
/** The declared array prefixes of `path` that need an id key (`headers`, `a.rows`). */
|
|
8813
|
-
function secretArrayPrefixes(path) {
|
|
8814
|
-
const segments = parseSecretPath(path);
|
|
8815
|
-
const prefixes = [];
|
|
8816
|
-
const walked = [];
|
|
8817
|
-
for (const segment of segments) {
|
|
8818
|
-
walked.push(segment.key);
|
|
8819
|
-
if (segment.each) prefixes.push(walked.join("."));
|
|
8820
|
-
}
|
|
8821
|
-
return prefixes;
|
|
8822
|
-
}
|
|
8823
|
-
/**
|
|
8824
8862
|
* Record kinds (D881): the declaration an addon makes of one kind of fact, the
|
|
8825
8863
|
* Zod row schema that describes it, and the helper that turns the two into the
|
|
8826
8864
|
* JSON the engine stores.
|
|
@@ -9785,6 +9823,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9785
9823
|
"humidity-sensor",
|
|
9786
9824
|
"image",
|
|
9787
9825
|
"image-settings",
|
|
9826
|
+
"integration-instances",
|
|
9788
9827
|
"integrations",
|
|
9789
9828
|
"intercom",
|
|
9790
9829
|
"ir-transmitter",
|
|
@@ -11718,7 +11757,7 @@ object({
|
|
|
11718
11757
|
params: record(string(), union([string(), number()])).optional()
|
|
11719
11758
|
});
|
|
11720
11759
|
//#endregion
|
|
11721
|
-
//#region ../types/dist/composition-
|
|
11760
|
+
//#region ../types/dist/composition-BDUoQsw9.mjs
|
|
11722
11761
|
/**
|
|
11723
11762
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11724
11763
|
*
|
|
@@ -21355,6 +21394,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
21355
21394
|
"load-contribution": system(INTERNAL),
|
|
21356
21395
|
"local-network": system(NETWORK),
|
|
21357
21396
|
"log-channels": system(INTERNAL),
|
|
21397
|
+
"integration-instances": system(INTERNAL),
|
|
21358
21398
|
"log-destination": system(INTERNAL),
|
|
21359
21399
|
"login-method": system(IDENTITY),
|
|
21360
21400
|
"mesh-network": system(NETWORK),
|
|
@@ -43655,7 +43695,7 @@ var TargetKindCapsSchema = object({
|
|
|
43655
43695
|
* the union is large and not meant for runtime validation here; the exported
|
|
43656
43696
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
43657
43697
|
*/
|
|
43658
|
-
var ConfigSchemaPassthrough$
|
|
43698
|
+
var ConfigSchemaPassthrough$4 = unknown();
|
|
43659
43699
|
var TargetKindSchema = object({
|
|
43660
43700
|
kind: string(),
|
|
43661
43701
|
label: string(),
|
|
@@ -43692,7 +43732,7 @@ var TargetKindSchema = object({
|
|
|
43692
43732
|
* its raster path, which is the safe default for an unknown image.
|
|
43693
43733
|
*/
|
|
43694
43734
|
iconMediaType: string().optional(),
|
|
43695
|
-
configSchema: ConfigSchemaPassthrough$
|
|
43735
|
+
configSchema: ConfigSchemaPassthrough$4,
|
|
43696
43736
|
/**
|
|
43697
43737
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
43698
43738
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -55700,9 +55740,15 @@ var AvailableIntegrationTypeSchema = object({
|
|
|
55700
55740
|
* branch on CAP — never on addon name. `device-adoption` integrations
|
|
55701
55741
|
* (Home Assistant, …) route through the broker step (Approach A);
|
|
55702
55742
|
* `device-provider` integrations (Reolink/ONVIF) keep the
|
|
55703
|
-
* legacy config → discovery flow.
|
|
55743
|
+
* legacy config → discovery flow. `connection` is an addon that only holds a
|
|
55744
|
+
* connection as typed records (`integration-instances`, D985): no devices,
|
|
55745
|
+
* no discovery.
|
|
55704
55746
|
*/
|
|
55705
|
-
kind: _enum([
|
|
55747
|
+
kind: _enum([
|
|
55748
|
+
"device-adoption",
|
|
55749
|
+
"device-provider",
|
|
55750
|
+
"connection"
|
|
55751
|
+
]),
|
|
55706
55752
|
/**
|
|
55707
55753
|
* For `device-adoption` addons: the broker kind to create/link
|
|
55708
55754
|
* (e.g. `home-assistant`), declared in the addon manifest. `null` for
|
|
@@ -55815,6 +55861,67 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
55815
55861
|
auth: "admin"
|
|
55816
55862
|
});
|
|
55817
55863
|
/**
|
|
55864
|
+
* `integration-instances` - the connection of an addon that holds ONE or SEVERAL
|
|
55865
|
+
* (an account, a relay, a destination) as typed records, shown on the
|
|
55866
|
+
* Integrations page like every other integration (D985, D986).
|
|
55867
|
+
*
|
|
55868
|
+
* A collection of providers: each addon that holds connections registers one
|
|
55869
|
+
* and the hub aggregator (`integrations.*`) reaches a provider with the pinned
|
|
55870
|
+
* `addonId`. It is internal and has no route: the operator-facing surface is
|
|
55871
|
+
* the `integrations` cap, which keeps ONE list.
|
|
55872
|
+
*
|
|
55873
|
+
* Identity: the instance id IS the `integrations` registry id (`int_NNNN`,
|
|
55874
|
+
* immutable, already in the `/integrations/<id>` URL). A second operator-chosen
|
|
55875
|
+
* slug would be a second authority over one identity (D224). `name` is the
|
|
55876
|
+
* editable label.
|
|
55877
|
+
*
|
|
55878
|
+
* Secrets: every field the instance kind marks `.meta({ secret: true })` reaches
|
|
55879
|
+
* the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
|
|
55880
|
+
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
55881
|
+
* clears it.
|
|
55882
|
+
*/
|
|
55883
|
+
var ConfigSchemaPassthrough$3 = custom();
|
|
55884
|
+
var IntegrationInstanceIdSchema = string().min(1);
|
|
55885
|
+
var IntegrationInstanceDescriptionSchema = object({
|
|
55886
|
+
/** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
|
|
55887
|
+
instanceMode: _enum(["single", "multiple"]),
|
|
55888
|
+
/** What the instance is, for the UI (`destination`, `relay`, `account`). */
|
|
55889
|
+
kind: string(),
|
|
55890
|
+
/** True when `testInstance` really probes the remote. */
|
|
55891
|
+
canTest: boolean()
|
|
55892
|
+
});
|
|
55893
|
+
var IntegrationInstanceStatusSchema = object({
|
|
55894
|
+
state: _enum([
|
|
55895
|
+
"ok",
|
|
55896
|
+
"off",
|
|
55897
|
+
"error",
|
|
55898
|
+
"unknown"
|
|
55899
|
+
]),
|
|
55900
|
+
detail: string().optional()
|
|
55901
|
+
});
|
|
55902
|
+
var IntegrationInstanceSchema = object({
|
|
55903
|
+
id: IntegrationInstanceIdSchema,
|
|
55904
|
+
name: string(),
|
|
55905
|
+
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
55906
|
+
configured: boolean(),
|
|
55907
|
+
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
55908
|
+
formValues: record(string(), unknown()),
|
|
55909
|
+
status: IntegrationInstanceStatusSchema.optional()
|
|
55910
|
+
});
|
|
55911
|
+
var IntegrationInstancePatchSchema = object({
|
|
55912
|
+
id: IntegrationInstanceIdSchema,
|
|
55913
|
+
name: string().min(1).optional(),
|
|
55914
|
+
/** Omitted / '' / redaction token keeps a secret; `{ $secretClear: true }` clears it. */
|
|
55915
|
+
values: record(string(), unknown())
|
|
55916
|
+
});
|
|
55917
|
+
method(_void(), IntegrationInstanceDescriptionSchema), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3), method(_void(), array(IntegrationInstanceSchema).readonly()), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable()), method(object({
|
|
55918
|
+
id: IntegrationInstanceIdSchema,
|
|
55919
|
+
name: string().min(1)
|
|
55920
|
+
}), object({ id: string() }), { kind: "mutation" }), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), { kind: "mutation" }), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), { kind: "mutation" }), optionalMethod(object({
|
|
55921
|
+
id: IntegrationInstanceIdSchema.nullable(),
|
|
55922
|
+
values: record(string(), unknown())
|
|
55923
|
+
}), TestConnectionResultSchema$1, { kind: "mutation" });
|
|
55924
|
+
/**
|
|
55818
55925
|
* Two-way audio / talk-back cap. Clients open a WebRTC audio-only
|
|
55819
55926
|
* session (server-initiated offer pattern, identical to the main
|
|
55820
55927
|
* video WebRTC flow) and the browser captures the mic; the server
|
|
@@ -85271,6 +85378,62 @@ Object.freeze({
|
|
|
85271
85378
|
access: "create",
|
|
85272
85379
|
role: "admin"
|
|
85273
85380
|
},
|
|
85381
|
+
"integrationInstances.createInstance": {
|
|
85382
|
+
capName: "integration-instances",
|
|
85383
|
+
capScope: "system",
|
|
85384
|
+
addonId: null,
|
|
85385
|
+
access: "create",
|
|
85386
|
+
role: "admin"
|
|
85387
|
+
},
|
|
85388
|
+
"integrationInstances.deleteInstance": {
|
|
85389
|
+
capName: "integration-instances",
|
|
85390
|
+
capScope: "system",
|
|
85391
|
+
addonId: null,
|
|
85392
|
+
access: "delete",
|
|
85393
|
+
role: "admin"
|
|
85394
|
+
},
|
|
85395
|
+
"integrationInstances.describe": {
|
|
85396
|
+
capName: "integration-instances",
|
|
85397
|
+
capScope: "system",
|
|
85398
|
+
addonId: null,
|
|
85399
|
+
access: "view",
|
|
85400
|
+
role: "admin"
|
|
85401
|
+
},
|
|
85402
|
+
"integrationInstances.getInstance": {
|
|
85403
|
+
capName: "integration-instances",
|
|
85404
|
+
capScope: "system",
|
|
85405
|
+
addonId: null,
|
|
85406
|
+
access: "view",
|
|
85407
|
+
role: "admin"
|
|
85408
|
+
},
|
|
85409
|
+
"integrationInstances.instanceFormSchema": {
|
|
85410
|
+
capName: "integration-instances",
|
|
85411
|
+
capScope: "system",
|
|
85412
|
+
addonId: null,
|
|
85413
|
+
access: "view",
|
|
85414
|
+
role: "admin"
|
|
85415
|
+
},
|
|
85416
|
+
"integrationInstances.listInstances": {
|
|
85417
|
+
capName: "integration-instances",
|
|
85418
|
+
capScope: "system",
|
|
85419
|
+
addonId: null,
|
|
85420
|
+
access: "view",
|
|
85421
|
+
role: "admin"
|
|
85422
|
+
},
|
|
85423
|
+
"integrationInstances.testInstance": {
|
|
85424
|
+
capName: "integration-instances",
|
|
85425
|
+
capScope: "system",
|
|
85426
|
+
addonId: null,
|
|
85427
|
+
access: "create",
|
|
85428
|
+
role: "admin"
|
|
85429
|
+
},
|
|
85430
|
+
"integrationInstances.updateInstance": {
|
|
85431
|
+
capName: "integration-instances",
|
|
85432
|
+
capScope: "system",
|
|
85433
|
+
addonId: null,
|
|
85434
|
+
access: "create",
|
|
85435
|
+
role: "admin"
|
|
85436
|
+
},
|
|
85274
85437
|
"integrations.create": {
|
|
85275
85438
|
capName: "integrations",
|
|
85276
85439
|
capScope: "system",
|
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
enumerable: true
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
//#endregion
|
|
27
|
-
const require_ha_broker_source = require("../ha-broker-source-
|
|
27
|
+
const require_ha_broker_source = require("../ha-broker-source-CE3nHkyW.js");
|
|
28
28
|
let node_crypto = require("node:crypto");
|
|
29
29
|
require("node:zlib");
|
|
30
30
|
let node_fs = require("node:fs");
|
|
@@ -4950,18 +4950,29 @@ async function call(slice, capName, make) {
|
|
|
4950
4950
|
*
|
|
4951
4951
|
* Idempotent: a store without the key yields an empty plan, which writes nothing.
|
|
4952
4952
|
*/
|
|
4953
|
-
/** The
|
|
4954
|
-
var
|
|
4953
|
+
/** The addon id, the prefix of its rows in the canonical settings table. */
|
|
4954
|
+
var HA_EXPORT_ADDON_ID = "homeassistant-export";
|
|
4955
|
+
/**
|
|
4956
|
+
* Where the addon's global settings live: the canonical `addon-settings` table, one row per
|
|
4957
|
+
* key, id `<addonId>.<key>`, data `{ addonId, key, value }` (never a per-addon scoped table,
|
|
4958
|
+
* which exists only when something lazily declared it).
|
|
4959
|
+
*/
|
|
4960
|
+
var ADDON_SETTINGS_TABLE = "addon-settings";
|
|
4955
4961
|
var RETIRED_URL_KEYS = ["publicBaseUrl"];
|
|
4956
4962
|
async function planDropPublicBaseUrl(ports) {
|
|
4957
4963
|
const stored = await ports.readStored();
|
|
4958
4964
|
const present = RETIRED_URL_KEYS.filter((key) => key in stored);
|
|
4959
4965
|
if (present.length === 0) return { edits: [] };
|
|
4966
|
+
const rowId = (key) => `${HA_EXPORT_ADDON_ID}.${key}`;
|
|
4960
4967
|
return { edits: [{
|
|
4961
4968
|
op: "deleteKeys",
|
|
4962
|
-
scope:
|
|
4963
|
-
keys:
|
|
4964
|
-
precondition: require_ha_broker_source.preconditionOf(Object.fromEntries(present.map((key) => [key,
|
|
4969
|
+
scope: ADDON_SETTINGS_TABLE,
|
|
4970
|
+
keys: present.map(rowId),
|
|
4971
|
+
precondition: require_ha_broker_source.preconditionOf(Object.fromEntries(present.map((key) => [rowId(key), {
|
|
4972
|
+
addonId: HA_EXPORT_ADDON_ID,
|
|
4973
|
+
key,
|
|
4974
|
+
value: stored[key]
|
|
4975
|
+
}])))
|
|
4965
4976
|
}] };
|
|
4966
4977
|
}
|
|
4967
4978
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as consumableKindOf, Bt as BaseAddon, Dt as sendToExportedTarget, Ht as baseUnitOf, Jt as isReadingQuantity, K as deviceExportCapability, Wt as defineAddonMigrations, Yt as nodePin, Zt as array, _ as PLUG_APPLIANCE_TYPES, _t as pruneDeletedEntities, c as COCO_TO_MACRO, d as ConsumableItemSchema, f as ConsumableStatusSchema, fn as EventCategory, g as MediaPlayerRepeatSchema, h as HvacModeSchema, i as AlarmArmModeSchema, in as string, j as buildAddonRouteProvider, l as CameraSwitchIdSchema, m as FanDirectionSchema, nn as object, on as unknown, pt as preconditionOf, r as listHomeAssistantBrokers, s as CAMERA_SWITCH_ORDER, u as ConsumableFormSchema, ut as oauthIntegrationCapability, x as addonRoutesCapability, xt as runExportedAction, z as consumableFormOf } from "../ha-broker-source-
|
|
1
|
+
import { B as consumableKindOf, Bt as BaseAddon, Dt as sendToExportedTarget, Ht as baseUnitOf, Jt as isReadingQuantity, K as deviceExportCapability, Wt as defineAddonMigrations, Yt as nodePin, Zt as array, _ as PLUG_APPLIANCE_TYPES, _t as pruneDeletedEntities, c as COCO_TO_MACRO, d as ConsumableItemSchema, f as ConsumableStatusSchema, fn as EventCategory, g as MediaPlayerRepeatSchema, h as HvacModeSchema, i as AlarmArmModeSchema, in as string, j as buildAddonRouteProvider, l as CameraSwitchIdSchema, m as FanDirectionSchema, nn as object, on as unknown, pt as preconditionOf, r as listHomeAssistantBrokers, s as CAMERA_SWITCH_ORDER, u as ConsumableFormSchema, ut as oauthIntegrationCapability, x as addonRoutesCapability, xt as runExportedAction, z as consumableFormOf } from "../ha-broker-source-D3FBA6uG.mjs";
|
|
2
2
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
3
|
import "node:zlib";
|
|
4
4
|
import "node:fs";
|
|
@@ -4915,18 +4915,29 @@ async function call(slice, capName, make) {
|
|
|
4915
4915
|
*
|
|
4916
4916
|
* Idempotent: a store without the key yields an empty plan, which writes nothing.
|
|
4917
4917
|
*/
|
|
4918
|
-
/** The
|
|
4919
|
-
var
|
|
4918
|
+
/** The addon id, the prefix of its rows in the canonical settings table. */
|
|
4919
|
+
var HA_EXPORT_ADDON_ID = "homeassistant-export";
|
|
4920
|
+
/**
|
|
4921
|
+
* Where the addon's global settings live: the canonical `addon-settings` table, one row per
|
|
4922
|
+
* key, id `<addonId>.<key>`, data `{ addonId, key, value }` (never a per-addon scoped table,
|
|
4923
|
+
* which exists only when something lazily declared it).
|
|
4924
|
+
*/
|
|
4925
|
+
var ADDON_SETTINGS_TABLE = "addon-settings";
|
|
4920
4926
|
var RETIRED_URL_KEYS = ["publicBaseUrl"];
|
|
4921
4927
|
async function planDropPublicBaseUrl(ports) {
|
|
4922
4928
|
const stored = await ports.readStored();
|
|
4923
4929
|
const present = RETIRED_URL_KEYS.filter((key) => key in stored);
|
|
4924
4930
|
if (present.length === 0) return { edits: [] };
|
|
4931
|
+
const rowId = (key) => `${HA_EXPORT_ADDON_ID}.${key}`;
|
|
4925
4932
|
return { edits: [{
|
|
4926
4933
|
op: "deleteKeys",
|
|
4927
|
-
scope:
|
|
4928
|
-
keys:
|
|
4929
|
-
precondition: preconditionOf(Object.fromEntries(present.map((key) => [key,
|
|
4934
|
+
scope: ADDON_SETTINGS_TABLE,
|
|
4935
|
+
keys: present.map(rowId),
|
|
4936
|
+
precondition: preconditionOf(Object.fromEntries(present.map((key) => [rowId(key), {
|
|
4937
|
+
addonId: HA_EXPORT_ADDON_ID,
|
|
4938
|
+
key,
|
|
4939
|
+
value: stored[key]
|
|
4940
|
+
}])))
|
|
4930
4941
|
}] };
|
|
4931
4942
|
}
|
|
4932
4943
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-homeassistant",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.649",
|
|
4
4
|
"description": "Home Assistant device provider addon for CamStack",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"description": "Device provider (import) + native entity export (push). Two addons, one bundle, separate processes."
|
|
128
128
|
},
|
|
129
129
|
"requires": {
|
|
130
|
-
"server": ">=1.2.
|
|
130
|
+
"server": ">=1.2.738"
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
133
|
"files": [
|