@camstack/addon-google-services 0.2.11 → 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 +411 -168
- package/dist/google-services.addon.mjs +411 -168
- 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",
|
|
@@ -42066,6 +42082,9 @@ var NcSystemEventKindSchema = _enum([
|
|
|
42066
42082
|
"server-updated",
|
|
42067
42083
|
"export-completed",
|
|
42068
42084
|
"backup-completed",
|
|
42085
|
+
"alert-critical",
|
|
42086
|
+
"alert-error",
|
|
42087
|
+
"alert-critical-resolved",
|
|
42069
42088
|
"camera-online",
|
|
42070
42089
|
"camera-offline",
|
|
42071
42090
|
"camera-disabled",
|
|
@@ -51111,7 +51130,10 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
|
|
|
51111
51130
|
}), method(object({ jobId: string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
51112
51131
|
kind: "mutation",
|
|
51113
51132
|
auth: "admin"
|
|
51114
|
-
}), 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() }), {
|
|
51115
51137
|
kind: "mutation",
|
|
51116
51138
|
auth: "admin"
|
|
51117
51139
|
}), method(StorageCleanupInputSchema, object({ jobId: string() }), {
|
|
@@ -54556,6 +54578,11 @@ object({
|
|
|
54556
54578
|
*
|
|
54557
54579
|
* `kind: 'poll'` — cloud REST APIs typically have no push channel; the
|
|
54558
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.
|
|
54559
54586
|
*/
|
|
54560
54587
|
var DishwasherStateSchema = _enum([
|
|
54561
54588
|
"idle",
|
|
@@ -54565,112 +54592,227 @@ var DishwasherStateSchema = _enum([
|
|
|
54565
54592
|
"error",
|
|
54566
54593
|
"unavailable"
|
|
54567
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
|
+
]);
|
|
54568
54622
|
var DishwasherConsumableLevelSchema = _enum([
|
|
54569
54623
|
"ok",
|
|
54570
54624
|
"low",
|
|
54571
54625
|
"empty"
|
|
54572
54626
|
]);
|
|
54573
54627
|
object({
|
|
54628
|
+
availability: DishwasherAvailabilitySchema,
|
|
54629
|
+
/** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
|
|
54574
54630
|
state: DishwasherStateSchema,
|
|
54575
|
-
/**
|
|
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. */
|
|
54576
54634
|
program: string().nullable(),
|
|
54577
|
-
/**
|
|
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. */
|
|
54578
54638
|
availablePrograms: array(string()),
|
|
54579
|
-
/**
|
|
54580
|
-
|
|
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(),
|
|
54581
54643
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
54582
54644
|
progressPercent: number().min(0).max(100).nullable(),
|
|
54583
|
-
/** True when the door is reported open. */
|
|
54584
|
-
doorOpen: boolean(),
|
|
54585
|
-
/** True when
|
|
54586
|
-
|
|
54587
|
-
/**
|
|
54588
|
-
|
|
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(),
|
|
54589
54659
|
saltLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
54590
54660
|
rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
|
|
54591
|
-
|
|
54592
|
-
|
|
54661
|
+
/** Option states: null when the appliance does not expose the option or has not said. */
|
|
54662
|
+
halfLoad: boolean().nullable(),
|
|
54663
|
+
hygienePlus: boolean().nullable(),
|
|
54593
54664
|
/** Extra dry option when the appliance exposes it. */
|
|
54594
|
-
extraDry: boolean(),
|
|
54665
|
+
extraDry: boolean().nullable(),
|
|
54595
54666
|
/** Intensive zone (lower basket) when the appliance exposes it. */
|
|
54596
|
-
intensiveZone: boolean(),
|
|
54667
|
+
intensiveZone: boolean().nullable(),
|
|
54597
54668
|
/** VarioSpeed / VarioSpeed+ when the appliance exposes it. */
|
|
54598
|
-
varioSpeedPlus: boolean(),
|
|
54599
|
-
/** Child lock (controls disabled on the appliance). */
|
|
54600
|
-
childLock: boolean(),
|
|
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. */
|
|
54601
54675
|
errorCode: number().nullable(),
|
|
54676
|
+
/** Name of the raised alert-level event while the appliance is in error; null otherwise. */
|
|
54602
54677
|
errorLabel: string().nullable(),
|
|
54678
|
+
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
54603
54679
|
lastFetchedAt: number()
|
|
54604
54680
|
});
|
|
54605
|
-
|
|
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(), {
|
|
54606
54749
|
kind: "mutation",
|
|
54607
54750
|
auth: "protected"
|
|
54608
|
-
}), method(object({ deviceId:
|
|
54751
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
54609
54752
|
kind: "mutation",
|
|
54610
54753
|
auth: "protected"
|
|
54611
|
-
}), method(object({ deviceId:
|
|
54754
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
54755
|
+
kind: "mutation",
|
|
54756
|
+
auth: "protected"
|
|
54757
|
+
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
54612
54758
|
kind: "mutation",
|
|
54613
54759
|
auth: "protected"
|
|
54614
54760
|
}), method(object({
|
|
54615
|
-
deviceId:
|
|
54761
|
+
deviceId: DeviceIdSchema,
|
|
54616
54762
|
program: string().min(1)
|
|
54617
54763
|
}), _void(), {
|
|
54618
54764
|
kind: "mutation",
|
|
54619
54765
|
auth: "protected"
|
|
54620
54766
|
}), method(object({
|
|
54621
|
-
deviceId:
|
|
54767
|
+
deviceId: DeviceIdSchema,
|
|
54622
54768
|
on: boolean()
|
|
54623
54769
|
}), _void(), {
|
|
54624
54770
|
kind: "mutation",
|
|
54625
54771
|
auth: "protected"
|
|
54626
54772
|
}), method(object({
|
|
54627
|
-
deviceId:
|
|
54773
|
+
deviceId: DeviceIdSchema,
|
|
54628
54774
|
on: boolean()
|
|
54629
54775
|
}), _void(), {
|
|
54630
54776
|
kind: "mutation",
|
|
54631
54777
|
auth: "protected"
|
|
54632
54778
|
}), method(object({
|
|
54633
|
-
deviceId:
|
|
54779
|
+
deviceId: DeviceIdSchema,
|
|
54634
54780
|
on: boolean()
|
|
54635
54781
|
}), _void(), {
|
|
54636
54782
|
kind: "mutation",
|
|
54637
54783
|
auth: "protected"
|
|
54638
54784
|
}), method(object({
|
|
54639
|
-
deviceId:
|
|
54785
|
+
deviceId: DeviceIdSchema,
|
|
54640
54786
|
on: boolean()
|
|
54641
54787
|
}), _void(), {
|
|
54642
54788
|
kind: "mutation",
|
|
54643
54789
|
auth: "protected"
|
|
54644
54790
|
}), method(object({
|
|
54645
|
-
deviceId:
|
|
54791
|
+
deviceId: DeviceIdSchema,
|
|
54646
54792
|
on: boolean()
|
|
54647
54793
|
}), _void(), {
|
|
54648
54794
|
kind: "mutation",
|
|
54649
54795
|
auth: "protected"
|
|
54650
54796
|
}), method(object({
|
|
54651
|
-
deviceId:
|
|
54797
|
+
deviceId: DeviceIdSchema,
|
|
54652
54798
|
on: boolean()
|
|
54653
54799
|
}), _void(), {
|
|
54654
54800
|
kind: "mutation",
|
|
54655
54801
|
auth: "protected"
|
|
54656
54802
|
}), method(object({
|
|
54657
|
-
deviceId:
|
|
54803
|
+
deviceId: DeviceIdSchema,
|
|
54658
54804
|
on: boolean()
|
|
54659
54805
|
}), _void(), {
|
|
54660
54806
|
kind: "mutation",
|
|
54661
54807
|
auth: "protected"
|
|
54662
54808
|
}), method(object({
|
|
54663
|
-
deviceId:
|
|
54809
|
+
deviceId: DeviceIdSchema,
|
|
54664
54810
|
key: string().min(1),
|
|
54665
|
-
value:
|
|
54666
|
-
boolean(),
|
|
54667
|
-
string(),
|
|
54668
|
-
number()
|
|
54669
|
-
])
|
|
54811
|
+
value: DishwasherValueSchema
|
|
54670
54812
|
}), _void(), {
|
|
54671
54813
|
kind: "mutation",
|
|
54672
54814
|
auth: "protected"
|
|
54673
|
-
});
|
|
54815
|
+
}), method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" });
|
|
54674
54816
|
object({
|
|
54675
54817
|
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
54676
54818
|
lastPressedAt: number().nullable(),
|
|
@@ -64730,6 +64872,12 @@ Object.freeze({
|
|
|
64730
64872
|
addonId: null,
|
|
64731
64873
|
access: "view"
|
|
64732
64874
|
},
|
|
64875
|
+
"dishwasher.getOptions": {
|
|
64876
|
+
capName: "dishwasher",
|
|
64877
|
+
capScope: "device",
|
|
64878
|
+
addonId: null,
|
|
64879
|
+
access: "view"
|
|
64880
|
+
},
|
|
64733
64881
|
"dishwasher.getStatus": {
|
|
64734
64882
|
capName: "dishwasher",
|
|
64735
64883
|
capScope: "device",
|
|
@@ -64742,6 +64890,12 @@ Object.freeze({
|
|
|
64742
64890
|
addonId: null,
|
|
64743
64891
|
access: "create"
|
|
64744
64892
|
},
|
|
64893
|
+
"dishwasher.resume": {
|
|
64894
|
+
capName: "dishwasher",
|
|
64895
|
+
capScope: "device",
|
|
64896
|
+
addonId: null,
|
|
64897
|
+
access: "create"
|
|
64898
|
+
},
|
|
64745
64899
|
"dishwasher.setChildLock": {
|
|
64746
64900
|
capName: "dishwasher",
|
|
64747
64901
|
capScope: "device",
|
|
@@ -69242,6 +69396,12 @@ Object.freeze({
|
|
|
69242
69396
|
addonId: null,
|
|
69243
69397
|
access: "view"
|
|
69244
69398
|
},
|
|
69399
|
+
"storageMigration.moverClearance": {
|
|
69400
|
+
capName: "storage-migration",
|
|
69401
|
+
capScope: "system",
|
|
69402
|
+
addonId: null,
|
|
69403
|
+
access: "view"
|
|
69404
|
+
},
|
|
69245
69405
|
"storageMigration.movers": {
|
|
69246
69406
|
capName: "storage-migration",
|
|
69247
69407
|
capScope: "system",
|
|
@@ -71435,6 +71595,11 @@ Object.freeze({
|
|
|
71435
71595
|
form: "single",
|
|
71436
71596
|
optional: false
|
|
71437
71597
|
}],
|
|
71598
|
+
"dishwasher.getOptions": [{
|
|
71599
|
+
name: "deviceId",
|
|
71600
|
+
form: "single",
|
|
71601
|
+
optional: false
|
|
71602
|
+
}],
|
|
71438
71603
|
"dishwasher.getStatus": [{
|
|
71439
71604
|
name: "deviceId",
|
|
71440
71605
|
form: "single",
|
|
@@ -71445,6 +71610,11 @@ Object.freeze({
|
|
|
71445
71610
|
form: "single",
|
|
71446
71611
|
optional: false
|
|
71447
71612
|
}],
|
|
71613
|
+
"dishwasher.resume": [{
|
|
71614
|
+
name: "deviceId",
|
|
71615
|
+
form: "single",
|
|
71616
|
+
optional: false
|
|
71617
|
+
}],
|
|
71448
71618
|
"dishwasher.setChildLock": [{
|
|
71449
71619
|
name: "deviceId",
|
|
71450
71620
|
form: "single",
|
|
@@ -73531,6 +73701,9 @@ new Map(Object.entries({
|
|
|
73531
73701
|
"wrapper-update-available": "system",
|
|
73532
73702
|
"backup-completed": "system",
|
|
73533
73703
|
"export-completed": "system",
|
|
73704
|
+
"alert-critical": "system",
|
|
73705
|
+
"alert-error": "system",
|
|
73706
|
+
"alert-critical-resolved": "system",
|
|
73534
73707
|
"storage-location-low": "system",
|
|
73535
73708
|
"storage-location-critical": "system",
|
|
73536
73709
|
"storage-location-ok": "system",
|
|
@@ -73655,6 +73828,16 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
73655
73828
|
"storage-location-ok"
|
|
73656
73829
|
]
|
|
73657
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
|
+
},
|
|
73658
73841
|
{
|
|
73659
73842
|
id: "alarm",
|
|
73660
73843
|
family: "alarm",
|
|
@@ -73699,6 +73882,21 @@ var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.ent
|
|
|
73699
73882
|
return [[descriptor.kind, field]];
|
|
73700
73883
|
})));
|
|
73701
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
|
+
});
|
|
73702
73900
|
var RULE_BODY_ONLY = ["body"];
|
|
73703
73901
|
var RULE_TEXT = ["title", "body"];
|
|
73704
73902
|
var BATTERY = ["device-battery-low", "device-battery-normal"];
|
|
@@ -73708,6 +73906,11 @@ var TANK = [
|
|
|
73708
73906
|
"device-tank-full",
|
|
73709
73907
|
"device-tank-normal"
|
|
73710
73908
|
];
|
|
73909
|
+
var ALERT_EPISODE = [
|
|
73910
|
+
"alert-critical",
|
|
73911
|
+
"alert-error",
|
|
73912
|
+
"alert-critical-resolved"
|
|
73913
|
+
];
|
|
73711
73914
|
var UPDATES = [
|
|
73712
73915
|
"addon-update-available",
|
|
73713
73916
|
"server-update-available",
|
|
@@ -73900,14 +74103,14 @@ var STATE_SUMMARY_VARS = [
|
|
|
73900
74103
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
73901
74104
|
([...RULE_TEXT], [...TANK]),
|
|
73902
74105
|
[...RULE_TEXT],
|
|
73903
|
-
([...RULE_TEXT], [...UPDATES]),
|
|
73904
|
-
([...RULE_TEXT], [...UPDATES]),
|
|
73905
|
-
[...RULE_TEXT],
|
|
74106
|
+
([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
|
|
74107
|
+
([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
|
|
73906
74108
|
[...RULE_TEXT],
|
|
73907
74109
|
[...RULE_TEXT],
|
|
73908
74110
|
[...RULE_TEXT],
|
|
73909
74111
|
[...RULE_TEXT],
|
|
73910
74112
|
[...RULE_TEXT],
|
|
74113
|
+
([...RULE_TEXT], [...ALERT_EPISODE]),
|
|
73911
74114
|
[...RULE_TEXT],
|
|
73912
74115
|
[...RULE_TEXT],
|
|
73913
74116
|
[...RULE_TEXT],
|
|
@@ -73925,7 +74128,11 @@ var STATE_SUMMARY_VARS = [
|
|
|
73925
74128
|
[...RULE_TEXT],
|
|
73926
74129
|
[...RULE_TEXT],
|
|
73927
74130
|
[...RULE_TEXT],
|
|
73928
|
-
[...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]),
|
|
73929
74136
|
[...RULE_TEXT],
|
|
73930
74137
|
[...RULE_TEXT],
|
|
73931
74138
|
[...RULE_TEXT],
|
|
@@ -74463,6 +74670,37 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
74463
74670
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
74464
74671
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
74465
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
|
+
}
|
|
74466
74704
|
var MB = 1024 * 1024;
|
|
74467
74705
|
1024 * MB, 3072 * MB;
|
|
74468
74706
|
//#endregion
|
|
@@ -74510,13 +74748,22 @@ var DEFAULT_OAUTH_STATE = {
|
|
|
74510
74748
|
accessExpiresAt: 0,
|
|
74511
74749
|
accountEmail: ""
|
|
74512
74750
|
};
|
|
74513
|
-
|
|
74751
|
+
/** Per-broker OAuth tokens (durable state `oauthAccounts`). */
|
|
74752
|
+
var GoogleOauthAccountSchema = object({
|
|
74514
74753
|
id: string().min(1),
|
|
74515
74754
|
refreshToken: string().default("").meta({ secret: true }),
|
|
74516
74755
|
accessToken: string().default("").meta({ secret: true }),
|
|
74517
74756
|
accessExpiresAt: number().default(0),
|
|
74518
|
-
accountEmail: string().default("")
|
|
74519
|
-
|
|
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([]) });
|
|
74520
74767
|
var GOOGLE_BROKER_KIND = "google";
|
|
74521
74768
|
var GOOGLE_BROKER_KIND_LABEL = "Google";
|
|
74522
74769
|
var GOOGLE_CALENDAR_SCOPE = "https://www.googleapis.com/auth/calendar.readonly";
|
|
@@ -74605,7 +74852,8 @@ function migrateLegacyToBrokers(rawConfig, legacyOauth) {
|
|
|
74605
74852
|
refreshToken: legacyOauth.refreshToken,
|
|
74606
74853
|
accessToken: legacyOauth.accessToken,
|
|
74607
74854
|
accessExpiresAt: legacyOauth.accessExpiresAt,
|
|
74608
|
-
accountEmail: legacyOauth.accountEmail
|
|
74855
|
+
accountEmail: legacyOauth.accountEmail,
|
|
74856
|
+
needsReauth: false
|
|
74609
74857
|
}] : [];
|
|
74610
74858
|
return {
|
|
74611
74859
|
brokers: [entry],
|
|
@@ -74640,7 +74888,10 @@ var GoogleBrokerRegistry = class {
|
|
|
74640
74888
|
oauthById = /* @__PURE__ */ new Map();
|
|
74641
74889
|
seedFrom(entries, accounts) {
|
|
74642
74890
|
this.entries = entries.map((e) => GoogleBrokerEntrySchema.parse(e));
|
|
74643
|
-
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
|
+
}));
|
|
74644
74895
|
this.seedCounterFromEntries(this.entries);
|
|
74645
74896
|
}
|
|
74646
74897
|
listEntries() {
|
|
@@ -74656,14 +74907,28 @@ var GoogleBrokerRegistry = class {
|
|
|
74656
74907
|
return [...this.oauthById.values()];
|
|
74657
74908
|
}
|
|
74658
74909
|
setOauth(account) {
|
|
74659
|
-
|
|
74910
|
+
const parsed = GoogleOauthAccountSchema.parse(account);
|
|
74911
|
+
this.oauthById.set(parsed.id, parsed);
|
|
74660
74912
|
}
|
|
74661
74913
|
removeOauth(id) {
|
|
74662
74914
|
this.oauthById.delete(id);
|
|
74663
74915
|
}
|
|
74664
74916
|
isConnected(id) {
|
|
74665
74917
|
const o = this.oauthById.get(id);
|
|
74666
|
-
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;
|
|
74667
74932
|
}
|
|
74668
74933
|
allocateId() {
|
|
74669
74934
|
const id = `google_${String(this.nextIdCounter).padStart(3, "0")}`;
|
|
@@ -74713,18 +74978,19 @@ var GoogleBrokerRegistry = class {
|
|
|
74713
74978
|
toBrokerInfo(entry, addonId) {
|
|
74714
74979
|
const oauth = this.oauthById.get(entry.id);
|
|
74715
74980
|
const connected = this.isConnected(entry.id);
|
|
74981
|
+
const reauth = this.needsReauth(entry.id);
|
|
74716
74982
|
return {
|
|
74717
74983
|
id: entry.id,
|
|
74718
74984
|
addonId,
|
|
74719
74985
|
name: entry.name,
|
|
74720
74986
|
kind: GOOGLE_BROKER_KIND,
|
|
74721
|
-
status: connected ? "connected" : "disconnected",
|
|
74987
|
+
status: connected ? "connected" : reauth ? "auth-failed" : "disconnected",
|
|
74722
74988
|
info: {
|
|
74723
74989
|
accountEmail: oauth?.accountEmail ?? "",
|
|
74724
74990
|
hasClient: entry.clientId.length > 0
|
|
74725
74991
|
},
|
|
74726
74992
|
lastCheckedAt: connected ? Date.now() : null,
|
|
74727
|
-
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"
|
|
74728
74994
|
};
|
|
74729
74995
|
}
|
|
74730
74996
|
listBrokerInfos(addonId, kind) {
|
|
@@ -74776,7 +75042,7 @@ function consoleLinksHtml$1() {
|
|
|
74776
75042
|
].join("");
|
|
74777
75043
|
}
|
|
74778
75044
|
function introHtml(cap, publicOrigin) {
|
|
74779
|
-
const redirect = escapeHtml$
|
|
75045
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
74780
75046
|
const console = consoleLinksHtml$1();
|
|
74781
75047
|
if (cap === "smtp-provider") return [
|
|
74782
75048
|
"<ol>",
|
|
@@ -74809,7 +75075,7 @@ function introHtml(cap, publicOrigin) {
|
|
|
74809
75075
|
].join("");
|
|
74810
75076
|
}
|
|
74811
75077
|
function connectHtml(cap, publicOrigin) {
|
|
74812
|
-
const redirect = escapeHtml$
|
|
75078
|
+
const redirect = escapeHtml$1(redirectUriDisplay(publicOrigin));
|
|
74813
75079
|
if (cap === "smtp-provider") return [
|
|
74814
75080
|
`After Save, open <a href="${OAUTH_START_PATH}">Connect Google</a> in this hub (admin session required). `,
|
|
74815
75081
|
"Connect requests <code>gmail.send</code> (and Calendar read for the Calendars page). ",
|
|
@@ -74829,7 +75095,7 @@ function connectHtml(cap, publicOrigin) {
|
|
|
74829
75095
|
`Console: ${consoleLinksHtml$1()}.`
|
|
74830
75096
|
].join("");
|
|
74831
75097
|
}
|
|
74832
|
-
function escapeHtml$
|
|
75098
|
+
function escapeHtml$1(s) {
|
|
74833
75099
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
74834
75100
|
}
|
|
74835
75101
|
//#endregion
|
|
@@ -74848,7 +75114,7 @@ function consoleLinksHtml() {
|
|
|
74848
75114
|
}
|
|
74849
75115
|
/** Setup steps shown on Add / Edit — includes Audience / Test users. */
|
|
74850
75116
|
function googleBrokerSetupHtml(publicOrigin) {
|
|
74851
|
-
const redirect = escapeHtml
|
|
75117
|
+
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
74852
75118
|
return [
|
|
74853
75119
|
"<ol>",
|
|
74854
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>`,
|
|
@@ -74924,7 +75190,7 @@ function buildGoogleBrokerSettingsSchema(publicOrigin) {
|
|
|
74924
75190
|
}
|
|
74925
75191
|
/** Connectivity Calendars / Email pages — calendar import picker lives here (not on broker Add). */
|
|
74926
75192
|
function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
74927
|
-
const redirect = escapeHtml
|
|
75193
|
+
const redirect = escapeHtml(redirectUriDisplay(publicOrigin));
|
|
74928
75194
|
if (cap === "calendar-source") return { sections: [{
|
|
74929
75195
|
id: "setup-intro",
|
|
74930
75196
|
title: "Google brokers",
|
|
@@ -74982,7 +75248,7 @@ function buildGoogleFeatureSettingsSchema(cap, publicOrigin) {
|
|
|
74982
75248
|
}]
|
|
74983
75249
|
}] };
|
|
74984
75250
|
}
|
|
74985
|
-
function escapeHtml
|
|
75251
|
+
function escapeHtml(s) {
|
|
74986
75252
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
74987
75253
|
}
|
|
74988
75254
|
//#endregion
|
|
@@ -75277,7 +75543,6 @@ function isInvalidGrantRefreshError(err) {
|
|
|
75277
75543
|
return (err instanceof Error ? err.message : String(err)).includes("invalid_grant");
|
|
75278
75544
|
}
|
|
75279
75545
|
function buildGoogleBrokerReconnectAlert(brokerId, brokerName) {
|
|
75280
|
-
const pushLine = `${brokerName}: Google session expired — sign in again`;
|
|
75281
75546
|
return {
|
|
75282
75547
|
id: googleBrokerReconnectAlertId(brokerId),
|
|
75283
75548
|
category: "google.broker-reconnect",
|
|
@@ -75286,7 +75551,8 @@ function buildGoogleBrokerReconnectAlert(brokerId, brokerName) {
|
|
|
75286
75551
|
message: `${brokerName} needs you to sign in to Google again.`,
|
|
75287
75552
|
action: {
|
|
75288
75553
|
label: "Sign in again",
|
|
75289
|
-
href: `/addon/google-services/oauth/start?brokerId=${encodeURIComponent(brokerId)}
|
|
75554
|
+
href: `/addon/google-services/oauth/start?brokerId=${encodeURIComponent(brokerId)}`,
|
|
75555
|
+
navigation: "server"
|
|
75290
75556
|
},
|
|
75291
75557
|
status: "active",
|
|
75292
75558
|
read: false,
|
|
@@ -75296,10 +75562,7 @@ function buildGoogleBrokerReconnectAlert(brokerId, brokerName) {
|
|
|
75296
75562
|
type: "google-broker",
|
|
75297
75563
|
id: brokerId
|
|
75298
75564
|
},
|
|
75299
|
-
metadata: {
|
|
75300
|
-
episodeKey: "reconnect",
|
|
75301
|
-
pushLine
|
|
75302
|
-
}
|
|
75565
|
+
metadata: { episodeKey: "reconnect" }
|
|
75303
75566
|
};
|
|
75304
75567
|
}
|
|
75305
75568
|
//#endregion
|
|
@@ -75508,6 +75771,14 @@ var GoogleBrokerStore = class GoogleBrokerStore {
|
|
|
75508
75771
|
* One OAuth account = one broker entry; feature caps aggregate connected brokers.
|
|
75509
75772
|
*/
|
|
75510
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
|
+
};
|
|
75511
75782
|
var CALLBACK_PATH = "/oauth/callback";
|
|
75512
75783
|
var START_PATH = "/oauth/start";
|
|
75513
75784
|
var DISCONNECT_PATH = "/oauth/disconnect";
|
|
@@ -75528,7 +75799,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75528
75799
|
registry = new GoogleBrokerRegistry();
|
|
75529
75800
|
brokerStore = null;
|
|
75530
75801
|
activeBrokers = [];
|
|
75531
|
-
/** CSRF state → { brokerId,
|
|
75802
|
+
/** CSRF state → { brokerId, issuedAt } for the in-flight OAuth consents. */
|
|
75532
75803
|
pendingStates = /* @__PURE__ */ new Map();
|
|
75533
75804
|
publicOrigin = null;
|
|
75534
75805
|
constructor() {
|
|
@@ -75564,7 +75835,8 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75564
75835
|
refreshToken: legacyOauth.refreshToken,
|
|
75565
75836
|
accessToken: legacyOauth.accessToken,
|
|
75566
75837
|
accessExpiresAt: legacyOauth.accessExpiresAt,
|
|
75567
|
-
accountEmail: legacyOauth.accountEmail
|
|
75838
|
+
accountEmail: legacyOauth.accountEmail,
|
|
75839
|
+
needsReauth: false
|
|
75568
75840
|
}] };
|
|
75569
75841
|
await this.oauthAccountsHandle.set(oauthAccounts);
|
|
75570
75842
|
await legacyHandle.set({ ...DEFAULT_OAUTH_STATE });
|
|
@@ -75835,12 +76107,9 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75835
76107
|
reply.status(500).send({ error: err instanceof Error ? err.message : String(err) });
|
|
75836
76108
|
return;
|
|
75837
76109
|
}
|
|
75838
|
-
const popupRaw = req.query["popup"];
|
|
75839
|
-
const popup = popupRaw === "1" || popupRaw === "true";
|
|
75840
76110
|
const state = (0, node_crypto.randomBytes)(16).toString("hex");
|
|
75841
76111
|
this.pendingStates.set(state, {
|
|
75842
76112
|
brokerId,
|
|
75843
|
-
popup,
|
|
75844
76113
|
issuedAt: Date.now()
|
|
75845
76114
|
});
|
|
75846
76115
|
this.prunePendingStates();
|
|
@@ -75852,42 +76121,39 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75852
76121
|
reply.redirect(url);
|
|
75853
76122
|
}
|
|
75854
76123
|
async handleCallback(req, reply) {
|
|
76124
|
+
const state = typeof req.query["state"] === "string" ? req.query["state"] : "";
|
|
75855
76125
|
const err = req.query["error"];
|
|
75856
76126
|
if (err) {
|
|
75857
|
-
const
|
|
75858
|
-
|
|
75859
|
-
|
|
75860
|
-
|
|
75861
|
-
|
|
75862
|
-
|
|
75863
|
-
|
|
75864
|
-
|
|
75865
|
-
});
|
|
75866
|
-
return;
|
|
75867
|
-
}
|
|
75868
|
-
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
|
+
});
|
|
75869
76135
|
return;
|
|
75870
76136
|
}
|
|
75871
76137
|
const code = req.query["code"];
|
|
75872
|
-
const
|
|
75873
|
-
const pending = typeof state === "string" ? this.pendingStates.get(state) : void 0;
|
|
76138
|
+
const pending = state ? this.pendingStates.get(state) : void 0;
|
|
75874
76139
|
if (!code || !state || pending === void 0) {
|
|
75875
|
-
this.
|
|
76140
|
+
await this.landOnAdminUi(reply, {
|
|
76141
|
+
status: "error",
|
|
76142
|
+
addonId: ADDON_ID,
|
|
76143
|
+
reason: "invalid_state"
|
|
76144
|
+
});
|
|
75876
76145
|
return;
|
|
75877
76146
|
}
|
|
75878
76147
|
this.pendingStates.delete(state);
|
|
75879
|
-
const { brokerId
|
|
76148
|
+
const { brokerId } = pending;
|
|
75880
76149
|
const entry = this.registry.getEntry(brokerId);
|
|
75881
76150
|
if (entry === null) {
|
|
75882
|
-
|
|
75883
|
-
|
|
75884
|
-
|
|
75885
|
-
|
|
75886
|
-
|
|
75887
|
-
|
|
75888
|
-
return;
|
|
75889
|
-
}
|
|
75890
|
-
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
|
+
});
|
|
75891
76157
|
return;
|
|
75892
76158
|
}
|
|
75893
76159
|
try {
|
|
@@ -75899,11 +76165,12 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75899
76165
|
}, code);
|
|
75900
76166
|
const prev = this.registry.getOauth(brokerId) ?? {
|
|
75901
76167
|
id: brokerId,
|
|
75902
|
-
...DEFAULT_OAUTH_STATE
|
|
76168
|
+
...DEFAULT_OAUTH_STATE,
|
|
76169
|
+
needsReauth: false
|
|
75903
76170
|
};
|
|
75904
|
-
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.");
|
|
75905
76172
|
const applied = applyTokenResponse({
|
|
75906
|
-
refreshToken: prev.refreshToken,
|
|
76173
|
+
refreshToken: prev.needsReauth && tokens.refresh_token ? "" : prev.refreshToken,
|
|
75907
76174
|
accessToken: prev.accessToken,
|
|
75908
76175
|
accessExpiresAt: prev.accessExpiresAt,
|
|
75909
76176
|
accountEmail: prev.accountEmail
|
|
@@ -75914,7 +76181,8 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75914
76181
|
refreshToken: applied.refreshToken,
|
|
75915
76182
|
accessToken: applied.accessToken,
|
|
75916
76183
|
accessExpiresAt: applied.accessExpiresAt,
|
|
75917
|
-
accountEmail: email || applied.accountEmail
|
|
76184
|
+
accountEmail: email || applied.accountEmail,
|
|
76185
|
+
needsReauth: false
|
|
75918
76186
|
};
|
|
75919
76187
|
this.registry.setOauth(next);
|
|
75920
76188
|
await this.persistOauthAccounts();
|
|
@@ -75927,65 +76195,44 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75927
76195
|
this.registry.replaceEntries(this.registry.listEntries().map((e) => e.id === brokerId ? renamed : e));
|
|
75928
76196
|
await this.persistBrokers();
|
|
75929
76197
|
}
|
|
75930
|
-
this.ctx.logger.info("google oauth connected", {
|
|
75931
|
-
brokerId,
|
|
75932
|
-
|
|
75933
|
-
accountEmail: next.accountEmail || void 0
|
|
75934
|
-
} });
|
|
75935
|
-
if (popup) {
|
|
75936
|
-
this.popupComplete(reply, {
|
|
75937
|
-
ok: true,
|
|
76198
|
+
this.ctx.logger.info("google oauth connected", {
|
|
76199
|
+
tags: { brokerId },
|
|
76200
|
+
meta: {
|
|
75938
76201
|
brokerId,
|
|
75939
|
-
|
|
75940
|
-
|
|
75941
|
-
|
|
75942
|
-
}
|
|
75943
|
-
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
|
+
});
|
|
75944
76211
|
} catch (e) {
|
|
75945
76212
|
const message = e instanceof Error ? e.message : String(e);
|
|
75946
76213
|
this.ctx.logger.warn("google oauth callback failed", { meta: {
|
|
75947
76214
|
brokerId,
|
|
75948
76215
|
error: message
|
|
75949
76216
|
} });
|
|
75950
|
-
|
|
75951
|
-
|
|
75952
|
-
|
|
75953
|
-
|
|
75954
|
-
|
|
75955
|
-
|
|
75956
|
-
return;
|
|
75957
|
-
}
|
|
75958
|
-
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
|
+
});
|
|
75959
76223
|
}
|
|
75960
76224
|
}
|
|
75961
76225
|
/**
|
|
75962
|
-
*
|
|
75963
|
-
*
|
|
75964
|
-
*
|
|
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.
|
|
75965
76232
|
*/
|
|
75966
|
-
|
|
75967
|
-
|
|
75968
|
-
|
|
75969
|
-
ok: outcome.ok,
|
|
75970
|
-
brokerId: outcome.brokerId,
|
|
75971
|
-
...outcome.error !== void 0 ? { error: outcome.error } : {}
|
|
75972
|
-
});
|
|
75973
|
-
const body = outcome.ok ? outcome.message ?? "Done. This window should close." : `OAuth failed: ${outcome.error ?? "unknown"}`;
|
|
75974
|
-
reply.header("content-type", "text/html; charset=utf-8");
|
|
75975
|
-
reply.status(outcome.ok ? 200 : 400).send(`<!doctype html><html><head><title>Google</title></head><body>
|
|
75976
|
-
<p>${escapeHtml(body)}</p>
|
|
75977
|
-
<script>
|
|
75978
|
-
(function () {
|
|
75979
|
-
var msg = ${payload};
|
|
75980
|
-
try {
|
|
75981
|
-
if (window.opener) {
|
|
75982
|
-
window.opener.postMessage(msg, window.location.origin);
|
|
75983
|
-
}
|
|
75984
|
-
} catch (e) {}
|
|
75985
|
-
setTimeout(function () { window.close(); }, 200);
|
|
75986
|
-
})();
|
|
75987
|
-
<\/script>
|
|
75988
|
-
</body></html>`);
|
|
76233
|
+
async landOnAdminUi(reply, outcome) {
|
|
76234
|
+
await this.refreshPublicOrigin();
|
|
76235
|
+
reply.redirect(oauthLandingUrl(this.publicOrigin, outcome));
|
|
75989
76236
|
}
|
|
75990
76237
|
async fetchAccountEmail(accessToken) {
|
|
75991
76238
|
try {
|
|
@@ -75997,10 +76244,6 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
75997
76244
|
return "";
|
|
75998
76245
|
}
|
|
75999
76246
|
}
|
|
76000
|
-
html(reply, code, body) {
|
|
76001
|
-
reply.header("content-type", "text/html; charset=utf-8");
|
|
76002
|
-
reply.status(code).send(`<!doctype html><html><body><p>${escapeHtml(body)}</p></body></html>`);
|
|
76003
|
-
}
|
|
76004
76247
|
prunePendingStates() {
|
|
76005
76248
|
const cutoff = Date.now() - 10 * 6e4;
|
|
76006
76249
|
for (const [k, v] of this.pendingStates) if (v.issuedAt < cutoff) this.pendingStates.delete(k);
|
|
@@ -76088,6 +76331,7 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76088
76331
|
const entry = this.registry.getEntry(brokerId);
|
|
76089
76332
|
const oauth = this.registry.getOauth(brokerId);
|
|
76090
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`);
|
|
76091
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");
|
|
76092
76336
|
const state = {
|
|
76093
76337
|
refreshToken: oauth.refreshToken.trim(),
|
|
@@ -76103,7 +76347,10 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76103
76347
|
clientSecret: entry.clientSecret.trim()
|
|
76104
76348
|
}, state.refreshToken);
|
|
76105
76349
|
} catch (err) {
|
|
76106
|
-
if (isInvalidGrantRefreshError(err))
|
|
76350
|
+
if (isInvalidGrantRefreshError(err)) {
|
|
76351
|
+
if (this.registry.markNeedsReauth(brokerId)) await this.persistOauthAccounts();
|
|
76352
|
+
this.emitGoogleBrokerReconnectAlert(brokerId, entry.name);
|
|
76353
|
+
}
|
|
76107
76354
|
throw err;
|
|
76108
76355
|
}
|
|
76109
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");
|
|
@@ -76113,7 +76360,8 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76113
76360
|
refreshToken: nextState.refreshToken,
|
|
76114
76361
|
accessToken: nextState.accessToken,
|
|
76115
76362
|
accessExpiresAt: nextState.accessExpiresAt,
|
|
76116
|
-
accountEmail: nextState.accountEmail
|
|
76363
|
+
accountEmail: nextState.accountEmail,
|
|
76364
|
+
needsReauth: false
|
|
76117
76365
|
});
|
|
76118
76366
|
await this.persistOauthAccounts();
|
|
76119
76367
|
return nextState.accessToken;
|
|
@@ -76129,20 +76377,18 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76129
76377
|
reply.status(404).send({ error: `Unknown Google broker: ${brokerId}` });
|
|
76130
76378
|
return;
|
|
76131
76379
|
}
|
|
76132
|
-
const popupRaw = req.query["popup"];
|
|
76133
|
-
const popup = popupRaw === "1" || popupRaw === "true";
|
|
76134
76380
|
this.registry.removeOauth(brokerId);
|
|
76135
76381
|
await this.persistOauthAccounts();
|
|
76136
|
-
this.ctx.logger.info("google oauth disconnected", {
|
|
76137
|
-
|
|
76138
|
-
|
|
76139
|
-
|
|
76140
|
-
|
|
76141
|
-
|
|
76142
|
-
|
|
76143
|
-
|
|
76144
|
-
|
|
76145
|
-
|
|
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
|
+
});
|
|
76146
76392
|
}
|
|
76147
76393
|
async resolveRedirectUri() {
|
|
76148
76394
|
await this.refreshPublicOrigin();
|
|
@@ -76179,9 +76425,6 @@ var GoogleServicesAddon = class extends BaseAddon {
|
|
|
76179
76425
|
function asSettingsCap(cap) {
|
|
76180
76426
|
if (cap === "calendar-source" || cap === "smtp-provider") return cap;
|
|
76181
76427
|
}
|
|
76182
|
-
function escapeHtml(s) {
|
|
76183
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
76184
|
-
}
|
|
76185
76428
|
//#endregion
|
|
76186
76429
|
exports.DEFAULT_GOOGLE_CONFIG = DEFAULT_GOOGLE_CONFIG;
|
|
76187
76430
|
exports.GOOGLE_BROKER_KIND = GOOGLE_BROKER_KIND;
|