@camstack/addon-import-alexa 0.2.164 → 0.2.165

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.165] - 2026-10-28
11
+
12
+ ### Changed
13
+
14
+ - The Alexa sign-in keeps Amazon's session cookie through the hub proxy (the email step still fails upstream, tracked separately)
15
+
10
16
  ## [0.2.164] - 2026-10-28
11
17
 
12
18
  ### Changed
package/dist/addon.js CHANGED
@@ -43907,6 +43907,17 @@ var LlmProfileSchema = object({
43907
43907
  * field in its editor only when its descriptor lists it in `profileFields`. */
43908
43908
  topK: number().int().positive().optional(),
43909
43909
  maxTokens: number().int().positive().optional(),
43910
+ /** How hard the model "thinks" before it answers. Omitted = the model's own
43911
+ * default. `off` matters for structured output: a thinking model can spend
43912
+ * the whole completion on reasoning and return an empty `content`. Carried
43913
+ * only by the wires that have it — a kind offers the field in its editor
43914
+ * only when its descriptor lists it in `profileFields`. */
43915
+ reasoningEffort: _enum([
43916
+ "off",
43917
+ "low",
43918
+ "medium",
43919
+ "high"
43920
+ ]).optional(),
43910
43921
  /** Prompt context window. Advisory for cloud kinds (they enforce their own). */
43911
43922
  contextLength: number().int().positive().optional(),
43912
43923
  /** Default system prompt. A caller's `system` REPLACES it (never appends —
@@ -43945,6 +43956,7 @@ var LlmProfileFieldSchema = _enum([
43945
43956
  "topP",
43946
43957
  "topK",
43947
43958
  "maxTokens",
43959
+ "reasoningEffort",
43948
43960
  "contextLength",
43949
43961
  "systemPrompt",
43950
43962
  "timeoutMs",
@@ -90818,6 +90830,29 @@ function rewriteSetCookie(cookie, route) {
90818
90830
  return kept.join("; ");
90819
90831
  }
90820
90832
  /**
90833
+ * Every Set-Cookie of one vendor response, rewritten. Dropping `Domain` folds cookies
90834
+ * the vendor keeps apart (`session-id` on `.amazon.it` and on `.www.amazon.it`) into
90835
+ * one, so a past-dated set that only deleted the other domain's copy would delete the
90836
+ * live one the same response just set: that deletion is dropped.
90837
+ */
90838
+ function rewriteSetCookies(cookies, route) {
90839
+ const now = Date.now();
90840
+ const liveNames = new Set(cookies.filter((c) => !isExpiredCookie(c, now)).map(cookieName));
90841
+ return cookies.filter((c) => !(isExpiredCookie(c, now) && liveNames.has(cookieName(c)))).map((c) => rewriteSetCookie(c, route));
90842
+ }
90843
+ function cookieName(cookie) {
90844
+ return cookie.split(";")[0]?.split("=")[0]?.trim() ?? "";
90845
+ }
90846
+ function isExpiredCookie(cookie, now) {
90847
+ const attrs = cookie.split(";").slice(1).map((part) => part.trim());
90848
+ const maxAge = attrs.find((a) => /^max-age=/i.test(a));
90849
+ if (maxAge !== void 0) return Number(maxAge.slice(8)) <= 0;
90850
+ const expires = attrs.find((a) => /^expires=/i.test(a));
90851
+ if (expires === void 0) return false;
90852
+ const at = Date.parse(expires.slice(8));
90853
+ return Number.isFinite(at) && at <= now;
90854
+ }
90855
+ /**
90821
90856
  * A browser `Referer` that points into this proxy, turned back into the vendor URL it
90822
90857
  * stands for. A Referer that is not ours is dropped (null): it names the hub.
90823
90858
  */
@@ -90934,6 +90969,26 @@ async function readRequestBody(req) {
90934
90969
  }
90935
90970
  return Buffer.concat(chunks);
90936
90971
  }
90972
+ /** The path of the Referer sent upstream, or why none was sent — the query is never logged. */
90973
+ function forwardedRefererPath(forwarded, received) {
90974
+ if (forwarded !== void 0) return URL.canParse(forwarded) ? new URL(forwarded).pathname : "unparsable";
90975
+ return received === void 0 ? "absent" : "unmapped";
90976
+ }
90977
+ /** The form targets a vendor page posts to, path only (no query), so a lost path prefix is visible. */
90978
+ function formActionPaths(html) {
90979
+ const actions = [...html.matchAll(/<form\b[^>]*\baction\s*=\s*["']([^"']*)["']/gi)].map((m) => (m[1] ?? "").split("?")[0] ?? "").slice(0, 5);
90980
+ return actions.length === 0 ? "none" : actions.join(" | ");
90981
+ }
90982
+ /** Cookie NAMES only — values are session secrets and are never logged. */
90983
+ function cookieNames(header) {
90984
+ if (header === void 0 || header.length === 0) return "none";
90985
+ return header.split(";").map((part) => part.split("=")[0]?.trim() ?? "").join(",");
90986
+ }
90987
+ /** A Set-Cookie's name and attributes without its value, so a cookie the browser refuses is visible. */
90988
+ function setCookieShape(cookie) {
90989
+ const [first, ...attrs] = cookie.split(";").map((part) => part.trim());
90990
+ return [first?.split("=")[0] ?? "", ...attrs.filter((a) => !/^expires=/i.test(a))].join(";");
90991
+ }
90937
90992
  function sendPlain(res, status, text) {
90938
90993
  res.writeHead(status, {
90939
90994
  "content-type": "text/plain; charset=utf-8",
@@ -91069,7 +91124,11 @@ var VendorLoginProxy = class {
91069
91124
  host: upstream.host,
91070
91125
  path: target.pathname,
91071
91126
  status: upstreamRes.status,
91072
- method
91127
+ method,
91128
+ refererPath: forwardedRefererPath(headers["referer"], req.headers["referer"]),
91129
+ formActions: kind === "html" && text !== null ? formActionPaths(text) : void 0,
91130
+ sentNames: cookieNames(headers["cookie"]),
91131
+ vendorSets: setCookies.map(setCookieShape).join(" | ")
91073
91132
  } });
91074
91133
  const settled = await attempt.evaluate({
91075
91134
  status: upstreamRes.status,
@@ -91091,7 +91150,7 @@ var VendorLoginProxy = class {
91091
91150
  upstreamRes.headers.forEach((value, name) => {
91092
91151
  if (!DROP_RESPONSE_HEADERS.has(name.toLowerCase())) out[name] = value;
91093
91152
  });
91094
- if (setCookies.length > 0) out["set-cookie"] = setCookies.map((c) => rewriteSetCookie(c, route));
91153
+ if (setCookies.length > 0) out["set-cookie"] = rewriteSetCookies(setCookies, route);
91095
91154
  if (location !== null) out["location"] = rewriteLocation(location, upstream, route);
91096
91155
  out["cache-control"] = "no-store";
91097
91156
  if (text !== null && kind !== null) {
package/dist/addon.mjs CHANGED
@@ -43906,6 +43906,17 @@ var LlmProfileSchema = object({
43906
43906
  * field in its editor only when its descriptor lists it in `profileFields`. */
43907
43907
  topK: number().int().positive().optional(),
43908
43908
  maxTokens: number().int().positive().optional(),
43909
+ /** How hard the model "thinks" before it answers. Omitted = the model's own
43910
+ * default. `off` matters for structured output: a thinking model can spend
43911
+ * the whole completion on reasoning and return an empty `content`. Carried
43912
+ * only by the wires that have it — a kind offers the field in its editor
43913
+ * only when its descriptor lists it in `profileFields`. */
43914
+ reasoningEffort: _enum([
43915
+ "off",
43916
+ "low",
43917
+ "medium",
43918
+ "high"
43919
+ ]).optional(),
43909
43920
  /** Prompt context window. Advisory for cloud kinds (they enforce their own). */
43910
43921
  contextLength: number().int().positive().optional(),
43911
43922
  /** Default system prompt. A caller's `system` REPLACES it (never appends —
@@ -43944,6 +43955,7 @@ var LlmProfileFieldSchema = _enum([
43944
43955
  "topP",
43945
43956
  "topK",
43946
43957
  "maxTokens",
43958
+ "reasoningEffort",
43947
43959
  "contextLength",
43948
43960
  "systemPrompt",
43949
43961
  "timeoutMs",
@@ -90817,6 +90829,29 @@ function rewriteSetCookie(cookie, route) {
90817
90829
  return kept.join("; ");
90818
90830
  }
90819
90831
  /**
90832
+ * Every Set-Cookie of one vendor response, rewritten. Dropping `Domain` folds cookies
90833
+ * the vendor keeps apart (`session-id` on `.amazon.it` and on `.www.amazon.it`) into
90834
+ * one, so a past-dated set that only deleted the other domain's copy would delete the
90835
+ * live one the same response just set: that deletion is dropped.
90836
+ */
90837
+ function rewriteSetCookies(cookies, route) {
90838
+ const now = Date.now();
90839
+ const liveNames = new Set(cookies.filter((c) => !isExpiredCookie(c, now)).map(cookieName));
90840
+ return cookies.filter((c) => !(isExpiredCookie(c, now) && liveNames.has(cookieName(c)))).map((c) => rewriteSetCookie(c, route));
90841
+ }
90842
+ function cookieName(cookie) {
90843
+ return cookie.split(";")[0]?.split("=")[0]?.trim() ?? "";
90844
+ }
90845
+ function isExpiredCookie(cookie, now) {
90846
+ const attrs = cookie.split(";").slice(1).map((part) => part.trim());
90847
+ const maxAge = attrs.find((a) => /^max-age=/i.test(a));
90848
+ if (maxAge !== void 0) return Number(maxAge.slice(8)) <= 0;
90849
+ const expires = attrs.find((a) => /^expires=/i.test(a));
90850
+ if (expires === void 0) return false;
90851
+ const at = Date.parse(expires.slice(8));
90852
+ return Number.isFinite(at) && at <= now;
90853
+ }
90854
+ /**
90820
90855
  * A browser `Referer` that points into this proxy, turned back into the vendor URL it
90821
90856
  * stands for. A Referer that is not ours is dropped (null): it names the hub.
90822
90857
  */
@@ -90933,6 +90968,26 @@ async function readRequestBody(req) {
90933
90968
  }
90934
90969
  return Buffer.concat(chunks);
90935
90970
  }
90971
+ /** The path of the Referer sent upstream, or why none was sent — the query is never logged. */
90972
+ function forwardedRefererPath(forwarded, received) {
90973
+ if (forwarded !== void 0) return URL.canParse(forwarded) ? new URL(forwarded).pathname : "unparsable";
90974
+ return received === void 0 ? "absent" : "unmapped";
90975
+ }
90976
+ /** The form targets a vendor page posts to, path only (no query), so a lost path prefix is visible. */
90977
+ function formActionPaths(html) {
90978
+ const actions = [...html.matchAll(/<form\b[^>]*\baction\s*=\s*["']([^"']*)["']/gi)].map((m) => (m[1] ?? "").split("?")[0] ?? "").slice(0, 5);
90979
+ return actions.length === 0 ? "none" : actions.join(" | ");
90980
+ }
90981
+ /** Cookie NAMES only — values are session secrets and are never logged. */
90982
+ function cookieNames(header) {
90983
+ if (header === void 0 || header.length === 0) return "none";
90984
+ return header.split(";").map((part) => part.split("=")[0]?.trim() ?? "").join(",");
90985
+ }
90986
+ /** A Set-Cookie's name and attributes without its value, so a cookie the browser refuses is visible. */
90987
+ function setCookieShape(cookie) {
90988
+ const [first, ...attrs] = cookie.split(";").map((part) => part.trim());
90989
+ return [first?.split("=")[0] ?? "", ...attrs.filter((a) => !/^expires=/i.test(a))].join(";");
90990
+ }
90936
90991
  function sendPlain(res, status, text) {
90937
90992
  res.writeHead(status, {
90938
90993
  "content-type": "text/plain; charset=utf-8",
@@ -91068,7 +91123,11 @@ var VendorLoginProxy = class {
91068
91123
  host: upstream.host,
91069
91124
  path: target.pathname,
91070
91125
  status: upstreamRes.status,
91071
- method
91126
+ method,
91127
+ refererPath: forwardedRefererPath(headers["referer"], req.headers["referer"]),
91128
+ formActions: kind === "html" && text !== null ? formActionPaths(text) : void 0,
91129
+ sentNames: cookieNames(headers["cookie"]),
91130
+ vendorSets: setCookies.map(setCookieShape).join(" | ")
91072
91131
  } });
91073
91132
  const settled = await attempt.evaluate({
91074
91133
  status: upstreamRes.status,
@@ -91090,7 +91149,7 @@ var VendorLoginProxy = class {
91090
91149
  upstreamRes.headers.forEach((value, name) => {
91091
91150
  if (!DROP_RESPONSE_HEADERS.has(name.toLowerCase())) out[name] = value;
91092
91151
  });
91093
- if (setCookies.length > 0) out["set-cookie"] = setCookies.map((c) => rewriteSetCookie(c, route));
91152
+ if (setCookies.length > 0) out["set-cookie"] = rewriteSetCookies(setCookies, route);
91094
91153
  if (location !== null) out["location"] = rewriteLocation(location, upstream, route);
91095
91154
  out["cache-control"] = "no-store";
91096
91155
  if (text !== null && kind !== null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-import-alexa",
3
- "version": "0.2.164",
3
+ "version": "0.2.165",
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",