@camstack/addon-import-alexa 0.2.162 → 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 +13 -0
  2. package/dist/addon.js +335 -411
  3. package/dist/addon.mjs +336 -410
  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-Ck3EMdmv.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-CoSVGWK6.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-BA8fy0_J.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.mjs CHANGED
@@ -8864,28 +8864,6 @@ function event(data) {
8864
8864
  var StaticDirOutputSchema$1 = object({ staticDir: string() });
8865
8865
  var VersionOutputSchema$1 = object({ version: string() });
8866
8866
  method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
8867
- /**
8868
- * Identity — preserves literal types for downstream inference.
8869
- *
8870
- * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
8871
- * TypeScript does not widen each entry's literal `kind`/`auth` fields to
8872
- * the broader unions declared on `CustomActionSpec`'s default generics.
8873
- * Shape validity is enforced separately by the `customAction(...)` helper
8874
- * whose return type is already a `CustomActionSpec<...>`.
8875
- */
8876
- function defineCustomActions(spec) {
8877
- return spec;
8878
- }
8879
- function customAction(input, output, options) {
8880
- return {
8881
- input,
8882
- output,
8883
- kind: options?.kind ?? "query",
8884
- auth: options?.auth ?? "protected",
8885
- scope: options?.scope ?? { kind: "system" },
8886
- ...options?.caller ? { caller: "required" } : {}
8887
- };
8888
- }
8889
8867
  var StaticDirOutputSchema = object({ staticDir: string() });
8890
8868
  var VersionOutputSchema = object({ version: string() });
8891
8869
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -11230,7 +11208,7 @@ function errMsg(err) {
11230
11208
  return String(err);
11231
11209
  }
11232
11210
  //#endregion
11233
- //#region ../types/dist/composition-CZImLpOI.mjs
11211
+ //#region ../types/dist/composition-DwA0uVW5.mjs
11234
11212
  /**
11235
11213
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11236
11214
  *
@@ -16475,24 +16453,7 @@ var AddonPageDeclarationSchema = object({
16475
16453
  /** Display label for a CUSTOM `section` id (ignored for well-known ids). */
16476
16454
  sectionLabel: string().optional()
16477
16455
  });
