@camstack/addon-provider-bosch 0.1.42 → 0.1.44
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 +12 -0
- package/dist/{account-integration-offline-D2xBMGsR.js → account-integration-offline-CFyuKmsG.js} +65 -122
- package/dist/{account-integration-offline-cC99EePp.mjs → account-integration-offline-IHodWmhD.mjs} +50 -113
- package/dist/{bosch-instances-BmvHlGiY.mjs → bosch-instances-C8FRjQzC.mjs} +19932 -17414
- package/dist/{bosch-instances-CINVxMF_.js → bosch-instances-CXMUquuA.js} +19978 -17478
- package/dist/home-connect/addon.js +20 -99
- package/dist/home-connect/addon.mjs +19 -98
- package/dist/homecom/addon.js +21 -108
- package/dist/homecom/addon.mjs +19 -106
- package/dist/shc/addon.js +8 -2
- package/dist/shc/addon.mjs +8 -2
- package/i18n/en.json +19 -4
- package/i18n/it.json +19 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.44] - 2026-11-09
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Bosch HomeCom and Home Connect sign-in no longer go through the hub login proxy: the form explains how to sign in with SingleKey ID in your own browser and copy the address back, in English and Italian.
|
|
15
|
+
|
|
16
|
+
## [0.1.43] - 2026-11-08
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Its integration-instances provider declares its instance kind and stamps it on every instance.
|
|
21
|
+
|
|
10
22
|
## [0.1.42] - 2026-11-06
|
|
11
23
|
|
|
12
24
|
### Changed
|
package/dist/{account-integration-offline-D2xBMGsR.js → account-integration-offline-CFyuKmsG.js}
RENAMED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
const require_bosch_instances = require("./bosch-instances-
|
|
1
|
+
const require_bosch_instances = require("./bosch-instances-CXMUquuA.js");
|
|
2
2
|
let node_crypto = require("node:crypto");
|
|
3
|
+
//#region src/shared/sign-in-redirects.ts
|
|
4
|
+
/** The addresses each vendor sends the browser to after a sign-in; no browser can open them. Kept free of imports so the form schema can name them. */
|
|
5
|
+
var HOMECOM_REDIRECT_URI = "com.bosch.tt.dashtt.pointt://app/login";
|
|
6
|
+
var HOME_CONNECT_REDIRECT_URI = "hcauth://auth/prod";
|
|
7
|
+
//#endregion
|
|
3
8
|
//#region src/shared/oauth-callback.ts
|
|
4
9
|
/** A code on its own: no scheme, no query, no separators. */
|
|
5
10
|
var BARE_CODE = /^[A-Za-z0-9._~-]+$/;
|
|
@@ -118,118 +123,50 @@ function loginFailureForAttempt(reason) {
|
|
|
118
123
|
return reason === "expired" ? "code-rejected" : "state-mismatch";
|
|
119
124
|
}
|
|
120
125
|
//#endregion
|
|
121
|
-
//#region src/shared/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const inBody = new RegExp(`${scheme.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"'<>\\s\\\\]+`);
|
|
155
|
-
return (view) => {
|
|
156
|
-
if (view.location !== null && view.location.startsWith(scheme)) return {
|
|
157
|
-
kind: "done",
|
|
158
|
-
payload: view.location
|
|
159
|
-
};
|
|
160
|
-
const fromBody = view.bodyText === null ? null : inBody.exec(unescapeHtml(view.bodyText));
|
|
161
|
-
if (fromBody !== null) return {
|
|
162
|
-
kind: "done",
|
|
163
|
-
payload: fromBody[0]
|
|
164
|
-
};
|
|
165
|
-
return { kind: "continue" };
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/** The result-page reason code for a sign-in failure reason (`HomeConnectLoginFailure`, …). */
|
|
169
|
-
function landingReasonForLogin(reason) {
|
|
170
|
-
switch (reason) {
|
|
171
|
-
case "denied": return "access_denied";
|
|
172
|
-
case "state-mismatch":
|
|
173
|
-
case "state-missing": return "invalid_state";
|
|
174
|
-
case "empty":
|
|
175
|
-
case "no-code": return "invalid_request";
|
|
176
|
-
case "code-rejected":
|
|
177
|
-
case "rejected": return "exchange_failed";
|
|
178
|
-
case "no-refresh": return "no_refresh_token";
|
|
179
|
-
default: return "unknown";
|
|
126
|
+
//#region src/shared/sign-in-guide.ts
|
|
127
|
+
var SIGN_IN_GUIDE_STEPS = [
|
|
128
|
+
{
|
|
129
|
+
id: "open",
|
|
130
|
+
text: (g) => `1. Click “Sign in with ${g.providerName}”. The sign-in page opens in a new tab of your own browser (it cannot be shown inside CamStack).`
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "login",
|
|
134
|
+
text: () => "2. Sign in there with your SingleKey ID: email, password and the one-time code if you are asked for one."
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "redirect",
|
|
138
|
+
text: (g) => `3. When you finish, the browser tries to open the Bosch app and fails or shows an empty page. That is expected: the address it tried to open is what CamStack needs. It starts with ${g.redirectStart}`
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: "chrome",
|
|
142
|
+
text: (g) => `Chrome, Edge, Brave: before you sign in press F12, open Network and tick “Preserve log”. After the failure pick the last request whose address starts with ${g.redirectStart} and use right click, Copy, Copy URL (or copy the address from the error line in the Console).`
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "firefox",
|
|
146
|
+
text: () => "Firefox: before you sign in press F12, open Network, open the gear menu and enable “Persist Logs”. After the failure right click the last request, Copy Value, Copy URL."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "safari",
|
|
150
|
+
text: () => "Safari: enable the Develop menu (Settings, Advanced), then Develop, Show Web Inspector, Network, and keep “Preserve Log” on. After the failure copy the address of the last request. Safari may also show the address in its “cannot open” message."
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: "phone",
|
|
154
|
+
text: () => "Phone or tablet: the failed address often stays in the address bar; press and hold it to copy. If it does not, repeat the sign-in from a computer."
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "paste",
|
|
158
|
+
text: () => "4. Paste the whole address (or only the code after “code=”) in the box that appears under the button. The sign-in completes by itself; the code is valid for about ten minutes."
|
|
180
159
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
* A failed exchange is parked as an operator-facing message and the browser lands on the
|
|
192
|
-
* result page with a reason code; nothing about the captured address leaves this function.
|
|
193
|
-
*/
|
|
194
|
-
function openSignInProxy(input) {
|
|
195
|
-
const started = input.nowMs();
|
|
196
|
-
input.proxy.open({
|
|
197
|
-
attemptId: input.attemptId,
|
|
198
|
-
expiresAtMs: started + SIGN_IN_ATTEMPT_TTL_MS,
|
|
199
|
-
upstreams: input.hosts.map((host) => ({
|
|
200
|
-
host,
|
|
201
|
-
scheme: input.scheme ?? "https"
|
|
202
|
-
})),
|
|
203
|
-
terminal: appSchemeTerminal(input.appScheme),
|
|
204
|
-
serverCookieJar: true,
|
|
205
|
-
onDone: async (callback) => {
|
|
206
|
-
try {
|
|
207
|
-
const result = await input.finish(callback);
|
|
208
|
-
input.claims.settle(input.attemptId, {
|
|
209
|
-
ok: true,
|
|
210
|
-
result
|
|
211
|
-
}, input.nowMs());
|
|
212
|
-
return {
|
|
213
|
-
status: "success",
|
|
214
|
-
addonId: input.addonId
|
|
215
|
-
};
|
|
216
|
-
} catch (err) {
|
|
217
|
-
const message = err instanceof Error ? err.message : "The sign-in failed.";
|
|
218
|
-
input.claims.settle(input.attemptId, {
|
|
219
|
-
ok: false,
|
|
220
|
-
message
|
|
221
|
-
}, input.nowMs());
|
|
222
|
-
return {
|
|
223
|
-
status: "error",
|
|
224
|
-
addonId: input.addonId,
|
|
225
|
-
reason: landingReasonForLogin(reasonOf(err))
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
const entry = input.proxy.entryPath(input.attemptId, input.authorizeUrl);
|
|
231
|
-
if (entry === null) throw new Error("The sign-in page host is not reachable through the proxy.");
|
|
232
|
-
return entry;
|
|
160
|
+
];
|
|
161
|
+
/** The info rows shown above the sign-in button: what to do and where each value comes from. */
|
|
162
|
+
function buildSignInGuideFields(input) {
|
|
163
|
+
return SIGN_IN_GUIDE_STEPS.map((step) => ({
|
|
164
|
+
type: "info",
|
|
165
|
+
key: `${input.keyPrefix}-guide-${step.id}`,
|
|
166
|
+
label: step.text(input),
|
|
167
|
+
labelKey: `provider-bosch:${input.i18nPrefix}.${input.keyPrefix}-guide-${step.id}.label`,
|
|
168
|
+
content: ""
|
|
169
|
+
}));
|
|
233
170
|
}
|
|
234
171
|
//#endregion
|
|
235
172
|
//#region src/shared/account-integration-cascade.ts
|
|
@@ -269,34 +206,40 @@ function setIntegrationDevicesOnline(devices, system, integrationId, online) {
|
|
|
269
206
|
return count;
|
|
270
207
|
}
|
|
271
208
|
//#endregion
|
|
272
|
-
Object.defineProperty(exports, "
|
|
209
|
+
Object.defineProperty(exports, "HOMECOM_REDIRECT_URI", {
|
|
273
210
|
enumerable: true,
|
|
274
211
|
get: function() {
|
|
275
|
-
return
|
|
212
|
+
return HOMECOM_REDIRECT_URI;
|
|
276
213
|
}
|
|
277
214
|
});
|
|
278
|
-
Object.defineProperty(exports, "
|
|
215
|
+
Object.defineProperty(exports, "HOME_CONNECT_REDIRECT_URI", {
|
|
279
216
|
enumerable: true,
|
|
280
217
|
get: function() {
|
|
281
|
-
return
|
|
218
|
+
return HOME_CONNECT_REDIRECT_URI;
|
|
282
219
|
}
|
|
283
220
|
});
|
|
284
|
-
Object.defineProperty(exports, "
|
|
221
|
+
Object.defineProperty(exports, "buildSignInGuideFields", {
|
|
285
222
|
enumerable: true,
|
|
286
223
|
get: function() {
|
|
287
|
-
return
|
|
224
|
+
return buildSignInGuideFields;
|
|
288
225
|
}
|
|
289
226
|
});
|
|
290
|
-
Object.defineProperty(exports, "
|
|
227
|
+
Object.defineProperty(exports, "cascadeRemoveDevicesForIntegration", {
|
|
291
228
|
enumerable: true,
|
|
292
229
|
get: function() {
|
|
293
|
-
return
|
|
230
|
+
return cascadeRemoveDevicesForIntegration;
|
|
294
231
|
}
|
|
295
232
|
});
|
|
296
|
-
Object.defineProperty(exports, "
|
|
233
|
+
Object.defineProperty(exports, "createSignInAttemptStore", {
|
|
297
234
|
enumerable: true,
|
|
298
235
|
get: function() {
|
|
299
|
-
return
|
|
236
|
+
return createSignInAttemptStore;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
Object.defineProperty(exports, "loginFailureForAttempt", {
|
|
240
|
+
enumerable: true,
|
|
241
|
+
get: function() {
|
|
242
|
+
return loginFailureForAttempt;
|
|
300
243
|
}
|
|
301
244
|
});
|
|
302
245
|
Object.defineProperty(exports, "parseOAuthCallback", {
|
package/dist/{account-integration-offline-cC99EePp.mjs → account-integration-offline-IHodWmhD.mjs}
RENAMED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as errMsg } from "./bosch-instances-C8FRjQzC.mjs";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
|
+
//#region src/shared/sign-in-redirects.ts
|
|
4
|
+
/** The addresses each vendor sends the browser to after a sign-in; no browser can open them. Kept free of imports so the form schema can name them. */
|
|
5
|
+
var HOMECOM_REDIRECT_URI = "com.bosch.tt.dashtt.pointt://app/login";
|
|
6
|
+
var HOME_CONNECT_REDIRECT_URI = "hcauth://auth/prod";
|
|
7
|
+
//#endregion
|
|
3
8
|
//#region src/shared/oauth-callback.ts
|
|
4
9
|
/** A code on its own: no scheme, no query, no separators. */
|
|
5
10
|
var BARE_CODE = /^[A-Za-z0-9._~-]+$/;
|
|
@@ -118,118 +123,50 @@ function loginFailureForAttempt(reason) {
|
|
|
118
123
|
return reason === "expired" ? "code-rejected" : "state-mismatch";
|
|
119
124
|
}
|
|
120
125
|
//#endregion
|
|
121
|
-
//#region src/shared/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const inBody = new RegExp(`${scheme.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"'<>\\s\\\\]+`);
|
|
155
|
-
return (view) => {
|
|
156
|
-
if (view.location !== null && view.location.startsWith(scheme)) return {
|
|
157
|
-
kind: "done",
|
|
158
|
-
payload: view.location
|
|
159
|
-
};
|
|
160
|
-
const fromBody = view.bodyText === null ? null : inBody.exec(unescapeHtml(view.bodyText));
|
|
161
|
-
if (fromBody !== null) return {
|
|
162
|
-
kind: "done",
|
|
163
|
-
payload: fromBody[0]
|
|
164
|
-
};
|
|
165
|
-
return { kind: "continue" };
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/** The result-page reason code for a sign-in failure reason (`HomeConnectLoginFailure`, …). */
|
|
169
|
-
function landingReasonForLogin(reason) {
|
|
170
|
-
switch (reason) {
|
|
171
|
-
case "denied": return "access_denied";
|
|
172
|
-
case "state-mismatch":
|
|
173
|
-
case "state-missing": return "invalid_state";
|
|
174
|
-
case "empty":
|
|
175
|
-
case "no-code": return "invalid_request";
|
|
176
|
-
case "code-rejected":
|
|
177
|
-
case "rejected": return "exchange_failed";
|
|
178
|
-
case "no-refresh": return "no_refresh_token";
|
|
179
|
-
default: return "unknown";
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function reasonOf(err) {
|
|
183
|
-
if (typeof err === "object" && err !== null && "reason" in err) {
|
|
184
|
-
const reason = err.reason;
|
|
185
|
-
if (typeof reason === "string") return reason;
|
|
126
|
+
//#region src/shared/sign-in-guide.ts
|
|
127
|
+
var SIGN_IN_GUIDE_STEPS = [
|
|
128
|
+
{
|
|
129
|
+
id: "open",
|
|
130
|
+
text: (g) => `1. Click “Sign in with ${g.providerName}”. The sign-in page opens in a new tab of your own browser (it cannot be shown inside CamStack).`
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "login",
|
|
134
|
+
text: () => "2. Sign in there with your SingleKey ID: email, password and the one-time code if you are asked for one."
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "redirect",
|
|
138
|
+
text: (g) => `3. When you finish, the browser tries to open the Bosch app and fails or shows an empty page. That is expected: the address it tried to open is what CamStack needs. It starts with ${g.redirectStart}`
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: "chrome",
|
|
142
|
+
text: (g) => `Chrome, Edge, Brave: before you sign in press F12, open Network and tick “Preserve log”. After the failure pick the last request whose address starts with ${g.redirectStart} and use right click, Copy, Copy URL (or copy the address from the error line in the Console).`
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "firefox",
|
|
146
|
+
text: () => "Firefox: before you sign in press F12, open Network, open the gear menu and enable “Persist Logs”. After the failure right click the last request, Copy Value, Copy URL."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: "safari",
|
|
150
|
+
text: () => "Safari: enable the Develop menu (Settings, Advanced), then Develop, Show Web Inspector, Network, and keep “Preserve Log” on. After the failure copy the address of the last request. Safari may also show the address in its “cannot open” message."
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: "phone",
|
|
154
|
+
text: () => "Phone or tablet: the failed address often stays in the address bar; press and hold it to copy. If it does not, repeat the sign-in from a computer."
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "paste",
|
|
158
|
+
text: () => "4. Paste the whole address (or only the code after “code=”) in the box that appears under the button. The sign-in completes by itself; the code is valid for about ten minutes."
|
|
186
159
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
attemptId: input.attemptId,
|
|
198
|
-
expiresAtMs: started + SIGN_IN_ATTEMPT_TTL_MS,
|
|
199
|
-
upstreams: input.hosts.map((host) => ({
|
|
200
|
-
host,
|
|
201
|
-
scheme: input.scheme ?? "https"
|
|
202
|
-
})),
|
|
203
|
-
terminal: appSchemeTerminal(input.appScheme),
|
|
204
|
-
serverCookieJar: true,
|
|
205
|
-
onDone: async (callback) => {
|
|
206
|
-
try {
|
|
207
|
-
const result = await input.finish(callback);
|
|
208
|
-
input.claims.settle(input.attemptId, {
|
|
209
|
-
ok: true,
|
|
210
|
-
result
|
|
211
|
-
}, input.nowMs());
|
|
212
|
-
return {
|
|
213
|
-
status: "success",
|
|
214
|
-
addonId: input.addonId
|
|
215
|
-
};
|
|
216
|
-
} catch (err) {
|
|
217
|
-
const message = err instanceof Error ? err.message : "The sign-in failed.";
|
|
218
|
-
input.claims.settle(input.attemptId, {
|
|
219
|
-
ok: false,
|
|
220
|
-
message
|
|
221
|
-
}, input.nowMs());
|
|
222
|
-
return {
|
|
223
|
-
status: "error",
|
|
224
|
-
addonId: input.addonId,
|
|
225
|
-
reason: landingReasonForLogin(reasonOf(err))
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
const entry = input.proxy.entryPath(input.attemptId, input.authorizeUrl);
|
|
231
|
-
if (entry === null) throw new Error("The sign-in page host is not reachable through the proxy.");
|
|
232
|
-
return entry;
|
|
160
|
+
];
|
|
161
|
+
/** The info rows shown above the sign-in button: what to do and where each value comes from. */
|
|
162
|
+
function buildSignInGuideFields(input) {
|
|
163
|
+
return SIGN_IN_GUIDE_STEPS.map((step) => ({
|
|
164
|
+
type: "info",
|
|
165
|
+
key: `${input.keyPrefix}-guide-${step.id}`,
|
|
166
|
+
label: step.text(input),
|
|
167
|
+
labelKey: `provider-bosch:${input.i18nPrefix}.${input.keyPrefix}-guide-${step.id}.label`,
|
|
168
|
+
content: ""
|
|
169
|
+
}));
|
|
233
170
|
}
|
|
234
171
|
//#endregion
|
|
235
172
|
//#region src/shared/account-integration-cascade.ts
|
|
@@ -269,4 +206,4 @@ function setIntegrationDevicesOnline(devices, system, integrationId, online) {
|
|
|
269
206
|
return count;
|
|
270
207
|
}
|
|
271
208
|
//#endregion
|
|
272
|
-
export {
|
|
209
|
+
export { loginFailureForAttempt as a, HOME_CONNECT_REDIRECT_URI as c, createSignInAttemptStore as i, cascadeRemoveDevicesForIntegration as n, parseOAuthCallback as o, buildSignInGuideFields as r, HOMECOM_REDIRECT_URI as s, setIntegrationDevicesOnline as t };
|