@camstack/addon-import-alexa 0.2.163 → 0.2.164

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/addon.js +292 -410
  3. package/dist/addon.mjs +293 -409
  4. package/dist/index.js +0 -1
  5. package/dist/index.mjs +2 -2
  6. package/package.json +2 -17
  7. package/dist/MaskShapeCanvas-B6q5RDLi.mjs +0 -13807
  8. package/dist/MotionZonesSettings-ByDKuk5o.mjs +0 -309
  9. package/dist/PrivacyMaskSettings-Cn-_9Syv.mjs +0 -368
  10. package/dist/SceneMonitorEditor-jDMtE1yg.mjs +0 -845
  11. package/dist/_stub.js +0 -39538
  12. package/dist/_virtual_mf-localSharedImportMap___mfe_internal__addon_import_alexa_page-CwP9H68T.mjs +0 -156
  13. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.js-BmoaT3cr.mjs +0 -25
  14. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DE1dtKjc.mjs +0 -26
  15. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-Bct5qZia.mjs +0 -25
  16. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_trpc_mf_1_client__loadShare__.js-BHqOw8oh.mjs +0 -25
  17. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.js-B3qxfxBT.mjs +0 -26
  18. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react__loadShare__.js-DwXXYAlk.mjs +0 -70
  19. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-CUiVYNMY.mjs +0 -26
  20. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react_mf_2_dom__loadShare__.js-CIPmAyPi.mjs +0 -25
  21. package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-tXm1x5EZ.mjs +0 -25
  22. package/dist/addon-import-alexa.css +0 -3
  23. package/dist/hostInit-Dy4CxmqQ.mjs +0 -129
  24. package/dist/player-overlays-CcdvNfxc.mjs +0 -51
  25. package/dist/remoteEntry.js +0 -2
  26. package/dist/remoteEntry.ssr.js +0 -33
  27. package/dist/responsive-BmleF5y2.mjs +0 -125
  28. package/dist/rolldown-runtime-HEgqtunE.mjs +0 -20
  29. package/dist/scene-monitor-copy-BEZSY5WG.mjs +0 -240
  30. package/dist/square-DZHhUhzl.mjs +0 -45
  31. package/dist/trash-2-BVrrPiKB.mjs +0 -25
  32. package/dist/ui-text-B5YO4hoJ.mjs +0 -34
  33. package/dist/virtualExposes-lZT06jeg.mjs +0 -2
  34. package/dist/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_import_alexa_page__remoteEntry_js-DarhMhK_.mjs +0 -2857
  35. package/dist/virtual_mf-exposes-ssr___mfe_internal__addon_import_alexa_page__remoteEntry_js-CViESMdd.mjs +0 -10
  36. package/dist/virtual_mf-exposes___mfe_internal__addon_import_alexa_page__remoteEntry_js-C3qJCjtV.mjs +0 -27