16478
- var addonPagesSourceCapability = {
16479
- name: "addon-pages-source",
16480
- scope: "system",
16481
- mode: "collection",
16482
- internal: true,
16483
- methods: {
16484
- /**
16485
- * `internal: true` did not gate the mount (see the 2026-08-26 note on
16486
- * `data-store-provider`) — this was reachable on the AppRouter by ANY
16487
- * authenticated session at the default `auth: 'protected'`. The only
16488
- * consumer, the `addon-pages-aggregator` builtin, reads it via
16489
- * `ctx.capabilities.getCollection('addon-pages-source')` — LOCAL-PROCESS
16490
- * only, never tRPC (per `ctx.capabilities` seeing only the local
16491
- * process). The public, enriched listing admin-ui actually consumes is
16492
- * the separate `addon-pages` cap, unaffected by this change.
16493
- */
16494
- listPages: method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" }) }
16495
- };
16456
+ method(_void(), array(AddonPageDeclarationSchema).readonly(), { auth: "admin" });
16496
16457
  var AddonHttpRouteSchema = object({
16497
16458
  method: _enum([
16498
16459
  "GET",
@@ -19450,12 +19411,13 @@ var CHANGED = {
19450
19411
  expr: "now"
19451
19412
  };
19452
19413
  function numeric(input) {
19414
+ const { alsoFrom, ...base } = input;
19453
19415
  return {
19454
- ...input,
19416
+ ...base,
19455
19417
  family: "numeric",
19456
19418
  deviceType: DeviceType.Sensor,
19457
19419
  memberDeviceType: DeviceType.Sensor,
19458
- memberDeviceTypes: [DeviceType.Sensor],
19420
+ memberDeviceTypes: [DeviceType.Sensor, ...alsoFrom?.deviceTypes ?? []],
19459
19421
  activeWhen: void 0,
19460
19422
  commandCaps: [],
19461
19423
  baseUnit: baseUnitOf(input.quantity),
@@ -19463,9 +19425,10 @@ function numeric(input) {
19463
19425
  accept: [{
19464
19426
  cap: input.outputCap,
19465
19427
  fieldPath: input.outputField
19466
- }]
19428
+ }, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
19467
19429
  };
19468
19430
  }
19431
+ var CLIMATE_DEVICES = [DeviceType.Thermostat, DeviceType.Climate];
19469
19432
  function binary(input) {
19470
19433
  return {
19471
19434
  ...input,
@@ -19491,7 +19454,14 @@ var GROUP_TEMPLATES = [
19491
19454
  quantity: "temperature",
19492
19455
  defaultAggregate: "mean",
19493
19456
  empty: "null",
19494
- companions: [NOW$2]
19457
+ companions: [NOW$2],
19458
+ alsoFrom: {
19459
+ deviceTypes: CLIMATE_DEVICES,
19460
+ accept: {
19461
+ cap: "climate-control",
19462
+ fieldPath: "currentTemp"
19463
+ }
19464
+ }
19495
19465
  }),
19496
19466
  numeric({
19497
19467
  id: "humidity",
@@ -19502,7 +19472,14 @@ var GROUP_TEMPLATES = [
19502
19472
  quantity: "humidity",
19503
19473
  defaultAggregate: "mean",
19504
19474
  empty: "null",
19505
- companions: [NOW$2]
19475
+ companions: [NOW$2],
19476
+ alsoFrom: {
19477
+ deviceTypes: CLIMATE_DEVICES,
19478
+ accept: {
19479
+ cap: "climate-control",
19480
+ fieldPath: "currentHumidity"
19481
+ }
19482
+ }
19506
19483
  }),
19507
19484
  numeric({
19508
19485
  id: "pressure",
@@ -45217,6 +45194,43 @@ var NotificationActionSchema = object({
45217
45194
  */
45218
45195
  mode: _enum(["navigate", "background"]).optional()
45219
45196
  });
45197
+ /** A delivery parameter value: what a notifier declared it takes. */
45198
+ var DeliveryParamValueSchema = union([
45199
+ string(),
45200
+ number(),
45201
+ boolean()
45202
+ ]);
45203
+ /**
45204
+ * One extra delivery parameter a notifier KIND declares (D938). A rule that
45205
+ * addresses a target of that kind may set it; a kind that declares nothing is
45206
+ * never handed one. The admin UI derives its control from this descriptor —
45207
+ * no editor knows a service by name.
45208
+ *
45209
+ * Optional fields are `.optional()`, never `.default()` (a Zod default does not
45210
+ * run on the addon cap path).
45211
+ */
45212
+ var DeliveryParamSchema = object({
45213
+ key: string().min(1),
45214
+ label: string(),
45215
+ /** i18n key of the label, resolved by the declaring addon's namespace. */
45216
+ labelKey: string().optional(),
45217
+ type: _enum([
45218
+ "enum",
45219
+ "number",
45220
+ "boolean",
45221
+ "string"
45222
+ ]),
45223
+ /** `enum` only: the closed set of accepted values. */
45224
+ options: array(object({
45225
+ value: string(),
45226
+ label: string()
45227
+ })).optional(),
45228
+ /** `number` only. */
45229
+ min: number().optional(),
45230
+ max: number().optional(),
45231
+ /** What the notifier does when the rule sets nothing; shown as the placeholder. */
45232
+ default: DeliveryParamValueSchema.optional()
45233
+ });
45220
45234
  /**
45221
45235
  * The canonical notification. `body` is the only hard field (Apprise model).
45222
45236
  * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
@@ -45239,7 +45253,13 @@ var NotificationSchema = object({
45239
45253
  tag: string().optional(),
45240
45254
  deviceId: number().optional(),
45241
45255
  eventId: string().optional(),
45242
- metadata: record(string(), unknown()).optional()
45256
+ metadata: record(string(), unknown()).optional(),
45257
+ /**
45258
+ * Values for the parameters the target's kind DECLARES in
45259
+ * {@link TargetKindSchema} `deliveryParams`. Already validated against the
45260
+ * declaration by the sender; a notifier reads only the keys it declared.
45261
+ */
45262
+ delivery: record(string(), DeliveryParamValueSchema).optional()
45243
45263
  });
45244
45264
  /** One declared native severity/priority level for a kind. */
45245
45265
  var TargetKindLevelSchema = object({
@@ -45345,6 +45365,11 @@ var TargetKindSchema = object({
45345
45365
  */
45346
45366
  destinationKeys: array(string()).optional(),
45347
45367
  supportsDiscovery: boolean(),
45368
+ /**
45369
+ * Extra delivery parameters a rule may set for a target of this kind (D938).
45370
+ * Absent = none; the rule editor then offers nothing.
45371
+ */
45372
+ deliveryParams: array(DeliveryParamSchema).optional(),
45348
45373
  caps: TargetKindCapsSchema
45349
45374
  });
45350
45375
  /**
@@ -57658,7 +57683,9 @@ var ManifestShapeSchema = object({
57658
57683
  icon: string().optional()
57659
57684
  }).optional(),
57660
57685
  protected: boolean().optional(),
57661
- removable: boolean().optional()
57686
+ removable: boolean().optional(),
57687
+ /** Declared Integrations-page block (mirror of `AddonDeclaration.integrationSection`). */
57688
+ integrationSection: _enum(["system", "providers"]).optional()
57662
57689
  });
57663
57690
  var AddonProcessSchema = object({
57664
57691
  pid: number().optional(),
@@ -90808,6 +90835,29 @@ function rewriteReferer(referer, route) {
90808
90835
  if (upstream === void 0) return null;
90809
90836
  return `${upstream.scheme}://${upstream.host}${rest?.[2] ?? "/"}${url.search}`;
90810
90837
  }
90838
+ function decodeFormComponent(raw) {
90839
+ try {
90840
+ return decodeURIComponent(raw.replace(/\+/g, " "));
90841
+ } catch {
90842
+ return null;
90843
+ }
90844
+ }
90845
+ /**
90846
+ * An `application/x-www-form-urlencoded` body whose field VALUES that are proxy URLs of
90847
+ * this attempt (absolute or root-relative) are turned back into the vendor URL they stand
90848
+ * for — the page's hidden inputs were rewritten on the way out (`rewriteBody`), and the
90849
+ * vendor must get its own URL back. Field order and every untouched pair stay byte-identical.
90850
+ */
90851
+ function rewriteFormBody(body, route) {
90852
+ return body.split("&").map((pair) => {
90853
+ const eq = pair.indexOf("=");
90854
+ if (eq === -1) return pair;
90855
+ const value = decodeFormComponent(pair.slice(eq + 1));
90856
+ if (value === null || !/^(?:https?:\/\/|\/)/i.test(value)) return pair;
90857
+ const back = rewriteReferer(value, route);
90858
+ return back === null ? pair : `${pair.slice(0, eq + 1)}${encodeURIComponent(back)}`;
90859
+ }).join("&");
90860
+ }
90811
90861
  /** Cookies the browser holds for the hub itself must never reach a vendor. */
90812
90862
  function filterForwardedCookies(cookieHeader) {
90813
90863
  return cookieHeader.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !part.toLowerCase().startsWith("camstack_")).join("; ");
@@ -90866,6 +90916,9 @@ function bodyKindOf(contentType) {
90866
90916
  if (type.startsWith("text/") || type.includes("javascript") || type.includes("json") || type.includes("xml")) return "other";
90867
90917
  return null;
90868
90918
  }
90919
+ function isFormEncoded(contentType) {
90920
+ return (firstHeader(contentType) ?? "").toLowerCase().startsWith("application/x-www-form-urlencoded");
90921
+ }
90869
90922
  function firstHeader(value) {
90870
90923
  return Array.isArray(value) ? value[0] : value;
90871
90924
  }
@@ -90996,7 +91049,7 @@ var VendorLoginProxy = class {
90996
91049
  sendPlain(res, 413, "Request too large");
90997
91050
  return;
90998
91051
  }
90999
- body = read;
91052
+ body = isFormEncoded(req.headers["content-type"]) ? Buffer.from(rewriteFormBody(read.toString("utf8"), route), "utf8") : read;
91000
91053
  }
91001
91054
  const headers = this.#requestHeaders(req, route, upstream);
91002
91055
  const upstreamRes = await this.#fetch(target, {
@@ -91010,9 +91063,10 @@ var VendorLoginProxy = class {
91010
91063
  const kind = bodyKindOf(upstreamRes.headers.get("content-type") ?? "");
91011
91064
  const declaredLength = Number(upstreamRes.headers.get("content-length") ?? "0");
91012
91065
  const text = kind !== null && !(declaredLength > MAX_TEXT_BODY_BYTES) && method !== "HEAD" ? await upstreamRes.text() : null;
91013
- this.#logger.debug("vendor login proxy: upstream answered", { meta: {
91066
+ this.#logger.info("vendor login proxy: upstream answered", { meta: {
91014
91067
  addonId: this.#addonId,
91015
91068
  host: upstream.host,
91069
+ path: target.pathname,
91016
91070
  status: upstreamRes.status,
91017
91071
  method
91018
91072
  } });
@@ -91145,59 +91199,37 @@ var AlexaBrokerEntrySchema = object({
91145
91199
  */
91146
91200
  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)") });
91147
91201
  /**
91148
- * Echo speech settings. Speech signs in as an import broker, so the targets
91149
- * are what lives here. `email`, `password`, `region` and `session` are the OLD
91150
- * standalone login: they are read once to migrate it onto the single broker
91151
- * (see `speak/migration.ts`) and written back empty.
91152
- */
91153
- var alexaSpeakSettingsSchema = object({
91154
- email: string().default(""),
91155
- password: string().default("").describe("Legacy Amazon password").meta({ secret: true }),
91156
- region: _enum([
91157
- "NA",
91158
- "EU",
91159
- "FE"
91160
- ]).default("EU"),
91161
- session: string().default("").describe("Legacy Alexa session blob").meta({ secret: true }),
91162
- targets: array(object({
91163
- id: string(),
91164
- name: string(),
91165
- enabled: boolean(),
91166
- /** The broker (Amazon account) the Echo belongs to; '' until one is chosen. */
91167
- brokerId: string().default(""),
91168
- echoId: string(),
91169
- mode: _enum(["speak", "announce"]),
91170
- volume: number(),
91171
- respectDnd: boolean()
91172
- })).default([])
91202
+ * The Echo notification targets this addon owns. Speech signs in as an import
91203
+ * broker, so a target is only the Echo it speaks on and how.
91204
+ */
91205
+ var alexaEchoTargetSchema = object({
91206
+ id: string(),
91207
+ name: string(),
91208
+ enabled: boolean(),
91209
+ /** The broker (Amazon account) the Echo belongs to; '' until one is chosen. */
91210
+ brokerId: string().default(""),
91211
+ echoId: string(),
91212
+ mode: _enum(["speak", "announce"]),
91213
+ volume: number(),
91214
+ respectDnd: boolean()
91173
91215
  });
91174
91216
  function isRecord$3(value) {
91175
91217
  return typeof value === "object" && value !== null && !Array.isArray(value);
91176
91218
  }
91177
- /** Resolved settings. A keychain marker (an object) is an empty secret, not a password. */
91178
- function readSpeakSettings(config) {
91179
- const raw = isRecord$3(config.speak) ? config.speak : {};
91180
- const region = raw["region"];
91181
- const targets = raw["targets"];
91182
- return alexaSpeakSettingsSchema.parse({
91183
- email: typeof raw["email"] === "string" ? raw["email"] : "",
91184
- password: typeof raw["password"] === "string" ? raw["password"] : "",
91185
- session: typeof raw["session"] === "string" ? raw["session"] : "",
91186
- ...region === "NA" || region === "EU" || region === "FE" ? { region } : {},
91187
- ...Array.isArray(targets) ? { targets } : {}
91219
+ /** The stored Echo targets; a row that no longer parses is dropped, never guessed. */
91220
+ function readEchoTargets(config) {
91221
+ const raw = config.echoTargets;
91222
+ if (!Array.isArray(raw)) return [];
91223
+ return raw.flatMap((row) => {
91224
+ const parsed = alexaEchoTargetSchema.safeParse(isRecord$3(row) ? row : {});
91225
+ return parsed.success ? [parsed.data] : [];
91188
91226
  });
91189
91227
  }
91190
91228
  /** Top-level addon config — broker entries + the proxy-login settings. */
91191
91229
  var alexaAddonConfigSchema = object({
91192
91230
  brokers: array(AlexaBrokerEntrySchema).default([]),
91193
91231
  proxyLogin: alexaProxyLoginSchema.default({ proxyPort: 3456 }),
91194
- speak: alexaSpeakSettingsSchema.default({
91195
- email: "",
91196
- password: "",
91197
- region: "EU",
91198
- session: "",
91199
- targets: []
91200
- })
91232
+ echoTargets: array(alexaEchoTargetSchema).default([])
91201
91233
  });
91202
91234
  /** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
91203
91235
  function toRecord(value) {
@@ -132744,6 +132776,67 @@ var ALEXA_API_USER_AGENT_POSTFIX = `AlexaRemote/${{
132744
132776
  readmeFilename: "readme.md"
132745
132777
  }.version}`;
132746
132778
  //#endregion
132779
+ //#region src/alexa-region-options.ts
132780
+ var DEFAULT_LOCALE = "en-US";
132781
+ var LOCALE_BY_DOMAIN = {
132782
+ "amazon.com": "en-US",
132783
+ "amazon.it": "it-IT",
132784
+ "amazon.de": "de-DE",
132785
+ "amazon.co.uk": "en-GB",
132786
+ "amazon.fr": "fr-FR",
132787
+ "amazon.es": "es-ES",
132788
+ "amazon.ca": "en-CA",
132789
+ "amazon.com.au": "en-AU",
132790
+ "amazon.co.jp": "ja-JP",
132791
+ "amazon.in": "en-IN",
132792
+ "amazon.com.br": "pt-BR",
132793
+ "amazon.com.mx": "es-MX"
132794
+ };
132795
+ function alexaRegionOptions(amazonDomain) {
132796
+ const domain = amazonDomain.length > 0 ? amazonDomain : "amazon.com";
132797
+ const locale = LOCALE_BY_DOMAIN[domain] ?? DEFAULT_LOCALE;
132798
+ return {
132799
+ baseAmazonPage: domain,
132800
+ amazonPage: domain,
132801
+ acceptLanguage: locale,
132802
+ amazonPageProxyLanguage: locale.replace("-", "_")
132803
+ };
132804
+ }
132805
+ //#endregion
132806
+ //#region src/alexa-proxy-trace.ts
132807
+ /**
132808
+ * Sanitised one-line trace of the alexa-cookie2 proxy's own log lines.
132809
+ *
132810
+ * alexa-cookie2 logs every step it takes (routing, upstream request, redirect),
132811
+ * but its lines also carry headers, cookies and query values. This keeps only
132812
+ * what diagnoses a failed login: the method, the path or upstream host, and the
132813
+ * redirect target — always WITHOUT a query string or fragment. Any line that is
132814
+ * not one of the known step lines yields `null` and is never traced.
132815
+ */
132816
+ var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+) \//;
132817
+ var PROXY_REQUEST_RE = /^Alexa-Cookie: Proxy-Request: ([A-Z]+) (\S+)$/;
132818
+ var PROXY_RESPONSE_RE = /^Alexa-Cookie: Proxy Response from Host: (\S+)$/;
132819
+ var REDIRECT_ORIGINAL_RE = /^Redirect: Original Location ----> (\S+)$/;
132820
+ var REDIRECT_FINAL_RE = /^Redirect: Final Location ----> (\S+)$/;
132821
+ /** Cut a URL or path at its query string or fragment. */
132822
+ function stripQuery(target) {
132823
+ const cut = target.search(/[?#]/);
132824
+ return cut === -1 ? target : target.slice(0, cut);
132825
+ }
132826
+ function sanitizeProxyTraceLine(line) {
132827
+ const router = ROUTER_RE.exec(line);
132828
+ if (router !== null) return `router ${router[2]} ${stripQuery(router[1] ?? "")}`;
132829
+ const request = PROXY_REQUEST_RE.exec(line);
132830
+ if (request !== null) return `proxy-request ${request[1]} ${stripQuery(request[2] ?? "")}`;
132831
+ const response = PROXY_RESPONSE_RE.exec(line);
132832
+ if (response !== null) return `proxy-response host=${response[1]}`;
132833
+ const original = REDIRECT_ORIGINAL_RE.exec(line);
132834
+ if (original !== null) return `redirect-original ${stripQuery(original[1] ?? "")}`;
132835
+ const final = REDIRECT_FINAL_RE.exec(line);
132836
+ if (final !== null) return `redirect-final ${stripQuery(final[1] ?? "")}`;
132837
+ return null;
132838
+ }
132839
+ //#endregion
132747
132840
  //#region src/alexa-proxy-login.ts
132748
132841
  /**
132749
132842
  * INTERACTIVE Amazon login via `alexa-remote2`'s built-in proxy auth mode, behind the
@@ -132842,7 +132935,7 @@ var AlexaProxyLoginSession = class {
132842
132935
  async start() {
132843
132936
  const remote = new import_alexa_remote.default();
132844
132937
  this.#remote = remote;
132845
- const amazonPage = this.#opts.amazonDomain.length > 0 ? this.#opts.amazonDomain : "amazon.com";
132938
+ const region = alexaRegionOptions(this.#opts.amazonDomain);
132846
132939
  remote.on("cookie", () => this.#onCookieEvent(remote));
132847
132940
  remote.init({
132848
132941
  proxyOnly: true,
@@ -132850,10 +132943,15 @@ var AlexaProxyLoginSession = class {
132850
132943
  proxyListenBind: ALEXA_PROXY_LOOPBACK_HOST,
132851
132944
  proxyPort: this.#opts.proxyPort,
132852
132945
  proxyLogLevel: "warn",
132853
- amazonPage,
132946
+ ...region,
132854
132947
  apiUserAgentPostfix: ALEXA_API_USER_AGENT_POSTFIX,
132855
132948
  logger: (...args) => {
132856
132949
  const line = args.map(String).join(" ");
132950
+ const trace = sanitizeProxyTraceLine(line);
132951
+ if (trace !== null) {
132952
+ this.#logger.info(`alexa proxy-login: ${trace}`, { meta: { sessionId: this.#opts.sessionId } });
132953
+ return;
132954
+ }
132857
132955
  if (SECRET_LINE_RE.test(line)) return;
132858
132956
  this.#logger.debug("alexa proxy-login", { meta: {
132859
132957
  sessionId: this.#opts.sessionId,
@@ -132915,7 +133013,10 @@ var AlexaProxyLoginSession = class {
132915
133013
  if (isProxyReadyError(err)) {
132916
133014
  if (this.#phase === "starting") {
132917
133015
  this.#phase = "awaiting-login";
132918
- this.#logger.info("alexa proxy-login: proxy ready — awaiting Amazon login", { meta: { sessionId: this.#opts.sessionId } });
133016
+ this.#logger.info("alexa proxy-login: proxy ready — awaiting Amazon login", { meta: {
133017
+ sessionId: this.#opts.sessionId,
133018
+ amazonDomain: this.#opts.amazonDomain
133019
+ } });
132919
133020
  }
132920
133021
  return;
132921
133022
  }
@@ -135108,126 +135209,6 @@ var AlexaBrokerStore = class AlexaBrokerStore {
135108
135209
  }
135109
135210
  };
135110
135211
  //#endregion
135111
- //#region src/speak/constants.ts
135112
- /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
135113
- var ALEXA_SPEAK_ADDON_ID = "import-alexa";
135114
- var ALEXA_SPEAK_KIND = "alexa-echo";
135115
- var DEVICE_REFRESH_MS = 5 * 6e4;
135116
- /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
135117
- var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
135118
- //#endregion
135119
- //#region src/speak/worker-protocol.ts
135120
- /** Line-delimited JSON the long-lived Python worker speaks. One object per line. */
135121
- var EchoDeviceSchema = object({
135122
- id: string().min(1),
135123
- name: string(),
135124
- family: string(),
135125
- online: boolean(),
135126
- kind: _enum(["device", "group"]),
135127
- dnd: boolean(),
135128
- deviceType: string(),
135129
- /** The room the account files the device under, when Amazon names one. */
135130
- room: string().optional()
135131
- });
135132
- var LoginStatusSchema = _enum(["ok", "failed"]);
135133
- var SessionStatusSchema = object({
135134
- loggedIn: boolean(),
135135
- ageSec: number().int().nonnegative(),
135136
- lastSuccessAt: number().int().nullable()
135137
- });
135138
- var TargetDeliverySchema = object({
135139
- targetId: string(),
135140
- ok: boolean(),
135141
- error: string().optional()
135142
- });
135143
- var WorkerLineSchema = union([object({
135144
- id: string(),
135145
- ok: literal(true),
135146
- result: unknown().optional()
135147
- }), object({
135148
- id: string(),
135149
- ok: literal(false),
135150
- error: string()
135151
- })]);
135152
- function requestLine(id, request) {
135153
- return `${JSON.stringify({
135154
- id,
135155
- ...request
135156
- })}\n`;
135157
- }
135158
- function parseLoginResult(result) {
135159
- const parsed = object({
135160
- status: LoginStatusSchema,
135161
- error: string().optional()
135162
- }).safeParse(result);
135163
- if (!parsed.success) return {
135164
- status: "failed",
135165
- error: "worker returned an unexpected login result"
135166
- };
135167
- return parsed.data;
135168
- }
135169
- function parseDevices(result) {
135170
- const parsed = object({ devices: array(EchoDeviceSchema) }).safeParse(result);
135171
- if (!parsed.success) throw new Error("worker returned an unexpected device list");
135172
- return parsed.data.devices;
135173
- }
135174
- function parseDeliveries(result) {
135175
- const parsed = object({ outcomes: array(TargetDeliverySchema) }).safeParse(result);
135176
- if (!parsed.success) throw new Error("worker returned an unexpected delivery list");
135177
- return parsed.data.outcomes;
135178
- }
135179
- function parseSessionStatus(result) {
135180
- const parsed = SessionStatusSchema.safeParse(result);
135181
- if (!parsed.success) throw new Error("worker returned an unexpected session status");
135182
- return parsed.data;
135183
- }
135184
- //#endregion
135185
- //#region src/speak/actions.ts
135186
- var BrokerEchoStateSchema = _enum([
135187
- "starting",
135188
- "ready",
135189
- "broker-not-signed-in",
135190
- "failed"
135191
- ]);
135192
- var SpeakStatusSchema = object({
135193
- brokers: array(object({
135194
- id: string(),
135195
- name: string(),
135196
- state: BrokerEchoStateSchema,
135197
- error: string().optional(),
135198
- devices: array(EchoDeviceSchema),
135199
- session: SessionStatusSchema
135200
- })),
135201
- /** Targets that name no Amazon account yet (they cannot speak until one is chosen). */
135202
- unassignedTargets: number().int().nonnegative(),
135203
- /** What the old standalone login left behind, named; '' when nothing is pending. */
135204
- notice: _enum([
135205
- "",
135206
- "no-broker",
135207
- "choose-broker"
135208
- ])
135209
- });
135210
- var alexaSpeakActions = defineCustomActions({
135211
- speakStatus: customAction(object({}), SpeakStatusSchema, {
135212
- kind: "mutation",
135213
- auth: "admin"
135214
- }),
135215
- sendTest: customAction(object({
135216
- brokerId: string().min(1),
135217
- echoIds: array(string().min(1)).min(1),
135218
- mode: _enum(["speak", "announce"]),
135219
- volume: number().int().min(0).max(100),
135220
- text: string().min(1).max(450)
135221
- }), object({ outcomes: array(object({
135222
- echoId: string(),
135223
- ok: boolean(),
135224
- error: string().optional()
135225
- })) }), {
135226
- kind: "mutation",
135227
- auth: "admin"
135228
- })
135229
- });
135230
- //#endregion
135231
135212
  //#region src/speak/broker-seed.ts
135232
135213
  /**
135233
135214
  * What the Echo worker needs from an import broker to act as the same Amazon
@@ -135462,6 +135443,72 @@ function scrubSecrets(text, secrets) {
135462
135443
  return out;
135463
135444
  }
135464
135445
  //#endregion
135446
+ //#region src/speak/worker-protocol.ts
135447
+ /** Line-delimited JSON the long-lived Python worker speaks. One object per line. */
135448
+ var EchoDeviceSchema = object({
135449
+ id: string().min(1),
135450
+ name: string(),
135451
+ family: string(),
135452
+ online: boolean(),
135453
+ kind: _enum(["device", "group"]),
135454
+ dnd: boolean(),
135455
+ deviceType: string(),
135456
+ /** The room the account files the device under, when Amazon names one. */
135457
+ room: string().optional()
135458
+ });
135459
+ var LoginStatusSchema = _enum(["ok", "failed"]);
135460
+ var SessionStatusSchema = object({
135461
+ loggedIn: boolean(),
135462
+ ageSec: number().int().nonnegative(),
135463
+ lastSuccessAt: number().int().nullable()
135464
+ });
135465
+ var TargetDeliverySchema = object({
135466
+ targetId: string(),
135467
+ ok: boolean(),
135468
+ error: string().optional()
135469
+ });
135470
+ var WorkerLineSchema = union([object({
135471
+ id: string(),
135472
+ ok: literal(true),
135473
+ result: unknown().optional()
135474
+ }), object({
135475
+ id: string(),
135476
+ ok: literal(false),
135477
+ error: string()
135478
+ })]);
135479
+ function requestLine(id, request) {
135480
+ return `${JSON.stringify({
135481
+ id,
135482
+ ...request
135483
+ })}\n`;
135484
+ }
135485
+ function parseLoginResult(result) {
135486
+ const parsed = object({
135487
+ status: LoginStatusSchema,
135488
+ error: string().optional()
135489
+ }).safeParse(result);
135490
+ if (!parsed.success) return {
135491
+ status: "failed",
135492
+ error: "worker returned an unexpected login result"
135493
+ };
135494
+ return parsed.data;
135495
+ }
135496
+ function parseDevices(result) {
135497
+ const parsed = object({ devices: array(EchoDeviceSchema) }).safeParse(result);
135498
+ if (!parsed.success) throw new Error("worker returned an unexpected device list");
135499
+ return parsed.data.devices;
135500
+ }
135501
+ function parseDeliveries(result) {
135502
+ const parsed = object({ outcomes: array(TargetDeliverySchema) }).safeParse(result);
135503
+ if (!parsed.success) throw new Error("worker returned an unexpected delivery list");
135504
+ return parsed.data.outcomes;
135505
+ }
135506
+ function parseSessionStatus(result) {
135507
+ const parsed = SessionStatusSchema.safeParse(result);
135508
+ if (!parsed.success) throw new Error("worker returned an unexpected session status");
135509
+ return parsed.data;
135510
+ }
135511
+ //#endregion
135465
135512
  //#region src/speak/broker-echo.ts
135466
135513
  var BrokerEcho = class {
135467
135514
  deps;
@@ -135590,40 +135637,13 @@ var BrokerEcho = class {
135590
135637
  }
135591
135638
  };
135592
135639
  //#endregion
135593
- //#region src/speak/migration.ts
135594
- function legacyLoginOf(settings) {
135595
- return {
135596
- email: settings.email,
135597
- password: settings.password,
135598
- session: settings.session
135599
- };
135600
- }
135601
- function planLegacyMigration(legacy, brokerIds, targets) {
135602
- const present = legacy.email.length > 0 || legacy.password.length > 0 || legacy.session.length > 0;
135603
- const only = brokerIds.length === 1 ? brokerIds[0] ?? null : null;
135604
- if (!present || only === null) return {
135605
- present,
135606
- attachTo: null,
135607
- targets
135608
- };
135609
- return {
135610
- present,
135611
- attachTo: legacy.session.length > 0 ? only : null,
135612
- targets: targets.map((row) => row.brokerId.length > 0 ? row : {
135613
- ...row,
135614
- brokerId: only
135615
- })
135616
- };
135617
- }
135618
- /** What the page must tell the operator about targets that name no account. */
135619
- function noticeFor(targets, brokerIds) {
135620
- if (!targets.some((row) => row.brokerId.length === 0)) return "";
135621
- if (brokerIds.length === 0) return "no-broker";
135622
- return brokerIds.length > 1 ? "choose-broker" : "";
135623
- }
135624
- function unassignedCount(targets) {
135625
- return targets.filter((row) => row.brokerId.length === 0).length;
135626
- }
135640
+ //#region src/speak/constants.ts
135641
+ /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
135642
+ var ALEXA_SPEAK_ADDON_ID = "import-alexa";
135643
+ var ALEXA_SPEAK_KIND = "alexa-echo";
135644
+ var DEVICE_REFRESH_MS = 5 * 6e4;
135645
+ /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
135646
+ var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
135627
135647
  //#endregion
135628
135648
  //#region src/speak/rate-limit.ts
135629
135649
  /**
@@ -135728,14 +135748,42 @@ function readString(config, key) {
135728
135748
  const value = config[key];
135729
135749
  return typeof value === "string" ? value : "";
135730
135750
  }
135731
- function readMode(config) {
135732
- return config["mode"] === "announce" ? "announce" : "speak";
135751
+ /** The rule's value for this send wins; the target's saved config is the fallback. */
135752
+ function readMode(config, delivery) {
135753
+ return (delivery?.["mode"] ?? config["mode"]) === "announce" ? "announce" : "speak";
135733
135754
  }
135734
- function readVolume(config) {
135735
- const value = config["volume"];
135755
+ function readVolume(config, delivery) {
135756
+ const ruled = delivery?.["volume"];
135757
+ const value = typeof ruled === "number" ? ruled : config["volume"];
135736
135758
  if (typeof value !== "number" || !Number.isFinite(value)) return 40;
135737
135759
  return Math.max(0, Math.min(100, Math.round(value)));
135738
135760
  }
135761
+ /**
135762
+ * The delivery parameters a rule may set for an Echo target (D938). The
135763
+ * target's own config holds the default; these override it per notification.
135764
+ */
135765
+ var DELIVERY_PARAMS = [{
135766
+ key: "mode",
135767
+ label: "Mode",
135768
+ labelKey: "import-alexa:echoTarget.s_echo.mode.label",
135769
+ type: "enum",
135770
+ options: [{
135771
+ value: "speak",
135772
+ label: "Speak (quiet hours apply)"
135773
+ }, {
135774
+ value: "announce",
135775
+ label: "Announce (chime, then speech)"
135776
+ }],
135777
+ default: "speak"
135778
+ }, {
135779
+ key: "volume",
135780
+ label: "Volume",
135781
+ labelKey: "import-alexa:echoTarget.s_echo.volume.label",
135782
+ type: "number",
135783
+ min: 0,
135784
+ max: 100,
135785
+ default: 40
135786
+ }];
135739
135787
  function respectDnd(config) {
135740
135788
  return config["respectDnd"] !== false;
135741
135789
  }
@@ -135836,6 +135884,7 @@ function alexaTargetKind(brokers) {
135836
135884
  supportsDiscovery: true,
135837
135885
  destinationKeys: ["brokerId", "echoId"],
135838
135886
  configSchema: configSchema(brokers),
135887
+ deliveryParams: DELIVERY_PARAMS,
135839
135888
  caps: {
135840
135889
  attachments: {
135841
135890
  mediaTypes: [],
@@ -136018,14 +136067,14 @@ function createAlexaNotificationProvider(ports) {
136018
136067
  renderedAs: speech.renderedAs
136019
136068
  };
136020
136069
  }
136021
- const mode = readMode(target.config);
136070
+ const mode = readMode(target.config, notification.delivery);
136022
136071
  try {
136023
136072
  const outcomes = await ports.speak({
136024
136073
  brokerId: broker.id,
136025
136074
  mode,
136026
136075
  targets: [echoId],
136027
136076
  text: speech.text,
136028
- volume: readVolume(target.config)
136077
+ volume: readVolume(target.config, notification.delivery)
136029
136078
  });
136030
136079
  const one = outcomes.find((row) => row.targetId === echoId) ?? outcomes[0];
136031
136080
  if (one === void 0 || !one.ok) {
@@ -136425,8 +136474,8 @@ function startAlexaWorker(deps) {
136425
136474
  }
136426
136475
  //#endregion
136427
136476
  //#region src/speak/controller.ts
136428
- function targetsFrom(settings) {
136429
- return settings.targets.map((row) => ({
136477
+ function targetsFrom(rows) {
136478
+ return rows.map((row) => ({
136430
136479
  id: row.id,
136431
136480
  name: row.name,
136432
136481
  kind: ALEXA_SPEAK_KIND,
@@ -136456,7 +136505,7 @@ function rowsFrom(targets) {
136456
136505
  var AlexaSpeakController = class {
136457
136506
  host;
136458
136507
  provider;
136459
- settings;
136508
+ rows;
136460
136509
  pythonPath = null;
136461
136510
  echoes = /* @__PURE__ */ new Map();
136462
136511
  timer = null;
@@ -136465,17 +136514,14 @@ var AlexaSpeakController = class {
136465
136514
  constructor(host) {
136466
136515
  this.host = host;
136467
136516
  this.now = host.now ?? (() => Date.now());
136468
- this.settings = readSpeakSettings(host.readConfig());
136517
+ this.rows = readEchoTargets(host.readConfig());
136469
136518
  this.provider = createAlexaNotificationProvider({
136470
136519
  brokers: () => this.brokerViews(),
136471
136520
  now: this.now,
136472
136521
  logger: host.logger,
136473
- loadTargets: () => targetsFrom(this.settings),
136522
+ loadTargets: () => targetsFrom(this.rows),
136474
136523
  saveTargets: (targets) => {
136475
- this.settings = {
136476
- ...this.settings,
136477
- targets: [...rowsFrom(targets)]
136478
- };
136524
+ this.rows = rowsFrom(targets);
136479
136525
  this.persist();
136480
136526
  },
136481
136527
  speak: (command) => this.utter(command.brokerId, command.mode, command.targets, command.text, command.volume)
@@ -136487,7 +136533,6 @@ var AlexaSpeakController = class {
136487
136533
  this.host.logger.warn("alexa speak: embedded Python unavailable; Echo speech will not run");
136488
136534
  return;
136489
136535
  }
136490
- await this.migrateLegacyLogin();
136491
136536
  await this.brokersChanged();
136492
136537
  this.timer = setInterval(() => {
136493
136538
  this.refreshAll();
@@ -136504,12 +136549,6 @@ var AlexaSpeakController = class {
136504
136549
  this.queue = this.queue.then(() => this.reconcile()).catch(() => void 0);
136505
136550
  return this.queue;
136506
136551
  }
136507
- handlers() {
136508
- return {
136509
- speakStatus: async () => this.status(),
136510
- sendTest: async (input) => this.sendTest(input)
136511
- };
136512
- }
136513
136552
  brokerViews() {
136514
136553
  return this.host.listBrokers().map((ref) => {
136515
136554
  const echo = this.echoes.get(ref.id);
@@ -136523,36 +136562,11 @@ var AlexaSpeakController = class {
136523
136562
  }
136524
136563
  async persist() {
136525
136564
  try {
136526
- await this.host.writeSpeak(this.settings);
136565
+ await this.host.writeTargets(this.rows);
136527
136566
  } catch (err) {
136528
- this.host.logger.warn("alexa speak: could not store settings", { meta: { error: err instanceof Error ? err.message : String(err) } });
136567
+ this.host.logger.warn("alexa speak: could not store targets", { meta: { error: err instanceof Error ? err.message : String(err) } });
136529
136568
  }
136530
136569
  }
136531
- /**
136532
- * Drain the old standalone login once. With exactly one broker its jar and
136533
- * its targets move onto that broker; otherwise the jar is dropped and the
136534
- * targets wait for the operator to choose (the status names that state).
136535
- */
136536
- async migrateLegacyLogin() {
136537
- const brokers = this.host.listBrokers();
136538
- const plan = planLegacyMigration(legacyLoginOf(this.settings), brokers.map((ref) => ref.id), this.settings.targets);
136539
- if (!plan.present) return;
136540
- if (plan.attachTo !== null) await this.host.sessions.put(plan.attachTo, this.settings.session);
136541
- this.settings = {
136542
- ...this.settings,
136543
- email: "",
136544
- password: "",
136545
- session: "",
136546
- targets: [...plan.targets]
136547
- };
136548
- await this.persist();
136549
- await this.host.clearLegacySecrets();
136550
- this.host.logger.info("alexa speak: the old standalone login was retired", { meta: {
136551
- attachedTo: plan.attachTo ?? "none",
136552
- brokers: brokers.length,
136553
- targets: plan.targets.length
136554
- } });
136555
- }
136556
136570
  makeWorker(brokerId, pythonPath) {
136557
136571
  if (this.host.startWorker !== void 0) return this.host.startWorker(brokerId, pythonPath);
136558
136572
  return startAlexaWorker({
@@ -136602,59 +136616,6 @@ var AlexaSpeakController = class {
136602
136616
  if (echo === void 0) throw new Error("no Echo session for this Amazon account");
136603
136617
  return echo.utter(mode === "announce" ? "announce" : "speak", targets, text, volume);
136604
136618
  }
136605
- async status() {
136606
- const brokers = this.host.listBrokers();
136607
- const rows = await Promise.all(brokers.map(async (ref) => {
136608
- const echo = this.echoes.get(ref.id);
136609
- return {
136610
- id: ref.id,
136611
- name: ref.name,
136612
- state: echo?.state ?? "failed",
136613
- ...echo === void 0 ? { error: "embedded Python unavailable" } : echo.error === null ? {} : { error: echo.error },
136614
- devices: [...echo?.devices ?? []],
136615
- session: echo === void 0 ? {
136616
- loggedIn: false,
136617
- ageSec: 0,
136618
- lastSuccessAt: null
136619
- } : await echo.session()
136620
- };
136621
- }));
136622
- const targets = this.settings.targets;
136623
- const ids = brokers.map((ref) => ref.id);
136624
- return {
136625
- brokers: rows,
136626
- unassignedTargets: unassignedCount(targets),
136627
- notice: noticeFor(targets, ids)
136628
- };
136629
- }
136630
- async sendTest(input) {
136631
- const outcomes = await this.utter(input.brokerId, input.mode, input.echoIds, input.text, input.volume);
136632
- const known = new Map((this.echoes.get(input.brokerId)?.devices ?? []).map((device) => [device.id, device]));
136633
- const next = [...this.settings.targets];
136634
- for (const echoId of input.echoIds) {
136635
- if (next.some((row) => row.echoId === echoId && row.brokerId === input.brokerId)) continue;
136636
- next.push({
136637
- id: `echo-${input.brokerId}-${echoId}`,
136638
- name: known.get(echoId)?.name ?? echoId,
136639
- enabled: true,
136640
- brokerId: input.brokerId,
136641
- echoId,
136642
- mode: input.mode,
136643
- volume: input.volume,
136644
- respectDnd: true
136645
- });
136646
- }
136647
- this.settings = {
136648
- ...this.settings,
136649
- targets: next
136650
- };
136651
- await this.persist();
136652
- return { outcomes: outcomes.map((row) => ({
136653
- echoId: row.targetId,
136654
- ok: row.ok,
136655
- ...row.error !== void 0 ? { error: row.error } : {}
136656
- })) };
136657
- }
136658
136619
  };
136659
136620
  //#endregion
136660
136621
  //#region src/speak/icon-routes.ts
@@ -136707,26 +136668,11 @@ var RecordsSpeakSessionStore = class RecordsSpeakSessionStore {
136707
136668
  };
136708
136669
  //#endregion
136709
136670
  //#region src/addon.ts
136710
- var SPEAK_PAGE = {
136711
- id: "speak",
136712
- label: "Speak on Echo",
136713
- icon: "megaphone",
136714
- path: "/addon/import-alexa",
136715
- remoteName: "addon_import_alexa_page",
136716
- bundle: "remoteEntry.js",
136717
- section: "administration"
136718
- };
136719
136671
  /** Default config — a fresh install starts with no accounts. */
136720
136672
  var DEFAULTS = {
136721
136673
  brokers: [],
136722
136674
  proxyLogin: { proxyPort: 3456 },
136723
- speak: {
136724
- email: "",
136725
- password: "",
136726
- region: "EU",
136727
- session: "",
136728
- targets: []
136729
- }
136675
+ echoTargets: []
136730
136676
  };
136731
136677
  /** Device types an imported Alexa appliance can materialise as. */
136732
136678
  var ALEXA_DEVICE_TYPES = [
@@ -136770,7 +136716,6 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
136770
136716
  brokerStore = null;
136771
136717
  activeBrokers = [];
136772
136718
  speak = null;
136773
- secretStatePaths = ["speak.password", "speak.session"];
136774
136719
  constructor() {
136775
136720
  super({ ...DEFAULTS });
136776
136721
  }
@@ -136825,8 +136770,8 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
136825
136770
  const speak = new AlexaSpeakController({
136826
136771
  logger: this.ctx.logger.child("speak"),
136827
136772
  readConfig: () => this.config,
136828
- writeSpeak: async (next) => {
136829
- await this.updateGlobalSettings({ speak: next });
136773
+ writeTargets: async (next) => {
136774
+ await this.updateGlobalSettings({ echoTargets: [...next] });
136830
136775
  },
136831
136776
  listBrokers: () => this.activeBrokers.map((entry) => ({
136832
136777
  id: entry.id,
@@ -136838,50 +136783,31 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
136838
136783
  return entry === void 0 ? null : readBrokerSeed(this.ctx.dataDir, entry);
136839
136784
  },
136840
136785
  sessions: RecordsSpeakSessionStore.of(records),
136841
- clearLegacySecrets: async () => {
136842
- await this.updateGlobalSettingsAsOperator({ speak: {
136843
- ...this.config.speak,
136844
- password: SECRET_CLEAR,
136845
- session: SECRET_CLEAR
136846
- } });
136847
- },
136848
136786
  ensurePython: () => this.ctx.deps.ensurePython()
136849
136787
  });
136850
136788
  this.speak = speak;
136851
136789
  speak.start().catch((err) => {
136852
136790
  this.ctx.logger.warn("alexa speak: worker did not start", { meta: { error: err instanceof Error ? err.message : String(err) } });
136853
136791
  });
136854
- const pages = {
136855
- id: this.addonId,
136856
- listPages: () => [SPEAK_PAGE]
136857
- };
136858
- return {
136859
- providers: [
136860
- ...regs,
136861
- {
136862
- capability: brokerCapability,
136863
- provider: this.buildBrokerProvider()
136864
- },
136865
- {
136866
- capability: deviceAdoptionCapability,
136867
- provider: this.buildAdoptionProvider()
136868
- },
136869
- {
136870
- capability: addonRoutesCapability,
136871
- provider: buildAddonRouteProvider("import-alexa", loginRoutes)
136872
- },
136873
- {
136874
- capability: notificationOutputCapability,
136875
- provider: speak.provider
136876
- },
136877
- {
136878
- capability: addonPagesSourceCapability,
136879
- provider: pages
136880
- }
136881
- ],
136882
- customActions: alexaSpeakActions,
136883
- actionHandlers: speak.handlers()
136884
- };
136792
+ return { providers: [
136793
+ ...regs,
136794
+ {
136795
+ capability: brokerCapability,
136796
+ provider: this.buildBrokerProvider()
136797
+ },
136798
+ {
136799
+ capability: deviceAdoptionCapability,
136800
+ provider: this.buildAdoptionProvider()
136801
+ },
136802
+ {
136803
+ capability: addonRoutesCapability,
136804
+ provider: buildAddonRouteProvider("import-alexa", loginRoutes)
136805
+ },
136806
+ {
136807
+ capability: notificationOutputCapability,
136808
+ provider: speak.provider
136809
+ }
136810
+ ] };
136885
136811
  }
136886
136812
  async loadPersistedBrokers() {
136887
136813
  const store = this.brokerStore;
@@ -137265,4 +137191,4 @@ function capsEqual(a, b) {
137265
137191
  return true;
137266
137192
  }
137267
137193
  //#endregion
137268
- export { AlexaImportAddon, AlexaClientNotWiredError as a, alexaAddonConfigSchema as c, alexaSpeakActions as customActions, connectionToCredentials as d, settingsToAlexaConnection as f, AlexaAuthError as i, alexaConnectionSchema as l, mappableAppliance as n, StubAlexaClient as o, mapAlexaCapabilitiesToCamStack as r, createAlexaClient as s, buildAlexaApplianceCandidate as t, buildConnectionFormSchema as u };
137194
+ export { AlexaImportAddon, AlexaClientNotWiredError as a, alexaAddonConfigSchema as c, connectionToCredentials as d, settingsToAlexaConnection as f, AlexaAuthError as i, alexaConnectionSchema as l, mappableAppliance as n, StubAlexaClient as o, mapAlexaCapabilitiesToCamStack as r, createAlexaClient as s, buildAlexaApplianceCandidate as t, buildConnectionFormSchema as u };