@camstack/addon-import-alexa 0.2.167 → 0.2.168

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 CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.168] - 2026-10-29
11
+
12
+ ### Changed
13
+
14
+ - Alexa sign-in trace now records the proxy's first request to Amazon (parameter names and whether OAuth parameters are present) and the browser family, to diagnose logins that end without an authorization code.
15
+
10
16
  ## [0.2.167] - 2026-10-29
11
17
 
12
18
  ### Changed
package/dist/addon.js CHANGED
@@ -55191,11 +55191,13 @@ var ImageContractSchema = object({
55191
55191
  * The shell verdict (see {@link WrapperKindSchema}) with the proofs that
55192
55192
  * produced it.
55193
55193
  *
55194
- * There is deliberately NO `canSelfUpdate` and no apply action: a wrapper
55195
- * update is an ANNOUNCEMENT. The notification body carries the exact command
55196
- * for a `docker` shell and says the app restarts into the new build for
55197
- * `electron`, and that sentence is chosen from `kind` alone — a boolean
55198
- * nothing reads is the defect this repo keeps paying for.
55194
+ * There is deliberately NO `canSelfUpdate` boolean: a wrapper update is an
55195
+ * ANNOUNCEMENT. The notification body carries the exact command for a `docker`
55196
+ * shell and says the app restarts into the new build for `electron`, and that
55197
+ * sentence is chosen from `kind` alone — a boolean nothing reads is the defect
55198
+ * this repo keeps paying for. The one shell that CAN be acted on is the
55199
+ * Electron app, and only through `installDesktopUpdate` (see
55200
+ * {@link DesktopAppStatusSchema} for what the node reads from the app first).
55199
55201
  */
55200
55202
  var WrapperIdentitySchema = object({
55201
55203
  kind: _enum([
@@ -55243,6 +55245,49 @@ var ServerRollbackInfoSchema = object({
55243
55245
  reason: string()
55244
55246
  });
55245
55247
  /**
55248
+ * What the native desktop app (Electron) that hosts this node says about
55249
+ * itself, read from `<dataDir>/server-root/desktop-app-state.json` (ADR-0971).
55250
+ * Absent on the status = no desktop app, or it never wrote the file.
55251
+ *
55252
+ * `stale` is the honest answer to an app that stopped writing (closed, hung,
55253
+ * crashed): the update facts are then the LAST ones known, not current ones,
55254
+ * and a consumer must not read `update.available: false` from a stale record
55255
+ * as "no update". `stateAtMs` is when the app last wrote.
55256
+ */
55257
+ var DesktopAppStatusSchema = object({
55258
+ /** The Electron app's own version (not the node closure's). */
55259
+ version: string(),
55260
+ update: object({
55261
+ available: boolean(),
55262
+ /** The new build is downloaded and `installDesktopUpdate` can apply it. */
55263
+ downloaded: boolean(),
55264
+ version: string().nullable(),
55265
+ checking: boolean(),
55266
+ /** Why the app's last update check produced no answer; `null` when it did. */
55267
+ error: string().nullable()
55268
+ }),
55269
+ stateAtMs: number(),
55270
+ stale: boolean()
55271
+ });
55272
+ /**
55273
+ * Why `installDesktopUpdate` did nothing. Stable names a UI maps to a sentence:
55274
+ * - `not-electron` — this node does not run inside the desktop app.
55275
+ * - `no-app-state` — the app never published its state (or the file is unreadable).
55276
+ * - `app-state-stale` — the app stopped writing; it is probably not running.
55277
+ * - `not-downloaded` — no update is downloaded yet; nothing to install.
55278
+ */
55279
+ var InstallDesktopUpdateReasonSchema = _enum([
55280
+ "not-electron",
55281
+ "no-app-state",
55282
+ "app-state-stale",
55283
+ "not-downloaded"
55284
+ ]);
55285
+ var InstallDesktopUpdateResultSchema = object({
55286
+ accepted: boolean(),
55287
+ /** Non-null exactly when `accepted` is false. */
55288
+ reason: InstallDesktopUpdateReasonSchema.nullable()
55289
+ });
55290
+ /**
55246
55291
  * The runner boot check (D877): the runners this node PLANNED (hub: one per
55247
55292
  * `resolveRunnerId` over its forked addons; agent: the runners its process
55248
55293
  * service holds, because only the hub plans) against the states the process
@@ -55346,7 +55391,12 @@ var ServerPackageStatusSchema = object({
55346
55391
  * on a provider that predates it. Absent is "not checked yet", never "all
55347
55392
  * well": a consumer that needs the answer treats it as pending.
55348
55393
  */
55349
- runners: RunnerBootCheckSchema.optional()
55394
+ runners: RunnerBootCheckSchema.optional(),
55395
+ /**
55396
+ * The desktop app hosting this node (see {@link DesktopAppStatusSchema}).
55397
+ * Optional for version skew AND because most nodes have no desktop app.
55398
+ */
55399
+ desktopApp: DesktopAppStatusSchema.optional()
55350
55400
  });
55351
55401
  var ServerUpdateCheckResultSchema = object({
55352
55402
  packageName: string(),
@@ -55451,6 +55501,9 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
55451
55501
  kind: "mutation",
55452
55502
  auth: "admin",
55453
55503
  timeoutMs: 16 * 6e4
55504
+ }), method(_void(), InstallDesktopUpdateResultSchema, {
55505
+ kind: "mutation",
55506
+ auth: "admin"
55454
55507
  }), method(_void(), ServerUpdateActionResultSchema, {
55455
55508
  kind: "mutation",
55456
55509
  auth: "admin"
@@ -81212,6 +81265,13 @@ Object.freeze({
81212
81265
  access: "view",
81213
81266
  role: "admin"
81214
81267
  },
81268
+ "serverManagement.installDesktopUpdate": {
81269
+ capName: "server-management",
81270
+ capScope: "system",
81271
+ addonId: null,
81272
+ access: "create",
81273
+ role: "admin"
81274
+ },
81215
81275
  "serverManagement.restartServer": {
81216
81276
  capName: "server-management",
81217
81277
  capScope: "system",
@@ -134100,7 +134160,9 @@ function formatUrlParams(url) {
134100
134160
  * redirect target — always WITHOUT a query string or fragment. Any line that is
134101
134161
  * not one of the known step lines yields `null` and is never traced.
134102
134162
  */
134103
- var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+) \//;
134163
+ var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+) \/ (.*)$/;
134164
+ var INITIAL_REQUEST_RE = /^Alexa-Cookie: Initial Page Request: (\S+)$/;
134165
+ var USER_AGENT_RE = /"user-agent":"([^"]*)"/i;
134104
134166
  var PROXY_REQUEST_RE = /^Alexa-Cookie: Proxy-Request: ([A-Z]+) (\S+)$/;
134105
134167
  var PROXY_RESPONSE_RE = /^Alexa-Cookie: Proxy Response from Host: (\S+)$/;
134106
134168
  var REDIRECT_ORIGINAL_RE = /^Redirect: Original Location ----> (\S+)$/;
@@ -134114,9 +134176,20 @@ function stripQuery(target) {
134114
134176
  function withParams(target) {
134115
134177
  return `${stripQuery(target)} ${formatUrlParams(target)}`;
134116
134178
  }
134179
+ /** Browser family of the header block's user-agent; the string itself is never kept. */
134180
+ function userAgentFamily(headers) {
134181
+ const ua = USER_AGENT_RE.exec(headers)?.[1];
134182
+ if (ua === void 0) return "none";
134183
+ if (/CriOS|Chrome|Chromium|Edg\//.test(ua)) return "chrome";
134184
+ if (/Firefox|FxiOS/.test(ua)) return "firefox";
134185
+ if (/Safari/.test(ua)) return "safari";
134186
+ return "other";
134187
+ }
134117
134188
  function sanitizeProxyTraceLine(line) {
134118
134189
  const router = ROUTER_RE.exec(line);
134119
- if (router !== null) return `router ${router[2]} ${withParams(router[1] ?? "")}`;
134190
+ if (router !== null) return `router ${router[2]} ${withParams(router[1] ?? "")} ua=${userAgentFamily(router[3] ?? "")}`;
134191
+ const initial = INITIAL_REQUEST_RE.exec(line);
134192
+ if (initial !== null) return `initial-request ${withParams(initial[1] ?? "")}`;
134120
134193
  const request = PROXY_REQUEST_RE.exec(line);
134121
134194
  if (request !== null) return `proxy-request ${request[1]} ${withParams(request[2] ?? "")}`;
134122
134195
  const response = PROXY_RESPONSE_RE.exec(line);
package/dist/addon.mjs CHANGED
@@ -55190,11 +55190,13 @@ var ImageContractSchema = object({
55190
55190
  * The shell verdict (see {@link WrapperKindSchema}) with the proofs that
55191
55191
  * produced it.
55192
55192
  *
55193
- * There is deliberately NO `canSelfUpdate` and no apply action: a wrapper
55194
- * update is an ANNOUNCEMENT. The notification body carries the exact command
55195
- * for a `docker` shell and says the app restarts into the new build for
55196
- * `electron`, and that sentence is chosen from `kind` alone — a boolean
55197
- * nothing reads is the defect this repo keeps paying for.
55193
+ * There is deliberately NO `canSelfUpdate` boolean: a wrapper update is an
55194
+ * ANNOUNCEMENT. The notification body carries the exact command for a `docker`
55195
+ * shell and says the app restarts into the new build for `electron`, and that
55196
+ * sentence is chosen from `kind` alone — a boolean nothing reads is the defect
55197
+ * this repo keeps paying for. The one shell that CAN be acted on is the
55198
+ * Electron app, and only through `installDesktopUpdate` (see
55199
+ * {@link DesktopAppStatusSchema} for what the node reads from the app first).
55198
55200
  */
55199
55201
  var WrapperIdentitySchema = object({
55200
55202
  kind: _enum([
@@ -55242,6 +55244,49 @@ var ServerRollbackInfoSchema = object({
55242
55244
  reason: string()
55243
55245
  });
55244
55246
  /**
55247
+ * What the native desktop app (Electron) that hosts this node says about
55248
+ * itself, read from `<dataDir>/server-root/desktop-app-state.json` (ADR-0971).
55249
+ * Absent on the status = no desktop app, or it never wrote the file.
55250
+ *
55251
+ * `stale` is the honest answer to an app that stopped writing (closed, hung,
55252
+ * crashed): the update facts are then the LAST ones known, not current ones,
55253
+ * and a consumer must not read `update.available: false` from a stale record
55254
+ * as "no update". `stateAtMs` is when the app last wrote.
55255
+ */
55256
+ var DesktopAppStatusSchema = object({
55257
+ /** The Electron app's own version (not the node closure's). */
55258
+ version: string(),
55259
+ update: object({
55260
+ available: boolean(),
55261
+ /** The new build is downloaded and `installDesktopUpdate` can apply it. */
55262
+ downloaded: boolean(),
55263
+ version: string().nullable(),
55264
+ checking: boolean(),
55265
+ /** Why the app's last update check produced no answer; `null` when it did. */
55266
+ error: string().nullable()
55267
+ }),
55268
+ stateAtMs: number(),
55269
+ stale: boolean()
55270
+ });
55271
+ /**
55272
+ * Why `installDesktopUpdate` did nothing. Stable names a UI maps to a sentence:
55273
+ * - `not-electron` — this node does not run inside the desktop app.
55274
+ * - `no-app-state` — the app never published its state (or the file is unreadable).
55275
+ * - `app-state-stale` — the app stopped writing; it is probably not running.
55276
+ * - `not-downloaded` — no update is downloaded yet; nothing to install.
55277
+ */
55278
+ var InstallDesktopUpdateReasonSchema = _enum([
55279
+ "not-electron",
55280
+ "no-app-state",
55281
+ "app-state-stale",
55282
+ "not-downloaded"
55283
+ ]);
55284
+ var InstallDesktopUpdateResultSchema = object({
55285
+ accepted: boolean(),
55286
+ /** Non-null exactly when `accepted` is false. */
55287
+ reason: InstallDesktopUpdateReasonSchema.nullable()
55288
+ });
55289
+ /**
55245
55290
  * The runner boot check (D877): the runners this node PLANNED (hub: one per
55246
55291
  * `resolveRunnerId` over its forked addons; agent: the runners its process
55247
55292
  * service holds, because only the hub plans) against the states the process
@@ -55345,7 +55390,12 @@ var ServerPackageStatusSchema = object({
55345
55390
  * on a provider that predates it. Absent is "not checked yet", never "all
55346
55391
  * well": a consumer that needs the answer treats it as pending.
55347
55392
  */
55348
- runners: RunnerBootCheckSchema.optional()
55393
+ runners: RunnerBootCheckSchema.optional(),
55394
+ /**
55395
+ * The desktop app hosting this node (see {@link DesktopAppStatusSchema}).
55396
+ * Optional for version skew AND because most nodes have no desktop app.
55397
+ */
55398
+ desktopApp: DesktopAppStatusSchema.optional()
55349
55399
  });
55350
55400
  var ServerUpdateCheckResultSchema = object({
55351
55401
  packageName: string(),
@@ -55450,6 +55500,9 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
55450
55500
  kind: "mutation",
55451
55501
  auth: "admin",
55452
55502
  timeoutMs: 16 * 6e4
55503
+ }), method(_void(), InstallDesktopUpdateResultSchema, {
55504
+ kind: "mutation",
55505
+ auth: "admin"
55453
55506
  }), method(_void(), ServerUpdateActionResultSchema, {
55454
55507
  kind: "mutation",
55455
55508
  auth: "admin"
@@ -81211,6 +81264,13 @@ Object.freeze({
81211
81264
  access: "view",
81212
81265
  role: "admin"
81213
81266
  },
81267
+ "serverManagement.installDesktopUpdate": {
81268
+ capName: "server-management",
81269
+ capScope: "system",
81270
+ addonId: null,
81271
+ access: "create",
81272
+ role: "admin"
81273
+ },
81214
81274
  "serverManagement.restartServer": {
81215
81275
  capName: "server-management",
81216
81276
  capScope: "system",
@@ -134099,7 +134159,9 @@ function formatUrlParams(url) {
134099
134159
  * redirect target — always WITHOUT a query string or fragment. Any line that is
134100
134160
  * not one of the known step lines yields `null` and is never traced.
134101
134161
  */
134102
- var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+) \//;
134162
+ var ROUTER_RE = /^Router: (\S+) \/ ([A-Z]+) \/ (.*)$/;
134163
+ var INITIAL_REQUEST_RE = /^Alexa-Cookie: Initial Page Request: (\S+)$/;
134164
+ var USER_AGENT_RE = /"user-agent":"([^"]*)"/i;
134103
134165
  var PROXY_REQUEST_RE = /^Alexa-Cookie: Proxy-Request: ([A-Z]+) (\S+)$/;
134104
134166
  var PROXY_RESPONSE_RE = /^Alexa-Cookie: Proxy Response from Host: (\S+)$/;
134105
134167
  var REDIRECT_ORIGINAL_RE = /^Redirect: Original Location ----> (\S+)$/;
@@ -134113,9 +134175,20 @@ function stripQuery(target) {
134113
134175
  function withParams(target) {
134114
134176
  return `${stripQuery(target)} ${formatUrlParams(target)}`;
134115
134177
  }
134178
+ /** Browser family of the header block's user-agent; the string itself is never kept. */
134179
+ function userAgentFamily(headers) {
134180
+ const ua = USER_AGENT_RE.exec(headers)?.[1];
134181
+ if (ua === void 0) return "none";
134182
+ if (/CriOS|Chrome|Chromium|Edg\//.test(ua)) return "chrome";
134183
+ if (/Firefox|FxiOS/.test(ua)) return "firefox";
134184
+ if (/Safari/.test(ua)) return "safari";
134185
+ return "other";
134186
+ }
134116
134187
  function sanitizeProxyTraceLine(line) {
134117
134188
  const router = ROUTER_RE.exec(line);
134118
- if (router !== null) return `router ${router[2]} ${withParams(router[1] ?? "")}`;
134189
+ if (router !== null) return `router ${router[2]} ${withParams(router[1] ?? "")} ua=${userAgentFamily(router[3] ?? "")}`;
134190
+ const initial = INITIAL_REQUEST_RE.exec(line);
134191
+ if (initial !== null) return `initial-request ${withParams(initial[1] ?? "")}`;
134119
134192
  const request = PROXY_REQUEST_RE.exec(line);
134120
134193
  if (request !== null) return `proxy-request ${request[1]} ${withParams(request[2] ?? "")}`;
134121
134194
  const response = PROXY_RESPONSE_RE.exec(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-import-alexa",
3
- "version": "0.2.167",
3
+ "version": "0.2.168",
4
4
  "description": "Alexa device-import provider for CamStack — imports the smart-home devices in a user's Alexa account via the unofficial alexa-remote2 cookie/token client (the inverse of the Alexa exporter)",
5
5
  "keywords": [
6
6
  "camstack",