@camstack/addon-google-services 0.2.10 → 0.2.12
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/dist/google-services.addon.js +483 -160
- package/dist/google-services.addon.mjs +483 -160
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
let node_crypto = require("node:crypto");
|
|
6
|
-
//#region ../types/dist/event-category-
|
|
6
|
+
//#region ../types/dist/event-category-DOl51-Ij.mjs
|
|
7
7
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
8
8
|
EventCategory["SystemBoot"] = "system.boot";
|
|
9
9
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -82,9 +82,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
82
82
|
/**
|
|
83
83
|
* The node's one-shot runner boot check (D877): planned runners against the
|
|
84
84
|
* process service, `degraded` when any is failed, refused or missing. The
|
|
85
|
-
* alert centre turns it into one
|
|
85
|
+
* alert centre turns it into one critical alert.
|
|
86
86
|
*/
|
|
87
87
|
EventCategory["SystemRunnerCheck"] = "system.runner-check";
|
|
88
|
+
/**
|
|
89
|
+
* An alert EPISODE opened or closed, emitted by the alert centre for alerts
|
|
90
|
+
* of severity `critical` or `error` (`AlertEpisodePayload`). The alert
|
|
91
|
+
* centre sends no notification of its own: Notification Center turns this
|
|
92
|
+
* into the `alert-critical` / `alert-error` / `alert-critical-resolved`
|
|
93
|
+
* system-event kinds, and reconciles from the alerts store because a bus
|
|
94
|
+
* event may be dropped (D8, D11).
|
|
95
|
+
*/
|
|
96
|
+
EventCategory["AlertEpisode"] = "alert.episode";
|
|
88
97
|
EventCategory["AddonError"] = "addon.error";
|
|
89
98
|
EventCategory["AddonPageReady"] = "addon.page-ready";
|
|
90
99
|
EventCategory["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -5542,7 +5551,7 @@ var ZodIssueCode = {
|
|
|
5542
5551
|
var ZodFirstPartyTypeKind;
|
|
5543
5552
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5544
5553
|
//#endregion
|
|
5545
|
-
//#region ../types/dist/sleep-
|
|
5554
|
+
//#region ../types/dist/sleep-BxjU_fJM.mjs
|
|
5546
5555
|
/**
|
|
5547
5556
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5548
5557
|
* window to float samples (D455).
|
|
@@ -13641,7 +13650,14 @@ var AlertSeveritySchema = _enum([
|
|
|
13641
13650
|
]);
|
|
13642
13651
|
var AlertActionSchema = object({
|
|
13643
13652
|
label: string(),
|
|
13644
|
-
href: string().optional()
|
|
13653
|
+
href: string().optional(),
|
|
13654
|
+
/**
|
|
13655
|
+
* How `href` is followed. Absent / `app`: an admin-ui route (client-side).
|
|
13656
|
+
* `server`: a route the HUB serves (an addon HTTP route such as an OAuth
|
|
13657
|
+
* start) — the browser must leave the SPA with a full navigation, because
|
|
13658
|
+
* the SPA has no such route and would bounce to the home page.
|
|
13659
|
+
*/
|
|
13660
|
+
navigation: _enum(["app", "server"]).optional()
|
|
13645
13661
|
});
|
|
13646
13662
|
var AlertStatusSchema = _enum([
|
|
13647
13663
|
"active",
|
|
@@ -17854,7 +17870,11 @@ DeviceType.Hub, DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number().int
|
|
|
17854
17870
|
});
|
|
17855
17871
|
/**
|
|
17856
17872
|
* `device-adoption` — generic discovery + adoption surface,
|
|
17857
|
-
* system-scoped
|
|
17873
|
+
* system-scoped collection (one provider per integration addon), routed
|
|
17874
|
+
* by explicit `{ addonId }` on direct `deviceAdoption.*` calls — the same
|
|
17875
|
+
* pin model as `connection-test`. The admin UI keeps using
|
|
17876
|
+
* `deviceManager.adoption*` (which already threads `addonId`). The de-HA-ified
|
|
17877
|
+
* successor to `ha-discovery`:
|
|
17858
17878
|
* keyed by `integrationId` (the addon resolves integrationId → broker
|
|
17859
17879
|
* internally) rather than a connection-specific `brokerId`, and it
|
|
17860
17880
|
* REUSES the shared `DiscoveredChildDevice` candidate type from
|
|
@@ -42062,6 +42082,9 @@ var NcSystemEventKindSchema = _enum([
|
|
|
42062
42082
|
"server-updated",
|
|
42063
42083
|
"export-completed",
|
|
42064
42084
|
"backup-completed",
|
|
42085
|
+
"alert-critical",
|
|
42086
|
+
"alert-error",
|
|
42087
|
+
"alert-critical-resolved",
|
|
42065
42088
|
"camera-online",
|
|
42066
42089
|
"camera-offline",
|
|
42067
42090
|
"camera-disabled",
|
|
@@ -51107,7 +51130,10 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
|
|
|
51107
51130
|
}), method(object({ jobId: string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
51108
51131
|
kind: "mutation",
|
|
51109
51132
|
auth: "admin"
|
|
51110
|
-
}), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({
|
|
51133
|
+
}), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({ locationIds: array(string()).readonly() }), object({
|
|
51134
|
+
clear: boolean(),
|
|
51135
|
+
repointLocationId: string().optional()
|
|
51136
|
+
}), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
|
|
51111
51137
|
kind: "mutation",
|
|
51112
51138
|
auth: "admin"
|
|
51113
51139
|
}), method(StorageCleanupInputSchema, object({ jobId: string() }), {
|
|
@@ -54552,6 +54578,11 @@ object({
|
|
|
54552
54578
|
*
|
|
54553
54579
|
* `kind: 'poll'` — cloud REST APIs typically have no push channel; the
|
|
54554
54580
|
* provider refreshes the slice on an interval and eagerly after commands.
|
|
54581
|
+
*
|
|
54582
|
+
* UNKNOWN IS NOT OFF. Every field the appliance reports separately is
|
|
54583
|
+
* nullable: `null` means "not known right now" (the appliance has not said,
|
|
54584
|
+
* the link is down, or the description does not carry it) and is never
|
|
54585
|
+
* rendered or exported as `false` / `0`. `availability` says why.
|
|
54555
54586
|
*/
|
|
54556
54587
|
var DishwasherStateSchema = _enum([
|
|
54557
54588
|
"idle",
|
|
@@ -54561,80 +54592,227 @@ var DishwasherStateSchema = _enum([
|
|
|
54561
54592
|
"error",
|
|
54562
54593
|
"unavailable"
|
|
54563
54594
|
]);
|
|
54595
|
+
/**
|
|
54596
|
+
* The appliance's own operation state, uncollapsed. `state` above is the coarse
|
|
54597
|
+
* view for compact surfaces; this is what the appliance said.
|
|
54598
|
+
*/
|
|
54599
|
+
var DishwasherOperationStateSchema = _enum([
|
|
54600
|
+
"Inactive",
|
|
54601
|
+
"Ready",
|
|
54602
|
+
"DelayedStart",
|
|
54603
|
+
"Run",
|
|
54604
|
+
"Pause",
|
|
54605
|
+
"ActionRequired",
|
|
54606
|
+
"Finished",
|
|
54607
|
+
"Error",
|
|
54608
|
+
"Aborting"
|
|
54609
|
+
]);
|
|
54610
|
+
/**
|
|
54611
|
+
* Why fields may be `null`:
|
|
54612
|
+
* - `available` — the link is up and the description is loaded;
|
|
54613
|
+
* - `offline` — the local link is down (values are unknown, not last-known);
|
|
54614
|
+
* - `description-missing` — the link is up but the appliance's description could not
|
|
54615
|
+
* be loaded, so its values cannot be decoded.
|
|
54616
|
+
*/
|
|
54617
|
+
var DishwasherAvailabilitySchema = _enum([
|
|
54618
|
+
"available",
|
|
54619
|
+
"offline",
|
|
54620
|
+
"description-missing"
|
|
54621
|
+
]);
|
|
54564
54622
|
var DishwasherConsumableLevelSchema = _enum([
|
|
54565
54623
|
"ok",
|
|
54566
54624
|
"low",
|
|
54567
54625
|
"empty"
|
|
54568
54626
|
]);
|
|
54569
54627
|
object({
|
|
54628
|
+
availability: DishwasherAvailabilitySchema,
|
|
54629
|
+
/** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
|
|
54570
54630
|
state: DishwasherStateSchema,
|
|
54571
|
-
/**
|
|
54631
|
+
/** The appliance's operation state as reported; null when not known. */
|
|
54632
|
+
operationState: DishwasherOperationStateSchema.nullable(),
|
|
54633
|
+
/** Feature key of the ACTIVE (running) program, or null when none runs / not known. */
|
|
54572
54634
|
program: string().nullable(),
|
|
54573
|
-
/**
|
|
54635
|
+
/** Feature key of the SELECTED program (what Start would run), or null when none / not known. */
|
|
54636
|
+
selectedProgram: string().nullable(),
|
|
54637
|
+
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
54574
54638
|
availablePrograms: array(string()),
|
|
54575
|
-
/**
|
|
54576
|
-
|
|
54639
|
+
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
54640
|
+
remainingSeconds: number().int().nonnegative().nullable(),
|
|
54641
|
+
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
54642
|
+
delayedStartSeconds: number().int().nonnegative().nullable(),
|
|
54577
54643
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
54578
54644
|
progressPercent: number().min(0).max(100).nullable(),
|
|
54579
|
-
/** True when the door is reported open. */
|
|
54580
|
-
doorOpen: boolean(),
|
|
54581
|
-
/** True when
|
|
54582
|
-
|
|
54583
|
-
/**
|
|
54584
|
-
|
|
54645
|
+
/** True when the door is reported open; null when the door state is not known. */
|
|
54646
|
+
doorOpen: boolean().nullable(),
|
|
54647
|
+
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
54648
|
+
doorLocked: boolean().nullable(),
|
|
54649
|
+
/** True when remote start is allowed (door closed, power on, etc.); null when not known. */
|
|
54650
|
+
remoteStartAllowed: boolean().nullable(),
|
|
54651
|
+
/** True when the appliance accepts remote control now; null when not known. */
|
|
54652
|
+
remoteControlActive: boolean().nullable(),
|
|
54653
|
+
/** True while somebody operates the appliance at its panel; null when not reported. */
|
|
54654
|
+
localControlActive: boolean().nullable(),
|
|
54655
|
+
/** Main power / switched-on state; null when not known. */
|
|
54656
|
+
powerOn: boolean().nullable(),
|
|
54657
|
+
/** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
|
|
54658
|
+
powerState: string().nullable(),
|
|
54585
54659
|
saltLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
54586
54660
|
rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
54587
|
-
|
|
54588
|
-
|
|
54661
|
+
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
54662
|
+
halfLoad: boolean().nullable(),
|
|
54663
|
+
hygienePlus: boolean().nullable(),
|
|
54664
|
+
/** Extra dry option when the appliance exposes it. */
|
|
54665
|
+
extraDry: boolean().nullable(),
|
|
54666
|
+
/** Intensive zone (lower basket) when the appliance exposes it. */
|
|
54667
|
+
intensiveZone: boolean().nullable(),
|
|
54668
|
+
/** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
|
|
54669
|
+
varioSpeedPlus: boolean().nullable(),
|
|
54670
|
+
/** Child lock (controls disabled on the appliance); null when not known. */
|
|
54671
|
+
childLock: boolean().nullable(),
|
|
54672
|
+
/** Feature keys of the events currently raised (salt low, rinse aid low, program finished, …). */
|
|
54673
|
+
activeEvents: array(string()),
|
|
54674
|
+
/** Numeric error code when the appliance reports one; null otherwise. */
|
|
54589
54675
|
errorCode: number().nullable(),
|
|
54676
|
+
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
54590
54677
|
errorLabel: string().nullable(),
|
|
54678
|
+
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
54591
54679
|
lastFetchedAt: number()
|
|
54592
54680
|
});
|
|
54593
|
-
|
|
54681
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
54682
|
+
/** A value a program option or setting can take. */
|
|
54683
|
+
var DishwasherValueSchema = union([
|
|
54684
|
+
boolean(),
|
|
54685
|
+
string(),
|
|
54686
|
+
number()
|
|
54687
|
+
]);
|
|
54688
|
+
var DishwasherAccessSchema = _enum([
|
|
54689
|
+
"none",
|
|
54690
|
+
"read",
|
|
54691
|
+
"readWrite",
|
|
54692
|
+
"writeOnly",
|
|
54693
|
+
"readStatic"
|
|
54694
|
+
]);
|
|
54695
|
+
/**
|
|
54696
|
+
* One option or setting the appliance declares, with the constraints its description
|
|
54697
|
+
* gives. A `readWrite` descriptor that is `available` is what `setOption` accepts.
|
|
54698
|
+
*/
|
|
54699
|
+
var DishwasherOptionDescriptorSchema = object({
|
|
54700
|
+
/** Feature key (`Dishcare.Dishwasher.Option.ExtraDry`). */
|
|
54701
|
+
key: string(),
|
|
54702
|
+
/** Last segment of the key, for display. */
|
|
54703
|
+
name: string(),
|
|
54704
|
+
type: _enum([
|
|
54705
|
+
"boolean",
|
|
54706
|
+
"number",
|
|
54707
|
+
"enum",
|
|
54708
|
+
"string"
|
|
54709
|
+
]),
|
|
54710
|
+
access: DishwasherAccessSchema,
|
|
54711
|
+
/** The appliance reports this entity as currently available. */
|
|
54712
|
+
available: boolean(),
|
|
54713
|
+
/** Unit the description declares for a number; null when it declares none. */
|
|
54714
|
+
unit: string().nullable(),
|
|
54715
|
+
min: number().nullable(),
|
|
54716
|
+
max: number().nullable(),
|
|
54717
|
+
step: number().nullable(),
|
|
54718
|
+
/** Allowed members of an enum; null for other types. */
|
|
54719
|
+
enumValues: array(string()).nullable(),
|
|
54720
|
+
defaultValue: DishwasherValueSchema.nullable()
|
|
54721
|
+
});
|
|
54722
|
+
var DishwasherProgramSchema = object({
|
|
54723
|
+
/** Feature key (`Dishcare.Dishwasher.Program.Eco50`) — what `setProgram` / `start` take. */
|
|
54724
|
+
key: string(),
|
|
54725
|
+
name: string(),
|
|
54726
|
+
/** The appliance currently offers this program. */
|
|
54727
|
+
available: boolean(),
|
|
54728
|
+
/** The options this program lists, with the program-level access / availability / default. */
|
|
54729
|
+
options: array(DishwasherOptionDescriptorSchema)
|
|
54730
|
+
});
|
|
54731
|
+
var DishwasherOptionsSchema = object({
|
|
54732
|
+
availability: DishwasherAvailabilitySchema,
|
|
54733
|
+
/** What the appliance can be told to do, from the commands its description declares. */
|
|
54734
|
+
supportsStart: boolean(),
|
|
54735
|
+
supportsPause: boolean(),
|
|
54736
|
+
supportsResume: boolean(),
|
|
54737
|
+
supportsStop: boolean(),
|
|
54738
|
+
supportsPower: boolean(),
|
|
54739
|
+
/** Every program the description lists, with its options. */
|
|
54740
|
+
programs: array(DishwasherProgramSchema),
|
|
54741
|
+
/** Every option and setting entity the description lists (programs reference these by key). */
|
|
54742
|
+
options: array(DishwasherOptionDescriptorSchema)
|
|
54743
|
+
});
|
|
54744
|
+
DeviceType.Dishwasher, method(object({
|
|
54745
|
+
deviceId: DeviceIdSchema,
|
|
54746
|
+
program: string().min(1).optional(),
|
|
54747
|
+
options: record(string(), DishwasherValueSchema).optional()
|
|
54748
|
+
}), _void(), {
|
|
54594
54749
|
kind: "mutation",
|
|
54595
54750
|
auth: "protected"
|
|
54596
|
-
}), method(object({ deviceId:
|
|
54751
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
54597
54752
|
kind: "mutation",
|
|
54598
54753
|
auth: "protected"
|
|
54599
|
-
}), method(object({ deviceId:
|
|
54754
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
54755
|
+
kind: "mutation",
|
|
54756
|
+
auth: "protected"
|
|
54757
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
54600
54758
|
kind: "mutation",
|
|
54601
54759
|
auth: "protected"
|
|
54602
54760
|
}), method(object({
|
|
54603
|
-
deviceId:
|
|
54761
|
+
deviceId: DeviceIdSchema,
|
|
54604
54762
|
program: string().min(1)
|
|
54605
54763
|
}), _void(), {
|
|
54606
54764
|
kind: "mutation",
|
|
54607
54765
|
auth: "protected"
|
|
54608
54766
|
}), method(object({
|
|
54609
|
-
deviceId:
|
|
54767
|
+
deviceId: DeviceIdSchema,
|
|
54610
54768
|
on: boolean()
|
|
54611
54769
|
}), _void(), {
|
|
54612
54770
|
kind: "mutation",
|
|
54613
54771
|
auth: "protected"
|
|
54614
54772
|
}), method(object({
|
|
54615
|
-
deviceId:
|
|
54773
|
+
deviceId: DeviceIdSchema,
|
|
54616
54774
|
on: boolean()
|
|
54617
54775
|
}), _void(), {
|
|
54618
54776
|
kind: "mutation",
|
|
54619
54777
|
auth: "protected"
|
|
54620
54778
|
}), method(object({
|
|
54621
|
-
deviceId:
|
|
54779
|
+
deviceId: DeviceIdSchema,
|
|
54622
54780
|
on: boolean()
|
|
54623
54781
|
}), _void(), {
|
|
54624
54782
|
kind: "mutation",
|
|
54625
54783
|
auth: "protected"
|
|
54626
54784
|
}), method(object({
|
|
54627
|
-
deviceId:
|
|
54785
|
+
deviceId: DeviceIdSchema,
|
|
54786
|
+
on: boolean()
|
|
54787
|
+
}), _void(), {
|
|
54788
|
+
kind: "mutation",
|
|
54789
|
+
auth: "protected"
|
|
54790
|
+
}), method(object({
|
|
54791
|
+
deviceId: DeviceIdSchema,
|
|
54792
|
+
on: boolean()
|
|
54793
|
+
}), _void(), {
|
|
54794
|
+
kind: "mutation",
|
|
54795
|
+
auth: "protected"
|
|
54796
|
+
}), method(object({
|
|
54797
|
+
deviceId: DeviceIdSchema,
|
|
54798
|
+
on: boolean()
|
|
54799
|
+
}), _void(), {
|
|
54800
|
+
kind: "mutation",
|
|
54801
|
+
auth: "protected"
|
|
54802
|
+
}), method(object({
|
|
54803
|
+
deviceId: DeviceIdSchema,
|
|
54804
|
+
on: boolean()
|
|
54805
|
+
}), _void(), {
|
|
54806
|
+
kind: "mutation",
|
|
54807
|
+
auth: "protected"
|
|
54808
|
+
}), method(object({
|
|
54809
|
+
deviceId: DeviceIdSchema,
|
|
54628
54810
|
key: string().min(1),
|
|
54629
|
-
value:
|
|
54630
|
-
boolean(),
|
|
54631
|
-
string(),
|
|
54632
|
-
number()
|
|
54633
|
-
])
|
|
54811
|
+
value: DishwasherValueSchema
|
|
54634
54812
|
}), _void(), {
|
|
54635
54813
|
kind: "mutation",
|
|
54636
54814
|
auth: "protected"
|
|
54637
|
-
});
|
|
54815
|
+
}), method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" });
|
|
54638
54816
|
object({
|
|
54639
54817
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
54640
54818
|
lastPressedAt: number().nullable(),
|
|
@@ -64694,6 +64872,12 @@ Object.freeze({
|
|
|
64694
64872
|
addonId: null,
|
|
64695
64873
|
access: "view"
|
|
64696
64874
|
},
|
|
64875
|
+
"dishwasher.getOptions": {
|
|
64876
|
+
capName: "dishwasher",
|
|
64877
|
+
capScope: "device",
|
|
64878
|
+
addonId: null,
|
|
64879
|
+
access: "view"
|
|
64880
|
+
},
|
|
64697
64881
|
"dishwasher.getStatus": {
|
|
64698
64882
|
capName: "dishwasher",
|
|
64699
64883
|
capScope: "device",
|
|
@@ -64706,6 +64890,24 @@ Object.freeze({
|
|
|
64706
64890
|
addonId: null,
|
|
64707
64891
|
access: "create"
|
|
64708
64892
|
},
|
|
64893
|
+
"dishwasher.resume": {
|
|
64894
|
+
capName: "dishwasher",
|
|
64895
|
+
capScope: "device",
|
|
64896
|
+
addonId: null,
|
|
64897
|
+
access: "create"
|
|
64898
|
+
},
|
|
64899
|
+
"dishwasher.setChildLock": {
|
|
64900
|
+
capName: "dishwasher",
|
|
64901
|
+
capScope: "device",
|
|
64902
|
+
addonId: null,
|
|
64903
|
+
access: "create"
|
|
64904
|
+
},
|
|
64905
|
+
"dishwasher.setExtraDry": {
|
|
64906
|
+
capName: "dishwasher",
|
|
64907
|
+
capScope: "device",
|
|
64908
|
+
addonId: null,
|
|
64909
|
+
access: "create"
|
|
64910
|
+
},
|
|
64709
64911
|
"dishwasher.setHalfLoad": {
|
|
64710
64912
|
capName: "dishwasher",
|
|
64711
64913
|
capScope: "device",
|
|
@@ -64718,6 +64920,12 @@ Object.freeze({
|
|
|
64718
64920
|
addonId: null,
|
|
64719
64921
|
access: "create"
|
|
64720
64922
|
},
|
|
64923
|
+
"dishwasher.setIntensiveZone": {
|
|
64924
|
+
capName: "dishwasher",
|
|
64925
|
+
capScope: "device",
|
|
64926
|
+
addonId: null,
|
|
64927
|
+
access: "create"
|
|
64928
|
+
},
|
|
64721
64929
|
"dishwasher.setOption": {
|
|
64722
64930
|
capName: "dishwasher",
|
|
64723
64931
|
capScope: "device",
|
|
@@ -64736,6 +64944,12 @@ Object.freeze({
|
|
|
64736
64944
|
addonId: null,
|
|
64737
64945
|
access: "create"
|
|
64738
64946
|
},
|
|
64947
|
+
"dishwasher.setVarioSpeedPlus": {
|
|
64948
|
+
capName: "dishwasher",
|
|
64949
|
+
capScope: "device",
|
|
64950
|
+
addonId: null,
|
|
64951
|
+
access: "create"
|
|
64952
|
+
},
|
|
64739
64953
|
"dishwasher.start": {
|
|
64740
64954
|
capName: "dishwasher",
|
|
64741
64955
|
capScope: "device",
|
|
@@ -69182,6 +69396,12 @@ Object.freeze({
|
|
|
69182
69396
|
addonId: null,
|
|
69183
69397
|
access: "view"
|
|
69184
69398
|
},
|
|
69399
|
+
"storageMigration.moverClearance": {
|
|
69400
|
+
capName: "storage-migration",
|
|
69401
|
+
capScope: "system",
|
|
69402
|
+
addonId: null,
|
|
69403
|
+
access: "view"
|
|
69404
|
+
},
|
|
69185
69405
|
"storageMigration.movers": {
|
|
69186
69406
|
capName: "storage-migration",
|
|
69187
69407
|
capScope: "system",
|
|
@@ -71375,6 +71595,11 @@ Object.freeze({
|
|
|
71375
71595
|
form: "single",
|
|
71376
71596
|
optional: false
|
|
71377
71597
|
}],
|
|
71598
|
+
"dishwasher.getOptions": [{
|
|
71599
|
+
name: "deviceId",
|
|
71600
|
+
form: "single",
|
|
71601
|
+
optional: false
|
|
71602
|
+
}],
|
|
71378
71603
|
"dishwasher.getStatus": [{
|
|
71379
71604
|
name: "deviceId",
|
|
71380
71605
|
form: "single",
|
|
@@ -71385,6 +71610,21 @@ Object.freeze({
|
|
|
71385
71610
|
form: "single",
|
|
71386
71611
|
optional: false
|
|
71387
71612
|
}],
|
|
71613
|
+
"dishwasher.resume": [{
|
|
71614
|
+
name: "deviceId",
|
|
71615
|
+
form: "single",
|
|
71616
|
+
optional: false
|
|
71617
|
+
}],
|
|
71618
|
+
"dishwasher.setChildLock": [{
|
|
71619
|
+
name: "deviceId",
|
|
71620
|
+
form: "single",
|
|
71621
|
+
optional: false
|
|
71622
|
+
}],
|
|
71623
|
+
"dishwasher.setExtraDry": [{
|
|
71624
|
+
name: "deviceId",
|
|
71625
|
+
form: "single",
|
|
71626
|
+
optional: false
|
|
71627
|
+
}],
|
|
71388
71628
|
"dishwasher.setHalfLoad": [{
|
|
71389
71629
|
name: "deviceId",
|
|
71390
71630
|
form: "single",
|
|
@@ -71395,6 +71635,11 @@ Object.freeze({
|
|
|
71395
71635
|
form: "single",
|
|
71396
71636
|
optional: false
|
|
71397
71637
|
}],
|
|
71638
|
+
"dishwasher.setIntensiveZone": [{
|
|
71639
|
+
name: "deviceId",
|
|
71640
|
+
form: "single",
|
|
71641
|
+
optional: false
|
|
71642
|
+
}],
|
|
71398
71643
|
"dishwasher.setOption": [{
|
|
71399
71644
|
name: "deviceId",
|
|
71400
71645
|
form: "single",
|
|
@@ -71410,6 +71655,11 @@ Object.freeze({
|
|
|
71410
71655
|
form: "single",
|
|
71411
71656
|
optional: false
|
|
71412
71657
|
}],
|
|
71658
|
+
"dishwasher.setVarioSpeedPlus": [{
|
|
71659
|
+
name: "deviceId",
|
|
71660
|
+
form: "single",
|
|
71661
|
+
optional: false
|
|
71662
|
+
}],
|
|
71413
71663
|
"dishwasher.start": [{
|
|
71414
71664
|
name: "deviceId",
|
|
71415
71665
|
form: "single",
|
|
@@ -73451,6 +73701,9 @@ new Map(Object.entries({
|
|
|
73451
73701
|
"wrapper-update-available": "system",
|
|
73452
73702
|
"backup-completed": "system",
|
|
73453
73703
|
"export-completed": "system",
|
|
73704
|
+
"alert-critical": "system",
|
|
73705
|
+
"alert-error": "system",
|
|
73706
|
+
"alert-critical-resolved": "system",
|
|
73454
73707
|
"storage-location-low": "system",
|
|
73455
73708
|
"storage-location-critical": "system",
|
|
73456
73709
|
"storage-location-ok": "system",
|
|
@@ -73575,6 +73828,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
73575
73828
|
"storage-location-ok"
|
|
73576
73829
|
]
|
|
73577
73830
|
},
|
|
73831
|
+
{
|
|
73832
|
+
id: "alerts",
|
|
73833
|
+
family: "system",
|
|
73834
|
+
label: "Alerts",
|
|
73835
|
+
kinds: [
|
|
73836
|
+
"alert-critical",
|
|
73837
|
+
"alert-error",
|
|
73838
|
+
"alert-critical-resolved"
|
|
73839
|
+
]
|
|
73840
|
+
},
|
|
73578
73841
|
{
|
|
73579
73842
|
id: "alarm",
|
|
73580
73843
|
family: "alarm",
|
|
@@ -73619,6 +73882,21 @@ var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.ent
|
|
|
73619
73882
|
return [[descriptor.kind, field]];
|
|
73620
73883
|
})));
|
|
73621
73884
|
Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
|
|
73885
|
+
object({
|
|
73886
|
+
phase: _enum(["raised", "resolved"]),
|
|
73887
|
+
episodeId: string().min(1),
|
|
73888
|
+
alertId: string().min(1),
|
|
73889
|
+
severity: AlertSeveritySchema,
|
|
73890
|
+
category: string(),
|
|
73891
|
+
title: string(),
|
|
73892
|
+
message: string(),
|
|
73893
|
+
sourceType: string().optional(),
|
|
73894
|
+
sourceId: string().optional(),
|
|
73895
|
+
nodeId: string().optional(),
|
|
73896
|
+
/** The alert's own action link (where the operator acts on it), when it has one. */
|
|
73897
|
+
actionUrl: string().optional(),
|
|
73898
|
+
at: number()
|
|
73899
|
+
});
|
|
73622
73900
|
var RULE_BODY_ONLY = ["body"];
|
|
73623
73901
|
var RULE_TEXT = ["title", "body"];
|
|
73624
73902
|
var BATTERY = ["device-battery-low", "device-battery-normal"];
|
|
@@ -73628,6 +73906,11 @@ var TANK = [
|
|
|
73628
73906
|
"device-tank-full",
|
|
73629
73907
|
"device-tank-normal"
|
|
73630
73908
|
];
|
|
73909
|
+
var ALERT_EPISODE = [
|
|
73910
|
+
"alert-critical",
|
|
73911
|
+
"alert-error",
|
|
73912
|
+
"alert-critical-resolved"
|
|
73913
|
+
];
|
|
73631
73914
|
var UPDATES = [
|
|
73632
73915
|
"addon-update-available",
|
|
73633
73916
|
"server-update-available",
|
|
@@ -73820,14 +74103,14 @@ var STATE_SUMMARY_VARS = [
|
|
|
73820
74103
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
73821
74104
|
([...RULE_TEXT], [...TANK]),
|
|
73822
74105
|
[...RULE_TEXT],
|
|
73823
|
-
([...RULE_TEXT], [...UPDATES]),
|
|
73824
|
-
([...RULE_TEXT], [...UPDATES]),
|
|
73825
|
-
[...RULE_TEXT],
|
|
74106
|
+
([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
|
|
74107
|
+
([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
|
|
73826
74108
|
[...RULE_TEXT],
|
|
73827
74109
|
[...RULE_TEXT],
|
|
73828
74110
|
[...RULE_TEXT],
|
|
73829
74111
|
[...RULE_TEXT],
|
|
73830
74112
|
[...RULE_TEXT],
|
|
74113
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
73831
74114
|
[...RULE_TEXT],
|
|
73832
74115
|
[...RULE_TEXT],
|
|
73833
74116
|
[...RULE_TEXT],
|
|
@@ -73845,7 +74128,11 @@ var STATE_SUMMARY_VARS = [
|
|
|
73845
74128
|
[...RULE_TEXT],
|
|
73846
74129
|
[...RULE_TEXT],
|
|
73847
74130
|
[...RULE_TEXT],
|
|
73848
|
-
[...RULE_TEXT],
|
|
74131
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
74132
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
74133
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
74134
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
74135
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
73849
74136
|
[...RULE_TEXT],
|
|
73850
74137
|
[...RULE_TEXT],
|
|
73851
74138
|
[...RULE_TEXT],
|
|
@@ -74383,6 +74670,37 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
74383
74670
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
74384
74671
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
74385
74672
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
74673
|
+
/**
|
|
74674
|
+
* Where an OAuth round-trip ends: the admin-ui completion route
|
|
74675
|
+
* (`/sso-complete`, the page every popup sign-in already lands on), with the
|
|
74676
|
+
* OUTCOME in the URL fragment.
|
|
74677
|
+
*
|
|
74678
|
+
* The outcome is a status plus a reason CODE from a closed list. It never
|
|
74679
|
+
* carries a token, a code, a client secret or a free-form error message: the
|
|
74680
|
+
* page words each code itself, and an unknown code reads as `unknown` rather
|
|
74681
|
+
* than being echoed. Fragment only, so nothing here reaches a server log.
|
|
74682
|
+
*
|
|
74683
|
+
* Pure and browser-safe.
|
|
74684
|
+
*/
|
|
74685
|
+
/** The admin-ui route the hub's OAuth completions land on (relative to its mount). */
|
|
74686
|
+
var OAUTH_LANDING_PATH = "/sso-complete";
|
|
74687
|
+
/**
|
|
74688
|
+
* The landing URL for `outcome`. `origin` is the hub address the flow already
|
|
74689
|
+
* runs on (from the `local-network` palette role, never a literal); `null`
|
|
74690
|
+
* gives a hub-relative URL.
|
|
74691
|
+
*/
|
|
74692
|
+
function oauthLandingUrl(origin, outcome) {
|
|
74693
|
+
const params = new URLSearchParams({
|
|
74694
|
+
return: "oauth",
|
|
74695
|
+
status: outcome.status
|
|
74696
|
+
});
|
|
74697
|
+
if (outcome.addonId !== void 0) params.set("addon", outcome.addonId);
|
|
74698
|
+
if (outcome.brokerId !== void 0) params.set("broker", outcome.brokerId);
|
|
74699
|
+
if (outcome.integration !== void 0) params.set("integration", outcome.integration);
|
|
74700
|
+
if (outcome.status === "error") params.set("reason", outcome.reason ?? "unknown");
|
|
74701
|
+
if (outcome.action === "disconnect") params.set("action", "disconnect");
|
|
74702
|
+
return `${origin === null ? "" : origin.replace(/\/+$/, "")}${OAUTH_LANDING_PATH}#${params.toString()}`;
|
|
74703
|
+
}
|
|
74386
74704
|
var MB = 1024 * 1024;
|
|
74387
74705
|
1024 * MB, 3072 * MB;
|
|
74388
74706
|
//#endregion
|
|
@@ -74430,13 +74748,22 @@ var DEFAULT_OAUTH_STATE = {
|
|
|
74430
74748
|
accessExpiresAt: 0,
|
|
74431
74749
|
accountEmail: ""
|
|
74432
74750
|
};
|
|
74433
|
-
|
|
74751
|
+
/** Per-broker OAuth tokens (durable state `oauthAccounts`). */
|
|
74752
|
+
var GoogleOauthAccountSchema = object({
|
|
74434
74753
|
id: string().min(1),
|
|
74435
74754
|
refreshToken: string().default("").meta({ secret: true }),
|
|
74436
74755
|
accessToken: string().default("").meta({ secret: true }),
|
|
74437
74756
|
accessExpiresAt: number().default(0),
|
|
74438
|
-
accountEmail: string().default("")
|
|
74439
|
-
|
|
74757
|
+
accountEmail: string().default(""),
|
|
74758
|
+
/**
|
|
74759
|
+
* Google refused the refresh token (`invalid_grant`): it is dead and only a
|
|
74760
|
+
* new sign-in replaces it. Set where the refusal is seen, cleared by the
|
|
74761
|
+
* OAuth callback that stores a fresh token — so the status, the Sign-in
|
|
74762
|
+
* button and the alert all read the same fact.
|
|
74763
|
+
*/
|
|
74764
|
+
needsReauth: boolean().default(false)
|
|
74765
|
+
});
|
|
74766
|
+
var GoogleOauthAccountsStateSchema = object({ accounts: array(GoogleOauthAccountSchema).default([]) });
|
|
74440
74767
|
var GOOGLE_BROKER_KIND = "google";
|
|
74441
74768
|
var GOOGLE_BROKER_KIND_LABEL = "Google";
|
|
74442
74769
|
var GOOGLE_CALENDAR_SCOPE = "https://www.googleapis.com/auth/calendar.readonly";
|
|
@@ -74525,7 +74852,8 @@ function migrateLegacyToBrokers(rawConfig, legacyOauth) {
|
|
|
74525
74852
|
refreshToken: legacyOauth.refreshToken,
|
|
74526
74853
|
accessToken: legacyOauth.accessToken,
|
|
74527
74854
|
accessExpiresAt: legacyOauth.accessExpiresAt,
|
|
74528
|
-
accountEmail: legacyOauth.accountEmail
|
|
74855
|
+
accountEmail: legacyOauth.accountEmail,
|
|
74856
|
+
needsReauth: false
|
|
74529
74857
|
}] : [];
|
|
74530
74858
|
return {
|
|
74531
74859
|
brokers: [entry],
|
|
@@ -74560,7 +74888,10 @@ var GoogleBrokerRegistry = class {
|
|
|
74560
74888
|
oauthById = /* @__PURE__ */ new Map();
|
|
74561
74889
|
seedFrom(entries, accounts) {
|
|
74562
74890
|
this.entries = entries.map((e) => GoogleBrokerEntrySchema.parse(e));
|
|
74563
|
-
this.oauthById = new Map(accounts.map((a) =>
|
|
74891
|
+
this.oauthById = new Map(accounts.map((a) => {
|
|
74892
|
+
const account = GoogleOauthAccountSchema.parse(a);
|
|
74893
|
+
return [account.id, account];
|
|
74894
|
+
}));
|
|
74564
74895
|
this.seedCounterFromEntries(this.entries);
|
|
74565
74896
|
}
|
|
74566
74897
|
listEntries() {
|
|
@@ -74576,14 +74907,28 @@ var GoogleBrokerRegistry = class {
|
|
|
74576
74907
|
return [...this.oauthById.values()];
|
|
74577
74908
|
}
|
|
74578
74909
|
setOauth(account) {
|
|
74579
|
-
|
|
74910
|
+
const parsed = GoogleOauthAccountSchema.parse(account);
|
|
74911
|
+
this.oauthById.set(parsed.id, parsed);
|
|
74580
74912
|
}
|
|
74581
74913
|
removeOauth(id) {
|
|
74582
74914
|
this.oauthById.delete(id);
|
|
74583
74915
|
}
|
|
74584
74916
|
isConnected(id) {
|
|
74585
74917
|
const o = this.oauthById.get(id);
|
|
74586
|
-
return o !== void 0 && o.refreshToken.length > 0;
|
|
74918
|
+
return o !== void 0 && o.refreshToken.length > 0 && !o.needsReauth;
|
|
74919
|
+
}
|
|
74920
|
+
/** A refresh token Google refused: kept (a sign-in replaces it) but not usable. */
|
|
74921
|
+
needsReauth(id) {
|
|
74922
|
+
return this.oauthById.get(id)?.needsReauth === true;
|
|
74923
|
+
}
|
|
74924
|
+
markNeedsReauth(id) {
|
|
74925
|
+
const o = this.oauthById.get(id);
|
|
74926
|
+
if (o === void 0 || o.needsReauth) return false;
|
|
74927
|
+
this.oauthById.set(id, {
|
|
74928
|
+
...o,
|
|
74929
|
+
needsReauth: true
|
|
74930
|
+
});
|
|
74931
|
+
return true;
|
|
74587
74932
|
}
|
|
74588
74933
|
allocateId() {
|
|
74589
74934
|
const id = `google_${String(this.nextIdCounter).padStart(3, "0")}`;
|
|
@@ -74633,18 +74978,19 @@ var GoogleBrokerRegistry = class {
|
|
|
74633
74978
|
toBrokerInfo(entry, addonId) {
|
|
74634
74979
|
const oauth = this.oauthById.get(entry.id);
|
|
74635
74980
|
const connected = this.isConnected(entry.id);
|
|
74981
|
+
const reauth = this.needsReauth(entry.id);
|
|
74636
74982
|
return {
|
|
74637
74983
|
id: entry.id,
|
|
74638
74984
|
addonId,
|
|
74639
74985
|
name: entry.name,
|
|
74640
74986
|
kind: GOOGLE_BROKER_KIND,
|
|
74641
|
-
status: connected ? "connected" : "disconnected",
|
|
74987
|
+
status: connected ? "connected" : reauth ? "auth-failed" : "disconnected",
|
|
74642
74988
|
info: {
|
|
74643
74989
|
accountEmail: oauth?.accountEmail ?? "",
|
|
74644
74990
|
hasClient: entry.clientId.length > 0
|
|
74645
74991
|
},
|
|
74646
74992
|
lastCheckedAt: connected ? Date.now() : null,
|
|
74647
|
-
error: connected ? null : entry.clientId.length === 0 ? "Configure Client ID / secret, then Connect Google" : "Not connected — open Connect Google on this broker"
|
|
74993
|
+
error: connected ? null : reauth ? "Google session expired — sign in again" : entry.clientId.length === 0 ? "Configure Client ID / secret, then Connect Google" : "Not connected — open Connect Google on this broker"
|
|
74648
74994
|
};
|
|
74649
74995
|
}
|
|
74650
74996
|
listBrokerInfos(addonId, kind) {
|
|
@@ -74696,7 +75042,7 @@ function consoleLinksHtml$1() {
|
|
|
74696
75042
|
].join("");
|
|
74697
75043
|
}
|
|
74698
75044
|
function introHtml(cap, publicOrigin) {
|
|
74699
|
-
const redirect = escapeHtml$
|
|
75045
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
74700
75046
|
const console = consoleLinksHtml$1();
|
|
74701
75047
|
if (cap === "smtp-provider") return [
|
|
74702
75048
|
"<ol>",
|
|
@@ -74729,7 +75075,7 @@ function introHtml(cap, publicOrigin) {
|
|
|
74729
75075
|
].join("");
|
|
74730
75076
|
}
|
|
74731
75077
|
function connectHtml(cap, publicOrigin) {
|
|
74732
|
-
const redirect = escapeHtml$
|
|
75078
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
74733
75079
|
if (cap === "smtp-provider") return [
|
|
74734
75080
|
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
74735
75081
|
"Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
|
|
@@ -74749,7 +75095,7 @@ function connectHtml(cap, publicOrigin) {
|
|
|
74749
75095
|
`Console: ${consoleLinksHtml$1()}.`
|
|
74750
75096
|
].join("");
|
|
74751
75097
|
}
|
|
74752
|
-
function escapeHtml$
|
|
75098
|
+
function escapeHtml$1(s) {
|
|
74753
75099
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
74754
75100
|
}
|
|
74755
75101
|
//#endregion
|
|
@@ -74768,7 +75114,7 @@ function consoleLinksHtml() {
|
|
|
74768
75114
|
}
|
|
74769
75115
|
/** Setup steps shown on Add / Edit — includes Audience / Test users. */
|
|
74770
75116
|
function googleBrokerSetupHtml(publicOrigin) {
|
|
74771
|
-
const redirect = escapeHtml
|
|
75117
|
+
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
74772
75118
|
return [
|
|
74773
75119
|
"<ol>",
|
|
74774
75120
|
`<li>Open Google Cloud Console (${consoleLinksHtml()}). Create a <strong>new project</strong> (or pick an existing one) from the project picker at the top.</li>`,
|
|
@@ -74844,7 +75190,7 @@ function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
|
74844
75190
|
}
|
|
74845
75191
|
/** Connectivity Calendars / Email pages — calendar import picker lives here (not on broker Add). */
|
|
74846
75192
|
function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
74847
|
-
const redirect = escapeHtml
|
|
75193
|
+
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
74848
75194
|
if (cap === "calendar-source") return { sections: [{
|
|
74849
75195
|
id: "setup-intro",
|
|
74850
75196
|
title: "Google brokers",
|
|
@@ -74902,7 +75248,7 @@ function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
|
74902
75248
|
}]
|
|
74903
75249
|
}] };
|
|
74904
75250
|
}
|
|
74905
|
-
function escapeHtml
|
|
75251
|
+
function escapeHtml(s) {
|
|
74906
75252
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
74907
75253
|
}
|
|
74908
75254
|
//#endregion
|
|
@@ -75197,7 +75543,6 @@ function isInvalidGrantRefreshError(err) {
|
|
|
75197
75543
|
return (err instanceof Error ? err.message : String(err)).includes("invalid_grant");
|
|
75198
75544
|
}
|
|
75199
75545
|
function buildGoogleBrokerReconnectAlert(brokerId, brokerName) {
|
|
75200
|
-
const pushLine = `${brokerName}: Google session expired — sign in again`;
|
|
75201
75546
|
return {
|
|
75202
75547
|
id: googleBrokerReconnectAlertId(brokerId),
|
|
75203
75548
|
category: "google.broker-reconnect",
|
|
@@ -75206,7 +75551,8 @@ function buildGoogleBrokerReconnectAlert(brokerId, brokerName) {
|
|
|
75206
75551
|
message: `${brokerName} needs you to sign in to Google again.`,
|
|
75207
75552
|
action: {
|
|
75208
75553
|
label: "Sign in again",
|
|
75209
|
-
href: `/addon/google-services/oauth/start?brokerId=${encodeURIComponent(brokerId)}
|
|
75554
|
+
href: `/addon/google-services/oauth/start?brokerId=${encodeURIComponent(brokerId)}`,
|
|
75555
|
+
navigation: "server"
|
|
75210
75556
|
},
|
|
75211
75557
|
status: "active",
|
|
75212
75558
|
read: false,
|
|
@@ -75216,10 +75562,7 @@ function buildGoogleBrokerReconnectAlert(brokerId, brokerName) {
|
|
|
75216
75562
|
type: "google-broker",
|
|
75217
75563
|
id: brokerId
|
|
75218
75564
|
},
|
|
75219
|
-
metadata: {
|
|
75220
|
-
episodeKey: "reconnect",
|
|
75221
|
-
pushLine
|
|
75222
|
-
}
|
|
75565
|
+
metadata: { episodeKey: "reconnect" }
|
|
75223
75566
|
};
|
|
75224
75567
|
}
|
|
75225
75568
|
//#endregion
|
|
@@ -75428,6 +75771,14 @@ var GoogleBrokerStore = class GoogleBrokerStore {
|
|
|
75428
75771
|
* One OAuth account = one broker entry; feature caps aggregate connected brokers.
|
|
75429
75772
|
*/
|
|
75430
75773
|
var ADDON_ID = "google-services";
|
|
75774
|
+
/** A callback failure the landing page can name by code. */
|
|
75775
|
+
var GoogleCallbackError = class extends Error {
|
|
75776
|
+
reason;
|
|
75777
|
+
constructor(reason, message) {
|
|
75778
|
+
super(message);
|
|
75779
|
+
this.reason = reason;
|
|
75780
|
+
}
|
|
75781
|
+
};
|
|
75431
75782
|
var CALLBACK_PATH = "/oauth/callback";
|
|
75432
75783
|
var START_PATH = "/oauth/start";
|
|
75433
75784
|
var DISCONNECT_PATH = "/oauth/disconnect";
|
|
@@ -75448,7 +75799,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75448
75799
|
registry = new GoogleBrokerRegistry();
|
|
75449
75800
|
brokerStore = null;
|
|
75450
75801
|
activeBrokers = [];
|
|
75451
|
-
/** CSRF state → { brokerId,
|
|
75802
|
+
/** CSRF state → { brokerId, issuedAt } for the in-flight OAuth consents. */
|
|
75452
75803
|
pendingStates = /* @__PURE__ */ new Map();
|
|
75453
75804
|
publicOrigin = null;
|
|
75454
75805
|
constructor() {
|
|
@@ -75484,7 +75835,8 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75484
75835
|
refreshToken: legacyOauth.refreshToken,
|
|
75485
75836
|
accessToken: legacyOauth.accessToken,
|
|
75486
75837
|
accessExpiresAt: legacyOauth.accessExpiresAt,
|
|
75487
|
-
accountEmail: legacyOauth.accountEmail
|
|
75838
|
+
accountEmail: legacyOauth.accountEmail,
|
|
75839
|
+
needsReauth: false
|
|
75488
75840
|
}] };
|
|
75489
75841
|
await this.oauthAccountsHandle.set(oauthAccounts);
|
|
75490
75842
|
await legacyHandle.set({ ...DEFAULT_OAUTH_STATE });
|
|
@@ -75755,12 +76107,9 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75755
76107
|
reply.status(500).send({ error: err instanceof Error ? err.message : String(err) });
|
|
75756
76108
|
return;
|
|
75757
76109
|
}
|
|
75758
|
-
const popupRaw = req.query["popup"];
|
|
75759
|
-
const popup = popupRaw === "1" || popupRaw === "true";
|
|
75760
76110
|
const state = (0, node_crypto.randomBytes)(16).toString("hex");
|
|
75761
76111
|
this.pendingStates.set(state, {
|
|
75762
76112
|
brokerId,
|
|
75763
|
-
popup,
|
|
75764
76113
|
issuedAt: Date.now()
|
|
75765
76114
|
});
|
|
75766
76115
|
this.prunePendingStates();
|
|
@@ -75772,42 +76121,39 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75772
76121
|
reply.redirect(url);
|
|
75773
76122
|
}
|
|
75774
76123
|
async handleCallback(req, reply) {
|
|
76124
|
+
const state = typeof req.query["state"] === "string" ? req.query["state"] : "";
|
|
75775
76125
|
const err = req.query["error"];
|
|
75776
76126
|
if (err) {
|
|
75777
|
-
const
|
|
75778
|
-
|
|
75779
|
-
|
|
75780
|
-
|
|
75781
|
-
|
|
75782
|
-
|
|
75783
|
-
|
|
75784
|
-
|
|
75785
|
-
});
|
|
75786
|
-
return;
|
|
75787
|
-
}
|
|
75788
|
-
this.html(reply, 400, `Google OAuth error: ${err}`);
|
|
76127
|
+
const pendingPeek = state ? this.pendingStates.get(state) : void 0;
|
|
76128
|
+
if (pendingPeek !== void 0) this.pendingStates.delete(state);
|
|
76129
|
+
await this.landOnAdminUi(reply, {
|
|
76130
|
+
status: "error",
|
|
76131
|
+
addonId: ADDON_ID,
|
|
76132
|
+
...pendingPeek !== void 0 ? { brokerId: pendingPeek.brokerId } : {},
|
|
76133
|
+
reason: err === "access_denied" ? "access_denied" : "invalid_request"
|
|
76134
|
+
});
|
|
75789
76135
|
return;
|
|
75790
76136
|
}
|
|
75791
76137
|
const code = req.query["code"];
|
|
75792
|
-
const
|
|
75793
|
-
const pending = typeof state === "string" ? this.pendingStates.get(state) : void 0;
|
|
76138
|
+
const pending = state ? this.pendingStates.get(state) : void 0;
|
|
75794
76139
|
if (!code || !state || pending === void 0) {
|
|
75795
|
-
this.
|
|
76140
|
+
await this.landOnAdminUi(reply, {
|
|
76141
|
+
status: "error",
|
|
76142
|
+
addonId: ADDON_ID,
|
|
76143
|
+
reason: "invalid_state"
|
|
76144
|
+
});
|
|
75796
76145
|
return;
|
|
75797
76146
|
}
|
|
75798
76147
|
this.pendingStates.delete(state);
|
|
75799
|
-
const { brokerId
|
|
76148
|
+
const { brokerId } = pending;
|
|
75800
76149
|
const entry = this.registry.getEntry(brokerId);
|
|
75801
76150
|
if (entry === null) {
|
|
75802
|
-
|
|
75803
|
-
|
|
75804
|
-
|
|
75805
|
-
|
|
75806
|
-
|
|
75807
|
-
|
|
75808
|
-
return;
|
|
75809
|
-
}
|
|
75810
|
-
this.html(reply, 404, `Unknown Google broker: ${brokerId}`);
|
|
76151
|
+
await this.landOnAdminUi(reply, {
|
|
76152
|
+
status: "error",
|
|
76153
|
+
addonId: ADDON_ID,
|
|
76154
|
+
brokerId,
|
|
76155
|
+
reason: "unknown_broker"
|
|
76156
|
+
});
|
|
75811
76157
|
return;
|
|
75812
76158
|
}
|
|
75813
76159
|
try {
|
|
@@ -75819,11 +76165,12 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75819
76165
|
}, code);
|
|
75820
76166
|
const prev = this.registry.getOauth(brokerId) ?? {
|
|
75821
76167
|
id: brokerId,
|
|
75822
|
-
...DEFAULT_OAUTH_STATE
|
|
76168
|
+
...DEFAULT_OAUTH_STATE,
|
|
76169
|
+
needsReauth: false
|
|
75823
76170
|
};
|
|
75824
|
-
if (!tokens.refresh_token && !prev.refreshToken) throw new
|
|
76171
|
+
if (!tokens.refresh_token && (!prev.refreshToken || prev.needsReauth)) throw new GoogleCallbackError("no_refresh_token", "Google did not return a refresh_token. Revoke CamStack access in Google Account → Security and retry with prompt=consent.");
|
|
75825
76172
|
const applied = applyTokenResponse({
|
|
75826
|
-
refreshToken: prev.refreshToken,
|
|
76173
|
+
refreshToken: prev.needsReauth && tokens.refresh_token ? "" : prev.refreshToken,
|
|
75827
76174
|
accessToken: prev.accessToken,
|
|
75828
76175
|
accessExpiresAt: prev.accessExpiresAt,
|
|
75829
76176
|
accountEmail: prev.accountEmail
|
|
@@ -75834,7 +76181,8 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75834
76181
|
refreshToken: applied.refreshToken,
|
|
75835
76182
|
accessToken: applied.accessToken,
|
|
75836
76183
|
accessExpiresAt: applied.accessExpiresAt,
|
|
75837
|
-
accountEmail: email || applied.accountEmail
|
|
76184
|
+
accountEmail: email || applied.accountEmail,
|
|
76185
|
+
needsReauth: false
|
|
75838
76186
|
};
|
|
75839
76187
|
this.registry.setOauth(next);
|
|
75840
76188
|
await this.persistOauthAccounts();
|
|
@@ -75847,65 +76195,44 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75847
76195
|
this.registry.replaceEntries(this.registry.listEntries().map((e) => e.id === brokerId ? renamed : e));
|
|
75848
76196
|
await this.persistBrokers();
|
|
75849
76197
|
}
|
|
75850
|
-
this.ctx.logger.info("google oauth connected", {
|
|
75851
|
-
brokerId,
|
|
75852
|
-
|
|
75853
|
-
accountEmail: next.accountEmail || void 0
|
|
75854
|
-
} });
|
|
75855
|
-
if (popup) {
|
|
75856
|
-
this.popupComplete(reply, {
|
|
75857
|
-
ok: true,
|
|
76198
|
+
this.ctx.logger.info("google oauth connected", {
|
|
76199
|
+
tags: { brokerId },
|
|
76200
|
+
meta: {
|
|
75858
76201
|
brokerId,
|
|
75859
|
-
|
|
75860
|
-
|
|
75861
|
-
|
|
75862
|
-
}
|
|
75863
|
-
this.
|
|
76202
|
+
hasRefresh: next.refreshToken.length > 0,
|
|
76203
|
+
accountEmail: next.accountEmail || void 0
|
|
76204
|
+
}
|
|
76205
|
+
});
|
|
76206
|
+
await this.landOnAdminUi(reply, {
|
|
76207
|
+
status: "success",
|
|
76208
|
+
addonId: ADDON_ID,
|
|
76209
|
+
brokerId
|
|
76210
|
+
});
|
|
75864
76211
|
} catch (e) {
|
|
75865
76212
|
const message = e instanceof Error ? e.message : String(e);
|
|
75866
76213
|
this.ctx.logger.warn("google oauth callback failed", { meta: {
|
|
75867
76214
|
brokerId,
|
|
75868
76215
|
error: message
|
|
75869
76216
|
} });
|
|
75870
|
-
|
|
75871
|
-
|
|
75872
|
-
|
|
75873
|
-
|
|
75874
|
-
|
|
75875
|
-
|
|
75876
|
-
return;
|
|
75877
|
-
}
|
|
75878
|
-
this.html(reply, 500, `OAuth failed: ${message}`);
|
|
76217
|
+
await this.landOnAdminUi(reply, {
|
|
76218
|
+
status: "error",
|
|
76219
|
+
addonId: ADDON_ID,
|
|
76220
|
+
brokerId,
|
|
76221
|
+
reason: e instanceof GoogleCallbackError ? e.reason : "exchange_failed"
|
|
76222
|
+
});
|
|
75879
76223
|
}
|
|
75880
76224
|
}
|
|
75881
76225
|
/**
|
|
75882
|
-
*
|
|
75883
|
-
*
|
|
75884
|
-
*
|
|
76226
|
+
* Every OAuth completion ends in CamStack: a redirect to the admin-ui
|
|
76227
|
+
* completion page (`/sso-complete`) on the hub's public address (the
|
|
76228
|
+
* `local-network` palette role), carrying a status and a reason CODE — never
|
|
76229
|
+
* a token, a code or an error message. The page hands a popup's outcome to
|
|
76230
|
+
* its opener and closes it, or, in a full-page flow, shows the result and
|
|
76231
|
+
* returns to Connectivity.
|
|
75885
76232
|
*/
|
|
75886
|
-
|
|
75887
|
-
|
|
75888
|
-
|
|
75889
|
-
ok: outcome.ok,
|
|
75890
|
-
brokerId: outcome.brokerId,
|
|
75891
|
-
...outcome.error !== void 0 ? { error: outcome.error } : {}
|
|
75892
|
-
});
|
|
75893
|
-
const body = outcome.ok ? outcome.message ?? "Done. This window should close." : `OAuth failed: ${outcome.error ?? "unknown"}`;
|
|
75894
|
-
reply.header("content-type", "text/html; charset=utf-8");
|
|
75895
|
-
reply.status(outcome.ok ? 200 : 400).send(`<!doctype html><html><head><title>Google</title></head><body>
|
|
75896
|
-
<p>${escapeHtml(body)}</p>
|
|
75897
|
-
<script>
|
|
75898
|
-
(function () {
|
|
75899
|
-
var msg = ${payload};
|
|
75900
|
-
try {
|
|
75901
|
-
if (window.opener) {
|
|
75902
|
-
window.opener.postMessage(msg, window.location.origin);
|
|
75903
|
-
}
|
|
75904
|
-
} catch (e) {}
|
|
75905
|
-
setTimeout(function () { window.close(); }, 200);
|
|
75906
|
-
})();
|
|
75907
|
-
<\/script>
|
|
75908
|
-
</body></html>`);
|
|
76233
|
+
async landOnAdminUi(reply, outcome) {
|
|
76234
|
+
await this.refreshPublicOrigin();
|
|
76235
|
+
reply.redirect(oauthLandingUrl(this.publicOrigin, outcome));
|
|
75909
76236
|
}
|
|
75910
76237
|
async fetchAccountEmail(accessToken) {
|
|
75911
76238
|
try {
|
|
@@ -75917,10 +76244,6 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75917
76244
|
return "";
|
|
75918
76245
|
}
|
|
75919
76246
|
}
|
|
75920
|
-
html(reply, code, body) {
|
|
75921
|
-
reply.header("content-type", "text/html; charset=utf-8");
|
|
75922
|
-
reply.status(code).send(`<!doctype html><html><body><p>${escapeHtml(body)}</p></body></html>`);
|
|
75923
|
-
}
|
|
75924
76247
|
prunePendingStates() {
|
|
75925
76248
|
const cutoff = Date.now() - 10 * 6e4;
|
|
75926
76249
|
for (const [k, v] of this.pendingStates) if (v.issuedAt < cutoff) this.pendingStates.delete(k);
|
|
@@ -76008,6 +76331,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76008
76331
|
const entry = this.registry.getEntry(brokerId);
|
|
76009
76332
|
const oauth = this.registry.getOauth(brokerId);
|
|
76010
76333
|
if (entry === null || oauth === null || !oauth.refreshToken.trim()) throw new Error(`Google broker ${brokerId} is not connected — use Sign in under Connectivity → Brokers`);
|
|
76334
|
+
if (oauth.needsReauth) throw new Error(`Google broker ${brokerId} needs a new sign-in — use Sign in under Connectivity → Brokers`);
|
|
76011
76335
|
if (!entry.clientId.trim() || !entry.clientSecret.trim()) throw userError("google.client-credentials-required", void 0, "Configure Client ID and Client secret before refreshing the Google token");
|
|
76012
76336
|
const state = {
|
|
76013
76337
|
refreshToken: oauth.refreshToken.trim(),
|
|
@@ -76023,7 +76347,10 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76023
76347
|
clientSecret: entry.clientSecret.trim()
|
|
76024
76348
|
}, state.refreshToken);
|
|
76025
76349
|
} catch (err) {
|
|
76026
|
-
if (isInvalidGrantRefreshError(err))
|
|
76350
|
+
if (isInvalidGrantRefreshError(err)) {
|
|
76351
|
+
if (this.registry.markNeedsReauth(brokerId)) await this.persistOauthAccounts();
|
|
76352
|
+
this.emitGoogleBrokerReconnectAlert(brokerId, entry.name);
|
|
76353
|
+
}
|
|
76027
76354
|
throw err;
|
|
76028
76355
|
}
|
|
76029
76356
|
if (typeof tokens.access_token !== "string" || tokens.access_token.trim().length === 0) throw new Error("Google token refresh returned an empty access_token — Sign out and Sign in again");
|
|
@@ -76033,7 +76360,8 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76033
76360
|
refreshToken: nextState.refreshToken,
|
|
76034
76361
|
accessToken: nextState.accessToken,
|
|
76035
76362
|
accessExpiresAt: nextState.accessExpiresAt,
|
|
76036
|
-
accountEmail: nextState.accountEmail
|
|
76363
|
+
accountEmail: nextState.accountEmail,
|
|
76364
|
+
needsReauth: false
|
|
76037
76365
|
});
|
|
76038
76366
|
await this.persistOauthAccounts();
|
|
76039
76367
|
return nextState.accessToken;
|
|
@@ -76049,20 +76377,18 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76049
76377
|
reply.status(404).send({ error: `Unknown Google broker: ${brokerId}` });
|
|
76050
76378
|
return;
|
|
76051
76379
|
}
|
|
76052
|
-
const popupRaw = req.query["popup"];
|
|
76053
|
-
const popup = popupRaw === "1" || popupRaw === "true";
|
|
76054
76380
|
this.registry.removeOauth(brokerId);
|
|
76055
76381
|
await this.persistOauthAccounts();
|
|
76056
|
-
this.ctx.logger.info("google oauth disconnected", {
|
|
76057
|
-
|
|
76058
|
-
|
|
76059
|
-
|
|
76060
|
-
|
|
76061
|
-
|
|
76062
|
-
|
|
76063
|
-
|
|
76064
|
-
|
|
76065
|
-
|
|
76382
|
+
this.ctx.logger.info("google oauth disconnected", {
|
|
76383
|
+
tags: { brokerId },
|
|
76384
|
+
meta: { brokerId }
|
|
76385
|
+
});
|
|
76386
|
+
await this.landOnAdminUi(reply, {
|
|
76387
|
+
status: "success",
|
|
76388
|
+
addonId: ADDON_ID,
|
|
76389
|
+
brokerId,
|
|
76390
|
+
action: "disconnect"
|
|
76391
|
+
});
|
|
76066
76392
|
}
|
|
76067
76393
|
async resolveRedirectUri() {
|
|
76068
76394
|
await this.refreshPublicOrigin();
|
|
@@ -76099,9 +76425,6 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76099
76425
|
function asSettingsCap(cap) {
|
|
76100
76426
|
if (cap === "calendar-source" || cap === "smtp-provider") return cap;
|
|
76101
76427
|
}
|
|
76102
|
-
function escapeHtml(s) {
|
|
76103
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
76104
|
-
}
|
|
76105
76428
|
//#endregion
|
|
76106
76429
|
exports.DEFAULT_GOOGLE_CONFIG = DEFAULT_GOOGLE_CONFIG;
|
|
76107
76430
|
exports.GOOGLE_BROKER_KIND = GOOGLE_BROKER_KIND;
|