@camstack/addon-google-services 0.2.42 → 0.2.44
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/google-services.addon.js +1116 -1244
- package/dist/google-services.addon.mjs +1117 -1245
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
let node_crypto = require("node:crypto");
|
|
6
|
-
//#region ../types/dist/event-category-
|
|
6
|
+
//#region ../types/dist/event-category-78tcBemo.mjs
|
|
7
7
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
8
8
|
EventCategory["SystemBoot"] = "system.boot";
|
|
9
9
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -124,6 +124,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
124
124
|
*/
|
|
125
125
|
EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
|
|
126
126
|
/**
|
|
127
|
+
* An addon is RUNNING although its installed package is below what the
|
|
128
|
+
* running server requires (`camstack.requires.addons` of the server, or the
|
|
129
|
+
* addon's own `requires.server`). Raised once per streak by the unsupported-
|
|
130
|
+
* addon tracker, on the hub and on agents; AlertCenter turns it into one
|
|
131
|
+
* operator alert per node and package. The addon is never held back for it.
|
|
132
|
+
*
|
|
133
|
+
* Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
|
|
134
|
+
*/
|
|
135
|
+
EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
|
|
136
|
+
/**
|
|
137
|
+
* The package of an {@link AddonUnsupportedVersion} alert reached a supported
|
|
138
|
+
* version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
|
|
139
|
+
*
|
|
140
|
+
* Payload: `{ packageName, addonId, nodeId }`.
|
|
141
|
+
*/
|
|
142
|
+
EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
|
|
143
|
+
/**
|
|
127
144
|
* Monitor scheduled the next retry for a failed addon. Transient —
|
|
128
145
|
* surfaced to the UI for live-updating the "next retry in Ns"
|
|
129
146
|
* countdown on the Addons page row, NOT persisted as an alert.
|
|
@@ -234,7 +251,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
234
251
|
EventCategory["ProcessRestarted"] = "process.restarted";
|
|
235
252
|
/**
|
|
236
253
|
* The SET of storage locations changed — one was created, edited, enabled,
|
|
237
|
-
* disabled or deleted through
|
|
254
|
+
* disabled or deleted through the location instances or the forced-delete action.
|
|
238
255
|
*
|
|
239
256
|
* Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
|
|
240
257
|
* it must also converge on its own periodic path, because a dropped event
|
|
@@ -789,7 +806,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
789
806
|
return EventCategory;
|
|
790
807
|
}({});
|
|
791
808
|
//#endregion
|
|
792
|
-
//#region ../types/dist/device-type-
|
|
809
|
+
//#region ../types/dist/device-type-DxCT0krO.mjs
|
|
793
810
|
/**
|
|
794
811
|
import { errMsg } from '@camstack/types'
|
|
795
812
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -951,6 +968,13 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
951
968
|
* filters that want "all devices with on-motion behaviour".
|
|
952
969
|
*/
|
|
953
970
|
DeviceFeature["MotionTrigger"] = "motion-trigger";
|
|
971
|
+
/**
|
|
972
|
+
* Device can be run for a chosen time and then stopped. Mirrors the
|
|
973
|
+
* `timed-run` wrapper cap — the cap is bound by the core-blocks addon, not by
|
|
974
|
+
* a driver, so the flag is derived from the device's bindings
|
|
975
|
+
* (`featuresOfBoundCaps`). Gates the quick-run control in the viewer.
|
|
976
|
+
*/
|
|
977
|
+
DeviceFeature["TimedRun"] = "timed-run";
|
|
954
978
|
/** Light supports rgb-triplet color via `color` cap. */
|
|
955
979
|
DeviceFeature["LightColorRgb"] = "light-color-rgb";
|
|
956
980
|
/** Light supports HSV color via `color` cap. */
|
|
@@ -5985,7 +6009,7 @@ var ZodIssueCode = {
|
|
|
5985
6009
|
var ZodFirstPartyTypeKind;
|
|
5986
6010
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5987
6011
|
//#endregion
|
|
5988
|
-
//#region ../types/dist/sleep-
|
|
6012
|
+
//#region ../types/dist/sleep-CGPZyzB5.mjs
|
|
5989
6013
|
/**
|
|
5990
6014
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5991
6015
|
* window to float samples (D455).
|
|
@@ -6349,6 +6373,59 @@ Object.fromEntries([
|
|
|
6349
6373
|
function isValuelessField(field) {
|
|
6350
6374
|
return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button" || field.type === "account-sign-in" || field.type === "secret-presence";
|
|
6351
6375
|
}
|
|
6376
|
+
/**
|
|
6377
|
+
* Base collector: flatten a hydrated `ConfigUISchemaWithValues` into
|
|
6378
|
+
* `key → { value, default }`, recursing through `group` and `sub-tabs`
|
|
6379
|
+
* layout fields. Keeping `value` and `default` SEPARATE (rather than
|
|
6380
|
+
* pre-resolving) lets callers both read the effective value AND detect
|
|
6381
|
+
* overrides (`value` differs from `default`). Structural over `unknown`;
|
|
6382
|
+
* malformed input yields an empty map, never throws.
|
|
6383
|
+
*/
|
|
6384
|
+
function collectHydratedFieldEntries(schema) {
|
|
6385
|
+
const out = /* @__PURE__ */ new Map();
|
|
6386
|
+
if (typeof schema !== "object" || schema === null) return out;
|
|
6387
|
+
const sections = schema.sections;
|
|
6388
|
+
if (!Array.isArray(sections)) return out;
|
|
6389
|
+
for (const section of sections) if (typeof section === "object" && section !== null) collectFieldEntriesInto(section.fields, out);
|
|
6390
|
+
return out;
|
|
6391
|
+
}
|
|
6392
|
+
/** Recursive field walker for {@link collectHydratedFieldEntries}. */
|
|
6393
|
+
function collectFieldEntriesInto(fields, into) {
|
|
6394
|
+
if (!Array.isArray(fields)) return;
|
|
6395
|
+
for (const field of fields) {
|
|
6396
|
+
if (typeof field !== "object" || field === null) continue;
|
|
6397
|
+
const type = field.type;
|
|
6398
|
+
if (type === "group") {
|
|
6399
|
+
collectFieldEntriesInto(field.fields, into);
|
|
6400
|
+
continue;
|
|
6401
|
+
}
|
|
6402
|
+
if (type === "sub-tabs") {
|
|
6403
|
+
const tabs = field.tabs;
|
|
6404
|
+
if (Array.isArray(tabs)) {
|
|
6405
|
+
for (const tab of tabs) if (typeof tab === "object" && tab !== null) collectFieldEntriesInto(tab.fields, into);
|
|
6406
|
+
}
|
|
6407
|
+
continue;
|
|
6408
|
+
}
|
|
6409
|
+
const key = field.key;
|
|
6410
|
+
if (typeof key !== "string" || key.length === 0) continue;
|
|
6411
|
+
if (!("value" in field)) continue;
|
|
6412
|
+
into.set(key, {
|
|
6413
|
+
value: field.value,
|
|
6414
|
+
default: field.default
|
|
6415
|
+
});
|
|
6416
|
+
}
|
|
6417
|
+
}
|
|
6418
|
+
/**
|
|
6419
|
+
* Flatten a hydrated settings payload into `key → EFFECTIVE value`
|
|
6420
|
+
* (`value ?? default`). The read-side inverse of `hydrateSchema` for the
|
|
6421
|
+
* common "just give me the resolved values" case. Built on
|
|
6422
|
+
* {@link collectHydratedFieldEntries}.
|
|
6423
|
+
*/
|
|
6424
|
+
function collectHydratedFieldValues(schema) {
|
|
6425
|
+
const out = /* @__PURE__ */ new Map();
|
|
6426
|
+
for (const [key, entry] of collectHydratedFieldEntries(schema)) out.set(key, entry.value ?? entry.default);
|
|
6427
|
+
return out;
|
|
6428
|
+
}
|
|
6352
6429
|
function hydrateSchema(schema, values, options = {}) {
|
|
6353
6430
|
return {
|
|
6354
6431
|
...schema.tabs ? { tabs: [...schema.tabs] } : {},
|
|
@@ -7149,6 +7226,7 @@ var RecordIntegrationInstances = class {
|
|
|
7149
7226
|
this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
|
|
7150
7227
|
this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
|
|
7151
7228
|
this.nameField = kind.declaration.entry?.nameField ?? "name";
|
|
7229
|
+
if (hooks.kindOf === void 0 && hooks.description.kinds.length !== 1) throw new Error(`record kind '${kind.name}': describe().kinds has ${hooks.description.kinds.length} entries, so kindOf is required to say which one an instance is`);
|
|
7152
7230
|
}
|
|
7153
7231
|
/**
|
|
7154
7232
|
* One instance read for the addon's own runtime; null when absent. When the keychain is
|
|
@@ -7190,10 +7268,21 @@ var RecordIntegrationInstances = class {
|
|
|
7190
7268
|
}
|
|
7191
7269
|
return out;
|
|
7192
7270
|
}
|
|
7271
|
+
/** The kind an instance body is of: the provider's `kindOf`, else its one declared kind. */
|
|
7272
|
+
kindOfBody(body) {
|
|
7273
|
+
if (this.hooks.kindOf !== void 0) {
|
|
7274
|
+
const id = this.hooks.kindOf(body);
|
|
7275
|
+
if (!this.hooks.description.kinds.some((declared) => declared.id === id)) throw new Error(`record kind '${this.kind.name}': kindOf returned '${id}', which describe().kinds does not declare (D1013)`);
|
|
7276
|
+
return id;
|
|
7277
|
+
}
|
|
7278
|
+
const only = this.hooks.description.kinds[0];
|
|
7279
|
+
if (only === void 0) throw new Error(`record kind '${this.kind.name}' declares no kinds`);
|
|
7280
|
+
return only.id;
|
|
7281
|
+
}
|
|
7193
7282
|
viewOf(record) {
|
|
7194
7283
|
const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
|
|
7195
7284
|
const rawName = record.data[this.nameField];
|
|
7196
|
-
const kind = this.
|
|
7285
|
+
const kind = this.kindOfBody(record.data);
|
|
7197
7286
|
const health = this.hooks.health?.(record.data, record.id);
|
|
7198
7287
|
return {
|
|
7199
7288
|
id: record.id,
|
|
@@ -7201,7 +7290,7 @@ var RecordIntegrationInstances = class {
|
|
|
7201
7290
|
configured: this.hooks.configured(record.data),
|
|
7202
7291
|
enabled: isInstanceEnabled(record.data),
|
|
7203
7292
|
formValues: shown,
|
|
7204
|
-
|
|
7293
|
+
kind,
|
|
7205
7294
|
...health?.status !== void 0 ? { status: health.status } : {},
|
|
7206
7295
|
...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
|
|
7207
7296
|
};
|
|
@@ -7234,17 +7323,35 @@ var RecordIntegrationInstances = class {
|
|
|
7234
7323
|
...kind !== void 0 ? { kind } : {}
|
|
7235
7324
|
});
|
|
7236
7325
|
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
7326
|
+
const initial = this.hooks.initialBody(name, kind);
|
|
7327
|
+
const patch = this.patchOf(values ?? {});
|
|
7328
|
+
if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
|
|
7329
|
+
kind,
|
|
7330
|
+
body: {
|
|
7331
|
+
...initial,
|
|
7332
|
+
...patch
|
|
7333
|
+
}
|
|
7334
|
+
}));
|
|
7237
7335
|
await this.handle.put({
|
|
7238
7336
|
id: minted,
|
|
7239
|
-
data:
|
|
7337
|
+
data: initial
|
|
7240
7338
|
}, { ifRev: 0 });
|
|
7241
|
-
const patch = this.patchOf(values ?? {});
|
|
7242
7339
|
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
7243
7340
|
await this.hooks.onChanged?.(minted, "created");
|
|
7244
7341
|
return { id: minted };
|
|
7245
7342
|
};
|
|
7246
7343
|
updateInstance = async ({ id, name, values }) => {
|
|
7247
7344
|
const patch = this.patchOf(values);
|
|
7345
|
+
if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
|
|
7346
|
+
const stored = await this.handle.get(id);
|
|
7347
|
+
if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
|
|
7348
|
+
kind: void 0,
|
|
7349
|
+
body: {
|
|
7350
|
+
...stored.data,
|
|
7351
|
+
...patch
|
|
7352
|
+
}
|
|
7353
|
+
}));
|
|
7354
|
+
}
|
|
7248
7355
|
if (name !== void 0) patch[this.nameField] = name;
|
|
7249
7356
|
await this.handle.patch(id, patch);
|
|
7250
7357
|
await this.hooks.onChanged?.(id, "updated");
|
|
@@ -7555,6 +7662,21 @@ var SCOPED_SETTINGS_SCOPES = [
|
|
|
7555
7662
|
"device-defaults",
|
|
7556
7663
|
"node"
|
|
7557
7664
|
];
|
|
7665
|
+
/** The one refusal of a key a scope did not declare. */
|
|
7666
|
+
function assertDeclaredKeys(keys, declared) {
|
|
7667
|
+
const undeclared = keys.filter((key) => !declared.has(key));
|
|
7668
|
+
if (undeclared.length > 0) throw new Error(`scoped settings: [${undeclared.join(", ")}] not declared for this scope (declared: [${[...declared].join(", ")}])`);
|
|
7669
|
+
}
|
|
7670
|
+
/** Build the handle over an addon's scoped read, write and unset. */
|
|
7671
|
+
function createScopedSettings(deps) {
|
|
7672
|
+
return {
|
|
7673
|
+
async get() {
|
|
7674
|
+
return deps.schema.parse(await deps.read());
|
|
7675
|
+
},
|
|
7676
|
+
patch: (patch) => deps.write(Object.fromEntries(Object.entries(patch))),
|
|
7677
|
+
unset: (keys) => deps.unset(keys)
|
|
7678
|
+
};
|
|
7679
|
+
}
|
|
7558
7680
|
/** A write to a secret path could not be stored in the keychain, so it was refused. */
|
|
7559
7681
|
var KeychainWriteRefusedError = class extends Error {
|
|
7560
7682
|
fieldPath;
|
|
@@ -8130,7 +8252,16 @@ function createHandle(raw, opts) {
|
|
|
8130
8252
|
return blob;
|
|
8131
8253
|
}
|
|
8132
8254
|
},
|
|
8133
|
-
writeAddonStore: writeAddon
|
|
8255
|
+
writeAddonStore: writeAddon,
|
|
8256
|
+
removeClearedKeys: (keys) => serialize("addon", async () => {
|
|
8257
|
+
const stored = await readStoredAddonBlob(raw);
|
|
8258
|
+
index.seen("addon", stored);
|
|
8259
|
+
const removable = keys.filter((key) => collectSecretMarkerRefs(stored[key]).length === 0);
|
|
8260
|
+
if (removable.length === 0) return;
|
|
8261
|
+
const patch = Object.fromEntries(removable.map((key) => [key, void 0]));
|
|
8262
|
+
await raw.writeAddonStore(patch);
|
|
8263
|
+
index.wrote("addon", patch);
|
|
8264
|
+
})
|
|
8134
8265
|
};
|
|
8135
8266
|
}
|
|
8136
8267
|
/** The context the addon sees, plus the private raw view and write outcome. */
|
|
@@ -8260,7 +8391,7 @@ function boundOf(list) {
|
|
|
8260
8391
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
8261
8392
|
*
|
|
8262
8393
|
* - Typed `config` property with automatic resolution from store + defaults
|
|
8263
|
-
* -
|
|
8394
|
+
* - the scoped settings API (`clusterSettings` / `nodeSettings` / `deviceDefaults`) auto-implemented
|
|
8264
8395
|
* - `getAddonSettings()` / `updateAddonSettings()` auto-implemented
|
|
8265
8396
|
* - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented
|
|
8266
8397
|
* - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)
|
|
@@ -8270,7 +8401,7 @@ function boundOf(list) {
|
|
|
8270
8401
|
* Subclasses override:
|
|
8271
8402
|
* - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]
|
|
8272
8403
|
* - `onShutdown()` — cleanup
|
|
8273
|
-
* - `
|
|
8404
|
+
* - `clusterSettingsSchema()` / `nodeSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
|
|
8274
8405
|
*
|
|
8275
8406
|
* @example
|
|
8276
8407
|
* ```ts
|
|
@@ -8284,7 +8415,7 @@ function boundOf(list) {
|
|
|
8284
8415
|
* super({ maxRetries: 3, endpoint: 'http://localhost' })
|
|
8285
8416
|
* }
|
|
8286
8417
|
*
|
|
8287
|
-
* protected
|
|
8418
|
+
* protected clusterSettingsSchema() {
|
|
8288
8419
|
* return this.schema({
|
|
8289
8420
|
* sections: [{
|
|
8290
8421
|
* id: 'main', title: 'Settings',
|
|
@@ -8327,7 +8458,7 @@ var BaseAddon = class {
|
|
|
8327
8458
|
defaults;
|
|
8328
8459
|
/**
|
|
8329
8460
|
* Hidden-state paths that hold secrets (D787) — values the addon writes
|
|
8330
|
-
* itself (`
|
|
8461
|
+
* itself (`state()`, the scoped writers) that no form field declares.
|
|
8331
8462
|
* Stored in the keychain exactly like a `password` field. Syntax:
|
|
8332
8463
|
* `'snapshotSigningKey'`, `'identity.pincode'`, `'tokens[userId].accessToken'`
|
|
8333
8464
|
* (`key[<id field>]` addresses array items by id, never by index).
|
|
@@ -8337,7 +8468,7 @@ var BaseAddon = class {
|
|
|
8337
8468
|
/**
|
|
8338
8469
|
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
8339
8470
|
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
8340
|
-
* see them. Empty until loaded
|
|
8471
|
+
* see them. Empty until loaded.
|
|
8341
8472
|
*/
|
|
8342
8473
|
_scopedByCap = /* @__PURE__ */ new Map();
|
|
8343
8474
|
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
@@ -8523,7 +8654,7 @@ var BaseAddon = class {
|
|
|
8523
8654
|
/** Addon-specific cleanup. Override if needed. */
|
|
8524
8655
|
async onShutdown() {}
|
|
8525
8656
|
/**
|
|
8526
|
-
* Called after config is resolved during
|
|
8657
|
+
* Called after config is resolved during a settings write.
|
|
8527
8658
|
* Override to react to config changes (e.g. restart a sampler, reconnect a service).
|
|
8528
8659
|
* Not called during initialize() — use onInitialize() for initial setup.
|
|
8529
8660
|
*/
|
|
@@ -8552,82 +8683,12 @@ var BaseAddon = class {
|
|
|
8552
8683
|
scopedSettingsCapabilities() {
|
|
8553
8684
|
return [];
|
|
8554
8685
|
}
|
|
8555
|
-
/** Override to provide global-level settings UI schema. */
|
|
8556
|
-
globalSettingsSchema(_cap) {
|
|
8557
|
-
return null;
|
|
8558
|
-
}
|
|
8559
8686
|
/** Override to provide device-level settings UI schema. */
|
|
8560
8687
|
deviceSettingsSchema() {
|
|
8561
8688
|
return null;
|
|
8562
8689
|
}
|
|
8563
8690
|
/**
|
|
8564
|
-
*
|
|
8565
|
-
* ARE the configuration of its integration.
|
|
8566
|
-
*
|
|
8567
|
-
* Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
|
|
8568
|
-
* operator should find on the addon's integration page (System →
|
|
8569
|
-
* Integrations → <name>) rather than only in the cluster-wide list of every
|
|
8570
|
-
* addon. Empty (the default) means the addon has no integration-level
|
|
8571
|
-
* settings and no such surface is offered — this is opt-in, because whether
|
|
8572
|
-
* an addon's configuration IS its integration's configuration depends on the
|
|
8573
|
-
* nature of the integration.
|
|
8574
|
-
*
|
|
8575
|
-
* WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
|
|
8576
|
-
* the ONE global schema, in the ONE addon store, written by the ONE
|
|
8577
|
-
* `updateGlobalSettings` path. There is deliberately no
|
|
8578
|
-
* `updateIntegrationSettings`: a second write path is how a surface acquires
|
|
8579
|
-
* a second store key, and this repo has shipped that twice (`btmPath@hub`,
|
|
8580
|
-
* D266). Selecting sections cannot introduce a key that selecting cannot.
|
|
8581
|
-
*
|
|
8582
|
-
* WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
|
|
8583
|
-
* `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
|
|
8584
|
-
* removed with the reason recorded at
|
|
8585
|
-
* `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
|
|
8586
|
-
* determined by WHICH schema it lives in, not by a field-level marker."* A
|
|
8587
|
-
* marker sprinkled across sections also has to borrow a field that already
|
|
8588
|
-
* means something else; borrowing `section.tab` put the literal word
|
|
8589
|
-
* "integration" into an operator-facing tab bar, because `tab` means "how to
|
|
8590
|
-
* GROUP this visually" and cannot also mean "where this lives" (D269
|
|
8591
|
-
* supersedes D268). One declaration, in one place, next to the schema whose
|
|
8592
|
-
* ids it names.
|
|
8593
|
-
*/
|
|
8594
|
-
integrationSettingSections() {
|
|
8595
|
-
return [];
|
|
8596
|
-
}
|
|
8597
|
-
async getGlobalSettings(overlay, cap, nodeId) {
|
|
8598
|
-
await this.loadScopedSchemas(cap);
|
|
8599
|
-
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
8600
|
-
if (!schema) return { sections: [] };
|
|
8601
|
-
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
8602
|
-
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
8603
|
-
}
|
|
8604
|
-
/**
|
|
8605
|
-
* The integration-level view of this addon's settings: exactly the sections
|
|
8606
|
-
* named by {@link integrationSettingSections}, hydrated from the SAME store
|
|
8607
|
-
* `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
|
|
8608
|
-
*
|
|
8609
|
-
* Returns `null` when the addon declared nothing — an addon that opts out has
|
|
8610
|
-
* no integration settings surface at all, rather than an empty one that reads
|
|
8611
|
-
* as a failed load.
|
|
8612
|
-
*
|
|
8613
|
-
* Three properties hold BY CONSTRUCTION, which is why they are here in core
|
|
8614
|
-
* and not in whichever UI happens to render this:
|
|
8615
|
-
*
|
|
8616
|
-
* 1. **One key.** The payload is a SUBSET of the global schema, so a field
|
|
8617
|
-
* shown here is the same field, with the same bare key, that the addon's
|
|
8618
|
-
* own page shows. There is no integration-specific writer — callers save
|
|
8619
|
-
* through `updateGlobalSettings` — so a second store key is unreachable,
|
|
8620
|
-
* not merely discouraged.
|
|
8621
|
-
* 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
|
|
8622
|
-
* is `<key>@<nodeId>` and an integration is not a node; whichever node
|
|
8623
|
-
* such a field silently picked would be a wrong answer for the operator
|
|
8624
|
-
* who opened the page (D266).
|
|
8625
|
-
* 3. **No silent typo.** A declared id that names no section throws. The
|
|
8626
|
-
* alternative — skip it — turns a rename into a surface that quietly
|
|
8627
|
-
* empties, which looks exactly like an addon with nothing to configure.
|
|
8628
|
-
*/
|
|
8629
|
-
/**
|
|
8630
|
-
* The form schema of ONE integration instance, or `null` to use {@link globalSettingsSchema}.
|
|
8691
|
+
* The form schema of ONE integration instance, or `null` to use the scoped form.
|
|
8631
8692
|
*
|
|
8632
8693
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
8633
8694
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
@@ -8675,37 +8736,178 @@ var BaseAddon = class {
|
|
|
8675
8736
|
}
|
|
8676
8737
|
}
|
|
8677
8738
|
/**
|
|
8739
|
+
* Hook: values layered over the stored ones when the FORM of one scope is served
|
|
8740
|
+
* ({@link getScopedSettings}) — a preview that derives dependent options without writing.
|
|
8741
|
+
* Never applied to {@link ScopedSettings.get} or to a write. `null` ⇒ no overlay.
|
|
8742
|
+
*/
|
|
8743
|
+
async scopedSettingsOverlay(_scope, _cap, _nodeId) {
|
|
8744
|
+
return null;
|
|
8745
|
+
}
|
|
8746
|
+
/**
|
|
8747
|
+
* Hook: a write of one scope changed stored values (not called when the store reports no change,
|
|
8748
|
+
* e.g. the write-back of a stored secret). `keys` are the bare field keys of the write, node keys included.
|
|
8749
|
+
*/
|
|
8750
|
+
async onScopedSettingsChanged(_scope, _cap, _keys) {}
|
|
8751
|
+
/** Typed handle over the CLUSTER settings `cap` declares (`clusterSettingsSchema`). */
|
|
8752
|
+
clusterSettings(cap, schema) {
|
|
8753
|
+
return this.scopedHandle("cluster", cap, schema);
|
|
8754
|
+
}
|
|
8755
|
+
/** Typed handle over the fleet defaults of the per-device settings `cap` declares. */
|
|
8756
|
+
deviceDefaults(cap, schema) {
|
|
8757
|
+
return this.scopedHandle("device-defaults", cap, schema);
|
|
8758
|
+
}
|
|
8759
|
+
/**
|
|
8760
|
+
* Typed handle over the settings `cap` owns PER NODE. `nodeId` omitted ⇒ the local node. A node
|
|
8761
|
+
* with no row yields the schema defaults — never a bare-key fallback.
|
|
8762
|
+
*/
|
|
8763
|
+
nodeSettings(cap, schema, nodeId) {
|
|
8764
|
+
return this.scopedHandle("node", cap, schema, nodeId);
|
|
8765
|
+
}
|
|
8766
|
+
/**
|
|
8767
|
+
* {@link state} for a value that is kept per node: stored under `<key>@<nodeId>`, and the
|
|
8768
|
+
* settings store carries `nodeId` in the row data (D986 §1), so a forgotten node's rows go in one
|
|
8769
|
+
* central delete. `nodeId` omitted ⇒ the local node.
|
|
8770
|
+
*/
|
|
8771
|
+
nodeState(key, schema, fallback, nodeId) {
|
|
8772
|
+
this._declaredStateKeys.add(key);
|
|
8773
|
+
return createDurableState({
|
|
8774
|
+
key: nodeScopedKey(key, nodeId ?? this._ctx?.kernel?.localNodeId ?? ""),
|
|
8775
|
+
schema,
|
|
8776
|
+
fallback,
|
|
8777
|
+
read: async () => this.resolveStoredSecrets(await this.readAddonStoreWithRetry()),
|
|
8778
|
+
write: async (patch) => {
|
|
8779
|
+
await this._ctx?.settings?.writeAddonStore(patch);
|
|
8780
|
+
},
|
|
8781
|
+
onParseError: (k, e) => this._ctx?.logger?.warn?.(`durable-state: stored "${k}" failed validation — using fallback`, { meta: {
|
|
8782
|
+
addonId: this._ctx?.id,
|
|
8783
|
+
key: k,
|
|
8784
|
+
error: String(e)
|
|
8785
|
+
} })
|
|
8786
|
+
});
|
|
8787
|
+
}
|
|
8788
|
+
/** The loaded schema `cap` declares for `scope`, or `null`. */
|
|
8789
|
+
async declaredScopedSchema(scope, cap) {
|
|
8790
|
+
await this.loadScopedSchemas(cap);
|
|
8791
|
+
return this.loadedScopedSchemas(cap).find((entry) => entry.scope === scope)?.schema ?? null;
|
|
8792
|
+
}
|
|
8793
|
+
async requireScopedSchema(scope, cap) {
|
|
8794
|
+
const schema = await this.declaredScopedSchema(scope, cap);
|
|
8795
|
+
if (schema === null) throw new Error(`${this.constructor.name}: cap "${cap}" declares no ${scope} settings`);
|
|
8796
|
+
return schema;
|
|
8797
|
+
}
|
|
8798
|
+
scopedKeys(schema) {
|
|
8799
|
+
return new Set(schema.sections.flatMap((section) => collectValueFieldKeys(section.fields)));
|
|
8800
|
+
}
|
|
8801
|
+
/** `store` narrowed to the keys `schema` declares. */
|
|
8802
|
+
narrowToScope(store, schema) {
|
|
8803
|
+
const keys = this.scopedKeys(schema);
|
|
8804
|
+
return Object.fromEntries(Object.entries(store).filter(([key]) => keys.has(key)));
|
|
8805
|
+
}
|
|
8806
|
+
scopedHandle(scope, cap, schema, nodeId) {
|
|
8807
|
+
return createScopedSettings({
|
|
8808
|
+
schema,
|
|
8809
|
+
read: async () => {
|
|
8810
|
+
const declared = await this.requireScopedSchema(scope, cap);
|
|
8811
|
+
const stored = this.narrowToScope(await this.resolveGlobalStore(nodeId, cap), declared);
|
|
8812
|
+
const effective = collectHydratedFieldValues(hydrateSchema(declared, stored));
|
|
8813
|
+
return {
|
|
8814
|
+
...Object.fromEntries([...effective].filter(([, value]) => value !== void 0)),
|
|
8815
|
+
...stored
|
|
8816
|
+
};
|
|
8817
|
+
},
|
|
8818
|
+
write: (patch) => this.writeScoped(scope, cap, patch, nodeId),
|
|
8819
|
+
unset: (keys) => this.unsetScoped(scope, cap, keys, nodeId)
|
|
8820
|
+
});
|
|
8821
|
+
}
|
|
8822
|
+
/** The one write path of the scoped API: declared keys only, then the global patch. */
|
|
8823
|
+
async writeScoped(scope, cap, patch, nodeId) {
|
|
8824
|
+
assertDeclaredKeys(Object.keys(patch), this.scopedKeys(await this.requireScopedSchema(scope, cap)));
|
|
8825
|
+
if (await this.applyGlobalPatch(patch, scope === "node" ? nodeId : void 0, cap)) await this.onScopedSettingsChanged(scope, cap, Object.keys(patch));
|
|
8826
|
+
}
|
|
8827
|
+
/**
|
|
8828
|
+
* Remove declared keys of one scope. A secret goes through {@link clearSecrets} — its keychain
|
|
8829
|
+
* value is released exactly as there — and the emptied row is then removed; every other key is
|
|
8830
|
+
* removed by an `undefined` patch (which a secret transform would read as "keep").
|
|
8831
|
+
*/
|
|
8832
|
+
async unsetScoped(scope, cap, keys, nodeId) {
|
|
8833
|
+
assertDeclaredKeys(keys, this.scopedKeys(await this.requireScopedSchema(scope, cap)));
|
|
8834
|
+
const target = scope === "node" ? nodeId : void 0;
|
|
8835
|
+
const tree = this.addonSecretTree();
|
|
8836
|
+
const secretKeys = keys.filter((key) => tree.has(key));
|
|
8837
|
+
const plainKeys = keys.filter((key) => !tree.has(key));
|
|
8838
|
+
let changed = false;
|
|
8839
|
+
if (plainKeys.length > 0) {
|
|
8840
|
+
const patch = Object.fromEntries(plainKeys.map((key) => [key, void 0]));
|
|
8841
|
+
changed = await this.applyGlobalPatch(patch, target, cap) || changed;
|
|
8842
|
+
}
|
|
8843
|
+
if (secretKeys.length > 0) {
|
|
8844
|
+
await this.clearSecretKeys(secretKeys, target, cap);
|
|
8845
|
+
const node = normalizeNodeId(target ?? this._ctx?.kernel?.localNodeId);
|
|
8846
|
+
const rows = secretKeys.map((key) => scope === "node" ? nodeScopedKey(key, node) : key);
|
|
8847
|
+
if (this._secretSettings !== null) await this._secretSettings.removeClearedKeys(rows);
|
|
8848
|
+
else await this._ctx?.settings?.writeAddonStore(Object.fromEntries(rows.map((row) => [row, void 0])));
|
|
8849
|
+
changed = true;
|
|
8850
|
+
}
|
|
8851
|
+
if (changed) await this.onScopedSettingsChanged(scope, cap, [...keys]);
|
|
8852
|
+
}
|
|
8853
|
+
/**
|
|
8854
|
+
* The hydrated form of one scope of one capability's settings (any
|
|
8855
|
+
* {@link scopedSettingsOverlay} applied), or `null` when the capability declares nothing there.
|
|
8856
|
+
*/
|
|
8857
|
+
async getScopedSettings(scope, cap, nodeId) {
|
|
8858
|
+
const schema = await this.declaredScopedSchema(scope, cap);
|
|
8859
|
+
if (schema === null) return null;
|
|
8860
|
+
const stored = this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema);
|
|
8861
|
+
const overlay = await this.scopedSettingsOverlay(scope, cap, nodeId);
|
|
8862
|
+
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(stored, overlay) : stored, { inlineSecretsAsViews: true });
|
|
8863
|
+
}
|
|
8864
|
+
/**
|
|
8865
|
+
* The scoped schemas this addon serves: one entry per loaded (capability, scope), capabilities
|
|
8866
|
+
* in declaration order, scopes in {@link SCOPED_SETTINGS_SCOPES} order. `[]` when it declares
|
|
8867
|
+
* nothing.
|
|
8868
|
+
*/
|
|
8869
|
+
async listScopedSettings() {
|
|
8870
|
+
await this.loadScopedSchemas();
|
|
8871
|
+
return [...this._scopedByCap].flatMap(([cap, entries]) => entries.map((entry) => ({
|
|
8872
|
+
cap,
|
|
8873
|
+
scope: entry.scope,
|
|
8874
|
+
sectionIds: entry.schema.sections.map((section) => section.id)
|
|
8875
|
+
})));
|
|
8876
|
+
}
|
|
8877
|
+
/** The OPERATOR's write of declared keys of one scope (D787 secret semantics). */
|
|
8878
|
+
async updateScopedSettings(scope, cap, patch, nodeId) {
|
|
8879
|
+
this._operatorWriteDepth++;
|
|
8880
|
+
try {
|
|
8881
|
+
await this.writeScoped(scope, cap, patch, nodeId);
|
|
8882
|
+
} finally {
|
|
8883
|
+
this._operatorWriteDepth--;
|
|
8884
|
+
}
|
|
8885
|
+
}
|
|
8886
|
+
/**
|
|
8887
|
+
* The effective values (`stored ?? default`) of one scope, a secret as its `set` view — never
|
|
8888
|
+
* its value. `null` when the capability declares nothing for the scope.
|
|
8889
|
+
*/
|
|
8890
|
+
async readScopedValues(scope, cap, nodeId) {
|
|
8891
|
+
const schema = await this.declaredScopedSchema(scope, cap);
|
|
8892
|
+
if (schema === null) return null;
|
|
8893
|
+
const form = hydrateSchema(schema, this.narrowToScope(await this.readRawProjectedStore(nodeId, cap), schema), { inlineSecretsAsViews: true });
|
|
8894
|
+
return Object.fromEntries([...collectHydratedFieldValues(form)].filter(([, value]) => value !== void 0));
|
|
8895
|
+
}
|
|
8896
|
+
/**
|
|
8678
8897
|
* What the create wizard and the integration edit page render for one instance: the addon's
|
|
8679
8898
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8680
8899
|
*/
|
|
8681
8900
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8682
8901
|
const scoped = await this.integrationFormSchema(instance);
|
|
8683
|
-
if (scoped === null) return this.
|
|
8902
|
+
if (scoped === null) return this.scopedForm(nodeId);
|
|
8684
8903
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8685
8904
|
}
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
const schema = this.
|
|
8690
|
-
if (!schema)
|
|
8691
|
-
|
|
8692
|
-
const sections = [];
|
|
8693
|
-
for (const id of declared) {
|
|
8694
|
-
const section = byId.get(id);
|
|
8695
|
-
if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
|
|
8696
|
-
const fields = dropPerNodeFields(section.fields);
|
|
8697
|
-
if (fields.length === 0) continue;
|
|
8698
|
-
sections.push({
|
|
8699
|
-
...section,
|
|
8700
|
-
fields
|
|
8701
|
-
});
|
|
8702
|
-
}
|
|
8703
|
-
if (sections.length === 0) return null;
|
|
8704
|
-
const projected = await this.readRawProjectedStore(nodeId);
|
|
8705
|
-
return hydrateSchema({
|
|
8706
|
-
...schema,
|
|
8707
|
-
sections
|
|
8708
|
-
}, projected, { inlineSecretsAsViews: true });
|
|
8905
|
+
/** The form of the scoped schemas this addon declared, hydrated from its store. */
|
|
8906
|
+
async scopedForm(nodeId) {
|
|
8907
|
+
await this.loadScopedSchemas();
|
|
8908
|
+
const schema = this.scopedFormSchema();
|
|
8909
|
+
if (!schema) return { sections: [] };
|
|
8910
|
+
return hydrateSchema(schema, await this.readRawProjectedStore(nodeId), { inlineSecretsAsViews: true });
|
|
8709
8911
|
}
|
|
8710
8912
|
/**
|
|
8711
8913
|
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
@@ -8714,8 +8916,8 @@ var BaseAddon = class {
|
|
|
8714
8916
|
* bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
|
|
8715
8917
|
* A no-op passthrough when the schema declares no `perNode` field.
|
|
8716
8918
|
*
|
|
8717
|
-
* This is the sanctioned way for
|
|
8718
|
-
* the store for custom
|
|
8919
|
+
* This is the sanctioned way for an addon that needs
|
|
8920
|
+
* the store for custom logic (option narrowing, value snapping) to
|
|
8719
8921
|
* read it per-node — never `ctx.settings.readAddonStore()` directly.
|
|
8720
8922
|
* `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
|
|
8721
8923
|
*
|
|
@@ -8767,7 +8969,7 @@ var BaseAddon = class {
|
|
|
8767
8969
|
}
|
|
8768
8970
|
/**
|
|
8769
8971
|
* Whether ONE global key of this addon's store was written, read three-way
|
|
8770
|
-
* (D651). For a question a hydrated view cannot answer:
|
|
8972
|
+
* (D651). For a question a hydrated view cannot answer: a hydrated form
|
|
8771
8973
|
* reports `value ?? default`, so "the operator chose the default" and "nobody
|
|
8772
8974
|
* chose" look the same, and a failed read looks like both. A `perNode` key is
|
|
8773
8975
|
* not supported here: its stored name is scoped.
|
|
@@ -8801,7 +9003,7 @@ var BaseAddon = class {
|
|
|
8801
9003
|
}
|
|
8802
9004
|
/**
|
|
8803
9005
|
* The OPERATOR's write — a settings form or an admin API call, dispatched by
|
|
8804
|
-
* the kernel.
|
|
9006
|
+
* the kernel. The one in-process write of the addon blob that is not a scoped write; a secret
|
|
8805
9007
|
* the operator sends is stored as sent even when it equals a pre-keychain
|
|
8806
9008
|
* inline value: the operator re-entering a password is a change, an addon
|
|
8807
9009
|
* writing back what it read is not (D787).
|
|
@@ -8814,9 +9016,6 @@ var BaseAddon = class {
|
|
|
8814
9016
|
this._operatorWriteDepth--;
|
|
8815
9017
|
}
|
|
8816
9018
|
}
|
|
8817
|
-
async updateGlobalSettings(patch, nodeId) {
|
|
8818
|
-
await this.applyGlobalPatch(patch, nodeId);
|
|
8819
|
-
}
|
|
8820
9019
|
/**
|
|
8821
9020
|
* REMOVE stored secrets — their keychain values and their markers (D787).
|
|
8822
9021
|
*
|
|
@@ -8826,6 +9025,9 @@ var BaseAddon = class {
|
|
|
8826
9025
|
* a keychain (an older kernel) it writes `''`, as such code always did.
|
|
8827
9026
|
*/
|
|
8828
9027
|
async clearSecrets(keys, nodeId) {
|
|
9028
|
+
await this.clearSecretKeys(keys, nodeId);
|
|
9029
|
+
}
|
|
9030
|
+
async clearSecretKeys(keys, nodeId, cap) {
|
|
8829
9031
|
const tree = this.addonSecretTree();
|
|
8830
9032
|
const keychain = this._ctx?.secrets !== void 0;
|
|
8831
9033
|
const stored = keychain ? await this.readAddonStoreWithRetry() : {};
|
|
@@ -8838,25 +9040,26 @@ var BaseAddon = class {
|
|
|
8838
9040
|
const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
|
|
8839
9041
|
patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
|
|
8840
9042
|
}
|
|
8841
|
-
await this.applyGlobalPatch(patch, nodeId);
|
|
9043
|
+
await this.applyGlobalPatch(patch, nodeId, cap);
|
|
8842
9044
|
}
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
9045
|
+
/** Resolves `true` when the write changed stored values. */
|
|
9046
|
+
async applyGlobalPatch(barePatch, nodeId, cap) {
|
|
9047
|
+
await this.loadScopedSchemas(cap);
|
|
9048
|
+
const keys = this.perNodeKeys(cap);
|
|
8846
9049
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8847
9050
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
8848
9051
|
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
8849
9052
|
const handle = this._secretSettings;
|
|
8850
9053
|
if (handle !== null) {
|
|
8851
9054
|
const { changed } = await handle.writeAddonStore(scoped);
|
|
8852
|
-
if (!changed) return;
|
|
9055
|
+
if (!changed) return false;
|
|
8853
9056
|
} else await this._ctx?.settings?.writeAddonStore(scoped);
|
|
8854
|
-
if (target !== localNode) return;
|
|
9057
|
+
if (target !== localNode) return true;
|
|
8855
9058
|
await this.resolveConfig();
|
|
8856
9059
|
await this.onConfigChanged();
|
|
8857
9060
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8858
|
-
|
|
8859
|
-
|
|
9061
|
+
this.maybeAutoRestart(Object.keys(barePatch), this.loadedScopedSchemas().map((entry) => entry.schema));
|
|
9062
|
+
return true;
|
|
8860
9063
|
}
|
|
8861
9064
|
/**
|
|
8862
9065
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8893,10 +9096,7 @@ var BaseAddon = class {
|
|
|
8893
9096
|
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8894
9097
|
this._addonSecretTree = null;
|
|
8895
9098
|
}
|
|
8896
|
-
/**
|
|
8897
|
-
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8898
|
-
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8899
|
-
*/
|
|
9099
|
+
/** The form of an addon's scoped schemas: their sections, each shown once. */
|
|
8900
9100
|
scopedFormSchema(cap) {
|
|
8901
9101
|
const byId = /* @__PURE__ */ new Map();
|
|
8902
9102
|
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
@@ -8908,8 +9108,7 @@ var BaseAddon = class {
|
|
|
8908
9108
|
return [...this._scopedByCap.values()].flat();
|
|
8909
9109
|
}
|
|
8910
9110
|
/**
|
|
8911
|
-
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}
|
|
8912
|
-
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
9111
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}. Derived once
|
|
8913
9112
|
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8914
9113
|
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8915
9114
|
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
@@ -8918,8 +9117,7 @@ var BaseAddon = class {
|
|
|
8918
9117
|
const cacheKey = cap ?? "";
|
|
8919
9118
|
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8920
9119
|
if (cached) return cached;
|
|
8921
|
-
const
|
|
8922
|
-
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
9120
|
+
const keys = /* @__PURE__ */ new Set();
|
|
8923
9121
|
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8924
9122
|
if (scope !== "node") continue;
|
|
8925
9123
|
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
@@ -9137,8 +9335,6 @@ var BaseAddon = class {
|
|
|
9137
9335
|
}
|
|
9138
9336
|
declaredKeySet() {
|
|
9139
9337
|
const keys = new Set(Object.keys(this.defaults));
|
|
9140
|
-
const schema = this.globalSettingsSchema();
|
|
9141
|
-
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
9142
9338
|
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
9143
9339
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
9144
9340
|
for (const path of this.secretStatePaths) {
|
|
@@ -9236,7 +9432,7 @@ var BaseAddon = class {
|
|
|
9236
9432
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
9237
9433
|
addonSecretTree() {
|
|
9238
9434
|
if (this._addonSecretTree === null) {
|
|
9239
|
-
const tree = new Map(
|
|
9435
|
+
const tree = /* @__PURE__ */ new Map();
|
|
9240
9436
|
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
9241
9437
|
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
9242
9438
|
}
|
|
@@ -9448,47 +9644,6 @@ function classifyStoreError(err) {
|
|
|
9448
9644
|
if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
|
|
9449
9645
|
return "permanent";
|
|
9450
9646
|
}
|
|
9451
|
-
/**
|
|
9452
|
-
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
9453
|
-
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
9454
|
-
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
9455
|
-
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
9456
|
-
*/
|
|
9457
|
-
/**
|
|
9458
|
-
* The same fields with every `perNode: true` one removed, recursing into layout
|
|
9459
|
-
* containers exactly as {@link collectPerNodeFieldKeys} does. A container left
|
|
9460
|
-
* with no child is dropped rather than rendered empty.
|
|
9461
|
-
*
|
|
9462
|
-
* Used by `getIntegrationSettings`: an integration is not a node, so a field
|
|
9463
|
-
* whose store key is `<key>@<nodeId>` has no node to belong to there.
|
|
9464
|
-
*/
|
|
9465
|
-
function dropPerNodeFields(fields) {
|
|
9466
|
-
const kept = [];
|
|
9467
|
-
for (const field of fields) {
|
|
9468
|
-
if (field.type === "group") {
|
|
9469
|
-
const inner = dropPerNodeFields(field.fields);
|
|
9470
|
-
if (inner.length > 0) kept.push({
|
|
9471
|
-
...field,
|
|
9472
|
-
fields: inner
|
|
9473
|
-
});
|
|
9474
|
-
continue;
|
|
9475
|
-
}
|
|
9476
|
-
if (field.type === "sub-tabs") {
|
|
9477
|
-
const tabs = field.tabs.map((tab) => ({
|
|
9478
|
-
...tab,
|
|
9479
|
-
fields: dropPerNodeFields(tab.fields)
|
|
9480
|
-
})).filter((tab) => tab.fields.length > 0);
|
|
9481
|
-
if (tabs.length > 0) kept.push({
|
|
9482
|
-
...field,
|
|
9483
|
-
tabs
|
|
9484
|
-
});
|
|
9485
|
-
continue;
|
|
9486
|
-
}
|
|
9487
|
-
if ("perNode" in field && field.perNode === true) continue;
|
|
9488
|
-
kept.push(field);
|
|
9489
|
-
}
|
|
9490
|
-
return kept;
|
|
9491
|
-
}
|
|
9492
9647
|
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
9493
9648
|
function collectValueFieldKeys(fields) {
|
|
9494
9649
|
const collected = [];
|
|
@@ -9521,21 +9676,6 @@ function collectFieldKeys(fields) {
|
|
|
9521
9676
|
}
|
|
9522
9677
|
return collected;
|
|
9523
9678
|
}
|
|
9524
|
-
function collectPerNodeFieldKeys(fields) {
|
|
9525
|
-
const collected = [];
|
|
9526
|
-
for (const field of fields) {
|
|
9527
|
-
if (field.type === "group") {
|
|
9528
|
-
collected.push(...collectPerNodeFieldKeys(field.fields));
|
|
9529
|
-
continue;
|
|
9530
|
-
}
|
|
9531
|
-
if (field.type === "sub-tabs") {
|
|
9532
|
-
for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
|
|
9533
|
-
continue;
|
|
9534
|
-
}
|
|
9535
|
-
if ("perNode" in field && field.perNode === true) collected.push(field.key);
|
|
9536
|
-
}
|
|
9537
|
-
return collected;
|
|
9538
|
-
}
|
|
9539
9679
|
/**
|
|
9540
9680
|
* Normalize an `ICamstackAddon.initialize()` return value into the
|
|
9541
9681
|
* `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
|
|
@@ -10586,7 +10726,6 @@ var CAP_NAMES = Object.freeze([
|
|
|
10586
10726
|
"notifier",
|
|
10587
10727
|
"numeric-sensor",
|
|
10588
10728
|
"oauth-integration",
|
|
10589
|
-
"oidc-connections",
|
|
10590
10729
|
"osd",
|
|
10591
10730
|
"osd-manager",
|
|
10592
10731
|
"pet-feeder",
|
|
@@ -12293,13 +12432,25 @@ Object.freeze({
|
|
|
12293
12432
|
"path": "temperature",
|
|
12294
12433
|
"valueType": "number"
|
|
12295
12434
|
}],
|
|
12296
|
-
"timed-run": [
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12435
|
+
"timed-run": [
|
|
12436
|
+
{
|
|
12437
|
+
"path": "activeUntil",
|
|
12438
|
+
"valueType": "number"
|
|
12439
|
+
},
|
|
12440
|
+
{
|
|
12441
|
+
"path": "requestedBy",
|
|
12442
|
+
"valueType": "string"
|
|
12443
|
+
},
|
|
12444
|
+
{
|
|
12445
|
+
"path": "quickRun.duration",
|
|
12446
|
+
"valueType": "number"
|
|
12447
|
+
},
|
|
12448
|
+
{
|
|
12449
|
+
"path": "quickRun.unit",
|
|
12450
|
+
"valueType": "enum",
|
|
12451
|
+
"enumValues": ["s", "min"]
|
|
12452
|
+
}
|
|
12453
|
+
],
|
|
12303
12454
|
"update": [
|
|
12304
12455
|
{
|
|
12305
12456
|
"path": "currentVersion",
|
|
@@ -12478,8 +12629,7 @@ Object.freeze({
|
|
|
12478
12629
|
"device-provider": { "reloadDevice": 18e4 },
|
|
12479
12630
|
"llm": {
|
|
12480
12631
|
"generate": 6e5,
|
|
12481
|
-
"generateVision": 6e5
|
|
12482
|
-
"testProfile": 6e5
|
|
12632
|
+
"generateVision": 6e5
|
|
12483
12633
|
},
|
|
12484
12634
|
"llm-runtime": {
|
|
12485
12635
|
"complete": 6e5,
|
|
@@ -12538,7 +12688,7 @@ function userError(code, params, englishFallback) {
|
|
|
12538
12688
|
return new CamstackError(code, params, englishFallback);
|
|
12539
12689
|
}
|
|
12540
12690
|
//#endregion
|
|
12541
|
-
//#region ../types/dist/composition-
|
|
12691
|
+
//#region ../types/dist/composition-Bx0JvKUU.mjs
|
|
12542
12692
|
/**
|
|
12543
12693
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
12544
12694
|
*
|
|
@@ -13700,6 +13850,387 @@ var NodeColorIdSchema = _enum([
|
|
|
13700
13850
|
"node-h"
|
|
13701
13851
|
]);
|
|
13702
13852
|
//#endregion
|
|
13853
|
+
//#region ../types/dist/convert-BAzH15HU.mjs
|
|
13854
|
+
/**
|
|
13855
|
+
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
13856
|
+
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
13857
|
+
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
13858
|
+
* decimals). K derives from `°C = K − 273.15`.
|
|
13859
|
+
*/
|
|
13860
|
+
var UNIT_TABLE = {
|
|
13861
|
+
"°C": {
|
|
13862
|
+
dimension: "temperature",
|
|
13863
|
+
factor: 1,
|
|
13864
|
+
offset: 0
|
|
13865
|
+
},
|
|
13866
|
+
"°F": {
|
|
13867
|
+
dimension: "temperature",
|
|
13868
|
+
factor: 5 / 9,
|
|
13869
|
+
offset: -160 / 9
|
|
13870
|
+
},
|
|
13871
|
+
K: {
|
|
13872
|
+
dimension: "temperature",
|
|
13873
|
+
factor: 1,
|
|
13874
|
+
offset: -273.15
|
|
13875
|
+
},
|
|
13876
|
+
hPa: {
|
|
13877
|
+
dimension: "pressure",
|
|
13878
|
+
factor: 1,
|
|
13879
|
+
offset: 0
|
|
13880
|
+
},
|
|
13881
|
+
kPa: {
|
|
13882
|
+
dimension: "pressure",
|
|
13883
|
+
factor: 10,
|
|
13884
|
+
offset: 0
|
|
13885
|
+
},
|
|
13886
|
+
Pa: {
|
|
13887
|
+
dimension: "pressure",
|
|
13888
|
+
factor: .01,
|
|
13889
|
+
offset: 0
|
|
13890
|
+
},
|
|
13891
|
+
mbar: {
|
|
13892
|
+
dimension: "pressure",
|
|
13893
|
+
factor: 1,
|
|
13894
|
+
offset: 0
|
|
13895
|
+
},
|
|
13896
|
+
bar: {
|
|
13897
|
+
dimension: "pressure",
|
|
13898
|
+
factor: 1e3,
|
|
13899
|
+
offset: 0
|
|
13900
|
+
},
|
|
13901
|
+
inHg: {
|
|
13902
|
+
dimension: "pressure",
|
|
13903
|
+
factor: 33.8639,
|
|
13904
|
+
offset: 0
|
|
13905
|
+
},
|
|
13906
|
+
mmHg: {
|
|
13907
|
+
dimension: "pressure",
|
|
13908
|
+
factor: 1.33322,
|
|
13909
|
+
offset: 0
|
|
13910
|
+
},
|
|
13911
|
+
psi: {
|
|
13912
|
+
dimension: "pressure",
|
|
13913
|
+
factor: 68.9476,
|
|
13914
|
+
offset: 0
|
|
13915
|
+
},
|
|
13916
|
+
"m/s": {
|
|
13917
|
+
dimension: "speed",
|
|
13918
|
+
factor: 1,
|
|
13919
|
+
offset: 0
|
|
13920
|
+
},
|
|
13921
|
+
"km/h": {
|
|
13922
|
+
dimension: "speed",
|
|
13923
|
+
factor: 1 / 3.6,
|
|
13924
|
+
offset: 0
|
|
13925
|
+
},
|
|
13926
|
+
mph: {
|
|
13927
|
+
dimension: "speed",
|
|
13928
|
+
factor: .44704,
|
|
13929
|
+
offset: 0
|
|
13930
|
+
},
|
|
13931
|
+
kn: {
|
|
13932
|
+
dimension: "speed",
|
|
13933
|
+
factor: .514444,
|
|
13934
|
+
offset: 0
|
|
13935
|
+
},
|
|
13936
|
+
"mm/h": {
|
|
13937
|
+
dimension: "precipitation-rate",
|
|
13938
|
+
factor: 1,
|
|
13939
|
+
offset: 0
|
|
13940
|
+
},
|
|
13941
|
+
"in/h": {
|
|
13942
|
+
dimension: "precipitation-rate",
|
|
13943
|
+
factor: 25.4,
|
|
13944
|
+
offset: 0
|
|
13945
|
+
},
|
|
13946
|
+
m: {
|
|
13947
|
+
dimension: "length",
|
|
13948
|
+
factor: 1,
|
|
13949
|
+
offset: 0
|
|
13950
|
+
},
|
|
13951
|
+
mm: {
|
|
13952
|
+
dimension: "length",
|
|
13953
|
+
factor: .001,
|
|
13954
|
+
offset: 0
|
|
13955
|
+
},
|
|
13956
|
+
cm: {
|
|
13957
|
+
dimension: "length",
|
|
13958
|
+
factor: .01,
|
|
13959
|
+
offset: 0
|
|
13960
|
+
},
|
|
13961
|
+
km: {
|
|
13962
|
+
dimension: "length",
|
|
13963
|
+
factor: 1e3,
|
|
13964
|
+
offset: 0
|
|
13965
|
+
},
|
|
13966
|
+
in: {
|
|
13967
|
+
dimension: "length",
|
|
13968
|
+
factor: .0254,
|
|
13969
|
+
offset: 0
|
|
13970
|
+
},
|
|
13971
|
+
ft: {
|
|
13972
|
+
dimension: "length",
|
|
13973
|
+
factor: .3048,
|
|
13974
|
+
offset: 0
|
|
13975
|
+
},
|
|
13976
|
+
mi: {
|
|
13977
|
+
dimension: "length",
|
|
13978
|
+
factor: 1609.344,
|
|
13979
|
+
offset: 0
|
|
13980
|
+
},
|
|
13981
|
+
"m²": {
|
|
13982
|
+
dimension: "area",
|
|
13983
|
+
factor: 1,
|
|
13984
|
+
offset: 0
|
|
13985
|
+
},
|
|
13986
|
+
lx: {
|
|
13987
|
+
dimension: "illuminance",
|
|
13988
|
+
factor: 1,
|
|
13989
|
+
offset: 0
|
|
13990
|
+
},
|
|
13991
|
+
"W/m²": {
|
|
13992
|
+
dimension: "irradiance",
|
|
13993
|
+
factor: 1,
|
|
13994
|
+
offset: 0
|
|
13995
|
+
},
|
|
13996
|
+
W: {
|
|
13997
|
+
dimension: "power",
|
|
13998
|
+
factor: 1,
|
|
13999
|
+
offset: 0
|
|
14000
|
+
},
|
|
14001
|
+
kW: {
|
|
14002
|
+
dimension: "power",
|
|
14003
|
+
factor: 1e3,
|
|
14004
|
+
offset: 0
|
|
14005
|
+
},
|
|
14006
|
+
VA: {
|
|
14007
|
+
dimension: "apparent-power",
|
|
14008
|
+
factor: 1,
|
|
14009
|
+
offset: 0
|
|
14010
|
+
},
|
|
14011
|
+
Wh: {
|
|
14012
|
+
dimension: "energy",
|
|
14013
|
+
factor: 1,
|
|
14014
|
+
offset: 0
|
|
14015
|
+
},
|
|
14016
|
+
kWh: {
|
|
14017
|
+
dimension: "energy",
|
|
14018
|
+
factor: 1e3,
|
|
14019
|
+
offset: 0
|
|
14020
|
+
},
|
|
14021
|
+
MWh: {
|
|
14022
|
+
dimension: "energy",
|
|
14023
|
+
factor: 1e6,
|
|
14024
|
+
offset: 0
|
|
14025
|
+
},
|
|
14026
|
+
V: {
|
|
14027
|
+
dimension: "voltage",
|
|
14028
|
+
factor: 1,
|
|
14029
|
+
offset: 0
|
|
14030
|
+
},
|
|
14031
|
+
mV: {
|
|
14032
|
+
dimension: "voltage",
|
|
14033
|
+
factor: .001,
|
|
14034
|
+
offset: 0
|
|
14035
|
+
},
|
|
14036
|
+
A: {
|
|
14037
|
+
dimension: "current",
|
|
14038
|
+
factor: 1,
|
|
14039
|
+
offset: 0
|
|
14040
|
+
},
|
|
14041
|
+
mA: {
|
|
14042
|
+
dimension: "current",
|
|
14043
|
+
factor: .001,
|
|
14044
|
+
offset: 0
|
|
14045
|
+
},
|
|
14046
|
+
ppm: {
|
|
14047
|
+
dimension: "concentration-volume",
|
|
14048
|
+
factor: 1,
|
|
14049
|
+
offset: 0
|
|
14050
|
+
},
|
|
14051
|
+
ppb: {
|
|
14052
|
+
dimension: "concentration-volume",
|
|
14053
|
+
factor: .001,
|
|
14054
|
+
offset: 0
|
|
14055
|
+
},
|
|
14056
|
+
"µg/m³": {
|
|
14057
|
+
dimension: "concentration-mass",
|
|
14058
|
+
factor: 1,
|
|
14059
|
+
offset: 0
|
|
14060
|
+
},
|
|
14061
|
+
"mg/m³": {
|
|
14062
|
+
dimension: "concentration-mass",
|
|
14063
|
+
factor: 1e3,
|
|
14064
|
+
offset: 0
|
|
14065
|
+
},
|
|
14066
|
+
kg: {
|
|
14067
|
+
dimension: "mass",
|
|
14068
|
+
factor: 1,
|
|
14069
|
+
offset: 0
|
|
14070
|
+
},
|
|
14071
|
+
g: {
|
|
14072
|
+
dimension: "mass",
|
|
14073
|
+
factor: .001,
|
|
14074
|
+
offset: 0
|
|
14075
|
+
},
|
|
14076
|
+
lb: {
|
|
14077
|
+
dimension: "mass",
|
|
14078
|
+
factor: .453592,
|
|
14079
|
+
offset: 0
|
|
14080
|
+
},
|
|
14081
|
+
oz: {
|
|
14082
|
+
dimension: "mass",
|
|
14083
|
+
factor: .0283495,
|
|
14084
|
+
offset: 0
|
|
14085
|
+
},
|
|
14086
|
+
"%": {
|
|
14087
|
+
dimension: "percentage",
|
|
14088
|
+
factor: 1,
|
|
14089
|
+
offset: 0
|
|
14090
|
+
},
|
|
14091
|
+
L: {
|
|
14092
|
+
dimension: "volume",
|
|
14093
|
+
factor: 1,
|
|
14094
|
+
offset: 0
|
|
14095
|
+
},
|
|
14096
|
+
mL: {
|
|
14097
|
+
dimension: "volume",
|
|
14098
|
+
factor: .001,
|
|
14099
|
+
offset: 0
|
|
14100
|
+
},
|
|
14101
|
+
gal: {
|
|
14102
|
+
dimension: "volume",
|
|
14103
|
+
factor: 3.785411784,
|
|
14104
|
+
offset: 0
|
|
14105
|
+
},
|
|
14106
|
+
s: {
|
|
14107
|
+
dimension: "duration",
|
|
14108
|
+
factor: 1,
|
|
14109
|
+
offset: 0
|
|
14110
|
+
},
|
|
14111
|
+
ms: {
|
|
14112
|
+
dimension: "duration",
|
|
14113
|
+
factor: .001,
|
|
14114
|
+
offset: 0
|
|
14115
|
+
},
|
|
14116
|
+
min: {
|
|
14117
|
+
dimension: "duration",
|
|
14118
|
+
factor: 60,
|
|
14119
|
+
offset: 0
|
|
14120
|
+
},
|
|
14121
|
+
h: {
|
|
14122
|
+
dimension: "duration",
|
|
14123
|
+
factor: 3600,
|
|
14124
|
+
offset: 0
|
|
14125
|
+
},
|
|
14126
|
+
d: {
|
|
14127
|
+
dimension: "duration",
|
|
14128
|
+
factor: 86400,
|
|
14129
|
+
offset: 0
|
|
14130
|
+
},
|
|
14131
|
+
B: {
|
|
14132
|
+
dimension: "information",
|
|
14133
|
+
factor: 1,
|
|
14134
|
+
offset: 0
|
|
14135
|
+
},
|
|
14136
|
+
KiB: {
|
|
14137
|
+
dimension: "information",
|
|
14138
|
+
factor: 1024,
|
|
14139
|
+
offset: 0
|
|
14140
|
+
},
|
|
14141
|
+
MiB: {
|
|
14142
|
+
dimension: "information",
|
|
14143
|
+
factor: 1024 ** 2,
|
|
14144
|
+
offset: 0
|
|
14145
|
+
},
|
|
14146
|
+
GiB: {
|
|
14147
|
+
dimension: "information",
|
|
14148
|
+
factor: 1024 ** 3,
|
|
14149
|
+
offset: 0
|
|
14150
|
+
},
|
|
14151
|
+
TiB: {
|
|
14152
|
+
dimension: "information",
|
|
14153
|
+
factor: 1024 ** 4,
|
|
14154
|
+
offset: 0
|
|
14155
|
+
},
|
|
14156
|
+
kB: {
|
|
14157
|
+
dimension: "information",
|
|
14158
|
+
factor: 1e3,
|
|
14159
|
+
offset: 0
|
|
14160
|
+
},
|
|
14161
|
+
MB: {
|
|
14162
|
+
dimension: "information",
|
|
14163
|
+
factor: 1e6,
|
|
14164
|
+
offset: 0
|
|
14165
|
+
},
|
|
14166
|
+
GB: {
|
|
14167
|
+
dimension: "information",
|
|
14168
|
+
factor: 1e9,
|
|
14169
|
+
offset: 0
|
|
14170
|
+
},
|
|
14171
|
+
"°": {
|
|
14172
|
+
dimension: "angle",
|
|
14173
|
+
factor: 1,
|
|
14174
|
+
offset: 0
|
|
14175
|
+
},
|
|
14176
|
+
dBFS: {
|
|
14177
|
+
dimension: "sound-level",
|
|
14178
|
+
factor: 1,
|
|
14179
|
+
offset: 0
|
|
14180
|
+
},
|
|
14181
|
+
dBm: {
|
|
14182
|
+
dimension: "signal-level",
|
|
14183
|
+
factor: 1,
|
|
14184
|
+
offset: 0
|
|
14185
|
+
},
|
|
14186
|
+
mired: {
|
|
14187
|
+
dimension: "colour-temperature",
|
|
14188
|
+
factor: 1,
|
|
14189
|
+
offset: 0
|
|
14190
|
+
},
|
|
14191
|
+
kbps: {
|
|
14192
|
+
dimension: "bitrate",
|
|
14193
|
+
factor: 1,
|
|
14194
|
+
offset: 0
|
|
14195
|
+
},
|
|
14196
|
+
Mbps: {
|
|
14197
|
+
dimension: "bitrate",
|
|
14198
|
+
factor: 1e3,
|
|
14199
|
+
offset: 0
|
|
14200
|
+
},
|
|
14201
|
+
fps: {
|
|
14202
|
+
dimension: "rate",
|
|
14203
|
+
factor: 1,
|
|
14204
|
+
offset: 0
|
|
14205
|
+
}
|
|
14206
|
+
};
|
|
14207
|
+
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
14208
|
+
function specFor(unit) {
|
|
14209
|
+
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
14210
|
+
}
|
|
14211
|
+
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
14212
|
+
function toCanonical(value, spec) {
|
|
14213
|
+
return value * spec.factor + spec.offset;
|
|
14214
|
+
}
|
|
14215
|
+
/** Canonical-unit value → the unit described by `spec`. */
|
|
14216
|
+
function fromCanonical(canonical, spec) {
|
|
14217
|
+
return (canonical - spec.offset) / spec.factor;
|
|
14218
|
+
}
|
|
14219
|
+
/**
|
|
14220
|
+
* Graceful variant for the render path: returns the converted value, or
|
|
14221
|
+
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
14222
|
+
* Never throws — a render caller falls back to the raw value + source unit.
|
|
14223
|
+
*/
|
|
14224
|
+
function tryConvertUnit(value, from, to) {
|
|
14225
|
+
if (from === to) return value;
|
|
14226
|
+
const fromSpec = specFor(from);
|
|
14227
|
+
if (!fromSpec) return void 0;
|
|
14228
|
+
const toSpec = specFor(to);
|
|
14229
|
+
if (!toSpec) return void 0;
|
|
14230
|
+
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
14231
|
+
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
14232
|
+
}
|
|
14233
|
+
//#endregion
|
|
13703
14234
|
//#region ../types/dist/index.mjs
|
|
13704
14235
|
var SMTP_INBOUND_DEFAULT_PORT = 2525;
|
|
13705
14236
|
var SMTP_INBOUND_DEFAULT_DOMAIN = "camstack.local";
|
|
@@ -14104,17 +14635,18 @@ function composeInstanceKinds(parts, base) {
|
|
|
14104
14635
|
* message to what the kind supports — callers never special-case a service.
|
|
14105
14636
|
*
|
|
14106
14637
|
* DESIGN DECISIONS (locked):
|
|
14107
|
-
* -
|
|
14108
|
-
*
|
|
14109
|
-
*
|
|
14110
|
-
*
|
|
14111
|
-
*
|
|
14112
|
-
*
|
|
14113
|
-
*
|
|
14114
|
-
*
|
|
14115
|
-
*
|
|
14116
|
-
* routable. `send` / `testTarget` /
|
|
14117
|
-
* `
|
|
14638
|
+
* - A target is an INSTANCE of this function cap (epic #440, step 6): the
|
|
14639
|
+
* providers serve their targets as `integration-instances` records, one
|
|
14640
|
+
* record kind per target kind, and the admin lists, adds, edits, switches
|
|
14641
|
+
* and deletes them through that cap. This cap keeps what an instance cannot
|
|
14642
|
+
* say: the kind catalogue (`listTargetKinds`: capabilities, delivery
|
|
14643
|
+
* parameters, brand icon), dispatch (`send`, `testTarget`) and the form
|
|
14644
|
+
* helpers (`discoverTargets`, `probeTargetConfig`).
|
|
14645
|
+
* - `listTargetKinds` / `discoverTargets` return arrays → the generated
|
|
14646
|
+
* cap-mount auto-`concatCollection`-fans them across every registered
|
|
14647
|
+
* provider so one catalog is routable. `send` / `testTarget` /
|
|
14648
|
+
* `probeTargetConfig` route to ONE provider by the `addonId` the generated
|
|
14649
|
+
* collection router extracts from the call input.
|
|
14118
14650
|
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
14119
14651
|
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
14120
14652
|
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
@@ -14359,7 +14891,7 @@ var TargetKindCapsSchema = object({
|
|
|
14359
14891
|
* the union is large and not meant for runtime validation here; the exported
|
|
14360
14892
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14361
14893
|
*/
|
|
14362
|
-
var ConfigSchemaPassthrough$
|
|
14894
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
14363
14895
|
var TargetKindSchema = object({
|
|
14364
14896
|
kind: string(),
|
|
14365
14897
|
label: string(),
|
|
@@ -14396,7 +14928,7 @@ var TargetKindSchema = object({
|
|
|
14396
14928
|
* its raster path, which is the safe default for an unknown image.
|
|
14397
14929
|
*/
|
|
14398
14930
|
iconMediaType: string().optional(),
|
|
14399
|
-
configSchema: ConfigSchemaPassthrough$
|
|
14931
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
14400
14932
|
/**
|
|
14401
14933
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
14402
14934
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -14418,9 +14950,11 @@ var TargetKindSchema = object({
|
|
|
14418
14950
|
caps: TargetKindCapsSchema
|
|
14419
14951
|
});
|
|
14420
14952
|
/**
|
|
14421
|
-
* A
|
|
14422
|
-
*
|
|
14423
|
-
*
|
|
14953
|
+
* A target as a provider's own code sees it (the notification-center bridge, the
|
|
14954
|
+
* send path): the flat record body split into the name, the switch and the
|
|
14955
|
+
* kind's fields. `config` holds secrets; a reader that serves it to a client
|
|
14956
|
+
* REDACTS secret fields (a presence marker only) — never round-trip a stored
|
|
14957
|
+
* secret to the UI.
|
|
14424
14958
|
*/
|
|
14425
14959
|
var TargetSchema = object({
|
|
14426
14960
|
id: string(),
|
|
@@ -14463,13 +14997,13 @@ var ProbeTargetConfigResultSchema = object({
|
|
|
14463
14997
|
stateInstance: string().optional(),
|
|
14464
14998
|
error: string().optional()
|
|
14465
14999
|
});
|
|
14466
|
-
method(object({}), array(TargetKindSchema)), method(object({
|
|
15000
|
+
method(object({}), array(TargetKindSchema)), method(object({
|
|
14467
15001
|
kind: string(),
|
|
14468
15002
|
config: record(string(), unknown()).optional(),
|
|
14469
15003
|
/**
|
|
14470
15004
|
* When editing / duplicating, the blank password field never round-trips
|
|
14471
15005
|
* the stored token. Pass the source target id so the provider can restore
|
|
14472
|
-
* secrets server-side before dialling
|
|
15006
|
+
* secrets server-side before dialling.
|
|
14473
15007
|
*/
|
|
14474
15008
|
targetId: string().optional()
|
|
14475
15009
|
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
@@ -14492,27 +15026,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
14492
15026
|
}), TestResultSchema, {
|
|
14493
15027
|
kind: "mutation",
|
|
14494
15028
|
auth: "admin"
|
|
14495
|
-
}), method(object({
|
|
14496
|
-
target: TargetSchema,
|
|
14497
|
-
/**
|
|
14498
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
14499
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
14500
|
-
* password fields are restored from this source (D634 still applies if
|
|
14501
|
-
* destinations diverge without a re-typed secret).
|
|
14502
|
-
*/
|
|
14503
|
-
cloneSecretsFrom: string().optional()
|
|
14504
|
-
}), TargetSchema, {
|
|
14505
|
-
kind: "mutation",
|
|
14506
|
-
auth: "admin"
|
|
14507
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
14508
|
-
kind: "mutation",
|
|
14509
|
-
auth: "admin"
|
|
14510
|
-
}), method(object({
|
|
14511
|
-
targetId: string(),
|
|
14512
|
-
enabled: boolean()
|
|
14513
|
-
}), _void(), {
|
|
14514
|
-
kind: "mutation",
|
|
14515
|
-
auth: "admin"
|
|
14516
15029
|
});
|
|
14517
15030
|
looseObject({
|
|
14518
15031
|
name: string().min(1),
|
|
@@ -22780,7 +23293,6 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
22780
23293
|
"notification-output": system("notification delivery — reached through notifier devices and rules"),
|
|
22781
23294
|
"notification-rules": system("notification rules — authored on their own page"),
|
|
22782
23295
|
"oauth-integration": system(IDENTITY),
|
|
22783
|
-
"oidc-connections": system(IDENTITY),
|
|
22784
23296
|
"osd-manager": system(PIPELINE),
|
|
22785
23297
|
"pipeline-executor": system(PIPELINE),
|
|
22786
23298
|
"pipeline-orchestrator": system(PIPELINE),
|
|
@@ -42801,6 +43313,7 @@ var ExpressionBindingSourceSchema = union([
|
|
|
42801
43313
|
object({
|
|
42802
43314
|
kind: literal("global"),
|
|
42803
43315
|
sourceStableId: string(),
|
|
43316
|
+
addonId: string().optional(),
|
|
42804
43317
|
cap: string(),
|
|
42805
43318
|
fieldPath: string()
|
|
42806
43319
|
})
|
|
@@ -42867,385 +43380,6 @@ var SourceInfoSchema = object({
|
|
|
42867
43380
|
raw: record(string(), unknown()).optional()
|
|
42868
43381
|
});
|
|
42869
43382
|
/**
|
|
42870
|
-
* Frozen unit → spec table. `•` in the doc marks each dimension's canonical
|
|
42871
|
-
* unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
|
|
42872
|
-
* `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
|
|
42873
|
-
* decimals). K derives from `°C = K − 273.15`.
|
|
42874
|
-
*/
|
|
42875
|
-
var UNIT_TABLE = {
|
|
42876
|
-
"°C": {
|
|
42877
|
-
dimension: "temperature",
|
|
42878
|
-
factor: 1,
|
|
42879
|
-
offset: 0
|
|
42880
|
-
},
|
|
42881
|
-
"°F": {
|
|
42882
|
-
dimension: "temperature",
|
|
42883
|
-
factor: 5 / 9,
|
|
42884
|
-
offset: -160 / 9
|
|
42885
|
-
},
|
|
42886
|
-
K: {
|
|
42887
|
-
dimension: "temperature",
|
|
42888
|
-
factor: 1,
|
|
42889
|
-
offset: -273.15
|
|
42890
|
-
},
|
|
42891
|
-
hPa: {
|
|
42892
|
-
dimension: "pressure",
|
|
42893
|
-
factor: 1,
|
|
42894
|
-
offset: 0
|
|
42895
|
-
},
|
|
42896
|
-
kPa: {
|
|
42897
|
-
dimension: "pressure",
|
|
42898
|
-
factor: 10,
|
|
42899
|
-
offset: 0
|
|
42900
|
-
},
|
|
42901
|
-
Pa: {
|
|
42902
|
-
dimension: "pressure",
|
|
42903
|
-
factor: .01,
|
|
42904
|
-
offset: 0
|
|
42905
|
-
},
|
|
42906
|
-
mbar: {
|
|
42907
|
-
dimension: "pressure",
|
|
42908
|
-
factor: 1,
|
|
42909
|
-
offset: 0
|
|
42910
|
-
},
|
|
42911
|
-
bar: {
|
|
42912
|
-
dimension: "pressure",
|
|
42913
|
-
factor: 1e3,
|
|
42914
|
-
offset: 0
|
|
42915
|
-
},
|
|
42916
|
-
inHg: {
|
|
42917
|
-
dimension: "pressure",
|
|
42918
|
-
factor: 33.8639,
|
|
42919
|
-
offset: 0
|
|
42920
|
-
},
|
|
42921
|
-
mmHg: {
|
|
42922
|
-
dimension: "pressure",
|
|
42923
|
-
factor: 1.33322,
|
|
42924
|
-
offset: 0
|
|
42925
|
-
},
|
|
42926
|
-
psi: {
|
|
42927
|
-
dimension: "pressure",
|
|
42928
|
-
factor: 68.9476,
|
|
42929
|
-
offset: 0
|
|
42930
|
-
},
|
|
42931
|
-
"m/s": {
|
|
42932
|
-
dimension: "speed",
|
|
42933
|
-
factor: 1,
|
|
42934
|
-
offset: 0
|
|
42935
|
-
},
|
|
42936
|
-
"km/h": {
|
|
42937
|
-
dimension: "speed",
|
|
42938
|
-
factor: 1 / 3.6,
|
|
42939
|
-
offset: 0
|
|
42940
|
-
},
|
|
42941
|
-
mph: {
|
|
42942
|
-
dimension: "speed",
|
|
42943
|
-
factor: .44704,
|
|
42944
|
-
offset: 0
|
|
42945
|
-
},
|
|
42946
|
-
kn: {
|
|
42947
|
-
dimension: "speed",
|
|
42948
|
-
factor: .514444,
|
|
42949
|
-
offset: 0
|
|
42950
|
-
},
|
|
42951
|
-
"mm/h": {
|
|
42952
|
-
dimension: "precipitation-rate",
|
|
42953
|
-
factor: 1,
|
|
42954
|
-
offset: 0
|
|
42955
|
-
},
|
|
42956
|
-
"in/h": {
|
|
42957
|
-
dimension: "precipitation-rate",
|
|
42958
|
-
factor: 25.4,
|
|
42959
|
-
offset: 0
|
|
42960
|
-
},
|
|
42961
|
-
m: {
|
|
42962
|
-
dimension: "length",
|
|
42963
|
-
factor: 1,
|
|
42964
|
-
offset: 0
|
|
42965
|
-
},
|
|
42966
|
-
mm: {
|
|
42967
|
-
dimension: "length",
|
|
42968
|
-
factor: .001,
|
|
42969
|
-
offset: 0
|
|
42970
|
-
},
|
|
42971
|
-
cm: {
|
|
42972
|
-
dimension: "length",
|
|
42973
|
-
factor: .01,
|
|
42974
|
-
offset: 0
|
|
42975
|
-
},
|
|
42976
|
-
km: {
|
|
42977
|
-
dimension: "length",
|
|
42978
|
-
factor: 1e3,
|
|
42979
|
-
offset: 0
|
|
42980
|
-
},
|
|
42981
|
-
in: {
|
|
42982
|
-
dimension: "length",
|
|
42983
|
-
factor: .0254,
|
|
42984
|
-
offset: 0
|
|
42985
|
-
},
|
|
42986
|
-
ft: {
|
|
42987
|
-
dimension: "length",
|
|
42988
|
-
factor: .3048,
|
|
42989
|
-
offset: 0
|
|
42990
|
-
},
|
|
42991
|
-
mi: {
|
|
42992
|
-
dimension: "length",
|
|
42993
|
-
factor: 1609.344,
|
|
42994
|
-
offset: 0
|
|
42995
|
-
},
|
|
42996
|
-
"m²": {
|
|
42997
|
-
dimension: "area",
|
|
42998
|
-
factor: 1,
|
|
42999
|
-
offset: 0
|
|
43000
|
-
},
|
|
43001
|
-
lx: {
|
|
43002
|
-
dimension: "illuminance",
|
|
43003
|
-
factor: 1,
|
|
43004
|
-
offset: 0
|
|
43005
|
-
},
|
|
43006
|
-
"W/m²": {
|
|
43007
|
-
dimension: "irradiance",
|
|
43008
|
-
factor: 1,
|
|
43009
|
-
offset: 0
|
|
43010
|
-
},
|
|
43011
|
-
W: {
|
|
43012
|
-
dimension: "power",
|
|
43013
|
-
factor: 1,
|
|
43014
|
-
offset: 0
|
|
43015
|
-
},
|
|
43016
|
-
kW: {
|
|
43017
|
-
dimension: "power",
|
|
43018
|
-
factor: 1e3,
|
|
43019
|
-
offset: 0
|
|
43020
|
-
},
|
|
43021
|
-
VA: {
|
|
43022
|
-
dimension: "apparent-power",
|
|
43023
|
-
factor: 1,
|
|
43024
|
-
offset: 0
|
|
43025
|
-
},
|
|
43026
|
-
Wh: {
|
|
43027
|
-
dimension: "energy",
|
|
43028
|
-
factor: 1,
|
|
43029
|
-
offset: 0
|
|
43030
|
-
},
|
|
43031
|
-
kWh: {
|
|
43032
|
-
dimension: "energy",
|
|
43033
|
-
factor: 1e3,
|
|
43034
|
-
offset: 0
|
|
43035
|
-
},
|
|
43036
|
-
MWh: {
|
|
43037
|
-
dimension: "energy",
|
|
43038
|
-
factor: 1e6,
|
|
43039
|
-
offset: 0
|
|
43040
|
-
},
|
|
43041
|
-
V: {
|
|
43042
|
-
dimension: "voltage",
|
|
43043
|
-
factor: 1,
|
|
43044
|
-
offset: 0
|
|
43045
|
-
},
|
|
43046
|
-
mV: {
|
|
43047
|
-
dimension: "voltage",
|
|
43048
|
-
factor: .001,
|
|
43049
|
-
offset: 0
|
|
43050
|
-
},
|
|
43051
|
-
A: {
|
|
43052
|
-
dimension: "current",
|
|
43053
|
-
factor: 1,
|
|
43054
|
-
offset: 0
|
|
43055
|
-
},
|
|
43056
|
-
mA: {
|
|
43057
|
-
dimension: "current",
|
|
43058
|
-
factor: .001,
|
|
43059
|
-
offset: 0
|
|
43060
|
-
},
|
|
43061
|
-
ppm: {
|
|
43062
|
-
dimension: "concentration-volume",
|
|
43063
|
-
factor: 1,
|
|
43064
|
-
offset: 0
|
|
43065
|
-
},
|
|
43066
|
-
ppb: {
|
|
43067
|
-
dimension: "concentration-volume",
|
|
43068
|
-
factor: .001,
|
|
43069
|
-
offset: 0
|
|
43070
|
-
},
|
|
43071
|
-
"µg/m³": {
|
|
43072
|
-
dimension: "concentration-mass",
|
|
43073
|
-
factor: 1,
|
|
43074
|
-
offset: 0
|
|
43075
|
-
},
|
|
43076
|
-
"mg/m³": {
|
|
43077
|
-
dimension: "concentration-mass",
|
|
43078
|
-
factor: 1e3,
|
|
43079
|
-
offset: 0
|
|
43080
|
-
},
|
|
43081
|
-
kg: {
|
|
43082
|
-
dimension: "mass",
|
|
43083
|
-
factor: 1,
|
|
43084
|
-
offset: 0
|
|
43085
|
-
},
|
|
43086
|
-
g: {
|
|
43087
|
-
dimension: "mass",
|
|
43088
|
-
factor: .001,
|
|
43089
|
-
offset: 0
|
|
43090
|
-
},
|
|
43091
|
-
lb: {
|
|
43092
|
-
dimension: "mass",
|
|
43093
|
-
factor: .453592,
|
|
43094
|
-
offset: 0
|
|
43095
|
-
},
|
|
43096
|
-
oz: {
|
|
43097
|
-
dimension: "mass",
|
|
43098
|
-
factor: .0283495,
|
|
43099
|
-
offset: 0
|
|
43100
|
-
},
|
|
43101
|
-
"%": {
|
|
43102
|
-
dimension: "percentage",
|
|
43103
|
-
factor: 1,
|
|
43104
|
-
offset: 0
|
|
43105
|
-
},
|
|
43106
|
-
L: {
|
|
43107
|
-
dimension: "volume",
|
|
43108
|
-
factor: 1,
|
|
43109
|
-
offset: 0
|
|
43110
|
-
},
|
|
43111
|
-
mL: {
|
|
43112
|
-
dimension: "volume",
|
|
43113
|
-
factor: .001,
|
|
43114
|
-
offset: 0
|
|
43115
|
-
},
|
|
43116
|
-
gal: {
|
|
43117
|
-
dimension: "volume",
|
|
43118
|
-
factor: 3.785411784,
|
|
43119
|
-
offset: 0
|
|
43120
|
-
},
|
|
43121
|
-
s: {
|
|
43122
|
-
dimension: "duration",
|
|
43123
|
-
factor: 1,
|
|
43124
|
-
offset: 0
|
|
43125
|
-
},
|
|
43126
|
-
ms: {
|
|
43127
|
-
dimension: "duration",
|
|
43128
|
-
factor: .001,
|
|
43129
|
-
offset: 0
|
|
43130
|
-
},
|
|
43131
|
-
min: {
|
|
43132
|
-
dimension: "duration",
|
|
43133
|
-
factor: 60,
|
|
43134
|
-
offset: 0
|
|
43135
|
-
},
|
|
43136
|
-
h: {
|
|
43137
|
-
dimension: "duration",
|
|
43138
|
-
factor: 3600,
|
|
43139
|
-
offset: 0
|
|
43140
|
-
},
|
|
43141
|
-
d: {
|
|
43142
|
-
dimension: "duration",
|
|
43143
|
-
factor: 86400,
|
|
43144
|
-
offset: 0
|
|
43145
|
-
},
|
|
43146
|
-
B: {
|
|
43147
|
-
dimension: "information",
|
|
43148
|
-
factor: 1,
|
|
43149
|
-
offset: 0
|
|
43150
|
-
},
|
|
43151
|
-
KiB: {
|
|
43152
|
-
dimension: "information",
|
|
43153
|
-
factor: 1024,
|
|
43154
|
-
offset: 0
|
|
43155
|
-
},
|
|
43156
|
-
MiB: {
|
|
43157
|
-
dimension: "information",
|
|
43158
|
-
factor: 1024 ** 2,
|
|
43159
|
-
offset: 0
|
|
43160
|
-
},
|
|
43161
|
-
GiB: {
|
|
43162
|
-
dimension: "information",
|
|
43163
|
-
factor: 1024 ** 3,
|
|
43164
|
-
offset: 0
|
|
43165
|
-
},
|
|
43166
|
-
TiB: {
|
|
43167
|
-
dimension: "information",
|
|
43168
|
-
factor: 1024 ** 4,
|
|
43169
|
-
offset: 0
|
|
43170
|
-
},
|
|
43171
|
-
kB: {
|
|
43172
|
-
dimension: "information",
|
|
43173
|
-
factor: 1e3,
|
|
43174
|
-
offset: 0
|
|
43175
|
-
},
|
|
43176
|
-
MB: {
|
|
43177
|
-
dimension: "information",
|
|
43178
|
-
factor: 1e6,
|
|
43179
|
-
offset: 0
|
|
43180
|
-
},
|
|
43181
|
-
GB: {
|
|
43182
|
-
dimension: "information",
|
|
43183
|
-
factor: 1e9,
|
|
43184
|
-
offset: 0
|
|
43185
|
-
},
|
|
43186
|
-
"°": {
|
|
43187
|
-
dimension: "angle",
|
|
43188
|
-
factor: 1,
|
|
43189
|
-
offset: 0
|
|
43190
|
-
},
|
|
43191
|
-
dBFS: {
|
|
43192
|
-
dimension: "sound-level",
|
|
43193
|
-
factor: 1,
|
|
43194
|
-
offset: 0
|
|
43195
|
-
},
|
|
43196
|
-
dBm: {
|
|
43197
|
-
dimension: "signal-level",
|
|
43198
|
-
factor: 1,
|
|
43199
|
-
offset: 0
|
|
43200
|
-
},
|
|
43201
|
-
mired: {
|
|
43202
|
-
dimension: "colour-temperature",
|
|
43203
|
-
factor: 1,
|
|
43204
|
-
offset: 0
|
|
43205
|
-
},
|
|
43206
|
-
kbps: {
|
|
43207
|
-
dimension: "bitrate",
|
|
43208
|
-
factor: 1,
|
|
43209
|
-
offset: 0
|
|
43210
|
-
},
|
|
43211
|
-
Mbps: {
|
|
43212
|
-
dimension: "bitrate",
|
|
43213
|
-
factor: 1e3,
|
|
43214
|
-
offset: 0
|
|
43215
|
-
},
|
|
43216
|
-
fps: {
|
|
43217
|
-
dimension: "rate",
|
|
43218
|
-
factor: 1,
|
|
43219
|
-
offset: 0
|
|
43220
|
-
}
|
|
43221
|
-
};
|
|
43222
|
-
/** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
|
|
43223
|
-
function specFor(unit) {
|
|
43224
|
-
return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
|
|
43225
|
-
}
|
|
43226
|
-
/** Value in `spec`'s unit → its dimension's canonical unit. */
|
|
43227
|
-
function toCanonical(value, spec) {
|
|
43228
|
-
return value * spec.factor + spec.offset;
|
|
43229
|
-
}
|
|
43230
|
-
/** Canonical-unit value → the unit described by `spec`. */
|
|
43231
|
-
function fromCanonical(canonical, spec) {
|
|
43232
|
-
return (canonical - spec.offset) / spec.factor;
|
|
43233
|
-
}
|
|
43234
|
-
/**
|
|
43235
|
-
* Graceful variant for the render path: returns the converted value, or
|
|
43236
|
-
* `undefined` when the conversion is refused (unknown unit / cross-dimension).
|
|
43237
|
-
* Never throws — a render caller falls back to the raw value + source unit.
|
|
43238
|
-
*/
|
|
43239
|
-
function tryConvertUnit(value, from, to) {
|
|
43240
|
-
if (from === to) return value;
|
|
43241
|
-
const fromSpec = specFor(from);
|
|
43242
|
-
if (!fromSpec) return void 0;
|
|
43243
|
-
const toSpec = specFor(to);
|
|
43244
|
-
if (!toSpec) return void 0;
|
|
43245
|
-
if (fromSpec.dimension !== toSpec.dimension) return void 0;
|
|
43246
|
-
return fromCanonical(toCanonical(value, fromSpec), toSpec);
|
|
43247
|
-
}
|
|
43248
|
-
/**
|
|
43249
43383
|
* A number a person types for a physical quantity — the input side of the
|
|
43250
43384
|
* value + unit contract (D972).
|
|
43251
43385
|
*
|
|
@@ -44941,15 +45075,28 @@ var SettingsSchemaWithValuesSchema = object({
|
|
|
44941
45075
|
});
|
|
44942
45076
|
/** Patch object — keys are field names, values are the new field values. */
|
|
44943
45077
|
var SettingsPatchSchema = record(string(), unknown());
|
|
45078
|
+
/** The scope of a scoped-settings call (D986): where the capability declared the values. */
|
|
45079
|
+
var ScopedSettingsScopeSchema = _enum(SCOPED_SETTINGS_SCOPES);
|
|
45080
|
+
/** One scoped schema an addon serves: its capability, scope and section ids. */
|
|
45081
|
+
var ScopedSettingsDeclarationSchema = object({
|
|
45082
|
+
cap: string(),
|
|
45083
|
+
scope: ScopedSettingsScopeSchema,
|
|
45084
|
+
sectionIds: array(string())
|
|
45085
|
+
});
|
|
44944
45086
|
/** Standard success response for update operations. */
|
|
44945
45087
|
var SettingsUpdateResultSchema = object({ success: literal(true) });
|
|
44946
45088
|
method(object({
|
|
44947
45089
|
addonId: string(),
|
|
44948
|
-
|
|
44949
|
-
|
|
44950
|
-
|
|
45090
|
+
cap: string(),
|
|
45091
|
+
scope: ScopedSettingsScopeSchema,
|
|
45092
|
+
nodeId: string().optional()
|
|
44951
45093
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
44952
45094
|
addonId: string(),
|
|
45095
|
+
nodeId: string().optional()
|
|
45096
|
+
}), array(ScopedSettingsDeclarationSchema).nullable()), method(object({
|
|
45097
|
+
addonId: string(),
|
|
45098
|
+
cap: string(),
|
|
45099
|
+
scope: ScopedSettingsScopeSchema,
|
|
44953
45100
|
nodeId: string().optional(),
|
|
44954
45101
|
patch: SettingsPatchSchema
|
|
44955
45102
|
}), SettingsUpdateResultSchema, {
|
|
@@ -44957,8 +45104,10 @@ method(object({
|
|
|
44957
45104
|
auth: "admin"
|
|
44958
45105
|
}), method(object({
|
|
44959
45106
|
addonId: string(),
|
|
45107
|
+
cap: string(),
|
|
45108
|
+
scope: ScopedSettingsScopeSchema,
|
|
44960
45109
|
nodeId: string().optional()
|
|
44961
|
-
}),
|
|
45110
|
+
}), record(string(), unknown()).nullable()), method(object({
|
|
44962
45111
|
addonId: string(),
|
|
44963
45112
|
nodeId: string().optional(),
|
|
44964
45113
|
instance: IntegrationInstanceRefSchema
|
|
@@ -46298,7 +46447,7 @@ var BackupDestinationInfoSchema = object({
|
|
|
46298
46447
|
id: string(),
|
|
46299
46448
|
/**
|
|
46300
46449
|
* Manifest id of the owning addon. The Settings button on the
|
|
46301
|
-
* destination card opens that addon's
|
|
46450
|
+
* destination card opens that addon's scoped settings panel.
|
|
46302
46451
|
*/
|
|
46303
46452
|
addonId: string(),
|
|
46304
46453
|
/** Operator-toggled enable flag from the destination-policy table. */
|
|
@@ -48907,7 +49056,7 @@ DeviceType.MediaPlayer, method(deviceOnly, _void(), {
|
|
|
48907
49056
|
kind: "mutation",
|
|
48908
49057
|
auth: "protected"
|
|
48909
49058
|
});
|
|
48910
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater;
|
|
49059
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.MediaPlayer;
|
|
48911
49060
|
var NUMBER = {
|
|
48912
49061
|
kind: "fixed",
|
|
48913
49062
|
result: ["number"]
|
|
@@ -50778,6 +50927,7 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
|
|
|
50778
50927
|
kind: "mutation",
|
|
50779
50928
|
auth: "admin"
|
|
50780
50929
|
});
|
|
50930
|
+
var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
50781
50931
|
/**
|
|
50782
50932
|
* A SITE ZONE — a named part of the property that several locations belong to.
|
|
50783
50933
|
*
|
|
@@ -51171,6 +51321,12 @@ var DeviceInfoSchema = object({
|
|
|
51171
51321
|
addonId: string(),
|
|
51172
51322
|
type: _enum(DeviceType),
|
|
51173
51323
|
name: string(),
|
|
51324
|
+
/** The name a value template uses for this device (`{{ stufa_veranda.temperature }}`),
|
|
51325
|
+
* entity_id style. Generated once from the name at first registration, unique
|
|
51326
|
+
* cluster-wide, editable through `setAlias`, NOT changed by a rename (D1021).
|
|
51327
|
+
* Optional: a record produced by an older build omits it until the boot
|
|
51328
|
+
* backfill has run. */
|
|
51329
|
+
alias: DeviceAliasSchema.optional(),
|
|
51174
51330
|
/** Operator-organisational location label. `null` when unset. */
|
|
51175
51331
|
location: string().nullable(),
|
|
51176
51332
|
/** Soft-disabled flag. */
|
|
@@ -51490,6 +51646,25 @@ method(object({
|
|
|
51490
51646
|
}), _void(), {
|
|
51491
51647
|
kind: "mutation",
|
|
51492
51648
|
auth: "admin"
|
|
51649
|
+
}), method(object({
|
|
51650
|
+
deviceId: number(),
|
|
51651
|
+
alias: string()
|
|
51652
|
+
}), discriminatedUnion("ok", [object({
|
|
51653
|
+
ok: literal(true),
|
|
51654
|
+
alias: DeviceAliasSchema
|
|
51655
|
+
}), object({
|
|
51656
|
+
ok: literal(false),
|
|
51657
|
+
reason: _enum([
|
|
51658
|
+
"empty",
|
|
51659
|
+
"too-long",
|
|
51660
|
+
"grammar",
|
|
51661
|
+
"reserved",
|
|
51662
|
+
"taken"
|
|
51663
|
+
]),
|
|
51664
|
+
takenBy: number().optional()
|
|
51665
|
+
})]), {
|
|
51666
|
+
kind: "mutation",
|
|
51667
|
+
auth: "admin"
|
|
51493
51668
|
}), method(object({
|
|
51494
51669
|
deviceId: number(),
|
|
51495
51670
|
location: string().nullable()
|
|
@@ -54515,7 +54690,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54515
54690
|
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
54516
54691
|
* clears it.
|
|
54517
54692
|
*/
|
|
54518
|
-
var ConfigSchemaPassthrough$
|
|
54693
|
+
var ConfigSchemaPassthrough$2 = custom();
|
|
54519
54694
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54520
54695
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54521
54696
|
/**
|
|
@@ -54561,7 +54736,16 @@ var IntegrationInstanceKindSchema = object({
|
|
|
54561
54736
|
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54562
54737
|
connectPath: string().min(1).optional(),
|
|
54563
54738
|
/** Relative hub path to disconnect that account. */
|
|
54564
|
-
disconnectPath: string().min(1).optional()
|
|
54739
|
+
disconnectPath: string().min(1).optional(),
|
|
54740
|
+
/**
|
|
54741
|
+
* Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
|
|
54742
|
+
* GENERIC widget id the client maps to a component; a client that knows none of them shows none.
|
|
54743
|
+
* `connection` widgets write connection values, `model` widgets own the model choice.
|
|
54744
|
+
*/
|
|
54745
|
+
widgets: array(object({
|
|
54746
|
+
id: string().min(1),
|
|
54747
|
+
placement: _enum(["connection", "model"])
|
|
54748
|
+
})).optional()
|
|
54565
54749
|
});
|
|
54566
54750
|
/**
|
|
54567
54751
|
* How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
|
|
@@ -54602,10 +54786,11 @@ var IntegrationInstanceDescriptionSchema = object({
|
|
|
54602
54786
|
/** True when `testInstance` really probes the remote. */
|
|
54603
54787
|
canTest: boolean(),
|
|
54604
54788
|
/**
|
|
54605
|
-
* The kinds an instance can be of
|
|
54606
|
-
* Add flow asks which first and sends it as
|
|
54789
|
+
* The kinds an instance can be of (D1013 rule 1), each naming its `functionCap`. One entry:
|
|
54790
|
+
* nothing to choose; more than one: the Add flow asks which first and sends it as
|
|
54791
|
+
* `createInstance.kind`. Never empty: every instance belongs to exactly one kind.
|
|
54607
54792
|
*/
|
|
54608
|
-
kinds: array(IntegrationInstanceKindSchema).
|
|
54793
|
+
kinds: array(IntegrationInstanceKindSchema).min(1),
|
|
54609
54794
|
/**
|
|
54610
54795
|
* The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
|
|
54611
54796
|
* devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
|
|
@@ -54641,8 +54826,8 @@ var IntegrationInstanceSchema = object({
|
|
|
54641
54826
|
* the body is the one store, `setInstanceEnabled` the one writer (epic #440, step 3).
|
|
54642
54827
|
*/
|
|
54643
54828
|
enabled: boolean().optional(),
|
|
54644
|
-
/** The kind of this instance
|
|
54645
|
-
kind: string().
|
|
54829
|
+
/** The kind of this instance: exactly one of `describe().kinds[].id` (D1013 rule 2). */
|
|
54830
|
+
kind: string().min(1),
|
|
54646
54831
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
54647
54832
|
formValues: record(string(), unknown()),
|
|
54648
54833
|
status: IntegrationInstanceStatusSchema.optional(),
|
|
@@ -54666,7 +54851,7 @@ var integrationInstancesCapability = {
|
|
|
54666
54851
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
54667
54852
|
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
54668
54853
|
kind: string().min(1).optional()
|
|
54669
|
-
}), ConfigSchemaPassthrough$
|
|
54854
|
+
}), ConfigSchemaPassthrough$2, { auth: "admin" }),
|
|
54670
54855
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
54671
54856
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
54672
54857
|
/**
|
|
@@ -55617,12 +55802,12 @@ var LlmModelInfoSchema = object({
|
|
|
55617
55802
|
});
|
|
55618
55803
|
/**
|
|
55619
55804
|
* The provider-specific part of a profile, validated by the provider that owns
|
|
55620
|
-
* the kind (
|
|
55805
|
+
* the kind (saving an instance refuses what it does not accept). A field its
|
|
55621
55806
|
* `connectionSchema` declares `type: 'password'` is a secret: it is held in the
|
|
55622
55807
|
* keychain, never in the row, and reads back as the redaction marker.
|
|
55623
55808
|
*/
|
|
55624
55809
|
var LlmProviderConfigSchema = record(string(), unknown());
|
|
55625
|
-
|
|
55810
|
+
object({
|
|
55626
55811
|
id: string(),
|
|
55627
55812
|
name: string(),
|
|
55628
55813
|
/** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
|
|
@@ -55716,8 +55901,8 @@ var LlmProviderWidgetSchema = object({
|
|
|
55716
55901
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
55717
55902
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
55718
55903
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
55719
|
-
var ConfigSchemaPassthrough$
|
|
55720
|
-
|
|
55904
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
55905
|
+
object({
|
|
55721
55906
|
kind: LlmProfileKindSchema,
|
|
55722
55907
|
label: string(),
|
|
55723
55908
|
/** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
|
|
@@ -55725,9 +55910,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
55725
55910
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
55726
55911
|
addonId: string(),
|
|
55727
55912
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
55728
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
55913
|
+
connectionSchema: ConfigSchemaPassthrough$1,
|
|
55729
55914
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
55730
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
55915
|
+
advancedSchema: ConfigSchemaPassthrough$1.optional(),
|
|
55731
55916
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
55732
55917
|
features: LlmModelFeaturesSchema,
|
|
55733
55918
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -55856,20 +56041,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
55856
56041
|
}), method(object({
|
|
55857
56042
|
addonId: string().optional(),
|
|
55858
56043
|
requestId: string()
|
|
55859
|
-
}), _void(), { kind: "mutation" }), method(
|
|
55860
|
-
kind: "mutation",
|
|
55861
|
-
auth: "admin"
|
|
55862
|
-
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
55863
|
-
kind: "mutation",
|
|
55864
|
-
auth: "admin"
|
|
55865
|
-
}), method(ProfileRefInputSchema, _void(), {
|
|
55866
|
-
kind: "mutation",
|
|
55867
|
-
auth: "admin"
|
|
55868
|
-
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
55869
|
-
kind: "mutation",
|
|
55870
|
-
auth: "admin",
|
|
55871
|
-
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
55872
|
-
}), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
56044
|
+
}), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55873
56045
|
kind: "mutation",
|
|
55874
56046
|
auth: "admin"
|
|
55875
56047
|
}), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
@@ -56864,20 +57036,7 @@ method(_void(), MeshStatusSchema), method(object({
|
|
|
56864
57036
|
/** Optional hostname override the host should advertise once joined. */
|
|
56865
57037
|
hostname: string().optional() }), object({
|
|
56866
57038
|
/** Authentication URL the operator should open in a browser. */
|
|
56867
|
-
loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }))
|
|
56868
|
-
/** Optional auth key — when provided, probes the key validity
|
|
56869
|
-
* against the provider's API. Omit when already joined to
|
|
56870
|
-
* just ping the daemon. */
|
|
56871
|
-
authKey: string().optional() }), object({
|
|
56872
|
-
ok: boolean(),
|
|
56873
|
-
/** Provider-side identifier resolved by the probe (tailnet
|
|
56874
|
-
* name for Tailscale, network id for ZeroTier, etc.). */
|
|
56875
|
-
tenant: string().optional(),
|
|
56876
|
-
/** Daemon binary version, when reachable. */
|
|
56877
|
-
daemonVersion: string().optional(),
|
|
56878
|
-
/** Human-readable error when `ok: false`. */
|
|
56879
|
-
error: string().optional()
|
|
56880
|
-
}), { kind: "mutation" });
|
|
57039
|
+
loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }));
|
|
56881
57040
|
object({
|
|
56882
57041
|
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
56883
57042
|
lastFetchedAt: number()
|
|
@@ -64238,6 +64397,11 @@ var NcScheduleSchema = object({
|
|
|
64238
64397
|
* as a STATE SUMMARY at the end of each window of `schedule`.
|
|
64239
64398
|
*/
|
|
64240
64399
|
var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
|
|
64400
|
+
_enum([
|
|
64401
|
+
"activation",
|
|
64402
|
+
"deactivation",
|
|
64403
|
+
"schedule"
|
|
64404
|
+
]);
|
|
64241
64405
|
/**
|
|
64242
64406
|
* How a summary window is SENT (D698): `rule` (absent) = ONE notification with
|
|
64243
64407
|
* every section; `section` = one notification per section with something to
|
|
@@ -64254,11 +64418,26 @@ var NcStateSummarySplitSchema = _enum(["rule", "section"]);
|
|
|
64254
64418
|
* midnight crossing); the send time is each window's END. Refused on the
|
|
64255
64419
|
* `alarm` family, which stays instant (`systemEventDeliveryProblems`).
|
|
64256
64420
|
*/
|
|
64257
|
-
var NcSystemEventDeliverySchema = discriminatedUnion("mode", [
|
|
64258
|
-
mode: literal("
|
|
64259
|
-
|
|
64260
|
-
|
|
64261
|
-
|
|
64421
|
+
var NcSystemEventDeliverySchema = discriminatedUnion("mode", [
|
|
64422
|
+
object({ mode: literal("instant") }),
|
|
64423
|
+
object({
|
|
64424
|
+
mode: literal("summary"),
|
|
64425
|
+
schedule: NcScheduleSchema,
|
|
64426
|
+
split: NcStateSummarySplitSchema.optional()
|
|
64427
|
+
}),
|
|
64428
|
+
object({
|
|
64429
|
+
mode: literal("modes"),
|
|
64430
|
+
/** The bad edge, and every kind that has a single edge. */
|
|
64431
|
+
onActivation: boolean(),
|
|
64432
|
+
/** The recovery edges of the classes of the selected kinds (derived, never stored per kind). */
|
|
64433
|
+
onDeactivation: boolean(),
|
|
64434
|
+
/** A state summary at the end of each window; absent = no summary. */
|
|
64435
|
+
onSchedule: object({
|
|
64436
|
+
schedule: NcScheduleSchema,
|
|
64437
|
+
grouping: NcStateSummarySplitSchema
|
|
64438
|
+
}).optional()
|
|
64439
|
+
})
|
|
64440
|
+
]);
|
|
64262
64441
|
/** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
|
|
64263
64442
|
var NcPlateMatcherSchema = object({
|
|
64264
64443
|
/**
|
|
@@ -66535,61 +66714,6 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
66535
66714
|
});
|
|
66536
66715
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
66537
66716
|
/**
|
|
66538
|
-
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
66539
|
-
*
|
|
66540
|
-
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
66541
|
-
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
66542
|
-
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
66543
|
-
* immutable after create.
|
|
66544
|
-
*/
|
|
66545
|
-
var ConfigSchemaPassthrough$1 = unknown();
|
|
66546
|
-
var OidcConnectionSchema = object({
|
|
66547
|
-
id: string().min(1),
|
|
66548
|
-
displayName: string(),
|
|
66549
|
-
icon: string(),
|
|
66550
|
-
issuerUrl: string(),
|
|
66551
|
-
clientId: string(),
|
|
66552
|
-
/** Client id + secret present — never the secret value. */
|
|
66553
|
-
configured: boolean(),
|
|
66554
|
-
/**
|
|
66555
|
-
* Values for the connection form schema. `clientSecret` is a keychain
|
|
66556
|
-
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
66557
|
-
*/
|
|
66558
|
-
formValues: record(string(), unknown()).optional()
|
|
66559
|
-
});
|
|
66560
|
-
/**
|
|
66561
|
-
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
66562
|
-
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
66563
|
-
*/
|
|
66564
|
-
var OidcConnectionUpdateInputSchema = object({
|
|
66565
|
-
id: string().min(1),
|
|
66566
|
-
displayName: string().min(1),
|
|
66567
|
-
icon: string().optional(),
|
|
66568
|
-
issuerUrl: string().optional(),
|
|
66569
|
-
clientId: string().optional(),
|
|
66570
|
-
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
66571
|
-
redirectUri: string().optional(),
|
|
66572
|
-
scopes: string().optional(),
|
|
66573
|
-
usernameClaim: _enum([
|
|
66574
|
-
"preferred_username",
|
|
66575
|
-
"email",
|
|
66576
|
-
"sub"
|
|
66577
|
-
]).optional()
|
|
66578
|
-
});
|
|
66579
|
-
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
66580
|
-
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
66581
|
-
displayName: string().min(1)
|
|
66582
|
-
}), object({ id: string() }), {
|
|
66583
|
-
kind: "mutation",
|
|
66584
|
-
auth: "admin"
|
|
66585
|
-
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
66586
|
-
kind: "mutation",
|
|
66587
|
-
auth: "admin"
|
|
66588
|
-
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
66589
|
-
kind: "mutation",
|
|
66590
|
-
auth: "admin"
|
|
66591
|
-
});
|
|
66592
|
-
/**
|
|
66593
66717
|
* Generic on-screen-display (video overlay) cap. Each camera exposes
|
|
66594
66718
|
* a firmware-defined list of overlays (channel name, timestamp,
|
|
66595
66719
|
* watermark, logo, …); the cap models them as a homogeneous array of
|
|
@@ -71640,6 +71764,7 @@ var RemoteLayoutComponentSchema = object({
|
|
|
71640
71764
|
});
|
|
71641
71765
|
});
|
|
71642
71766
|
method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
|
|
71767
|
+
DeviceType.MediaPlayer;
|
|
71643
71768
|
var RunningStateSourceSchema = _enum(["native", "assigned"]);
|
|
71644
71769
|
object({
|
|
71645
71770
|
/** null = unknown — never coerced to false (D393). */
|
|
@@ -72775,21 +72900,6 @@ method(object({
|
|
|
72775
72900
|
*/
|
|
72776
72901
|
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
72777
72902
|
}), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
|
|
72778
|
-
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
72779
|
-
providerId: string().min(1),
|
|
72780
|
-
displayName: string().min(1),
|
|
72781
|
-
configSchema: unknown(),
|
|
72782
|
-
nodeLocal: boolean(),
|
|
72783
|
-
shouldSaveDiskSpace: literal(true),
|
|
72784
|
-
minFreePercent: number().min(0).max(100)
|
|
72785
|
-
}), object({
|
|
72786
|
-
providerId: string().min(1),
|
|
72787
|
-
displayName: string().min(1),
|
|
72788
|
-
configSchema: unknown(),
|
|
72789
|
-
nodeLocal: boolean(),
|
|
72790
|
-
shouldSaveDiskSpace: literal(false),
|
|
72791
|
-
minFreePercent: literal(null)
|
|
72792
|
-
})]);
|
|
72793
72903
|
method(object({
|
|
72794
72904
|
location: StorageLocationRefSchema,
|
|
72795
72905
|
relativePath: string()
|
|
@@ -72851,42 +72961,7 @@ method(object({
|
|
|
72851
72961
|
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
|
|
72852
72962
|
kind: "mutation",
|
|
72853
72963
|
auth: "admin"
|
|
72854
|
-
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(
|
|
72855
|
-
createdAt: true,
|
|
72856
|
-
updatedAt: true
|
|
72857
|
-
}), StorageLocationSchema, {
|
|
72858
|
-
kind: "mutation",
|
|
72859
|
-
auth: "admin"
|
|
72860
|
-
}), method(object({
|
|
72861
|
-
id: string(),
|
|
72862
|
-
force: boolean().optional()
|
|
72863
|
-
}), _void(), {
|
|
72864
|
-
kind: "mutation",
|
|
72865
|
-
auth: "admin"
|
|
72866
|
-
}), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
|
|
72867
|
-
ok: boolean(),
|
|
72868
|
-
error: string().optional()
|
|
72869
|
-
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
72870
|
-
providerId: string(),
|
|
72871
|
-
/**
|
|
72872
|
-
* The location this config is an UNSAVED edit of, when there is one.
|
|
72873
|
-
*
|
|
72874
|
-
* `listLocations` replaces every declared secret with the redaction
|
|
72875
|
-
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
72876
|
-
* credential the operator did not retype — and posting that here
|
|
72877
|
-
* without a way to resolve it makes the provider try to authenticate
|
|
72878
|
-
* as `__camstack_redacted__` and report the operator's own working
|
|
72879
|
-
* password as wrong. Given this id, the orchestrator restores each
|
|
72880
|
-
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
72881
|
-
* before dispatching. Omitted by the "Add location" wizard, where
|
|
72882
|
-
* every value was typed just now and nothing is stored yet.
|
|
72883
|
-
*/
|
|
72884
|
-
locationId: string().optional(),
|
|
72885
|
-
config: record(string(), unknown())
|
|
72886
|
-
}), object({
|
|
72887
|
-
ok: boolean(),
|
|
72888
|
-
error: string().optional()
|
|
72889
|
-
}), { auth: "admin" });
|
|
72964
|
+
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
|
|
72890
72965
|
/** One table of the settings database. */
|
|
72891
72966
|
var StorageTableSchema = object({
|
|
72892
72967
|
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
@@ -74117,7 +74192,7 @@ var TerminalInstanceInfoSchema = object({
|
|
|
74117
74192
|
/**
|
|
74118
74193
|
* False while the node the instance is bound to is not in the cluster
|
|
74119
74194
|
* topology (forgotten, renamed, re-paired). The instance is kept; the
|
|
74120
|
-
* operator re-points it
|
|
74195
|
+
* operator re-points it on the Terminal page.
|
|
74121
74196
|
*/
|
|
74122
74197
|
nodeInCluster: boolean(),
|
|
74123
74198
|
profileId: string(),
|
|
@@ -74155,41 +74230,7 @@ var TerminalOutputBatchSchema = object({
|
|
|
74155
74230
|
snapshot: string().optional(),
|
|
74156
74231
|
events: array(TerminalOutputEventSchema).readonly()
|
|
74157
74232
|
});
|
|
74158
|
-
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(
|
|
74159
|
-
targetNodeId: string().min(1),
|
|
74160
|
-
profileId: string().min(1),
|
|
74161
|
-
name: string().trim().min(1).max(160).optional(),
|
|
74162
|
-
executable: string().max(1024).optional(),
|
|
74163
|
-
args: array(string().max(2048)).max(64).optional(),
|
|
74164
|
-
cwd: string().max(1024).optional(),
|
|
74165
|
-
environment: array(string().max(4096)).max(64).optional(),
|
|
74166
|
-
profileSettings: ProfileSettingsBagSchema.optional()
|
|
74167
|
-
}), TerminalInstanceInfoSchema, {
|
|
74168
|
-
kind: "mutation",
|
|
74169
|
-
auth: "admin"
|
|
74170
|
-
}), method(object({
|
|
74171
|
-
instanceId: string().min(1),
|
|
74172
|
-
/** Re-point the instance to another node; the profile must exist there. */
|
|
74173
|
-
targetNodeId: string().min(1).max(256).optional(),
|
|
74174
|
-
name: string().trim().min(1).max(160).optional(),
|
|
74175
|
-
executable: string().max(1024).optional(),
|
|
74176
|
-
args: array(string().max(2048)).max(64).optional(),
|
|
74177
|
-
cwd: string().max(1024).optional(),
|
|
74178
|
-
environment: array(string().max(4096)).max(64).optional(),
|
|
74179
|
-
profileSettings: ProfileSettingsBagSchema.optional()
|
|
74180
|
-
}), TerminalInstanceInfoSchema, {
|
|
74181
|
-
kind: "mutation",
|
|
74182
|
-
auth: "admin"
|
|
74183
|
-
}), method(object({ instanceId: string().min(1) }), _void(), {
|
|
74184
|
-
kind: "mutation",
|
|
74185
|
-
auth: "admin"
|
|
74186
|
-
}), method(object({
|
|
74187
|
-
instanceId: string().min(1),
|
|
74188
|
-
enabled: boolean()
|
|
74189
|
-
}), TerminalInstanceInfoSchema, {
|
|
74190
|
-
kind: "mutation",
|
|
74191
|
-
auth: "admin"
|
|
74192
|
-
}), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
|
|
74233
|
+
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalLegacyCameraSchema).readonly(), { auth: "admin" }), method(object({
|
|
74193
74234
|
stableId: string().min(1),
|
|
74194
74235
|
name: string().trim().min(1).max(160).optional()
|
|
74195
74236
|
}), TerminalInstanceInfoSchema, {
|
|
@@ -74258,6 +74299,11 @@ var TimedRunUnitSchema = _enum(["s", "min"]);
|
|
|
74258
74299
|
_enum(["switch", "pet-fountain"]);
|
|
74259
74300
|
DeviceType.Heater, DeviceType.PetFountain;
|
|
74260
74301
|
DeviceType.Heater, DeviceType.PetFountain;
|
|
74302
|
+
/** One duration, as the one-tap run asks for it. */
|
|
74303
|
+
var TimedRunQuickRunSchema = object({
|
|
74304
|
+
duration: number().positive().finite(),
|
|
74305
|
+
unit: TimedRunUnitSchema
|
|
74306
|
+
});
|
|
74261
74307
|
/** Why a request was not carried out. Typed, so a caller can say it in words. */
|
|
74262
74308
|
var TimedRunRefusalSchema = _enum([
|
|
74263
74309
|
"unsupported-device",
|
|
@@ -74278,9 +74324,30 @@ object({
|
|
|
74278
74324
|
activeUntil: number().nullable(),
|
|
74279
74325
|
/** Who asked for the run (a user id or an addon id); `null` when nothing is armed or it did not say. */
|
|
74280
74326
|
requestedBy: string().nullable(),
|
|
74327
|
+
/**
|
|
74328
|
+
* The one-tap run of THIS device: what a "Run" button on any surface starts. `null` when
|
|
74329
|
+
* the device is gone or its type has no timed run; a status from before this field
|
|
74330
|
+
* existed has none, and a surface falls back to the type's default.
|
|
74331
|
+
*/
|
|
74332
|
+
quickRun: TimedRunQuickRunSchema.nullable(),
|
|
74281
74333
|
lastFetchedAt: number()
|
|
74282
74334
|
});
|
|
74283
|
-
|
|
74335
|
+
/** What the settings form needs: the unit, the bounds and the type's default. */
|
|
74336
|
+
var TimedRunOptionsSchema = object({
|
|
74337
|
+
unit: TimedRunUnitSchema,
|
|
74338
|
+
/** The longest run, in `unit`. */
|
|
74339
|
+
maxDuration: number(),
|
|
74340
|
+
defaultDuration: number()
|
|
74341
|
+
});
|
|
74342
|
+
/** The one-tap run duration, in the type's unit; `null` returns to the type's default. */
|
|
74343
|
+
var TimedRunSettingsPatchSchema = object({ quickRunDuration: number().positive().finite().nullable().optional() });
|
|
74344
|
+
method(object({ deviceId: number().int() }), TimedRunOptionsSchema), method(object({
|
|
74345
|
+
deviceId: number().int(),
|
|
74346
|
+
settings: TimedRunSettingsPatchSchema
|
|
74347
|
+
}), _void(), {
|
|
74348
|
+
kind: "mutation",
|
|
74349
|
+
auth: "admin"
|
|
74350
|
+
}), method(object({
|
|
74284
74351
|
deviceId: number().int(),
|
|
74285
74352
|
duration: number().positive().finite(),
|
|
74286
74353
|
unit: TimedRunUnitSchema
|
|
@@ -76986,21 +77053,28 @@ Object.freeze({
|
|
|
76986
77053
|
access: "view",
|
|
76987
77054
|
role: "admin"
|
|
76988
77055
|
},
|
|
76989
|
-
"addonSettings.
|
|
77056
|
+
"addonSettings.getIntegrationFormSchema": {
|
|
77057
|
+
capName: "addon-settings",
|
|
77058
|
+
capScope: "system",
|
|
77059
|
+
addonId: null,
|
|
77060
|
+
access: "view",
|
|
77061
|
+
role: "admin"
|
|
77062
|
+
},
|
|
77063
|
+
"addonSettings.getScopedSettings": {
|
|
76990
77064
|
capName: "addon-settings",
|
|
76991
77065
|
capScope: "system",
|
|
76992
77066
|
addonId: null,
|
|
76993
77067
|
access: "view",
|
|
76994
77068
|
role: "admin"
|
|
76995
77069
|
},
|
|
76996
|
-
"addonSettings.
|
|
77070
|
+
"addonSettings.listScopedSettings": {
|
|
76997
77071
|
capName: "addon-settings",
|
|
76998
77072
|
capScope: "system",
|
|
76999
77073
|
addonId: null,
|
|
77000
77074
|
access: "view",
|
|
77001
77075
|
role: "admin"
|
|
77002
77076
|
},
|
|
77003
|
-
"addonSettings.
|
|
77077
|
+
"addonSettings.readScopedValues": {
|
|
77004
77078
|
capName: "addon-settings",
|
|
77005
77079
|
capScope: "system",
|
|
77006
77080
|
addonId: null,
|
|
@@ -77021,7 +77095,7 @@ Object.freeze({
|
|
|
77021
77095
|
access: "create",
|
|
77022
77096
|
role: "admin"
|
|
77023
77097
|
},
|
|
77024
|
-
"addonSettings.
|
|
77098
|
+
"addonSettings.updateScopedSettings": {
|
|
77025
77099
|
capName: "addon-settings",
|
|
77026
77100
|
capScope: "system",
|
|
77027
77101
|
addonId: null,
|
|
@@ -79191,6 +79265,13 @@ Object.freeze({
|
|
|
79191
79265
|
access: "create",
|
|
79192
79266
|
role: "admin"
|
|
79193
79267
|
},
|
|
79268
|
+
"deviceManager.setAlias": {
|
|
79269
|
+
capName: "device-manager",
|
|
79270
|
+
capScope: "system",
|
|
79271
|
+
addonId: null,
|
|
79272
|
+
access: "create",
|
|
79273
|
+
role: "admin"
|
|
79274
|
+
},
|
|
79194
79275
|
"deviceManager.setChildLayout": {
|
|
79195
79276
|
capName: "device-manager",
|
|
79196
79277
|
capScope: "system",
|
|
@@ -80479,13 +80560,6 @@ Object.freeze({
|
|
|
80479
80560
|
access: "delete",
|
|
80480
80561
|
role: "admin"
|
|
80481
80562
|
},
|
|
80482
|
-
"llm.deleteProfile": {
|
|
80483
|
-
capName: "llm",
|
|
80484
|
-
capScope: "system",
|
|
80485
|
-
addonId: null,
|
|
80486
|
-
access: "delete",
|
|
80487
|
-
role: "admin"
|
|
80488
|
-
},
|
|
80489
80563
|
"llm.generate": {
|
|
80490
80564
|
capName: "llm",
|
|
80491
80565
|
capScope: "system",
|
|
@@ -80549,20 +80623,6 @@ Object.freeze({
|
|
|
80549
80623
|
access: "view",
|
|
80550
80624
|
role: "admin"
|
|
80551
80625
|
},
|
|
80552
|
-
"llm.listProfileKinds": {
|
|
80553
|
-
capName: "llm",
|
|
80554
|
-
capScope: "system",
|
|
80555
|
-
addonId: null,
|
|
80556
|
-
access: "view",
|
|
80557
|
-
role: "admin"
|
|
80558
|
-
},
|
|
80559
|
-
"llm.listProfiles": {
|
|
80560
|
-
capName: "llm",
|
|
80561
|
-
capScope: "system",
|
|
80562
|
-
addonId: null,
|
|
80563
|
-
access: "view",
|
|
80564
|
-
role: "admin"
|
|
80565
|
-
},
|
|
80566
80626
|
"llm.listRuntimeNodes": {
|
|
80567
80627
|
capName: "llm",
|
|
80568
80628
|
capScope: "system",
|
|
@@ -80570,13 +80630,6 @@ Object.freeze({
|
|
|
80570
80630
|
access: "view",
|
|
80571
80631
|
role: "admin"
|
|
80572
80632
|
},
|
|
80573
|
-
"llm.migrateRawSecrets": {
|
|
80574
|
-
capName: "llm",
|
|
80575
|
-
capScope: "system",
|
|
80576
|
-
addonId: null,
|
|
80577
|
-
access: "create",
|
|
80578
|
-
role: "admin"
|
|
80579
|
-
},
|
|
80580
80633
|
"llm.resolveModelRef": {
|
|
80581
80634
|
capName: "llm",
|
|
80582
80635
|
capScope: "system",
|
|
@@ -80605,20 +80658,6 @@ Object.freeze({
|
|
|
80605
80658
|
access: "create",
|
|
80606
80659
|
role: "admin"
|
|
80607
80660
|
},
|
|
80608
|
-
"llm.testProfile": {
|
|
80609
|
-
capName: "llm",
|
|
80610
|
-
capScope: "system",
|
|
80611
|
-
addonId: null,
|
|
80612
|
-
access: "create",
|
|
80613
|
-
role: "admin"
|
|
80614
|
-
},
|
|
80615
|
-
"llm.upsertProfile": {
|
|
80616
|
-
capName: "llm",
|
|
80617
|
-
capScope: "system",
|
|
80618
|
-
addonId: null,
|
|
80619
|
-
access: "create",
|
|
80620
|
-
role: "admin"
|
|
80621
|
-
},
|
|
80622
80661
|
"llmRuntime.appleAvailability": {
|
|
80623
80662
|
capName: "llm-runtime",
|
|
80624
80663
|
capScope: "system",
|
|
@@ -81123,13 +81162,6 @@ Object.freeze({
|
|
|
81123
81162
|
access: "create",
|
|
81124
81163
|
role: "admin"
|
|
81125
81164
|
},
|
|
81126
|
-
"meshNetwork.testConnection": {
|
|
81127
|
-
capName: "mesh-network",
|
|
81128
|
-
capScope: "system",
|
|
81129
|
-
addonId: null,
|
|
81130
|
-
access: "create",
|
|
81131
|
-
role: "admin"
|
|
81132
|
-
},
|
|
81133
81165
|
"meter.getStatus": {
|
|
81134
81166
|
capName: "meter",
|
|
81135
81167
|
capScope: "device",
|
|
@@ -81788,13 +81820,6 @@ Object.freeze({
|
|
|
81788
81820
|
access: "create",
|
|
81789
81821
|
role: "admin"
|
|
81790
81822
|
},
|
|
81791
|
-
"notificationOutput.deleteTarget": {
|
|
81792
|
-
capName: "notification-output",
|
|
81793
|
-
capScope: "system",
|
|
81794
|
-
addonId: null,
|
|
81795
|
-
access: "delete",
|
|
81796
|
-
role: "admin"
|
|
81797
|
-
},
|
|
81798
81823
|
"notificationOutput.discoverTargets": {
|
|
81799
81824
|
capName: "notification-output",
|
|
81800
81825
|
capScope: "system",
|
|
@@ -81802,13 +81827,6 @@ Object.freeze({
|
|
|
81802
81827
|
access: "view",
|
|
81803
81828
|
role: "admin"
|
|
81804
81829
|
},
|
|
81805
|
-
"notificationOutput.getTarget": {
|
|
81806
|
-
capName: "notification-output",
|
|
81807
|
-
capScope: "system",
|
|
81808
|
-
addonId: null,
|
|
81809
|
-
access: "view",
|
|
81810
|
-
role: "admin"
|
|
81811
|
-
},
|
|
81812
81830
|
"notificationOutput.listTargetKinds": {
|
|
81813
81831
|
capName: "notification-output",
|
|
81814
81832
|
capScope: "system",
|
|
@@ -81816,13 +81834,6 @@ Object.freeze({
|
|
|
81816
81834
|
access: "view",
|
|
81817
81835
|
role: "admin"
|
|
81818
81836
|
},
|
|
81819
|
-
"notificationOutput.listTargets": {
|
|
81820
|
-
capName: "notification-output",
|
|
81821
|
-
capScope: "system",
|
|
81822
|
-
addonId: null,
|
|
81823
|
-
access: "view",
|
|
81824
|
-
role: "admin"
|
|
81825
|
-
},
|
|
81826
81837
|
"notificationOutput.probeTargetConfig": {
|
|
81827
81838
|
capName: "notification-output",
|
|
81828
81839
|
capScope: "system",
|
|
@@ -81837,13 +81848,6 @@ Object.freeze({
|
|
|
81837
81848
|
access: "create",
|
|
81838
81849
|
role: "admin"
|
|
81839
81850
|
},
|
|
81840
|
-
"notificationOutput.setTargetEnabled": {
|
|
81841
|
-
capName: "notification-output",
|
|
81842
|
-
capScope: "system",
|
|
81843
|
-
addonId: null,
|
|
81844
|
-
access: "create",
|
|
81845
|
-
role: "admin"
|
|
81846
|
-
},
|
|
81847
81851
|
"notificationOutput.testTarget": {
|
|
81848
81852
|
capName: "notification-output",
|
|
81849
81853
|
capScope: "system",
|
|
@@ -81851,13 +81855,6 @@ Object.freeze({
|
|
|
81851
81855
|
access: "create",
|
|
81852
81856
|
role: "admin"
|
|
81853
81857
|
},
|
|
81854
|
-
"notificationOutput.upsertTarget": {
|
|
81855
|
-
capName: "notification-output",
|
|
81856
|
-
capScope: "system",
|
|
81857
|
-
addonId: null,
|
|
81858
|
-
access: "create",
|
|
81859
|
-
role: "admin"
|
|
81860
|
-
},
|
|
81861
81858
|
"notificationRules.activateAlarmProfile": {
|
|
81862
81859
|
capName: "notification-rules",
|
|
81863
81860
|
capScope: "system",
|
|
@@ -82117,41 +82114,6 @@ Object.freeze({
|
|
|
82117
82114
|
access: "view",
|
|
82118
82115
|
role: "admin"
|
|
82119
82116
|
},
|
|
82120
|
-
"oidcConnections.connectionFormSchema": {
|
|
82121
|
-
capName: "oidc-connections",
|
|
82122
|
-
capScope: "system",
|
|
82123
|
-
addonId: null,
|
|
82124
|
-
access: "view",
|
|
82125
|
-
role: "admin"
|
|
82126
|
-
},
|
|
82127
|
-
"oidcConnections.createConnection": {
|
|
82128
|
-
capName: "oidc-connections",
|
|
82129
|
-
capScope: "system",
|
|
82130
|
-
addonId: null,
|
|
82131
|
-
access: "create",
|
|
82132
|
-
role: "admin"
|
|
82133
|
-
},
|
|
82134
|
-
"oidcConnections.deleteConnection": {
|
|
82135
|
-
capName: "oidc-connections",
|
|
82136
|
-
capScope: "system",
|
|
82137
|
-
addonId: null,
|
|
82138
|
-
access: "delete",
|
|
82139
|
-
role: "admin"
|
|
82140
|
-
},
|
|
82141
|
-
"oidcConnections.listConnections": {
|
|
82142
|
-
capName: "oidc-connections",
|
|
82143
|
-
capScope: "system",
|
|
82144
|
-
addonId: null,
|
|
82145
|
-
access: "view",
|
|
82146
|
-
role: "admin"
|
|
82147
|
-
},
|
|
82148
|
-
"oidcConnections.updateConnection": {
|
|
82149
|
-
capName: "oidc-connections",
|
|
82150
|
-
capScope: "system",
|
|
82151
|
-
addonId: null,
|
|
82152
|
-
access: "create",
|
|
82153
|
-
role: "admin"
|
|
82154
|
-
},
|
|
82155
82117
|
"osd.getStatus": {
|
|
82156
82118
|
capName: "osd",
|
|
82157
82119
|
capScope: "device",
|
|
@@ -85183,13 +85145,6 @@ Object.freeze({
|
|
|
85183
85145
|
access: "delete",
|
|
85184
85146
|
role: "admin"
|
|
85185
85147
|
},
|
|
85186
|
-
"storage.deleteLocation": {
|
|
85187
|
-
capName: "storage",
|
|
85188
|
-
capScope: "system",
|
|
85189
|
-
addonId: null,
|
|
85190
|
-
access: "delete",
|
|
85191
|
-
role: "admin"
|
|
85192
|
-
},
|
|
85193
85148
|
"storage.endDownload": {
|
|
85194
85149
|
capName: "storage",
|
|
85195
85150
|
capScope: "system",
|
|
@@ -85246,13 +85201,6 @@ Object.freeze({
|
|
|
85246
85201
|
access: "view",
|
|
85247
85202
|
role: "admin"
|
|
85248
85203
|
},
|
|
85249
|
-
"storage.listProviders": {
|
|
85250
|
-
capName: "storage",
|
|
85251
|
-
capScope: "system",
|
|
85252
|
-
addonId: null,
|
|
85253
|
-
access: "view",
|
|
85254
|
-
role: "admin"
|
|
85255
|
-
},
|
|
85256
85204
|
"storage.read": {
|
|
85257
85205
|
capName: "storage",
|
|
85258
85206
|
capScope: "system",
|
|
@@ -85274,27 +85222,6 @@ Object.freeze({
|
|
|
85274
85222
|
access: "view",
|
|
85275
85223
|
role: "admin"
|
|
85276
85224
|
},
|
|
85277
|
-
"storage.testConfig": {
|
|
85278
|
-
capName: "storage",
|
|
85279
|
-
capScope: "system",
|
|
85280
|
-
addonId: null,
|
|
85281
|
-
access: "view",
|
|
85282
|
-
role: "admin"
|
|
85283
|
-
},
|
|
85284
|
-
"storage.testLocation": {
|
|
85285
|
-
capName: "storage",
|
|
85286
|
-
capScope: "system",
|
|
85287
|
-
addonId: null,
|
|
85288
|
-
access: "view",
|
|
85289
|
-
role: "admin"
|
|
85290
|
-
},
|
|
85291
|
-
"storage.upsertLocation": {
|
|
85292
|
-
capName: "storage",
|
|
85293
|
-
capScope: "system",
|
|
85294
|
-
addonId: null,
|
|
85295
|
-
access: "create",
|
|
85296
|
-
role: "admin"
|
|
85297
|
-
},
|
|
85298
85225
|
"storage.write": {
|
|
85299
85226
|
capName: "storage",
|
|
85300
85227
|
capScope: "system",
|
|
@@ -86072,27 +85999,6 @@ Object.freeze({
|
|
|
86072
85999
|
access: "create",
|
|
86073
86000
|
role: "admin"
|
|
86074
86001
|
},
|
|
86075
|
-
"terminalSession.createInstance": {
|
|
86076
|
-
capName: "terminal-session",
|
|
86077
|
-
capScope: "system",
|
|
86078
|
-
addonId: null,
|
|
86079
|
-
access: "create",
|
|
86080
|
-
role: "admin"
|
|
86081
|
-
},
|
|
86082
|
-
"terminalSession.deleteInstance": {
|
|
86083
|
-
capName: "terminal-session",
|
|
86084
|
-
capScope: "system",
|
|
86085
|
-
addonId: null,
|
|
86086
|
-
access: "delete",
|
|
86087
|
-
role: "admin"
|
|
86088
|
-
},
|
|
86089
|
-
"terminalSession.listInstances": {
|
|
86090
|
-
capName: "terminal-session",
|
|
86091
|
-
capScope: "system",
|
|
86092
|
-
addonId: null,
|
|
86093
|
-
access: "view",
|
|
86094
|
-
role: "admin"
|
|
86095
|
-
},
|
|
86096
86002
|
"terminalSession.listLegacyCameras": {
|
|
86097
86003
|
capName: "terminal-session",
|
|
86098
86004
|
capScope: "system",
|
|
@@ -86135,20 +86041,6 @@ Object.freeze({
|
|
|
86135
86041
|
access: "create",
|
|
86136
86042
|
role: "admin"
|
|
86137
86043
|
},
|
|
86138
|
-
"terminalSession.setInstanceEnabled": {
|
|
86139
|
-
capName: "terminal-session",
|
|
86140
|
-
capScope: "system",
|
|
86141
|
-
addonId: null,
|
|
86142
|
-
access: "create",
|
|
86143
|
-
role: "admin"
|
|
86144
|
-
},
|
|
86145
|
-
"terminalSession.updateInstance": {
|
|
86146
|
-
capName: "terminal-session",
|
|
86147
|
-
capScope: "system",
|
|
86148
|
-
addonId: null,
|
|
86149
|
-
access: "create",
|
|
86150
|
-
role: "admin"
|
|
86151
|
-
},
|
|
86152
86044
|
"terminalSession.writeInput": {
|
|
86153
86045
|
capName: "terminal-session",
|
|
86154
86046
|
capScope: "system",
|
|
@@ -86163,6 +86055,13 @@ Object.freeze({
|
|
|
86163
86055
|
access: "create",
|
|
86164
86056
|
role: "viewer"
|
|
86165
86057
|
},
|
|
86058
|
+
"timedRun.getOptions": {
|
|
86059
|
+
capName: "timed-run",
|
|
86060
|
+
capScope: "device",
|
|
86061
|
+
addonId: null,
|
|
86062
|
+
access: "view",
|
|
86063
|
+
role: "viewer"
|
|
86064
|
+
},
|
|
86166
86065
|
"timedRun.getStatus": {
|
|
86167
86066
|
capName: "timed-run",
|
|
86168
86067
|
capScope: "device",
|
|
@@ -86177,6 +86076,13 @@ Object.freeze({
|
|
|
86177
86076
|
access: "create",
|
|
86178
86077
|
role: "viewer"
|
|
86179
86078
|
},
|
|
86079
|
+
"timedRun.setSettings": {
|
|
86080
|
+
capName: "timed-run",
|
|
86081
|
+
capScope: "device",
|
|
86082
|
+
addonId: null,
|
|
86083
|
+
access: "create",
|
|
86084
|
+
role: "admin"
|
|
86085
|
+
},
|
|
86180
86086
|
"toast.onToast": {
|
|
86181
86087
|
capName: "toast",
|
|
86182
86088
|
capScope: "system",
|
|
@@ -87107,6 +87013,7 @@ function offered(received) {
|
|
|
87107
87013
|
};
|
|
87108
87014
|
}
|
|
87109
87015
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
87016
|
+
string().min(1).max(4096);
|
|
87110
87017
|
Object.freeze({
|
|
87111
87018
|
"accessories.getStatus": [{
|
|
87112
87019
|
name: "deviceId",
|
|
@@ -87790,6 +87697,11 @@ Object.freeze({
|
|
|
87790
87697
|
form: "single",
|
|
87791
87698
|
optional: false
|
|
87792
87699
|
}],
|
|
87700
|
+
"deviceManager.setAlias": [{
|
|
87701
|
+
name: "deviceId",
|
|
87702
|
+
form: "single",
|
|
87703
|
+
optional: false
|
|
87704
|
+
}],
|
|
87793
87705
|
"deviceManager.setChildLayout": [{
|
|
87794
87706
|
name: "deviceId",
|
|
87795
87707
|
form: "single",
|
|
@@ -89854,6 +89766,11 @@ Object.freeze({
|
|
|
89854
89766
|
form: "single",
|
|
89855
89767
|
optional: false
|
|
89856
89768
|
}],
|
|
89769
|
+
"timedRun.getOptions": [{
|
|
89770
|
+
name: "deviceId",
|
|
89771
|
+
form: "single",
|
|
89772
|
+
optional: false
|
|
89773
|
+
}],
|
|
89857
89774
|
"timedRun.getStatus": [{
|
|
89858
89775
|
name: "deviceId",
|
|
89859
89776
|
form: "single",
|
|
@@ -89864,6 +89781,11 @@ Object.freeze({
|
|
|
89864
89781
|
form: "single",
|
|
89865
89782
|
optional: false
|
|
89866
89783
|
}],
|
|
89784
|
+
"timedRun.setSettings": [{
|
|
89785
|
+
name: "deviceId",
|
|
89786
|
+
form: "single",
|
|
89787
|
+
optional: false
|
|
89788
|
+
}],
|
|
89867
89789
|
"update.getStatus": [{
|
|
89868
89790
|
name: "deviceId",
|
|
89869
89791
|
form: "single",
|
|
@@ -90726,6 +90648,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
90726
90648
|
[...RULE_TEXT],
|
|
90727
90649
|
[...RULE_TEXT],
|
|
90728
90650
|
[...RULE_TEXT],
|
|
90651
|
+
[...RULE_TEXT],
|
|
90729
90652
|
[...RULE_BODY_ONLY],
|
|
90730
90653
|
[...RULE_TEXT],
|
|
90731
90654
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
@@ -91847,222 +91770,6 @@ var GoogleBrokerRegistry = class {
|
|
|
91847
91770
|
}
|
|
91848
91771
|
};
|
|
91849
91772
|
//#endregion
|
|
91850
|
-
//#region src/google-settings-copy.ts
|
|
91851
|
-
/**
|
|
91852
|
-
* Operator-facing setup copy for Connectivity → Calendars / Email.
|
|
91853
|
-
* Keep Connect as a same-origin relative link so the admin session cookie
|
|
91854
|
-
* rides along (an absolute Public URL from a LAN admin tab drops the cookie).
|
|
91855
|
-
* Root-relative hrefs must survive `sanitizeInfoHtml` in ui-library.
|
|
91856
|
-
*/
|
|
91857
|
-
/** Enable APIs library in Google Cloud Console. */
|
|
91858
|
-
var GOOGLE_CONSOLE_APIS_URL = "https://console.cloud.google.com/apis/library";
|
|
91859
|
-
/** Create / manage OAuth Web clients. */
|
|
91860
|
-
var GOOGLE_CONSOLE_CREDENTIALS_URL = "https://console.cloud.google.com/apis/credentials";
|
|
91861
|
-
function redirectUriDisplay(publicOrigin) {
|
|
91862
|
-
if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
|
|
91863
|
-
return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
|
|
91864
|
-
}
|
|
91865
|
-
function consoleLinksHtml$1() {
|
|
91866
|
-
return [
|
|
91867
|
-
`<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
|
|
91868
|
-
` · `,
|
|
91869
|
-
`<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
|
|
91870
|
-
].join("");
|
|
91871
|
-
}
|
|
91872
|
-
function introHtml(cap, publicOrigin) {
|
|
91873
|
-
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
91874
|
-
const console = consoleLinksHtml$1();
|
|
91875
|
-
if (cap === "smtp-provider") return [
|
|
91876
|
-
"<ol>",
|
|
91877
|
-
`<li>Open Google Cloud Console (${console}) and enable the <strong>Gmail API</strong> (and Calendar API if you also use Calendars).</li>`,
|
|
91878
|
-
"<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
|
|
91879
|
-
`<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
|
|
91880
|
-
"<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
|
|
91881
|
-
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
91882
|
-
"<li>Set From address (connected account or a verified send-as). Turn this integration off with the page <strong>Enable</strong> toggle when you do not want Gmail send — Calendar can stay connected.</li>",
|
|
91883
|
-
"</ol>"
|
|
91884
|
-
].join("");
|
|
91885
|
-
if (cap === "calendar-source") return [
|
|
91886
|
-
"<ol>",
|
|
91887
|
-
`<li>Open Google Cloud Console (${console}) and enable the <strong>Google Calendar API</strong> (and Gmail API if you also use Email).</li>`,
|
|
91888
|
-
"<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
|
|
91889
|
-
`<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
|
|
91890
|
-
"<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
|
|
91891
|
-
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
91892
|
-
"<li>Optionally restrict which calendars are exposed with the filter below.</li>",
|
|
91893
|
-
"</ol>"
|
|
91894
|
-
].join("");
|
|
91895
|
-
return [
|
|
91896
|
-
"<ol>",
|
|
91897
|
-
`<li>Open Google Cloud Console (${console}) and enable <strong>Google Calendar API</strong> and <strong>Gmail API</strong>.</li>`,
|
|
91898
|
-
"<li>Create a <strong>Web application</strong> OAuth client under Credentials.</li>",
|
|
91899
|
-
`<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
|
|
91900
|
-
`<li>Paste Client ID / secret, Save, then <a href="${OAUTH_START_PATH}">Connect Google</a>.</li>`,
|
|
91901
|
-
"<li>Calendars and Email share one Google account; use each page’s <strong>Enable</strong> toggle to turn a capability off without revoking OAuth.</li>",
|
|
91902
|
-
"</ol>"
|
|
91903
|
-
].join("");
|
|
91904
|
-
}
|
|
91905
|
-
function connectHtml(cap, publicOrigin) {
|
|
91906
|
-
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
91907
|
-
if (cap === "smtp-provider") return [
|
|
91908
|
-
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
91909
|
-
"Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
|
|
91910
|
-
`Google must list exactly: <code>${redirect}</code>. `,
|
|
91911
|
-
"If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again. ",
|
|
91912
|
-
`Console: ${consoleLinksHtml$1()}.`
|
|
91913
|
-
].join("");
|
|
91914
|
-
if (cap === "calendar-source") return [
|
|
91915
|
-
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
91916
|
-
"Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
|
|
91917
|
-
`Google must list exactly: <code>${redirect}</code>. `,
|
|
91918
|
-
`Console: ${consoleLinksHtml$1()}.`
|
|
91919
|
-
].join("");
|
|
91920
|
-
return [
|
|
91921
|
-
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `,
|
|
91922
|
-
`Redirect URI registered in Google Cloud must be <code>${redirect}</code>. `,
|
|
91923
|
-
`Console: ${consoleLinksHtml$1()}.`
|
|
91924
|
-
].join("");
|
|
91925
|
-
}
|
|
91926
|
-
function escapeHtml$1(s) {
|
|
91927
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
91928
|
-
}
|
|
91929
|
-
//#endregion
|
|
91930
|
-
//#region src/google-broker-settings-schema.ts
|
|
91931
|
-
/**
|
|
91932
|
-
* Per-kind broker creation/edit form for kind=`google`.
|
|
91933
|
-
* Account linking (Sign in) is driven by the kind's `connectPath`
|
|
91934
|
-
* on the instances page — not a hard-coded Connect link in this form.
|
|
91935
|
-
*/
|
|
91936
|
-
function consoleLinksHtml() {
|
|
91937
|
-
return [
|
|
91938
|
-
`<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
|
|
91939
|
-
` · `,
|
|
91940
|
-
`<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
|
|
91941
|
-
].join("");
|
|
91942
|
-
}
|
|
91943
|
-
/** Setup steps shown on Add / Edit — includes Audience / Test users. */
|
|
91944
|
-
function googleBrokerSetupHtml(publicOrigin) {
|
|
91945
|
-
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
91946
|
-
return [
|
|
91947
|
-
"<ol>",
|
|
91948
|
-
`<li>Open Google Cloud Console (${consoleLinksHtml()}). Create a <strong>new project</strong> (or pick an existing one) from the project picker at the top.</li>`,
|
|
91949
|
-
"<li>In that project, enable <strong>Google Calendar API</strong> and/or <strong>Gmail API</strong> (API library).</li>",
|
|
91950
|
-
"<li>Open <strong>Google Auth Platform → Audience</strong> (or APIs & Services → OAuth consent screen → <strong>Audience</strong>). User type <strong>External</strong> + publishing status <strong>Testing</strong> is fine for a personal hub. Under <strong>Test users</strong>, add the Google account(s) that will Sign in — otherwise Google returns <code>403: access_denied</code>. (If you only see Branding / Data Access, open the <strong>Audience</strong> tab in the left sidebar of Google Auth Platform.) Publishing / verification is optional for a private hub.</li>",
|
|
91951
|
-
"<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
|
|
91952
|
-
`<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
|
|
91953
|
-
"<li>Paste Client ID and Client secret below. <strong>Test connection</strong> checks those with Google (not the account yet).</li>",
|
|
91954
|
-
"<li>Add the broker — the hub then opens Google sign-in in a popup to link the account (Calendar + Gmail). Use <strong>Sign in</strong> on the row if you skip or cancel.</li>",
|
|
91955
|
-
"</ol>"
|
|
91956
|
-
].join("");
|
|
91957
|
-
}
|
|
91958
|
-
function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
91959
|
-
return { sections: [
|
|
91960
|
-
{
|
|
91961
|
-
id: "setup-intro",
|
|
91962
|
-
title: "Setup",
|
|
91963
|
-
titleKey: "google-services:googleBrokerSettings.s_setup-intro.title",
|
|
91964
|
-
description: "Google Cloud Console — register the hub redirect before Add / Sign in.",
|
|
91965
|
-
descriptionKey: "google-services:googleBrokerSettings.s_setup-intro.description",
|
|
91966
|
-
columns: 1,
|
|
91967
|
-
fields: [{
|
|
91968
|
-
type: "info",
|
|
91969
|
-
key: "_setupSteps",
|
|
91970
|
-
label: "Steps",
|
|
91971
|
-
labelKey: "google-services:googleBrokerSettings.s_setup-intro._setupSteps.label",
|
|
91972
|
-
format: "html",
|
|
91973
|
-
content: googleBrokerSetupHtml(publicOrigin)
|
|
91974
|
-
}]
|
|
91975
|
-
},
|
|
91976
|
-
{
|
|
91977
|
-
id: "google-oauth-client",
|
|
91978
|
-
title: "Google OAuth client",
|
|
91979
|
-
titleKey: "google-services:googleBrokerSettings.s_google-oauth-client.title",
|
|
91980
|
-
description: "Web application client from Google Cloud Console. The same Cloud project can be reused across Google brokers.",
|
|
91981
|
-
descriptionKey: "google-services:googleBrokerSettings.s_google-oauth-client.description",
|
|
91982
|
-
columns: 1,
|
|
91983
|
-
fields: [{
|
|
91984
|
-
type: "text",
|
|
91985
|
-
key: "clientId",
|
|
91986
|
-
label: "Client ID",
|
|
91987
|
-
labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientId.label"
|
|
91988
|
-
}, {
|
|
91989
|
-
type: "password",
|
|
91990
|
-
key: "clientSecret",
|
|
91991
|
-
label: "Client secret",
|
|
91992
|
-
labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientSecret.label",
|
|
91993
|
-
showToggle: true
|
|
91994
|
-
}]
|
|
91995
|
-
},
|
|
91996
|
-
{
|
|
91997
|
-
id: "gmail-send",
|
|
91998
|
-
title: "Gmail send",
|
|
91999
|
-
titleKey: "google-services:googleBrokerSettings.s_gmail-send.title",
|
|
92000
|
-
description: "From must be this connected account or a verified send-as. Disable Email on Connectivity to turn Gmail off without removing the broker.",
|
|
92001
|
-
descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.description",
|
|
92002
|
-
columns: 1,
|
|
92003
|
-
fields: [{
|
|
92004
|
-
type: "text",
|
|
92005
|
-
key: "fromAddress",
|
|
92006
|
-
label: "From address",
|
|
92007
|
-
labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.label",
|
|
92008
|
-
description: "Defaults to the connected Google account email when empty.",
|
|
92009
|
-
descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.description"
|
|
92010
|
-
}, {
|
|
92011
|
-
type: "text",
|
|
92012
|
-
key: "fromName",
|
|
92013
|
-
label: "From display name (optional)",
|
|
92014
|
-
labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromName.label"
|
|
92015
|
-
}]
|
|
92016
|
-
}
|
|
92017
|
-
] };
|
|
92018
|
-
}
|
|
92019
|
-
/** Connectivity Calendars / Email pages — the calendar import picker is on each broker. */
|
|
92020
|
-
function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
92021
|
-
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
92022
|
-
if (cap === "calendar-source") return { sections: [{
|
|
92023
|
-
id: "setup-intro",
|
|
92024
|
-
title: "Google brokers",
|
|
92025
|
-
titleKey: "google-services:googleFeatureSettings.s_setup-intro.title",
|
|
92026
|
-
columns: 1,
|
|
92027
|
-
fields: [{
|
|
92028
|
-
type: "info",
|
|
92029
|
-
key: "_setupSteps",
|
|
92030
|
-
label: "Setup",
|
|
92031
|
-
labelKey: "google-services:googleFeatureSettings.s_setup-intro._setupSteps.label",
|
|
92032
|
-
format: "html",
|
|
92033
|
-
content: [
|
|
92034
|
-
"<p>Link a Google account under Connectivity → <strong>Brokers</strong> ",
|
|
92035
|
-
"(Client ID / secret + Sign in), then add a calendar under Connectivity → <strong>Calendars</strong>. ",
|
|
92036
|
-
`OAuth redirect URI: <code>${redirect}</code>.</p>`
|
|
92037
|
-
].join("")
|
|
92038
|
-
}]
|
|
92039
|
-
}] };
|
|
92040
|
-
return { sections: [{
|
|
92041
|
-
id: "setup-intro",
|
|
92042
|
-
title: cap === "smtp-provider" ? "Gmail send uses Google brokers" : "Google brokers",
|
|
92043
|
-
columns: 1,
|
|
92044
|
-
fields: [{
|
|
92045
|
-
type: "info",
|
|
92046
|
-
key: "_setupSteps",
|
|
92047
|
-
label: "Setup",
|
|
92048
|
-
labelKey: "google-services:googleFeatureSettings._setupSteps.label",
|
|
92049
|
-
format: "html",
|
|
92050
|
-
content: cap === "smtp-provider" ? [
|
|
92051
|
-
"<p>Add or edit a <strong>Google</strong> broker under Connectivity → <strong>Brokers</strong> ",
|
|
92052
|
-
"(Client ID / secret + Sign in). From address and send-as live on that broker. ",
|
|
92053
|
-
`OAuth redirect URI: <code>${redirect}</code>.</p>`
|
|
92054
|
-
].join("") : [
|
|
92055
|
-
"<p>Add or edit a <strong>Google</strong> broker under Connectivity → <strong>Brokers</strong> ",
|
|
92056
|
-
"(Client ID / secret + Sign in). ",
|
|
92057
|
-
`OAuth redirect URI: <code>${redirect}</code>.</p>`
|
|
92058
|
-
].join("")
|
|
92059
|
-
}]
|
|
92060
|
-
}] };
|
|
92061
|
-
}
|
|
92062
|
-
function escapeHtml(s) {
|
|
92063
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
92064
|
-
}
|
|
92065
|
-
//#endregion
|
|
92066
91773
|
//#region src/google-calendar-source.ts
|
|
92067
91774
|
function parseGoogleInstant(value) {
|
|
92068
91775
|
const ms = Date.parse(value);
|
|
@@ -92559,7 +92266,13 @@ function buildGoogleCalendarInstances(records, deps) {
|
|
|
92559
92266
|
role: "services",
|
|
92560
92267
|
instanceMode: "multiple",
|
|
92561
92268
|
kind: GOOGLE_CALENDAR_INSTANCE_KIND,
|
|
92562
|
-
canTest: false
|
|
92269
|
+
canTest: false,
|
|
92270
|
+
kinds: [{
|
|
92271
|
+
id: GOOGLE_CALENDAR_INSTANCE_KIND,
|
|
92272
|
+
label: "Google calendar",
|
|
92273
|
+
functionCap: "calendar-source",
|
|
92274
|
+
role: "services"
|
|
92275
|
+
}]
|
|
92563
92276
|
},
|
|
92564
92277
|
labels: { accountId: {
|
|
92565
92278
|
label: "Google account",
|
|
@@ -92583,6 +92296,179 @@ function buildGoogleCalendarInstances(records, deps) {
|
|
|
92583
92296
|
});
|
|
92584
92297
|
}
|
|
92585
92298
|
//#endregion
|
|
92299
|
+
//#region src/google-settings-copy.ts
|
|
92300
|
+
/**
|
|
92301
|
+
* Operator-facing setup copy for Connectivity → Calendars / Email.
|
|
92302
|
+
* Keep Connect as a same-origin relative link so the admin session cookie
|
|
92303
|
+
* rides along (an absolute Public URL from a LAN admin tab drops the cookie).
|
|
92304
|
+
* Root-relative hrefs must survive `sanitizeInfoHtml` in ui-library.
|
|
92305
|
+
*/
|
|
92306
|
+
/** Enable APIs library in Google Cloud Console. */
|
|
92307
|
+
var GOOGLE_CONSOLE_APIS_URL = "https://console.cloud.google.com/apis/library";
|
|
92308
|
+
/** Create / manage OAuth Web clients. */
|
|
92309
|
+
var GOOGLE_CONSOLE_CREDENTIALS_URL = "https://console.cloud.google.com/apis/credentials";
|
|
92310
|
+
function redirectUriDisplay(publicOrigin) {
|
|
92311
|
+
if (publicOrigin === null || publicOrigin.length === 0) return `<public-hub>${OAUTH_CALLBACK_SUFFIX}`;
|
|
92312
|
+
return `${publicOrigin.replace(/\/$/, "")}${OAUTH_CALLBACK_SUFFIX}`;
|
|
92313
|
+
}
|
|
92314
|
+
function consoleLinksHtml$1() {
|
|
92315
|
+
return [
|
|
92316
|
+
`<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
|
|
92317
|
+
` · `,
|
|
92318
|
+
`<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
|
|
92319
|
+
].join("");
|
|
92320
|
+
}
|
|
92321
|
+
function introHtml(cap, publicOrigin) {
|
|
92322
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
92323
|
+
const console = consoleLinksHtml$1();
|
|
92324
|
+
if (cap === "smtp-provider") return [
|
|
92325
|
+
"<ol>",
|
|
92326
|
+
`<li>Open Google Cloud Console (${console}) and enable the <strong>Gmail API</strong> (and Calendar API if you also use Calendars).</li>`,
|
|
92327
|
+
"<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
|
|
92328
|
+
`<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
|
|
92329
|
+
"<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
|
|
92330
|
+
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
92331
|
+
"<li>Set From address (connected account or a verified send-as). Turn this integration off with the page <strong>Enable</strong> toggle when you do not want Gmail send — Calendar can stay connected.</li>",
|
|
92332
|
+
"</ol>"
|
|
92333
|
+
].join("");
|
|
92334
|
+
if (cap === "calendar-source") return [
|
|
92335
|
+
"<ol>",
|
|
92336
|
+
`<li>Open Google Cloud Console (${console}) and enable the <strong>Google Calendar API</strong> (and Gmail API if you also use Email).</li>`,
|
|
92337
|
+
"<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
|
|
92338
|
+
`<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
|
|
92339
|
+
"<li>Paste Client ID and Client secret below, then <strong>Save</strong>.</li>",
|
|
92340
|
+
`<li>Open <a href="${OAUTH_START_PATH}">Connect Google</a> while signed in as admin (same browser tab as this hub).</li>`,
|
|
92341
|
+
"<li>Optionally restrict which calendars are exposed with the filter below.</li>",
|
|
92342
|
+
"</ol>"
|
|
92343
|
+
].join("");
|
|
92344
|
+
return [
|
|
92345
|
+
"<ol>",
|
|
92346
|
+
`<li>Open Google Cloud Console (${console}) and enable <strong>Google Calendar API</strong> and <strong>Gmail API</strong>.</li>`,
|
|
92347
|
+
"<li>Create a <strong>Web application</strong> OAuth client under Credentials.</li>",
|
|
92348
|
+
`<li>Register redirect URI: <code>${redirect}</code> (Network → Public endpoint).</li>`,
|
|
92349
|
+
`<li>Paste Client ID / secret, Save, then <a href="${OAUTH_START_PATH}">Connect Google</a>.</li>`,
|
|
92350
|
+
"<li>Calendars and Email share one Google account; use each page’s <strong>Enable</strong> toggle to turn a capability off without revoking OAuth.</li>",
|
|
92351
|
+
"</ol>"
|
|
92352
|
+
].join("");
|
|
92353
|
+
}
|
|
92354
|
+
function connectHtml(cap, publicOrigin) {
|
|
92355
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
92356
|
+
if (cap === "smtp-provider") return [
|
|
92357
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
92358
|
+
"Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
|
|
92359
|
+
`Google must list exactly: <code>${redirect}</code>. `,
|
|
92360
|
+
"If an older Connect predates Gmail send, revoke CamStack in Google Account → Security and Connect again. ",
|
|
92361
|
+
`Console: ${consoleLinksHtml$1()}.`
|
|
92362
|
+
].join("");
|
|
92363
|
+
if (cap === "calendar-source") return [
|
|
92364
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
92365
|
+
"Connect requests <code>calendar.readonly</code> (and Gmail send for the Email page). ",
|
|
92366
|
+
`Google must list exactly: <code>${redirect}</code>. `,
|
|
92367
|
+
`Console: ${consoleLinksHtml$1()}.`
|
|
92368
|
+
].join("");
|
|
92369
|
+
return [
|
|
92370
|
+
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a>. `,
|
|
92371
|
+
`Redirect URI registered in Google Cloud must be <code>${redirect}</code>. `,
|
|
92372
|
+
`Console: ${consoleLinksHtml$1()}.`
|
|
92373
|
+
].join("");
|
|
92374
|
+
}
|
|
92375
|
+
function escapeHtml$1(s) {
|
|
92376
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
92377
|
+
}
|
|
92378
|
+
//#endregion
|
|
92379
|
+
//#region src/google-broker-settings-schema.ts
|
|
92380
|
+
/**
|
|
92381
|
+
* Per-kind broker creation/edit form for kind=`google`.
|
|
92382
|
+
* Account linking (Sign in) is driven by the kind's `connectPath`
|
|
92383
|
+
* on the instances page — not a hard-coded Connect link in this form.
|
|
92384
|
+
*/
|
|
92385
|
+
function consoleLinksHtml() {
|
|
92386
|
+
return [
|
|
92387
|
+
`<a href="${GOOGLE_CONSOLE_APIS_URL}">API library</a>`,
|
|
92388
|
+
` · `,
|
|
92389
|
+
`<a href="${GOOGLE_CONSOLE_CREDENTIALS_URL}">Credentials</a>`
|
|
92390
|
+
].join("");
|
|
92391
|
+
}
|
|
92392
|
+
/** Setup steps shown on Add / Edit — includes Audience / Test users. */
|
|
92393
|
+
function googleBrokerSetupHtml(publicOrigin) {
|
|
92394
|
+
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
92395
|
+
return [
|
|
92396
|
+
"<ol>",
|
|
92397
|
+
`<li>Open Google Cloud Console (${consoleLinksHtml()}). Create a <strong>new project</strong> (or pick an existing one) from the project picker at the top.</li>`,
|
|
92398
|
+
"<li>In that project, enable <strong>Google Calendar API</strong> and/or <strong>Gmail API</strong> (API library).</li>",
|
|
92399
|
+
"<li>Open <strong>Google Auth Platform → Audience</strong> (or APIs & Services → OAuth consent screen → <strong>Audience</strong>). User type <strong>External</strong> + publishing status <strong>Testing</strong> is fine for a personal hub. Under <strong>Test users</strong>, add the Google account(s) that will Sign in — otherwise Google returns <code>403: access_denied</code>. (If you only see Branding / Data Access, open the <strong>Audience</strong> tab in the left sidebar of Google Auth Platform.) Publishing / verification is optional for a private hub.</li>",
|
|
92400
|
+
"<li>Under Credentials, create an OAuth client of type <strong>Web application</strong>.</li>",
|
|
92401
|
+
`<li>Add this authorized redirect URI (Network → <strong>Public endpoint</strong>, not Internal): <code>${redirect}</code></li>`,
|
|
92402
|
+
"<li>Paste Client ID and Client secret below. <strong>Test connection</strong> checks those with Google (not the account yet).</li>",
|
|
92403
|
+
"<li>Add the broker — the hub then opens Google sign-in in a popup to link the account (Calendar + Gmail). Use <strong>Sign in</strong> on the row if you skip or cancel.</li>",
|
|
92404
|
+
"</ol>"
|
|
92405
|
+
].join("");
|
|
92406
|
+
}
|
|
92407
|
+
function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
92408
|
+
return { sections: [
|
|
92409
|
+
{
|
|
92410
|
+
id: "setup-intro",
|
|
92411
|
+
title: "Setup",
|
|
92412
|
+
titleKey: "google-services:googleBrokerSettings.s_setup-intro.title",
|
|
92413
|
+
description: "Google Cloud Console — register the hub redirect before Add / Sign in.",
|
|
92414
|
+
descriptionKey: "google-services:googleBrokerSettings.s_setup-intro.description",
|
|
92415
|
+
columns: 1,
|
|
92416
|
+
fields: [{
|
|
92417
|
+
type: "info",
|
|
92418
|
+
key: "_setupSteps",
|
|
92419
|
+
label: "Steps",
|
|
92420
|
+
labelKey: "google-services:googleBrokerSettings.s_setup-intro._setupSteps.label",
|
|
92421
|
+
format: "html",
|
|
92422
|
+
content: googleBrokerSetupHtml(publicOrigin)
|
|
92423
|
+
}]
|
|
92424
|
+
},
|
|
92425
|
+
{
|
|
92426
|
+
id: "google-oauth-client",
|
|
92427
|
+
title: "Google OAuth client",
|
|
92428
|
+
titleKey: "google-services:googleBrokerSettings.s_google-oauth-client.title",
|
|
92429
|
+
description: "Web application client from Google Cloud Console. The same Cloud project can be reused across Google brokers.",
|
|
92430
|
+
descriptionKey: "google-services:googleBrokerSettings.s_google-oauth-client.description",
|
|
92431
|
+
columns: 1,
|
|
92432
|
+
fields: [{
|
|
92433
|
+
type: "text",
|
|
92434
|
+
key: "clientId",
|
|
92435
|
+
label: "Client ID",
|
|
92436
|
+
labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientId.label"
|
|
92437
|
+
}, {
|
|
92438
|
+
type: "password",
|
|
92439
|
+
key: "clientSecret",
|
|
92440
|
+
label: "Client secret",
|
|
92441
|
+
labelKey: "google-services:googleBrokerSettings.s_google-oauth-client.clientSecret.label",
|
|
92442
|
+
showToggle: true
|
|
92443
|
+
}]
|
|
92444
|
+
},
|
|
92445
|
+
{
|
|
92446
|
+
id: "gmail-send",
|
|
92447
|
+
title: "Gmail send",
|
|
92448
|
+
titleKey: "google-services:googleBrokerSettings.s_gmail-send.title",
|
|
92449
|
+
description: "From must be this connected account or a verified send-as. Disable Email on Connectivity to turn Gmail off without removing the broker.",
|
|
92450
|
+
descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.description",
|
|
92451
|
+
columns: 1,
|
|
92452
|
+
fields: [{
|
|
92453
|
+
type: "text",
|
|
92454
|
+
key: "fromAddress",
|
|
92455
|
+
label: "From address",
|
|
92456
|
+
labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.label",
|
|
92457
|
+
description: "Defaults to the connected Google account email when empty.",
|
|
92458
|
+
descriptionKey: "google-services:googleBrokerSettings.s_gmail-send.fromAddress.description"
|
|
92459
|
+
}, {
|
|
92460
|
+
type: "text",
|
|
92461
|
+
key: "fromName",
|
|
92462
|
+
label: "From display name (optional)",
|
|
92463
|
+
labelKey: "google-services:googleBrokerSettings.s_gmail-send.fromName.label"
|
|
92464
|
+
}]
|
|
92465
|
+
}
|
|
92466
|
+
] };
|
|
92467
|
+
}
|
|
92468
|
+
function escapeHtml(s) {
|
|
92469
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
92470
|
+
}
|
|
92471
|
+
//#endregion
|
|
92586
92472
|
//#region src/google-instances.ts
|
|
92587
92473
|
/**
|
|
92588
92474
|
* The Google accounts as `integration-instances` (planning#408 P3): one instance per broker
|
|
@@ -92619,7 +92505,8 @@ function buildGoogleInstances(records, deps) {
|
|
|
92619
92505
|
instanceMode: "multiple",
|
|
92620
92506
|
kind: "account",
|
|
92621
92507
|
canTest: true,
|
|
92622
|
-
allowRestoreId: true
|
|
92508
|
+
allowRestoreId: true,
|
|
92509
|
+
kinds: [googleAccountKind(deps)]
|
|
92623
92510
|
},
|
|
92624
92511
|
formSchema: () => buildGoogleBrokerSettingsSchema(deps.publicOrigin()),
|
|
92625
92512
|
initialBody: (name) => ({
|
|
@@ -93869,22 +93756,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
93869
93756
|
this.publicOrigin = null;
|
|
93870
93757
|
}
|
|
93871
93758
|
}
|
|
93872
|
-
/**
|
|
93873
|
-
* Connectivity Calendars / Email: point at Brokers (no duplicate OAuth fields).
|
|
93874
|
-
* Addon page without cap: same pointer + note.
|
|
93875
|
-
*/
|
|
93876
|
-
globalSettingsSchema(cap) {
|
|
93877
|
-
const scoped = asSettingsCap(cap);
|
|
93878
|
-
return this.schema(buildGoogleFeatureSettingsSchema(scoped, this.publicOrigin));
|
|
93879
|
-
}
|
|
93880
|
-
async getGlobalSettings(overlay, cap, nodeId) {
|
|
93881
|
-
await this.refreshPublicOrigin();
|
|
93882
|
-
return super.getGlobalSettings(overlay, cap, nodeId);
|
|
93883
|
-
}
|
|
93884
93759
|
};
|
|
93885
|
-
function asSettingsCap(cap) {
|
|
93886
|
-
if (cap === "calendar-source" || cap === "smtp-provider") return cap;
|
|
93887
|
-
}
|
|
93888
93760
|
//#endregion
|
|
93889
93761
|
exports.DEFAULT_GOOGLE_CONFIG = DEFAULT_GOOGLE_CONFIG;
|
|
93890
93762
|
exports.GOOGLE_BROKER_KIND = GOOGLE_BROKER_KIND;
|