@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.
- package/CHANGELOG.md +7 -0
- package/dist/addon.js +292 -410
- package/dist/addon.mjs +293 -409
- package/dist/index.js +0 -1
- package/dist/index.mjs +2 -2
- package/package.json +2 -17
- package/dist/MaskShapeCanvas-B6q5RDLi.mjs +0 -13807
- package/dist/MotionZonesSettings-ByDKuk5o.mjs +0 -309
- package/dist/PrivacyMaskSettings-Cn-_9Syv.mjs +0 -368
- package/dist/SceneMonitorEditor-jDMtE1yg.mjs +0 -845
- package/dist/_stub.js +0 -39538
- package/dist/_virtual_mf-localSharedImportMap___mfe_internal__addon_import_alexa_page-CwP9H68T.mjs +0 -156
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_camstack_mf_1_sdk__loadShare__.js-BmoaT3cr.mjs +0 -25
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DE1dtKjc.mjs +0 -26
- 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
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_trpc_mf_1_client__loadShare__.js-BHqOw8oh.mjs +0 -25
- 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
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react__loadShare__.js-DwXXYAlk.mjs +0 -70
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-CUiVYNMY.mjs +0 -26
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react_mf_2_dom__loadShare__.js-CIPmAyPi.mjs +0 -25
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-tXm1x5EZ.mjs +0 -25
- package/dist/addon-import-alexa.css +0 -3
- package/dist/hostInit-Dy4CxmqQ.mjs +0 -129
- package/dist/player-overlays-CcdvNfxc.mjs +0 -51
- package/dist/remoteEntry.js +0 -2
- package/dist/remoteEntry.ssr.js +0 -33
- package/dist/responsive-BmleF5y2.mjs +0 -125
- package/dist/rolldown-runtime-HEgqtunE.mjs +0 -20
- package/dist/scene-monitor-copy-BEZSY5WG.mjs +0 -240
- package/dist/square-DZHhUhzl.mjs +0 -45
- package/dist/trash-2-BVrrPiKB.mjs +0 -25
- package/dist/ui-text-B5YO4hoJ.mjs +0 -34
- package/dist/virtualExposes-lZT06jeg.mjs +0 -2
- package/dist/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_import_alexa_page__remoteEntry_js-DarhMhK_.mjs +0 -2857
- package/dist/virtual_mf-exposes-ssr___mfe_internal__addon_import_alexa_page__remoteEntry_js-CViESMdd.mjs +0 -10
- 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-
|
|
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
|
-
|
|
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
|
-
...
|
|
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.
|
|
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
|
|
91149
|
-
*
|
|
91150
|
-
|
|
91151
|
-
|
|
91152
|
-
|
|
91153
|
-
|
|
91154
|
-
|
|
91155
|
-
|
|
91156
|
-
|
|
91157
|
-
|
|
91158
|
-
|
|
91159
|
-
|
|
91160
|
-
|
|
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
|
-
/**
|
|
91178
|
-
function
|
|
91179
|
-
const raw =
|
|
91180
|
-
|
|
91181
|
-
|
|
91182
|
-
|
|
91183
|
-
|
|
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
|
-
|
|
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,33 @@ 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
|
|
132747
132806
|
//#region src/alexa-proxy-trace.ts
|
|
132748
132807
|
/**
|
|
132749
132808
|
* Sanitised one-line trace of the alexa-cookie2 proxy's own log lines.
|
|
@@ -132876,7 +132935,7 @@ var AlexaProxyLoginSession = class {
|
|
|
132876
132935
|
async start() {
|
|
132877
132936
|
const remote = new import_alexa_remote.default();
|
|
132878
132937
|
this.#remote = remote;
|
|
132879
|
-
const
|
|
132938
|
+
const region = alexaRegionOptions(this.#opts.amazonDomain);
|
|
132880
132939
|
remote.on("cookie", () => this.#onCookieEvent(remote));
|
|
132881
132940
|
remote.init({
|
|
132882
132941
|
proxyOnly: true,
|
|
@@ -132884,7 +132943,7 @@ var AlexaProxyLoginSession = class {
|
|
|
132884
132943
|
proxyListenBind: ALEXA_PROXY_LOOPBACK_HOST,
|
|
132885
132944
|
proxyPort: this.#opts.proxyPort,
|
|
132886
132945
|
proxyLogLevel: "warn",
|
|
132887
|
-
|
|
132946
|
+
...region,
|
|
132888
132947
|
apiUserAgentPostfix: ALEXA_API_USER_AGENT_POSTFIX,
|
|
132889
132948
|
logger: (...args) => {
|
|
132890
132949
|
const line = args.map(String).join(" ");
|
|
@@ -135150,126 +135209,6 @@ var AlexaBrokerStore = class AlexaBrokerStore {
|
|
|
135150
135209
|
}
|
|
135151
135210
|
};
|
|
135152
135211
|
//#endregion
|
|
135153
|
-
//#region src/speak/constants.ts
|
|
135154
|
-
/** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
|
|
135155
|
-
var ALEXA_SPEAK_ADDON_ID = "import-alexa";
|
|
135156
|
-
var ALEXA_SPEAK_KIND = "alexa-echo";
|
|
135157
|
-
var DEVICE_REFRESH_MS = 5 * 6e4;
|
|
135158
|
-
/** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
|
|
135159
|
-
var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
|
|
135160
|
-
//#endregion
|
|
135161
|
-
//#region src/speak/worker-protocol.ts
|
|
135162
|
-
/** Line-delimited JSON the long-lived Python worker speaks. One object per line. */
|
|
135163
|
-
var EchoDeviceSchema = object({
|
|
135164
|
-
id: string().min(1),
|
|
135165
|
-
name: string(),
|
|
135166
|
-
family: string(),
|
|
135167
|
-
online: boolean(),
|
|
135168
|
-
kind: _enum(["device", "group"]),
|
|
135169
|
-
dnd: boolean(),
|
|
135170
|
-
deviceType: string(),
|
|
135171
|
-
/** The room the account files the device under, when Amazon names one. */
|
|
135172
|
-
room: string().optional()
|
|
135173
|
-
});
|
|
135174
|
-
var LoginStatusSchema = _enum(["ok", "failed"]);
|
|
135175
|
-
var SessionStatusSchema = object({
|
|
135176
|
-
loggedIn: boolean(),
|
|
135177
|
-
ageSec: number().int().nonnegative(),
|
|
135178
|
-
lastSuccessAt: number().int().nullable()
|
|
135179
|
-
});
|
|
135180
|
-
var TargetDeliverySchema = object({
|
|
135181
|
-
targetId: string(),
|
|
135182
|
-
ok: boolean(),
|
|
135183
|
-
error: string().optional()
|
|
135184
|
-
});
|
|
135185
|
-
var WorkerLineSchema = union([object({
|
|
135186
|
-
id: string(),
|
|
135187
|
-
ok: literal(true),
|
|
135188
|
-
result: unknown().optional()
|
|
135189
|
-
}), object({
|
|
135190
|
-
id: string(),
|
|
135191
|
-
ok: literal(false),
|
|
135192
|
-
error: string()
|
|
135193
|
-
})]);
|
|
135194
|
-
function requestLine(id, request) {
|
|
135195
|
-
return `${JSON.stringify({
|
|
135196
|
-
id,
|
|
135197
|
-
...request
|
|
135198
|
-
})}\n`;
|
|
135199
|
-
}
|
|
135200
|
-
function parseLoginResult(result) {
|
|
135201
|
-
const parsed = object({
|
|
135202
|
-
status: LoginStatusSchema,
|
|
135203
|
-
error: string().optional()
|
|
135204
|
-
}).safeParse(result);
|
|
135205
|
-
if (!parsed.success) return {
|
|
135206
|
-
status: "failed",
|
|
135207
|
-
error: "worker returned an unexpected login result"
|
|
135208
|
-
};
|
|
135209
|
-
return parsed.data;
|
|
135210
|
-
}
|
|
135211
|
-
function parseDevices(result) {
|
|
135212
|
-
const parsed = object({ devices: array(EchoDeviceSchema) }).safeParse(result);
|
|
135213
|
-
if (!parsed.success) throw new Error("worker returned an unexpected device list");
|
|
135214
|
-
return parsed.data.devices;
|
|
135215
|
-
}
|
|
135216
|
-
function parseDeliveries(result) {
|
|
135217
|
-
const parsed = object({ outcomes: array(TargetDeliverySchema) }).safeParse(result);
|
|
135218
|
-
if (!parsed.success) throw new Error("worker returned an unexpected delivery list");
|
|
135219
|
-
return parsed.data.outcomes;
|
|
135220
|
-
}
|
|
135221
|
-
function parseSessionStatus(result) {
|
|
135222
|
-
const parsed = SessionStatusSchema.safeParse(result);
|
|
135223
|
-
if (!parsed.success) throw new Error("worker returned an unexpected session status");
|
|
135224
|
-
return parsed.data;
|
|
135225
|
-
}
|
|
135226
|
-
//#endregion
|
|
135227
|
-
//#region src/speak/actions.ts
|
|
135228
|
-
var BrokerEchoStateSchema = _enum([
|
|
135229
|
-
"starting",
|
|
135230
|
-
"ready",
|
|
135231
|
-
"broker-not-signed-in",
|
|
135232
|
-
"failed"
|
|
135233
|
-
]);
|
|
135234
|
-
var SpeakStatusSchema = object({
|
|
135235
|
-
brokers: array(object({
|
|
135236
|
-
id: string(),
|
|
135237
|
-
name: string(),
|
|
135238
|
-
state: BrokerEchoStateSchema,
|
|
135239
|
-
error: string().optional(),
|
|
135240
|
-
devices: array(EchoDeviceSchema),
|
|
135241
|
-
session: SessionStatusSchema
|
|
135242
|
-
})),
|
|
135243
|
-
/** Targets that name no Amazon account yet (they cannot speak until one is chosen). */
|
|
135244
|
-
unassignedTargets: number().int().nonnegative(),
|
|
135245
|
-
/** What the old standalone login left behind, named; '' when nothing is pending. */
|
|
135246
|
-
notice: _enum([
|
|
135247
|
-
"",
|
|
135248
|
-
"no-broker",
|
|
135249
|
-
"choose-broker"
|
|
135250
|
-
])
|
|
135251
|
-
});
|
|
135252
|
-
var alexaSpeakActions = defineCustomActions({
|
|
135253
|
-
speakStatus: customAction(object({}), SpeakStatusSchema, {
|
|
135254
|
-
kind: "mutation",
|
|
135255
|
-
auth: "admin"
|
|
135256
|
-
}),
|
|
135257
|
-
sendTest: customAction(object({
|
|
135258
|
-
brokerId: string().min(1),
|
|
135259
|
-
echoIds: array(string().min(1)).min(1),
|
|
135260
|
-
mode: _enum(["speak", "announce"]),
|
|
135261
|
-
volume: number().int().min(0).max(100),
|
|
135262
|
-
text: string().min(1).max(450)
|
|
135263
|
-
}), object({ outcomes: array(object({
|
|
135264
|
-
echoId: string(),
|
|
135265
|
-
ok: boolean(),
|
|
135266
|
-
error: string().optional()
|
|
135267
|
-
})) }), {
|
|
135268
|
-
kind: "mutation",
|
|
135269
|
-
auth: "admin"
|
|
135270
|
-
})
|
|
135271
|
-
});
|
|
135272
|
-
//#endregion
|
|
135273
135212
|
//#region src/speak/broker-seed.ts
|
|
135274
135213
|
/**
|
|
135275
135214
|
* What the Echo worker needs from an import broker to act as the same Amazon
|
|
@@ -135504,6 +135443,72 @@ function scrubSecrets(text, secrets) {
|
|
|
135504
135443
|
return out;
|
|
135505
135444
|
}
|
|
135506
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
|
|
135507
135512
|
//#region src/speak/broker-echo.ts
|
|
135508
135513
|
var BrokerEcho = class {
|
|
135509
135514
|
deps;
|
|
@@ -135632,40 +135637,13 @@ var BrokerEcho = class {
|
|
|
135632
135637
|
}
|
|
135633
135638
|
};
|
|
135634
135639
|
//#endregion
|
|
135635
|
-
//#region src/speak/
|
|
135636
|
-
|
|
135637
|
-
|
|
135638
|
-
|
|
135639
|
-
|
|
135640
|
-
|
|
135641
|
-
|
|
135642
|
-
}
|
|
135643
|
-
function planLegacyMigration(legacy, brokerIds, targets) {
|
|
135644
|
-
const present = legacy.email.length > 0 || legacy.password.length > 0 || legacy.session.length > 0;
|
|
135645
|
-
const only = brokerIds.length === 1 ? brokerIds[0] ?? null : null;
|
|
135646
|
-
if (!present || only === null) return {
|
|
135647
|
-
present,
|
|
135648
|
-
attachTo: null,
|
|
135649
|
-
targets
|
|
135650
|
-
};
|
|
135651
|
-
return {
|
|
135652
|
-
present,
|
|
135653
|
-
attachTo: legacy.session.length > 0 ? only : null,
|
|
135654
|
-
targets: targets.map((row) => row.brokerId.length > 0 ? row : {
|
|
135655
|
-
...row,
|
|
135656
|
-
brokerId: only
|
|
135657
|
-
})
|
|
135658
|
-
};
|
|
135659
|
-
}
|
|
135660
|
-
/** What the page must tell the operator about targets that name no account. */
|
|
135661
|
-
function noticeFor(targets, brokerIds) {
|
|
135662
|
-
if (!targets.some((row) => row.brokerId.length === 0)) return "";
|
|
135663
|
-
if (brokerIds.length === 0) return "no-broker";
|
|
135664
|
-
return brokerIds.length > 1 ? "choose-broker" : "";
|
|
135665
|
-
}
|
|
135666
|
-
function unassignedCount(targets) {
|
|
135667
|
-
return targets.filter((row) => row.brokerId.length === 0).length;
|
|
135668
|
-
}
|
|
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}`;
|
|
135669
135647
|
//#endregion
|
|
135670
135648
|
//#region src/speak/rate-limit.ts
|
|
135671
135649
|
/**
|
|
@@ -135770,14 +135748,42 @@ function readString(config, key) {
|
|
|
135770
135748
|
const value = config[key];
|
|
135771
135749
|
return typeof value === "string" ? value : "";
|
|
135772
135750
|
}
|
|
135773
|
-
|
|
135774
|
-
|
|
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";
|
|
135775
135754
|
}
|
|
135776
|
-
function readVolume(config) {
|
|
135777
|
-
const
|
|
135755
|
+
function readVolume(config, delivery) {
|
|
135756
|
+
const ruled = delivery?.["volume"];
|
|
135757
|
+
const value = typeof ruled === "number" ? ruled : config["volume"];
|
|
135778
135758
|
if (typeof value !== "number" || !Number.isFinite(value)) return 40;
|
|
135779
135759
|
return Math.max(0, Math.min(100, Math.round(value)));
|
|
135780
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
|
+
}];
|
|
135781
135787
|
function respectDnd(config) {
|
|
135782
135788
|
return config["respectDnd"] !== false;
|
|
135783
135789
|
}
|
|
@@ -135878,6 +135884,7 @@ function alexaTargetKind(brokers) {
|
|
|
135878
135884
|
supportsDiscovery: true,
|
|
135879
135885
|
destinationKeys: ["brokerId", "echoId"],
|
|
135880
135886
|
configSchema: configSchema(brokers),
|
|
135887
|
+
deliveryParams: DELIVERY_PARAMS,
|
|
135881
135888
|
caps: {
|
|
135882
135889
|
attachments: {
|
|
135883
135890
|
mediaTypes: [],
|
|
@@ -136060,14 +136067,14 @@ function createAlexaNotificationProvider(ports) {
|
|
|
136060
136067
|
renderedAs: speech.renderedAs
|
|
136061
136068
|
};
|
|
136062
136069
|
}
|
|
136063
|
-
const mode = readMode(target.config);
|
|
136070
|
+
const mode = readMode(target.config, notification.delivery);
|
|
136064
136071
|
try {
|
|
136065
136072
|
const outcomes = await ports.speak({
|
|
136066
136073
|
brokerId: broker.id,
|
|
136067
136074
|
mode,
|
|
136068
136075
|
targets: [echoId],
|
|
136069
136076
|
text: speech.text,
|
|
136070
|
-
volume: readVolume(target.config)
|
|
136077
|
+
volume: readVolume(target.config, notification.delivery)
|
|
136071
136078
|
});
|
|
136072
136079
|
const one = outcomes.find((row) => row.targetId === echoId) ?? outcomes[0];
|
|
136073
136080
|
if (one === void 0 || !one.ok) {
|
|
@@ -136467,8 +136474,8 @@ function startAlexaWorker(deps) {
|
|
|
136467
136474
|
}
|
|
136468
136475
|
//#endregion
|
|
136469
136476
|
//#region src/speak/controller.ts
|
|
136470
|
-
function targetsFrom(
|
|
136471
|
-
return
|
|
136477
|
+
function targetsFrom(rows) {
|
|
136478
|
+
return rows.map((row) => ({
|
|
136472
136479
|
id: row.id,
|
|
136473
136480
|
name: row.name,
|
|
136474
136481
|
kind: ALEXA_SPEAK_KIND,
|
|
@@ -136498,7 +136505,7 @@ function rowsFrom(targets) {
|
|
|
136498
136505
|
var AlexaSpeakController = class {
|
|
136499
136506
|
host;
|
|
136500
136507
|
provider;
|
|
136501
|
-
|
|
136508
|
+
rows;
|
|
136502
136509
|
pythonPath = null;
|
|
136503
136510
|
echoes = /* @__PURE__ */ new Map();
|
|
136504
136511
|
timer = null;
|
|
@@ -136507,17 +136514,14 @@ var AlexaSpeakController = class {
|
|
|
136507
136514
|
constructor(host) {
|
|
136508
136515
|
this.host = host;
|
|
136509
136516
|
this.now = host.now ?? (() => Date.now());
|
|
136510
|
-
this.
|
|
136517
|
+
this.rows = readEchoTargets(host.readConfig());
|
|
136511
136518
|
this.provider = createAlexaNotificationProvider({
|
|
136512
136519
|
brokers: () => this.brokerViews(),
|
|
136513
136520
|
now: this.now,
|
|
136514
136521
|
logger: host.logger,
|
|
136515
|
-
loadTargets: () => targetsFrom(this.
|
|
136522
|
+
loadTargets: () => targetsFrom(this.rows),
|
|
136516
136523
|
saveTargets: (targets) => {
|
|
136517
|
-
this.
|
|
136518
|
-
...this.settings,
|
|
136519
|
-
targets: [...rowsFrom(targets)]
|
|
136520
|
-
};
|
|
136524
|
+
this.rows = rowsFrom(targets);
|
|
136521
136525
|
this.persist();
|
|
136522
136526
|
},
|
|
136523
136527
|
speak: (command) => this.utter(command.brokerId, command.mode, command.targets, command.text, command.volume)
|
|
@@ -136529,7 +136533,6 @@ var AlexaSpeakController = class {
|
|
|
136529
136533
|
this.host.logger.warn("alexa speak: embedded Python unavailable; Echo speech will not run");
|
|
136530
136534
|
return;
|
|
136531
136535
|
}
|
|
136532
|
-
await this.migrateLegacyLogin();
|
|
136533
136536
|
await this.brokersChanged();
|
|
136534
136537
|
this.timer = setInterval(() => {
|
|
136535
136538
|
this.refreshAll();
|
|
@@ -136546,12 +136549,6 @@ var AlexaSpeakController = class {
|
|
|
136546
136549
|
this.queue = this.queue.then(() => this.reconcile()).catch(() => void 0);
|
|
136547
136550
|
return this.queue;
|
|
136548
136551
|
}
|
|
136549
|
-
handlers() {
|
|
136550
|
-
return {
|
|
136551
|
-
speakStatus: async () => this.status(),
|
|
136552
|
-
sendTest: async (input) => this.sendTest(input)
|
|
136553
|
-
};
|
|
136554
|
-
}
|
|
136555
136552
|
brokerViews() {
|
|
136556
136553
|
return this.host.listBrokers().map((ref) => {
|
|
136557
136554
|
const echo = this.echoes.get(ref.id);
|
|
@@ -136565,36 +136562,11 @@ var AlexaSpeakController = class {
|
|
|
136565
136562
|
}
|
|
136566
136563
|
async persist() {
|
|
136567
136564
|
try {
|
|
136568
|
-
await this.host.
|
|
136565
|
+
await this.host.writeTargets(this.rows);
|
|
136569
136566
|
} catch (err) {
|
|
136570
|
-
this.host.logger.warn("alexa speak: could not store
|
|
136567
|
+
this.host.logger.warn("alexa speak: could not store targets", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
136571
136568
|
}
|
|
136572
136569
|
}
|
|
136573
|
-
/**
|
|
136574
|
-
* Drain the old standalone login once. With exactly one broker its jar and
|
|
136575
|
-
* its targets move onto that broker; otherwise the jar is dropped and the
|
|
136576
|
-
* targets wait for the operator to choose (the status names that state).
|
|
136577
|
-
*/
|
|
136578
|
-
async migrateLegacyLogin() {
|
|
136579
|
-
const brokers = this.host.listBrokers();
|
|
136580
|
-
const plan = planLegacyMigration(legacyLoginOf(this.settings), brokers.map((ref) => ref.id), this.settings.targets);
|
|
136581
|
-
if (!plan.present) return;
|
|
136582
|
-
if (plan.attachTo !== null) await this.host.sessions.put(plan.attachTo, this.settings.session);
|
|
136583
|
-
this.settings = {
|
|
136584
|
-
...this.settings,
|
|
136585
|
-
email: "",
|
|
136586
|
-
password: "",
|
|
136587
|
-
session: "",
|
|
136588
|
-
targets: [...plan.targets]
|
|
136589
|
-
};
|
|
136590
|
-
await this.persist();
|
|
136591
|
-
await this.host.clearLegacySecrets();
|
|
136592
|
-
this.host.logger.info("alexa speak: the old standalone login was retired", { meta: {
|
|
136593
|
-
attachedTo: plan.attachTo ?? "none",
|
|
136594
|
-
brokers: brokers.length,
|
|
136595
|
-
targets: plan.targets.length
|
|
136596
|
-
} });
|
|
136597
|
-
}
|
|
136598
136570
|
makeWorker(brokerId, pythonPath) {
|
|
136599
136571
|
if (this.host.startWorker !== void 0) return this.host.startWorker(brokerId, pythonPath);
|
|
136600
136572
|
return startAlexaWorker({
|
|
@@ -136644,59 +136616,6 @@ var AlexaSpeakController = class {
|
|
|
136644
136616
|
if (echo === void 0) throw new Error("no Echo session for this Amazon account");
|
|
136645
136617
|
return echo.utter(mode === "announce" ? "announce" : "speak", targets, text, volume);
|
|
136646
136618
|
}
|
|
136647
|
-
async status() {
|
|
136648
|
-
const brokers = this.host.listBrokers();
|
|
136649
|
-
const rows = await Promise.all(brokers.map(async (ref) => {
|
|
136650
|
-
const echo = this.echoes.get(ref.id);
|
|
136651
|
-
return {
|
|
136652
|
-
id: ref.id,
|
|
136653
|
-
name: ref.name,
|
|
136654
|
-
state: echo?.state ?? "failed",
|
|
136655
|
-
...echo === void 0 ? { error: "embedded Python unavailable" } : echo.error === null ? {} : { error: echo.error },
|
|
136656
|
-
devices: [...echo?.devices ?? []],
|
|
136657
|
-
session: echo === void 0 ? {
|
|
136658
|
-
loggedIn: false,
|
|
136659
|
-
ageSec: 0,
|
|
136660
|
-
lastSuccessAt: null
|
|
136661
|
-
} : await echo.session()
|
|
136662
|
-
};
|
|
136663
|
-
}));
|
|
136664
|
-
const targets = this.settings.targets;
|
|
136665
|
-
const ids = brokers.map((ref) => ref.id);
|
|
136666
|
-
return {
|
|
136667
|
-
brokers: rows,
|
|
136668
|
-
unassignedTargets: unassignedCount(targets),
|
|
136669
|
-
notice: noticeFor(targets, ids)
|
|
136670
|
-
};
|
|
136671
|
-
}
|
|
136672
|
-
async sendTest(input) {
|
|
136673
|
-
const outcomes = await this.utter(input.brokerId, input.mode, input.echoIds, input.text, input.volume);
|
|
136674
|
-
const known = new Map((this.echoes.get(input.brokerId)?.devices ?? []).map((device) => [device.id, device]));
|
|
136675
|
-
const next = [...this.settings.targets];
|
|
136676
|
-
for (const echoId of input.echoIds) {
|
|
136677
|
-
if (next.some((row) => row.echoId === echoId && row.brokerId === input.brokerId)) continue;
|
|
136678
|
-
next.push({
|
|
136679
|
-
id: `echo-${input.brokerId}-${echoId}`,
|
|
136680
|
-
name: known.get(echoId)?.name ?? echoId,
|
|
136681
|
-
enabled: true,
|
|
136682
|
-
brokerId: input.brokerId,
|
|
136683
|
-
echoId,
|
|
136684
|
-
mode: input.mode,
|
|
136685
|
-
volume: input.volume,
|
|
136686
|
-
respectDnd: true
|
|
136687
|
-
});
|
|
136688
|
-
}
|
|
136689
|
-
this.settings = {
|
|
136690
|
-
...this.settings,
|
|
136691
|
-
targets: next
|
|
136692
|
-
};
|
|
136693
|
-
await this.persist();
|
|
136694
|
-
return { outcomes: outcomes.map((row) => ({
|
|
136695
|
-
echoId: row.targetId,
|
|
136696
|
-
ok: row.ok,
|
|
136697
|
-
...row.error !== void 0 ? { error: row.error } : {}
|
|
136698
|
-
})) };
|
|
136699
|
-
}
|
|
136700
136619
|
};
|
|
136701
136620
|
//#endregion
|
|
136702
136621
|
//#region src/speak/icon-routes.ts
|
|
@@ -136749,26 +136668,11 @@ var RecordsSpeakSessionStore = class RecordsSpeakSessionStore {
|
|
|
136749
136668
|
};
|
|
136750
136669
|
//#endregion
|
|
136751
136670
|
//#region src/addon.ts
|
|
136752
|
-
var SPEAK_PAGE = {
|
|
136753
|
-
id: "speak",
|
|
136754
|
-
label: "Speak on Echo",
|
|
136755
|
-
icon: "megaphone",
|
|
136756
|
-
path: "/addon/import-alexa",
|
|
136757
|
-
remoteName: "addon_import_alexa_page",
|
|
136758
|
-
bundle: "remoteEntry.js",
|
|
136759
|
-
section: "administration"
|
|
136760
|
-
};
|
|
136761
136671
|
/** Default config — a fresh install starts with no accounts. */
|
|
136762
136672
|
var DEFAULTS = {
|
|
136763
136673
|
brokers: [],
|
|
136764
136674
|
proxyLogin: { proxyPort: 3456 },
|
|
136765
|
-
|
|
136766
|
-
email: "",
|
|
136767
|
-
password: "",
|
|
136768
|
-
region: "EU",
|
|
136769
|
-
session: "",
|
|
136770
|
-
targets: []
|
|
136771
|
-
}
|
|
136675
|
+
echoTargets: []
|
|
136772
136676
|
};
|
|
136773
136677
|
/** Device types an imported Alexa appliance can materialise as. */
|
|
136774
136678
|
var ALEXA_DEVICE_TYPES = [
|
|
@@ -136812,7 +136716,6 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
136812
136716
|
brokerStore = null;
|
|
136813
136717
|
activeBrokers = [];
|
|
136814
136718
|
speak = null;
|
|
136815
|
-
secretStatePaths = ["speak.password", "speak.session"];
|
|
136816
136719
|
constructor() {
|
|
136817
136720
|
super({ ...DEFAULTS });
|
|
136818
136721
|
}
|
|
@@ -136867,8 +136770,8 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
136867
136770
|
const speak = new AlexaSpeakController({
|
|
136868
136771
|
logger: this.ctx.logger.child("speak"),
|
|
136869
136772
|
readConfig: () => this.config,
|
|
136870
|
-
|
|
136871
|
-
await this.updateGlobalSettings({
|
|
136773
|
+
writeTargets: async (next) => {
|
|
136774
|
+
await this.updateGlobalSettings({ echoTargets: [...next] });
|
|
136872
136775
|
},
|
|
136873
136776
|
listBrokers: () => this.activeBrokers.map((entry) => ({
|
|
136874
136777
|
id: entry.id,
|
|
@@ -136880,50 +136783,31 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
136880
136783
|
return entry === void 0 ? null : readBrokerSeed(this.ctx.dataDir, entry);
|
|
136881
136784
|
},
|
|
136882
136785
|
sessions: RecordsSpeakSessionStore.of(records),
|
|
136883
|
-
clearLegacySecrets: async () => {
|
|
136884
|
-
await this.updateGlobalSettingsAsOperator({ speak: {
|
|
136885
|
-
...this.config.speak,
|
|
136886
|
-
password: SECRET_CLEAR,
|
|
136887
|
-
session: SECRET_CLEAR
|
|
136888
|
-
} });
|
|
136889
|
-
},
|
|
136890
136786
|
ensurePython: () => this.ctx.deps.ensurePython()
|
|
136891
136787
|
});
|
|
136892
136788
|
this.speak = speak;
|
|
136893
136789
|
speak.start().catch((err) => {
|
|
136894
136790
|
this.ctx.logger.warn("alexa speak: worker did not start", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
136895
136791
|
});
|
|
136896
|
-
|
|
136897
|
-
|
|
136898
|
-
|
|
136899
|
-
|
|
136900
|
-
|
|
136901
|
-
|
|
136902
|
-
|
|
136903
|
-
|
|
136904
|
-
|
|
136905
|
-
|
|
136906
|
-
|
|
136907
|
-
|
|
136908
|
-
|
|
136909
|
-
|
|
136910
|
-
|
|
136911
|
-
|
|
136912
|
-
|
|
136913
|
-
|
|
136914
|
-
|
|
136915
|
-
{
|
|
136916
|
-
capability: notificationOutputCapability,
|
|
136917
|
-
provider: speak.provider
|
|
136918
|
-
},
|
|
136919
|
-
{
|
|
136920
|
-
capability: addonPagesSourceCapability,
|
|
136921
|
-
provider: pages
|
|
136922
|
-
}
|
|
136923
|
-
],
|
|
136924
|
-
customActions: alexaSpeakActions,
|
|
136925
|
-
actionHandlers: speak.handlers()
|
|
136926
|
-
};
|
|
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
|
+
] };
|
|
136927
136811
|
}
|
|
136928
136812
|
async loadPersistedBrokers() {
|
|
136929
136813
|
const store = this.brokerStore;
|
|
@@ -137307,4 +137191,4 @@ function capsEqual(a, b) {
|
|
|
137307
137191
|
return true;
|
|
137308
137192
|
}
|
|
137309
137193
|
//#endregion
|
|
137310
|
-
export { AlexaImportAddon, AlexaClientNotWiredError as a, alexaAddonConfigSchema as c,
|
|
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 };
|