package/dist/addon.js CHANGED
@@ -8865,28 +8865,6 @@ function event(data) {
8865
8865
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
8866
8866
  var VersionOutputSchema$1 = object({ version: string() });
8867
8867
  method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
8868
- /**
8869
- * Identity — preserves literal types for downstream inference.
8870
- *
8871
- * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
8872
- * TypeScript does not widen each entry's literal `kind`/`auth` fields to
8873
- * the broader unions declared on `CustomActionSpec`'s default generics.
8874
- * Shape validity is enforced separately by the `customAction(...)` helper
8875
- * whose return type is already a `CustomActionSpec<...>`.
8876
- */
8877
- function defineCustomActions(spec) {
8878
- return spec;
8879
- }
8880
- function customAction(input, output, options) {
8881
- return {
8882
- input,
8883
- output,
8884
- kind: options?.kind ?? "query",
8885
- auth: options?.auth ?? "protected",
8886
- scope: options?.scope ?? { kind: "system" },
8887
- ...options?.caller ? { caller: "required" } : {}
8888
- };
8889
- }
8890
8868
  var StaticDirOutputSchema = object({ staticDir: string() });
8891
8869
  var VersionOutputSchema = object({ version: string() });
8892
8870
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -11231,7 +11209,7 @@ function errMsg(err) {
11231
11209
  return String(err);
11232
11210
  }
11233
11211
  //#endregion
11234
- //#region ../types/dist/composition-CZImLpOI.mjs
11212
+ //#region ../types/dist/composition-DwA0uVW5.mjs
11235
11213
  /**
11236
11214
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11237
11215
  *
@@ -16476,24 +16454,7 @@ var AddonPageDeclarationSchema = object({
16476
16454
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
16477
16455
  sectionLabel: string().optional()
16478
16456
  });
16479
- var addonPagesSourceCapability = {
16480
- name: "addon-pages-source",
16481
- scope: "system",
16482
- mode: "collection",
16483
- internal: true,
16484
- methods: {
16485
- /**
16486
- * `internal: true` did not gate the mount (see the 2026-08-26 note on
16487
- * `data-store-provider`) — this was reachable on the AppRouter by ANY
16488
- * authenticated session at the default `auth: 'protected'`. The only
16489
- * consumer, the `addon-pages-aggregator` builtin, reads it via
16490
- * `ctx.capabilities.getCollection('addon-pages-source')` — LOCAL-PROCESS
16491
- * only, never tRPC (per `ctx.capabilities` seeing only the local
16492
- * process). The public, enriched listing admin-ui actually consumes is
16493
- * the separate `addon-pages` cap, unaffected by this change.
16494
- */
16495
- listPages: method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" }) }
16496
- };
16457
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
16497
16458
  var AddonHttpRouteSchema = object({
16498
16459
  method: _enum([
16499
16460
  "GET",
@@ -19451,12 +19412,13 @@ var CHANGED = {
19451
19412
  expr: "now"
19452
19413
  };
19453
19414
  function numeric(input) {
19415
+ const { alsoFrom, ...base } = input;
19454
19416
  return {
19455
- ...input,
19417
+ ...base,
19456
19418
  family: "numeric",
19457
19419
  deviceType: DeviceType.Sensor,
19458
19420
  memberDeviceType: DeviceType.Sensor,
19459
- memberDeviceTypes: [DeviceType.Sensor],
19421
+ memberDeviceTypes: [DeviceType.Sensor, ...alsoFrom?.deviceTypes ?? []],
19460
19422
  activeWhen: void 0,
19461
19423
  commandCaps: [],
19462
19424
  baseUnit: baseUnitOf(input.quantity),
@@ -19464,9 +19426,10 @@ function numeric(input) {
19464
19426
  accept: [{
19465
19427
  cap: input.outputCap,
19466
19428
  fieldPath: input.outputField
19467
- }]
19429
+ }, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
19468
19430
  };
19469
19431
  }
19432
+ var CLIMATE_DEVICES = [DeviceType.Thermostat, DeviceType.Climate];
19470
19433
  function binary(input) {
19471
19434
  return {
19472
19435
  ...input,
@@ -19492,7 +19455,14 @@ var GROUP_TEMPLATES = [
19492
19455
  quantity: "temperature",
19493
19456
  defaultAggregate: "mean",
19494
19457
  empty: "null",
19495
- companions: [NOW$2]
19458
+ companions: [NOW$2],
19459
+ alsoFrom: {
19460
+ deviceTypes: CLIMATE_DEVICES,
19461
+ accept: {
19462
+ cap: "climate-control",
19463
+ fieldPath: "currentTemp"
19464
+ }
19465
+ }
19496
19466
  }),
19497
19467
  numeric({
19498
19468
  id: "humidity",
@@ -19503,7 +19473,14 @@ var GROUP_TEMPLATES = [
19503
19473
  quantity: "humidity",
19504
19474
  defaultAggregate: "mean",
19505
19475
  empty: "null",
19506
- companions: [NOW$2]
19476
+ companions: [NOW$2],
19477
+ alsoFrom: {
19478
+ deviceTypes: CLIMATE_DEVICES,
19479
+ accept: {
19480
+ cap: "climate-control",
19481
+ fieldPath: "currentHumidity"
19482
+ }
19483
+ }
19507
19484
  }),
19508
19485
  numeric({
19509
19486
  id: "pressure",
@@ -45218,6 +45195,43 @@ var NotificationActionSchema = object({
45218
45195
  */
45219
45196
  mode: _enum(["navigate", "background"]).optional()
45220
45197
  });
45198
+ /** A delivery parameter value: what a notifier declared it takes. */
45199
+ var DeliveryParamValueSchema = union([
45200
+ string(),
45201
+ number(),
45202
+ boolean()
45203
+ ]);
45204
+ /**
45205
+ * One extra delivery parameter a notifier KIND declares (D938). A rule that
45206
+ * addresses a target of that kind may set it; a kind that declares nothing is
45207
+ * never handed one. The admin UI derives its control from this descriptor —
45208
+ * no editor knows a service by name.
45209
+ *
45210
+ * Optional fields are `.optional()`, never `.default()` (a Zod default does not
45211
+ * run on the addon cap path).
45212
+ */
45213
+ var DeliveryParamSchema = object({
45214
+ key: string().min(1),
45215
+ label: string(),
45216
+ /** i18n key of the label, resolved by the declaring addon's namespace. */
45217
+ labelKey: string().optional(),
45218
+ type: _enum([
45219
+ "enum",
45220
+ "number",
45221
+ "boolean",
45222
+ "string"
45223
+ ]),
45224
+ /** `enum` only: the closed set of accepted values. */
45225
+ options: array(object({
45226
+ value: string(),
45227
+ label: string()
45228
+ })).optional(),
45229
+ /** `number` only. */
45230
+ min: number().optional(),
45231
+ max: number().optional(),
45232
+ /** What the notifier does when the rule sets nothing; shown as the placeholder. */
45233
+ default: DeliveryParamValueSchema.optional()
45234
+ });
45221
45235
  /**
45222
45236
  * The canonical notification. `body` is the only hard field (Apprise model).
45223
45237
  * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
@@ -45240,7 +45254,13 @@ var NotificationSchema = object({
45240
45254
  tag: string().optional(),
45241
45255
  deviceId: number().optional(),
45242
45256
  eventId: string().optional(),
45243
- metadata: record(string(), unknown()).optional()
45257
+ metadata: record(string(), unknown()).optional(),
45258
+ /**
45259
+ * Values for the parameters the target's kind DECLARES in
45260
+ * {@link TargetKindSchema} `deliveryParams`. Already validated against the
45261
+ * declaration by the sender; a notifier reads only the keys it declared.
45262
+ */
45263
+ delivery: record(string(), DeliveryParamValueSchema).optional()
45244
45264
  });
45245
45265
  /** One declared native severity/priority level for a kind. */
45246
45266
  var TargetKindLevelSchema = object({
@@ -45346,6 +45366,11 @@ var TargetKindSchema = object({
45346
45366
  */
45347
45367
  destinationKeys: array(string()).optional(),
45348
45368
  supportsDiscovery: boolean(),
45369
+ /**
45370
+ * Extra delivery parameters a rule may set for a target of this kind (D938).
45371
+ * Absent = none; the rule editor then offers nothing.
45372
+ */
45373
+ deliveryParams: array(DeliveryParamSchema).optional(),
45349
45374
  caps: TargetKindCapsSchema
45350
45375
  });
45351
45376
  /**
@@ -57659,7 +57684,9 @@ var ManifestShapeSchema = object({
57659
57684
  icon: string().optional()
57660
57685
  }).optional(),
57661
57686
  protected: boolean().optional(),
57662
- removable: boolean().optional()
57687
+ removable: boolean().optional(),
57688
+ /** Declared Integrations-page block (mirror of `AddonDeclaration.integrationSection`). */
57689
+ integrationSection: _enum(["system", "providers"]).optional()
57663
57690
  });
57664
57691
  var AddonProcessSchema = object({
57665
57692
  pid: number().optional(),
@@ -90809,6 +90836,29 @@ function rewriteReferer(referer, route) {
90809
90836
  if (upstream === void 0) return null;
90810
90837
  return `${upstream.scheme}://${upstream.host}${rest?.[2] ?? "/"}${url.search}`;
90811
90838
  }
90839
+ function decodeFormComponent(raw) {
90840
+ try {
90841
+ return decodeURIComponent(raw.replace(/\+/g, " "));
90842
+ } catch {
90843
+ return null;
90844
+ }
90845
+ }
90846
+ /**
90847
+ * An `application/x-www-form-urlencoded` body whose field VALUES that are proxy URLs of
90848
+ * this attempt (absolute or root-relative) are turned back into the vendor URL they stand
90849
+ * for — the page's hidden inputs were rewritten on the way out (`rewriteBody`), and the
90850
+ * vendor must get its own URL back. Field order and every untouched pair stay byte-identical.
90851
+ */
90852
+ function rewriteFormBody(body, route) {
90853
+ return body.split("&").map((pair) => {
90854
+ const eq = pair.indexOf("=");
90855
+ if (eq === -1) return pair;
90856
+ const value = decodeFormComponent(pair.slice(eq + 1));
90857
+ if (value === null || !/^(?:https?:\/\/|\/)/i.test(value)) return pair;
90858
+ const back = rewriteReferer(value, route);
90859
+ return back === null ? pair : `${pair.slice(0, eq + 1)}${encodeURIComponent(back)}`;
90860
+ }).join("&");
90861
+ }
90812
90862
  /** Cookies the browser holds for the hub itself must never reach a vendor. */
90813
90863
  function filterForwardedCookies(cookieHeader) {
90814
90864
  return cookieHeader.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !part.toLowerCase().startsWith("camstack_")).join("; ");
@@ -90867,6 +90917,9 @@ function bodyKindOf(contentType) {
90867
90917
  if (type.startsWith("text/") || type.includes("javascript") || type.includes("json") || type.includes("xml")) return "other";
90868
90918
  return null;
90869
90919
  }
90920
+ function isFormEncoded(contentType) {
90921
+ return (firstHeader(contentType) ?? "").toLowerCase().startsWith("application/x-www-form-urlencoded");
90922
+ }
90870
90923
  function firstHeader(value) {
90871
90924
  return Array.isArray(value) ? value[0] : value;
90872
90925
  }
@@ -90997,7 +91050,7 @@ var VendorLoginProxy = class {
90997
91050
  sendPlain(res, 413, "Request too large");
90998
91051
  return;
90999
91052
  }
91000
- body = read;
91053
+ body = isFormEncoded(req.headers["content-type"]) ? Buffer.from(rewriteFormBody(read.toString("utf8"), route), "utf8") : read;
91001
91054
  }
91002
91055
  const headers = this.#requestHeaders(req, route, upstream);
91003
91056
  const upstreamRes = await this.#fetch(target, {
@@ -91011,9 +91064,10 @@ var VendorLoginProxy = class {
91011
91064
  const kind = bodyKindOf(upstreamRes.headers.get("content-type") ?? "");
91012
91065
  const declaredLength = Number(upstreamRes.headers.get("content-length") ?? "0");
91013
91066
  const text = kind !== null && !(declaredLength > MAX_TEXT_BODY_BYTES) && method !== "HEAD" ? await upstreamRes.text() : null;
91014
- this.#logger.debug("vendor login proxy: upstream answered", { meta: {
91067
+ this.#logger.info("vendor login proxy: upstream answered", { meta: {
91015
91068
  addonId: this.#addonId,
91016
91069
  host: upstream.host,
91070
+ path: target.pathname,
91017
91071
  status: upstreamRes.status,
91018
91072
  method
91019
91073
  } });
@@ -91146,59 +91200,37 @@ var AlexaBrokerEntrySchema = object({
91146
91200
  */
91147
91201
  var alexaProxyLoginSchema = object({ proxyPort: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1).max(65535).default(3456)).describe("Loopback port the local Amazon-login proxy listens on (never exposed)") });
91148
91202
  /**
91149
- * Echo speech settings. Speech signs in as an import broker, so the targets
91150
- * are what lives here. `email`, `password`, `region` and `session` are the OLD
91151
- * standalone login: they are read once to migrate it onto the single broker
91152
- * (see `speak/migration.ts`) and written back empty.
91153
- */
91154
- var alexaSpeakSettingsSchema = object({
91155
- email: string().default(""),
91156
- password: string().default("").describe("Legacy Amazon password").meta({ secret: true }),
91157
- region: _enum([
91158
- "NA",
91159
- "EU",
91160
- "FE"
91161
- ]).default("EU"),
91162
- session: string().default("").describe("Legacy Alexa session blob").meta({ secret: true }),
91163
- targets: array(object({
91164
- id: string(),
91165
- name: string(),
91166
- enabled: boolean(),
91167
- /** The broker (Amazon account) the Echo belongs to; '' until one is chosen. */
91168
- brokerId: string().default(""),
91169
- echoId: string(),
91170
- mode: _enum(["speak", "announce"]),
91171
- volume: number(),
91172
- respectDnd: boolean()
91173
- })).default([])
91203
+ * The Echo notification targets this addon owns. Speech signs in as an import
91204
+ * broker, so a target is only the Echo it speaks on and how.
91205
+ */
91206
+ var alexaEchoTargetSchema = object({
91207
+ id: string(),
91208
+ name: string(),
91209
+ enabled: boolean(),
91210
+ /** The broker (Amazon account) the Echo belongs to; '' until one is chosen. */
91211
+ brokerId: string().default(""),
91212
+ echoId: string(),
91213
+ mode: _enum(["speak", "announce"]),
91214
+ volume: number(),
91215
+ respectDnd: boolean()
91174
91216
  });
91175
91217
  function isRecord$3(value) {
91176
91218
  return typeof value === "object" && value !== null && !Array.isArray(value);
91177
91219
  }
91178
- /** Resolved settings. A keychain marker (an object) is an empty secret, not a password. */
91179
- function readSpeakSettings(config) {
91180
- const raw = isRecord$3(config.speak) ? config.speak : {};
91181
- const region = raw["region"];
91182
- const targets = raw["targets"];
91183
- return alexaSpeakSettingsSchema.parse({
91184
- email: typeof raw["email"] === "string" ? raw["email"] : "",
91185
- password: typeof raw["password"] === "string" ? raw["password"] : "",
91186
- session: typeof raw["session"] === "string" ? raw["session"] : "",
91187
- ...region === "NA" || region === "EU" || region === "FE" ? { region } : {},
91188
- ...Array.isArray(targets) ? { targets } : {}
91220
+ /** The stored Echo targets; a row that no longer parses is dropped, never guessed. */
91221
+ function readEchoTargets(config) {
91222
+ const raw = config.echoTargets;
91223
+ if (!Array.isArray(raw)) return [];
91224
+ return raw.flatMap((row) => {
91225
+ const parsed = alexaEchoTargetSchema.safeParse(isRecord$3(row) ? row : {});
91226
+ return parsed.success ? [parsed.data] : [];
91189
91227
  });
91190
91228
  }
91191
91229
  /** Top-level addon config — broker entries + the proxy-login settings. */
91192
91230
  var alexaAddonConfigSchema = object({
91193
91231
  brokers: array(AlexaBrokerEntrySchema).default([]),
91194
91232
  proxyLogin: alexaProxyLoginSchema.default({ proxyPort: 3456 }),
91195
- speak: alexaSpeakSettingsSchema.default({
91196
- email: "",
91197
- password: "",
91198
- region: "EU",
91199
- session: "",
91200
- targets: []
91201
- })
91233
+ echoTargets: array(alexaEchoTargetSchema).default([])
91202
91234
  });
91203
91235
  /** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
91204
91236
  function toRecord(value) {
@@ -132745,6 +132777,33 @@ var ALEXA_API_USER_AGENT_POSTFIX = `AlexaRemote/${{
132745
132777
  readmeFilename: "readme.md"
132746
132778
  }.version}`;
132747
132779
  //#endregion
132780
+ //#region src/alexa-region-options.ts
132781
+ var DEFAULT_LOCALE = "en-US";
132782
+ var LOCALE_BY_DOMAIN = {
132783
+ "amazon.com": "en-US",
132784
+ "amazon.it": "it-IT",
132785
+ "amazon.de": "de-DE",
132786
+ "amazon.co.uk": "en-GB",
132787
+ "amazon.fr": "fr-FR",
132788
+ "amazon.es": "es-ES",
132789
+ "amazon.ca": "en-CA",
132790
+ "amazon.com.au": "en-AU",
132791
+ "amazon.co.jp": "ja-JP",
132792
+ "amazon.in": "en-IN",
132793
+ "amazon.com.br": "pt-BR",
132794
+ "amazon.com.mx": "es-MX"
132795
+ };
132796
+ function alexaRegionOptions(amazonDomain) {
132797
+ const domain = amazonDomain.length > 0 ? amazonDomain : "amazon.com";
132798
+ const locale = LOCALE_BY_DOMAIN[domain] ?? DEFAULT_LOCALE;
132799
+ return {
132800
+ baseAmazonPage: domain,
132801
+ amazonPage: domain,
132802
+ acceptLanguage: locale,
132803
+ amazonPageProxyLanguage: locale.replace("-", "_")
132804
+ };
132805
+ }
132806
+ //#endregion
132748
132807
  //#region src/alexa-proxy-trace.ts
132749
132808
  /**
132750
132809
  * Sanitised one-line trace of the alexa-cookie2 proxy's own log lines.
@@ -132877,7 +132936,7 @@ var AlexaProxyLoginSession = class {
132877
132936
  async start() {
132878
132937
  const remote = new import_alexa_remote.default();
132879
132938
  this.#remote = remote;
132880
- const amazonPage = this.#opts.amazonDomain.length > 0 ? this.#opts.amazonDomain : "amazon.com";
132939
+ const region = alexaRegionOptions(this.#opts.amazonDomain);
132881
132940
  remote.on("cookie", () => this.#onCookieEvent(remote));
132882
132941
  remote.init({
132883
132942
  proxyOnly: true,
@@ -132885,7 +132944,7 @@ var AlexaProxyLoginSession = class {
132885
132944
  proxyListenBind: ALEXA_PROXY_LOOPBACK_HOST,
132886
132945
  proxyPort: this.#opts.proxyPort,
132887
132946
  proxyLogLevel: "warn",
132888
- amazonPage,
132947
+ ...region,
132889
132948
  apiUserAgentPostfix: ALEXA_API_USER_AGENT_POSTFIX,
132890
132949
  logger: (...args) => {
132891
132950
  const line = args.map(String).join(" ");
@@ -135151,126 +135210,6 @@ var AlexaBrokerStore = class AlexaBrokerStore {
135151
135210
  }
135152
135211
  };
135153
135212
  //#endregion
135154
- //#region src/speak/constants.ts
135155
- /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
135156
- var ALEXA_SPEAK_ADDON_ID = "import-alexa";
135157
- var ALEXA_SPEAK_KIND = "alexa-echo";
135158
- var DEVICE_REFRESH_MS = 5 * 6e4;
135159
- /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
135160
- var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
135161
- //#endregion
135162
- //#region src/speak/worker-protocol.ts
135163
- /** Line-delimited JSON the long-lived Python worker speaks. One object per line. */
135164
- var EchoDeviceSchema = object({
135165
- id: string().min(1),
135166
- name: string(),
135167
- family: string(),
135168
- online: boolean(),
135169
- kind: _enum(["device", "group"]),
135170
- dnd: boolean(),
135171
- deviceType: string(),
135172
- /** The room the account files the device under, when Amazon names one. */
135173
- room: string().optional()
135174
- });
135175
- var LoginStatusSchema = _enum(["ok", "failed"]);
135176
- var SessionStatusSchema = object({
135177
- loggedIn: boolean(),
135178
- ageSec: number().int().nonnegative(),
135179
- lastSuccessAt: number().int().nullable()
135180
- });
135181
- var TargetDeliverySchema = object({
135182
- targetId: string(),
135183
- ok: boolean(),
135184
- error: string().optional()
135185
- });
135186
- var WorkerLineSchema = union([object({
135187
- id: string(),
135188
- ok: literal(true),
135189
- result: unknown().optional()
135190
- }), object({
135191
- id: string(),
135192
- ok: literal(false),
135193
- error: string()
135194
- })]);
135195
- function requestLine(id, request) {
135196
- return `${JSON.stringify({
135197
- id,
135198
- ...request
135199
- })}\n`;
135200
- }
135201
- function parseLoginResult(result) {
135202
- const parsed = object({
135203
- status: LoginStatusSchema,
135204
- error: string().optional()
135205
- }).safeParse(result);
135206
- if (!parsed.success) return {
135207
- status: "failed",
135208
- error: "worker returned an unexpected login result"
135209
- };
135210
- return parsed.data;
135211
- }
135212
- function parseDevices(result) {
135213
- const parsed = object({ devices: array(EchoDeviceSchema) }).safeParse(result);
135214
- if (!parsed.success) throw new Error("worker returned an unexpected device list");
135215
- return parsed.data.devices;
135216
- }
135217
- function parseDeliveries(result) {
135218
- const parsed = object({ outcomes: array(TargetDeliverySchema) }).safeParse(result);
135219
- if (!parsed.success) throw new Error("worker returned an unexpected delivery list");
135220
- return parsed.data.outcomes;
135221
- }
135222
- function parseSessionStatus(result) {
135223
- const parsed = SessionStatusSchema.safeParse(result);
135224
- if (!parsed.success) throw new Error("worker returned an unexpected session status");
135225
- return parsed.data;
135226
- }
135227
- //#endregion
135228
- //#region src/speak/actions.ts
135229
- var BrokerEchoStateSchema = _enum([
135230
- "starting",
135231
- "ready",
135232
- "broker-not-signed-in",
135233
- "failed"
135234
- ]);
135235
- var SpeakStatusSchema = object({
135236
- brokers: array(object({
135237
- id: string(),
135238
- name: string(),
135239
- state: BrokerEchoStateSchema,
135240
- error: string().optional(),
135241
- devices: array(EchoDeviceSchema),
135242
- session: SessionStatusSchema
135243
- })),
135244
- /** Targets that name no Amazon account yet (they cannot speak until one is chosen). */
135245
- unassignedTargets: number().int().nonnegative(),
135246
- /** What the old standalone login left behind, named; '' when nothing is pending. */
135247
- notice: _enum([
135248
- "",
135249
- "no-broker",
135250
- "choose-broker"
135251
- ])
135252
- });
135253
- var alexaSpeakActions = defineCustomActions({
135254
- speakStatus: customAction(object({}), SpeakStatusSchema, {
135255
- kind: "mutation",
135256
- auth: "admin"
135257
- }),
135258
- sendTest: customAction(object({
135259
- brokerId: string().min(1),
135260
- echoIds: array(string().min(1)).min(1),
135261
- mode: _enum(["speak", "announce"]),
135262
- volume: number().int().min(0).max(100),
135263
- text: string().min(1).max(450)
135264
- }), object({ outcomes: array(object({
135265
- echoId: string(),
135266
- ok: boolean(),
135267
- error: string().optional()
135268
- })) }), {
135269
- kind: "mutation",
135270
- auth: "admin"
135271
- })
135272
- });
135273
- //#endregion
135274
135213
  //#region src/speak/broker-seed.ts
135275
135214
  /**
135276
135215
  * What the Echo worker needs from an import broker to act as the same Amazon
@@ -135505,6 +135444,72 @@ function scrubSecrets(text, secrets) {
135505
135444
  return out;
135506
135445
  }
135507
135446
  //#endregion
135447
+ //#region src/speak/worker-protocol.ts
135448
+ /** Line-delimited JSON the long-lived Python worker speaks. One object per line. */
135449
+ var EchoDeviceSchema = object({
135450
+ id: string().min(1),
135451
+ name: string(),
135452
+ family: string(),
135453
+ online: boolean(),
135454
+ kind: _enum(["device", "group"]),
135455
+ dnd: boolean(),
135456
+ deviceType: string(),
135457
+ /** The room the account files the device under, when Amazon names one. */
135458
+ room: string().optional()
135459
+ });
135460
+ var LoginStatusSchema = _enum(["ok", "failed"]);
135461
+ var SessionStatusSchema = object({
135462
+ loggedIn: boolean(),
135463
+ ageSec: number().int().nonnegative(),
135464
+ lastSuccessAt: number().int().nullable()
135465
+ });
135466
+ var TargetDeliverySchema = object({
135467
+ targetId: string(),
135468
+ ok: boolean(),
135469
+ error: string().optional()
135470
+ });
135471
+ var WorkerLineSchema = union([object({
135472
+ id: string(),
135473
+ ok: literal(true),
135474
+ result: unknown().optional()
135475
+ }), object({
135476
+ id: string(),
135477
+ ok: literal(false),
135478
+ error: string()
135479
+ })]);
135480
+ function requestLine(id, request) {
135481
+ return `${JSON.stringify({
135482
+ id,
135483
+ ...request
135484
+ })}\n`;
135485
+ }
135486
+ function parseLoginResult(result) {
135487
+ const parsed = object({
135488
+ status: LoginStatusSchema,
135489
+ error: string().optional()
135490
+ }).safeParse(result);
135491
+ if (!parsed.success) return {
135492
+ status: "failed",
135493
+ error: "worker returned an unexpected login result"
135494
+ };
135495
+ return parsed.data;
135496
+ }
135497
+ function parseDevices(result) {
135498
+ const parsed = object({ devices: array(EchoDeviceSchema) }).safeParse(result);
135499
+ if (!parsed.success) throw new Error("worker returned an unexpected device list");
135500
+ return parsed.data.devices;
135501
+ }
135502
+ function parseDeliveries(result) {
135503
+ const parsed = object({ outcomes: array(TargetDeliverySchema) }).safeParse(result);
135504
+ if (!parsed.success) throw new Error("worker returned an unexpected delivery list");
135505
+ return parsed.data.outcomes;
135506
+ }
135507
+ function parseSessionStatus(result) {
135508
+ const parsed = SessionStatusSchema.safeParse(result);
135509
+ if (!parsed.success) throw new Error("worker returned an unexpected session status");
135510
+ return parsed.data;
135511
+ }
135512
+ //#endregion
135508
135513
  //#region src/speak/broker-echo.ts
135509
135514
  var BrokerEcho = class {
135510
135515
  deps;
@@ -135633,40 +135638,13 @@ var BrokerEcho = class {
135633
135638
  }
135634
135639
  };
135635
135640
  //#endregion
135636
- //#region src/speak/migration.ts
135637
- function legacyLoginOf(settings) {
135638
- return {
135639
- email: settings.email,
135640
- password: settings.password,
135641
- session: settings.session
135642
- };
135643
- }
135644
- function planLegacyMigration(legacy, brokerIds, targets) {
135645
- const present = legacy.email.length > 0 || legacy.password.length > 0 || legacy.session.length > 0;
135646
- const only = brokerIds.length === 1 ? brokerIds[0] ?? null : null;
135647
- if (!present || only === null) return {
135648
- present,
135649
- attachTo: null,
135650
- targets
135651
- };
135652
- return {
135653
- present,
135654
- attachTo: legacy.session.length > 0 ? only : null,
135655
- targets: targets.map((row) => row.brokerId.length > 0 ? row : {
135656
- ...row,
135657
- brokerId: only
135658
- })
135659
- };
135660
- }
135661
- /** What the page must tell the operator about targets that name no account. */
135662
- function noticeFor(targets, brokerIds) {
135663
- if (!targets.some((row) => row.brokerId.length === 0)) return "";
135664
- if (brokerIds.length === 0) return "no-broker";
135665
- return brokerIds.length > 1 ? "choose-broker" : "";
135666
- }
135667
- function unassignedCount(targets) {
135668
- return targets.filter((row) => row.brokerId.length === 0).length;
135669
- }
135641
+ //#region src/speak/constants.ts
135642
+ /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
135643
+ var ALEXA_SPEAK_ADDON_ID = "import-alexa";
135644
+ var ALEXA_SPEAK_KIND = "alexa-echo";
135645
+ var DEVICE_REFRESH_MS = 5 * 6e4;
135646
+ /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
135647
+ var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
135670
135648
  //#endregion
135671
135649
  //#region src/speak/rate-limit.ts
135672
135650
  /**
@@ -135771,14 +135749,42 @@ function readString(config, key) {
135771
135749
  const value = config[key];
135772
135750
  return typeof value === "string" ? value : "";
135773
135751
  }
135774
- function readMode(config) {
135775
- return config["mode"] === "announce" ? "announce" : "speak";
135752
+ /** The rule's value for this send wins; the target's saved config is the fallback. */
135753
+ function readMode(config, delivery) {
135754
+ return (delivery?.["mode"] ?? config["mode"]) === "announce" ? "announce" : "speak";
135776
135755
  }
135777
- function readVolume(config) {
135778
- const value = config["volume"];
135756
+ function readVolume(config, delivery) {
135757
+ const ruled = delivery?.["volume"];
135758
+ const value = typeof ruled === "number" ? ruled : config["volume"];
135779
135759
  if (typeof value !== "number" || !Number.isFinite(value)) return 40;
135780
135760
  return Math.max(0, Math.min(100, Math.round(value)));
135781
135761
  }
135762
+ /**
135763
+ * The delivery parameters a rule may set for an Echo target (D938). The
135764
+ * target's own config holds the default; these override it per notification.
135765
+ */
135766
+ var DELIVERY_PARAMS = [{
135767
+ key: "mode",
135768
+ label: "Mode",
135769
+ labelKey: "import-alexa:echoTarget.s_echo.mode.label",
135770
+ type: "enum",
135771
+ options: [{
135772
+ value: "speak",
135773
+ label: "Speak (quiet hours apply)"
135774
+ }, {
135775
+ value: "announce",
135776
+ label: "Announce (chime, then speech)"
135777
+ }],
135778
+ default: "speak"
135779
+ }, {
135780
+ key: "volume",
135781
+ label: "Volume",
135782
+ labelKey: "import-alexa:echoTarget.s_echo.volume.label",
135783
+ type: "number",
135784
+ min: 0,
135785
+ max: 100,
135786
+ default: 40
135787
+ }];
135782
135788
  function respectDnd(config) {
135783
135789
  return config["respectDnd"] !== false;
135784
135790
  }
@@ -135879,6 +135885,7 @@ function alexaTargetKind(brokers) {
135879
135885
  supportsDiscovery: true,
135880
135886
  destinationKeys: ["brokerId", "echoId"],
135881
135887
  configSchema: configSchema(brokers),
135888
+ deliveryParams: DELIVERY_PARAMS,
135882
135889
  caps: {
135883
135890
  attachments: {
135884
135891
  mediaTypes: [],
@@ -136061,14 +136068,14 @@ function createAlexaNotificationProvider(ports) {
136061
136068
  renderedAs: speech.renderedAs
136062
136069
  };
136063
136070
  }
136064
- const mode = readMode(target.config);
136071
+ const mode = readMode(target.config, notification.delivery);
136065
136072
  try {
136066
136073
  const outcomes = await ports.speak({
136067
136074
  brokerId: broker.id,
136068
136075
  mode,
136069
136076
  targets: [echoId],
136070
136077
  text: speech.text,
136071
- volume: readVolume(target.config)
136078
+ volume: readVolume(target.config, notification.delivery)
136072
136079
  });
136073
136080
  const one = outcomes.find((row) => row.targetId === echoId) ?? outcomes[0];
136074
136081
  if (one === void 0 || !one.ok) {
@@ -136468,8 +136475,8 @@ function startAlexaWorker(deps) {
136468
136475
  }
136469
136476
  //#endregion
136470
136477
  //#region src/speak/controller.ts
136471
- function targetsFrom(settings) {
136472
- return settings.targets.map((row) => ({
136478
+ function targetsFrom(rows) {
136479
+ return rows.map((row) => ({
136473
136480
  id: row.id,
136474
136481
  name: row.name,
136475
136482
  kind: ALEXA_SPEAK_KIND,
@@ -136499,7 +136506,7 @@ function rowsFrom(targets) {
136499
136506
  var AlexaSpeakController = class {
136500
136507
  host;
136501
136508
  provider;
136502
- settings;
136509
+ rows;
136503
136510
  pythonPath = null;
136504
136511
  echoes = /* @__PURE__ */ new Map();
136505
136512
  timer = null;
@@ -136508,17 +136515,14 @@ var AlexaSpeakController = class {
136508
136515
  constructor(host) {
136509
136516
  this.host = host;
136510
136517
  this.now = host.now ?? (() => Date.now());
136511
- this.settings = readSpeakSettings(host.readConfig());
136518
+ this.rows = readEchoTargets(host.readConfig());
136512
136519
  this.provider = createAlexaNotificationProvider({
136513
136520
  brokers: () => this.brokerViews(),
136514
136521
  now: this.now,
136515
136522
  logger: host.logger,
136516
- loadTargets: () => targetsFrom(this.settings),
136523
+ loadTargets: () => targetsFrom(this.rows),
136517
136524
  saveTargets: (targets) => {
136518
- this.settings = {
136519
- ...this.settings,
136520
- targets: [...rowsFrom(targets)]
136521
- };
136525
+ this.rows = rowsFrom(targets);
136522
136526
  this.persist();
136523
136527
  },
136524
136528
  speak: (command) => this.utter(command.brokerId, command.mode, command.targets, command.text, command.volume)
@@ -136530,7 +136534,6 @@ var AlexaSpeakController = class {
136530
136534
  this.host.logger.warn("alexa speak: embedded Python unavailable; Echo speech will not run");
136531
136535
  return;
136532
136536
  }
136533
- await this.migrateLegacyLogin();
136534
136537
  await this.brokersChanged();
136535
136538
  this.timer = setInterval(() => {
136536
136539
  this.refreshAll();
@@ -136547,12 +136550,6 @@ var AlexaSpeakController = class {
136547
136550
  this.queue = this.queue.then(() => this.reconcile()).catch(() => void 0);
136548
136551
  return this.queue;
136549
136552
  }
136550
- handlers() {
136551
- return {
136552
- speakStatus: async () => this.status(),
136553
- sendTest: async (input) => this.sendTest(input)
136554
- };
136555
- }
136556
136553
  brokerViews() {
136557
136554
  return this.host.listBrokers().map((ref) => {
136558
136555
  const echo = this.echoes.get(ref.id);
@@ -136566,36 +136563,11 @@ var AlexaSpeakController = class {
136566
136563
  }
136567
136564
  async persist() {
136568
136565
  try {
136569
- await this.host.writeSpeak(this.settings);
136566
+ await this.host.writeTargets(this.rows);
136570
136567
  } catch (err) {
136571
- this.host.logger.warn("alexa speak: could not store settings", { meta: { error: err instanceof Error ? err.message : String(err) } });
136568
+ this.host.logger.warn("alexa speak: could not store targets", { meta: { error: err instanceof Error ? err.message : String(err) } });
136572
136569
  }
136573
136570
  }
136574
- /**
136575
- * Drain the old standalone login once. With exactly one broker its jar and
136576
- * its targets move onto that broker; otherwise the jar is dropped and the
136577
- * targets wait for the operator to choose (the status names that state).
136578
- */
136579
- async migrateLegacyLogin() {
136580
- const brokers = this.host.listBrokers();
136581
- const plan = planLegacyMigration(legacyLoginOf(this.settings), brokers.map((ref) => ref.id), this.settings.targets);
136582
- if (!plan.present) return;
136583
- if (plan.attachTo !== null) await this.host.sessions.put(plan.attachTo, this.settings.session);
136584
- this.settings = {
136585
- ...this.settings,
136586
- email: "",
136587
- password: "",
136588
- session: "",
136589
- targets: [...plan.targets]
136590
- };
136591
- await this.persist();
136592
- await this.host.clearLegacySecrets();
136593
- this.host.logger.info("alexa speak: the old standalone login was retired", { meta: {
136594
- attachedTo: plan.attachTo ?? "none",
136595
- brokers: brokers.length,
136596
- targets: plan.targets.length
136597
- } });
136598
- }
136599
136571
  makeWorker(brokerId, pythonPath) {
136600
136572
  if (this.host.startWorker !== void 0) return this.host.startWorker(brokerId, pythonPath);
136601
136573
  return startAlexaWorker({
@@ -136645,59 +136617,6 @@ var AlexaSpeakController = class {
136645
136617
  if (echo === void 0) throw new Error("no Echo session for this Amazon account");
136646
136618
  return echo.utter(mode === "announce" ? "announce" : "speak", targets, text, volume);
136647
136619
  }
136648
- async status() {
136649
- const brokers = this.host.listBrokers();
136650
- const rows = await Promise.all(brokers.map(async (ref) => {
136651
- const echo = this.echoes.get(ref.id);
136652
- return {
136653
- id: ref.id,
136654
- name: ref.name,
136655
- state: echo?.state ?? "failed",
136656
- ...echo === void 0 ? { error: "embedded Python unavailable" } : echo.error === null ? {} : { error: echo.error },
136657
- devices: [...echo?.devices ?? []],
136658
- session: echo === void 0 ? {
136659
- loggedIn: false,
136660
- ageSec: 0,
136661
- lastSuccessAt: null
136662
- } : await echo.session()
136663
- };
136664
- }));
136665
- const targets = this.settings.targets;
136666
- const ids = brokers.map((ref) => ref.id);
136667
- return {
136668
- brokers: rows,
136669
- unassignedTargets: unassignedCount(targets),
136670
- notice: noticeFor(targets, ids)
136671
- };
136672
- }
136673
- async sendTest(input) {
136674
- const outcomes = await this.utter(input.brokerId, input.mode, input.echoIds, input.text, input.volume);
136675
- const known = new Map((this.echoes.get(input.brokerId)?.devices ?? []).map((device) => [device.id, device]));
136676
- const next = [...this.settings.targets];
136677
- for (const echoId of input.echoIds) {
136678
- if (next.some((row) => row.echoId === echoId && row.brokerId === input.brokerId)) continue;
136679
- next.push({
136680
- id: `echo-${input.brokerId}-${echoId}`,
136681
- name: known.get(echoId)?.name ?? echoId,
136682
- enabled: true,
136683
- brokerId: input.brokerId,
136684
- echoId,
136685
- mode: input.mode,
136686
- volume: input.volume,
136687
- respectDnd: true
136688
- });
136689
- }
136690
- this.settings = {
136691
- ...this.settings,
136692
- targets: next
136693
- };
136694
- await this.persist();
136695
- return { outcomes: outcomes.map((row) => ({
136696
- echoId: row.targetId,
136697
- ok: row.ok,
136698
- ...row.error !== void 0 ? { error: row.error } : {}
136699
- })) };
136700
- }
136701
136620
  };
136702
136621
  //#endregion
136703
136622
  //#region src/speak/icon-routes.ts
@@ -136750,26 +136669,11 @@ var RecordsSpeakSessionStore = class RecordsSpeakSessionStore {
136750
136669
  };
136751
136670
  //#endregion
136752
136671
  //#region src/addon.ts
136753
- var SPEAK_PAGE = {
136754
- id: "speak",
136755
- label: "Speak on Echo",
136756
- icon: "megaphone",
136757
- path: "/addon/import-alexa",
136758
- remoteName: "addon_import_alexa_page",
136759
- bundle: "remoteEntry.js",
136760
- section: "administration"
136761
- };
136762
136672
  /** Default config — a fresh install starts with no accounts. */
136763
136673
  var DEFAULTS = {
136764
136674
  brokers: [],
136765
136675
  proxyLogin: { proxyPort: 3456 },
136766
- speak: {
136767
- email: "",
136768
- password: "",
136769
- region: "EU",
136770
- session: "",
136771
- targets: []
136772
- }
136676
+ echoTargets: []
136773
136677
  };
136774
136678
  /** Device types an imported Alexa appliance can materialise as. */
136775
136679
  var ALEXA_DEVICE_TYPES = [
@@ -136813,7 +136717,6 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
136813
136717
  brokerStore = null;
136814
136718
  activeBrokers = [];
136815
136719
  speak = null;
136816
- secretStatePaths = ["speak.password", "speak.session"];
136817
136720
  constructor() {
136818
136721
  super({ ...DEFAULTS });
136819
136722
  }
@@ -136868,8 +136771,8 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
136868
136771
  const speak = new AlexaSpeakController({
136869
136772
  logger: this.ctx.logger.child("speak"),
136870
136773
  readConfig: () => this.config,
136871
- writeSpeak: async (next) => {
136872
- await this.updateGlobalSettings({ speak: next });
136774
+ writeTargets: async (next) => {
136775
+ await this.updateGlobalSettings({ echoTargets: [...next] });
136873
136776
  },
136874
136777
  listBrokers: () => this.activeBrokers.map((entry) => ({
136875
136778
  id: entry.id,
@@ -136881,50 +136784,31 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
136881
136784
  return entry === void 0 ? null : readBrokerSeed(this.ctx.dataDir, entry);
136882
136785
  },
136883
136786
  sessions: RecordsSpeakSessionStore.of(records),
136884
- clearLegacySecrets: async () => {
136885
- await this.updateGlobalSettingsAsOperator({ speak: {
136886
- ...this.config.speak,
136887
- password: SECRET_CLEAR,
136888
- session: SECRET_CLEAR
136889
- } });
136890
- },
136891
136787
  ensurePython: () => this.ctx.deps.ensurePython()
136892
136788
  });
136893
136789
  this.speak = speak;
136894
136790
  speak.start().catch((err) => {
136895
136791
  this.ctx.logger.warn("alexa speak: worker did not start", { meta: { error: err instanceof Error ? err.message : String(err) } });
136896
136792
  });
136897
- const pages = {
136898
- id: this.addonId,
136899
- listPages: () => [SPEAK_PAGE]
136900
- };
136901
- return {
136902
- providers: [
136903
- ...regs,
136904
- {
136905
- capability: brokerCapability,
136906
- provider: this.buildBrokerProvider()
136907
- },
136908
- {
136909
- capability: deviceAdoptionCapability,
136910
- provider: this.buildAdoptionProvider()
136911
- },
136912
- {
136913
- capability: addonRoutesCapability,
136914
- provider: buildAddonRouteProvider("import-alexa", loginRoutes)
136915
- },
136916
- {
136917
- capability: notificationOutputCapability,
136918
- provider: speak.provider
136919
- },
136920
- {
136921
- capability: addonPagesSourceCapability,
136922
- provider: pages
136923
- }
136924
- ],
136925
- customActions: alexaSpeakActions,
136926
- actionHandlers: speak.handlers()
136927
- };
136793
+ return { providers: [
136794
+ ...regs,
136795
+ {
136796
+ capability: brokerCapability,
136797
+ provider: this.buildBrokerProvider()
136798
+ },
136799
+ {
136800
+ capability: deviceAdoptionCapability,
136801
+ provider: this.buildAdoptionProvider()
136802
+ },
136803
+ {
136804
+ capability: addonRoutesCapability,
136805
+ provider: buildAddonRouteProvider("import-alexa", loginRoutes)
136806
+ },
136807
+ {
136808
+ capability: notificationOutputCapability,
136809
+ provider: speak.provider
136810
+ }
136811
+ ] };
136928
136812
  }
136929
136813
  async loadPersistedBrokers() {
136930
136814
  const store = this.brokerStore;
@@ -137315,12 +137199,10 @@ exports.StubAlexaClient = StubAlexaClient;
137315
137199
  exports.__toCommonJS = __toCommonJS;
137316
137200
  exports.alexaAddonConfigSchema = alexaAddonConfigSchema;
137317
137201
  exports.alexaConnectionSchema = alexaConnectionSchema;
137318
- exports.alexaSpeakActions = alexaSpeakActions;
137319
137202
  exports.buildAlexaApplianceCandidate = buildAlexaApplianceCandidate;
137320
137203
  exports.buildConnectionFormSchema = buildConnectionFormSchema;
137321
137204
  exports.connectionToCredentials = connectionToCredentials;
137322
137205
  exports.createAlexaClient = createAlexaClient;
137323
- exports.customActions = alexaSpeakActions;
137324
137206
  exports.mapAlexaCapabilitiesToCamStack = mapAlexaCapabilitiesToCamStack;
137325
137207
  exports.mappableAppliance = mappableAppliance;
137326
137208
  exports.settingsToAlexaConnection = settingsToAlexaConnection;