@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 +6 -0
- package/dist/addon.js +61 -2
- package/dist/addon.mjs +61 -2
- package/package.json +1 -1
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
|
|
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
|
|
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.
|
|
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",
|