@camstack/addon-provider-dreame 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/addon.js +216 -11
- package/dist/addon.mjs +216 -11
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -50504,7 +50504,7 @@ objectType({
|
|
|
50504
50504
|
})
|
|
50505
50505
|
});
|
|
50506
50506
|
//#endregion
|
|
50507
|
-
//#region ../types/dist/event-category-
|
|
50507
|
+
//#region ../types/dist/event-category-Cv9dO26A.mjs
|
|
50508
50508
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
50509
50509
|
EventCategory["SystemBoot"] = "system.boot";
|
|
50510
50510
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -50520,6 +50520,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
50520
50520
|
*/
|
|
50521
50521
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
50522
50522
|
/**
|
|
50523
|
+
* A newer addon or server-root package version was found by the
|
|
50524
|
+
* authoritative registry check. Emitted once per
|
|
50525
|
+
* `(target, packageName, currentVersion, latestVersion)` transition; repeated
|
|
50526
|
+
* polling of the same result is deduplicated by the checker.
|
|
50527
|
+
*/
|
|
50528
|
+
EventCategory["UpdateAvailable"] = "update.available";
|
|
50529
|
+
/**
|
|
50523
50530
|
* Readiness transition for a capability provider. Every producer emits
|
|
50524
50531
|
* this event on `onInitialize` completion, `onDestroy`, and
|
|
50525
50532
|
* `$node.reconnect`; every consumer that needs to gate on a cross-process
|
|
@@ -64574,8 +64581,35 @@ var NcDeliverySchema = _enum([
|
|
|
64574
64581
|
"immediate",
|
|
64575
64582
|
"track-end",
|
|
64576
64583
|
"device-event",
|
|
64577
|
-
"package-event"
|
|
64584
|
+
"package-event",
|
|
64585
|
+
"system-event"
|
|
64586
|
+
]);
|
|
64587
|
+
/**
|
|
64588
|
+
* Stable Notification Center vocabulary over infrastructure/liveness events.
|
|
64589
|
+
* Bus categories are normalized into these intent-level kinds so rules do not
|
|
64590
|
+
* depend on a provider's raw event name or payload shape.
|
|
64591
|
+
*/
|
|
64592
|
+
var NcSystemEventKindSchema = _enum([
|
|
64593
|
+
"camera-online",
|
|
64594
|
+
"camera-offline",
|
|
64595
|
+
"stream-online",
|
|
64596
|
+
"stream-offline",
|
|
64597
|
+
"node-online",
|
|
64598
|
+
"node-offline",
|
|
64599
|
+
"addon-update-available",
|
|
64600
|
+
"server-update-available"
|
|
64578
64601
|
]);
|
|
64602
|
+
/**
|
|
64603
|
+
* One coherent system-event condition. `kinds` is the required opt-in safety
|
|
64604
|
+
* gate; the remaining lists are optional narrowing filters relevant to the
|
|
64605
|
+
* selected kinds.
|
|
64606
|
+
*/
|
|
64607
|
+
var NcSystemEventConditionSchema = object({
|
|
64608
|
+
kinds: array(NcSystemEventKindSchema).min(1),
|
|
64609
|
+
deviceIds: array(number().int()).min(1).optional(),
|
|
64610
|
+
nodeIds: array(string().min(1)).min(1).optional(),
|
|
64611
|
+
packageNames: array(string().min(1)).min(1).optional()
|
|
64612
|
+
});
|
|
64579
64613
|
/** Weekly schedule — OR of windows; absence on the rule = always active. */
|
|
64580
64614
|
var NcScheduleSchema = object({
|
|
64581
64615
|
windows: array(object({
|
|
@@ -64880,6 +64914,8 @@ var NcConditionsSchema = object({
|
|
|
64880
64914
|
"picked-up",
|
|
64881
64915
|
"both"
|
|
64882
64916
|
]).optional(),
|
|
64917
|
+
/** Infrastructure/liveness/update event matcher (`system-event` delivery). */
|
|
64918
|
+
systemEvent: NcSystemEventConditionSchema.optional(),
|
|
64883
64919
|
/**
|
|
64884
64920
|
* PERSONAL-RULE custom zones (viewer-drawn). Inline normalized polygons
|
|
64885
64921
|
* (MaskShape vocabulary). A record passes when its bbox overlaps ANY
|
|
@@ -65099,7 +65135,8 @@ var NcTestResultSchema = object({
|
|
|
65099
65135
|
"object-event",
|
|
65100
65136
|
"track",
|
|
65101
65137
|
"device-event",
|
|
65102
|
-
"package-event"
|
|
65138
|
+
"package-event",
|
|
65139
|
+
"system-event"
|
|
65103
65140
|
]),
|
|
65104
65141
|
deviceId: number(),
|
|
65105
65142
|
timestamp: number(),
|
|
@@ -65121,7 +65158,8 @@ var NcConditionDescriptorSchema = object({
|
|
|
65121
65158
|
"schedule",
|
|
65122
65159
|
"device",
|
|
65123
65160
|
"package",
|
|
65124
|
-
"occupancy"
|
|
65161
|
+
"occupancy",
|
|
65162
|
+
"system"
|
|
65125
65163
|
]),
|
|
65126
65164
|
label: string(),
|
|
65127
65165
|
/** Editor widget the UI renders — never hardcode per-condition forms. */
|
|
@@ -65139,7 +65177,8 @@ var NcConditionDescriptorSchema = object({
|
|
|
65139
65177
|
"crossingSelect",
|
|
65140
65178
|
"polygonDraw",
|
|
65141
65179
|
"occupancy",
|
|
65142
|
-
"deviceState"
|
|
65180
|
+
"deviceState",
|
|
65181
|
+
"systemEvent"
|
|
65143
65182
|
]),
|
|
65144
65183
|
operator: _enum([
|
|
65145
65184
|
"in",
|
|
@@ -65198,7 +65237,8 @@ var NcHistoryRecordKindSchema = _enum([
|
|
|
65198
65237
|
"object-event",
|
|
65199
65238
|
"track-end",
|
|
65200
65239
|
"device-event",
|
|
65201
|
-
"package-event"
|
|
65240
|
+
"package-event",
|
|
65241
|
+
"system-event"
|
|
65202
65242
|
]);
|
|
65203
65243
|
/** Subject summary frozen on the row at fire time (survives rule/record edits). */
|
|
65204
65244
|
var NcHistorySubjectSchema = object({
|
|
@@ -65206,7 +65246,14 @@ var NcHistorySubjectSchema = object({
|
|
|
65206
65246
|
label: string().optional(),
|
|
65207
65247
|
confidence: number().optional(),
|
|
65208
65248
|
zones: array(string()),
|
|
65209
|
-
timestamp: number()
|
|
65249
|
+
timestamp: number(),
|
|
65250
|
+
systemEvent: object({
|
|
65251
|
+
kind: NcSystemEventKindSchema,
|
|
65252
|
+
subject: string(),
|
|
65253
|
+
title: string(),
|
|
65254
|
+
body: string(),
|
|
65255
|
+
data: record(string(), unknown())
|
|
65256
|
+
}).optional()
|
|
65210
65257
|
});
|
|
65211
65258
|
/**
|
|
65212
65259
|
* One delivery-history row. This is a read-only VIEW over the durable
|
|
@@ -65566,6 +65613,76 @@ object({
|
|
|
65566
65613
|
* Each provider returns a static descriptor; the core enumerates them
|
|
65567
65614
|
* to validate the `integration=` query param and resolve the consent
|
|
65568
65615
|
* label + the scopes baked into the issued token.
|
|
65616
|
+
*
|
|
65617
|
+
* ## Declaring one
|
|
65618
|
+
*
|
|
65619
|
+
* An OAuth client is integration-specific knowledge — who the client is, what
|
|
65620
|
+
* it may ask for, where it may be sent — so it is declared by the ADDON that
|
|
65621
|
+
* owns the integration, never by the kernel and never as a branch inside
|
|
65622
|
+
* `oauth2-routes.ts` ([D101](../../../../docs/decisions/adr-0101.md)). Three
|
|
65623
|
+
* steps, no others:
|
|
65624
|
+
*
|
|
65625
|
+
* 1. Add `{ "name": "oauth-integration" }` to the addon's `camstack.addons[]`
|
|
65626
|
+
* manifest entry. This is also what tells the hub, at addon-LOAD time, that
|
|
65627
|
+
* a descriptor is owed — see "the boot window" below.
|
|
65628
|
+
* 2. Return a provider from `onInitialize()`:
|
|
65629
|
+
*
|
|
65630
|
+
* ```ts
|
|
65631
|
+
* const provider: IOauthIntegrationProvider = {
|
|
65632
|
+
* getDescriptor: async () => ({
|
|
65633
|
+
* integrationId: 'my-thing', // the `integration=` query param
|
|
65634
|
+
* displayName: 'My Thing',
|
|
65635
|
+
* requestedScopes: [ … ], // see below
|
|
65636
|
+
* allowedRedirectPrefixes: ['https://callback.example/'],
|
|
65637
|
+
* }),
|
|
65638
|
+
* }
|
|
65639
|
+
* return [{ capability: oauthIntegrationCapability, provider }]
|
|
65640
|
+
* ```
|
|
65641
|
+
*
|
|
65642
|
+
* The descriptor must be **static** — it is read on the authorize path, so
|
|
65643
|
+
* never put an await on network or disk behind it, and never register it
|
|
65644
|
+
* behind one either (a provider is registered only once `onInitialize`
|
|
65645
|
+
* RETURNS, so anything awaited before the return delays linking).
|
|
65646
|
+
* 3. Nothing else. There is no allow-list to join, no id to register with the
|
|
65647
|
+
* core, and no per-integration branch anywhere: `/api/oauth2/authorize` and
|
|
65648
|
+
* `/api/oauth2/integrations` are built from this collection alone.
|
|
65649
|
+
*
|
|
65650
|
+
* **Scopes. `requestedScopes` has exactly ONE meaning: what the integration
|
|
65651
|
+
* NEEDS to function.** Not a blast radius, not a conservative
|
|
65652
|
+
* under-declaration, not a description of some other path the addon happens to
|
|
65653
|
+
* have. Derive it from what the client actually calls **with this token** —
|
|
65654
|
+
* every tRPC path against `METHOD_ACCESS_MAP`, plus an `addon:` grant for every
|
|
65655
|
+
* addon HTTP route it posts to — and write the call that justifies each entry
|
|
65656
|
+
* next to it. Two integrations once used this field to mean two different
|
|
65657
|
+
* things; the operator ruled there is one meaning, and any third integration
|
|
65658
|
+
* inherits it (2026-08-09).
|
|
65659
|
+
*
|
|
65660
|
+
* This is not documentation, it is the ENFORCEMENT INPUT. Since
|
|
65661
|
+
* [D103](../../../../docs/decisions/adr-0103.md) the `/addon/:addonId/*` gate
|
|
65662
|
+
* checks an integration token's grant before letting it reach an
|
|
65663
|
+
* `access: 'authenticated'` route, so an **under-declaration is an integration
|
|
65664
|
+
* that stops working** — a missing `addon:` entry means `403 Token scope
|
|
65665
|
+
* mismatch` on every control the client tries to actuate. Widen the descriptor
|
|
65666
|
+
* honestly rather than weakening a check to make a route pass.
|
|
65667
|
+
*
|
|
65668
|
+
* Prefer a narrow `capability:` scope to a `category:` one unless the client
|
|
65669
|
+
* genuinely needs a whole family; a category scope grants every future member
|
|
65670
|
+
* of that category too. `category:system [create]` has been rejected once and
|
|
65671
|
+
* should stay rejected: it hands `addons.installPackage` to an integration.
|
|
65672
|
+
*
|
|
65673
|
+
* Calls the ADDON itself makes over `ctx.api` run as the addon and are not
|
|
65674
|
+
* scope-checked, so they are not what this field describes — but reaching the
|
|
65675
|
+
* addon's route in the first place IS, and that is the entry to declare.
|
|
65676
|
+
*
|
|
65677
|
+
* **The boot window.** An addon registers its provider after its runner forks
|
|
65678
|
+
* and initialises, so between hub start and that moment this collection is
|
|
65679
|
+
* incomplete and an `integrationId` can be legitimately absent. The core does
|
|
65680
|
+
* not wait, poll or cache around this ([D3](../../../../docs/decisions/adr-0003.md)):
|
|
65681
|
+
* it compares the manifest declarers against the registered providers and
|
|
65682
|
+
* answers `503 temporarily_unavailable` (with `Retry-After` and the pending
|
|
65683
|
+
* addon ids) instead of `400 unknown integration`, and reports
|
|
65684
|
+
* `complete: false` on `GET /api/oauth2/integrations`. A client should retry
|
|
65685
|
+
* while the list is incomplete rather than conclude the hub cannot do OAuth.
|
|
65569
65686
|
*/
|
|
65570
65687
|
var OauthIntegrationDescriptorSchema = object({
|
|
65571
65688
|
/** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
|
|
@@ -65596,7 +65713,30 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
65596
65713
|
* present, /api/oauth2/authorize bakes THIS into the code instead of the
|
|
65597
65714
|
* hub-global `publicHubUrl()`, so a forked exporter addon (which can't set
|
|
65598
65715
|
* the hub's env) drives the claim that its cloud Lambda routes back on. */
|
|
65599
|
-
hubUrl: string().optional()
|
|
65716
|
+
hubUrl: string().optional(),
|
|
65717
|
+
/**
|
|
65718
|
+
* How long a REFRESH token issued for this integration lives — seconds, or
|
|
65719
|
+
* `'never'` for a token minted with no `exp` claim at all. Omit to keep the
|
|
65720
|
+
* 30-day default, which is what every link used before this field existed.
|
|
65721
|
+
*
|
|
65722
|
+
* Declared here for the same reason `requestedScopes` is: the integration
|
|
65723
|
+
* knows what it needs. Amazon's account linking and a Home Assistant config
|
|
65724
|
+
* entry are both meant to survive indefinitely, and re-linking is a manual
|
|
65725
|
+
* user action, so a 30-day expiry silently unlinks a working integration.
|
|
65726
|
+
*
|
|
65727
|
+
* **The security posture, stated so it is owned deliberately.** A refresh
|
|
65728
|
+
* token that never expires is permanent access if it leaks. What bounds it is
|
|
65729
|
+
* revocation, not time: `oauthRefresh` re-reads the session on every use and
|
|
65730
|
+
* returns `null` once `revokedAt` is set, as does `oauthVerifyAccessToken`.
|
|
65731
|
+
* The one gap is the ACCESS token — it is a plain signed JWT that nothing
|
|
65732
|
+
* re-checks against the session on the `/trpc` and `/addon/*` paths, so
|
|
65733
|
+
* revoking a link takes effect there only after its remaining hour. That hour
|
|
65734
|
+
* is why the access TTL is not configurable.
|
|
65735
|
+
*
|
|
65736
|
+
* The value is baked into the authorization code at `/authorize` and travels
|
|
65737
|
+
* on the tokens, so editing this field changes FUTURE links only.
|
|
65738
|
+
*/
|
|
65739
|
+
refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
65600
65740
|
});
|
|
65601
65741
|
method(_void(), OauthIntegrationDescriptorSchema);
|
|
65602
65742
|
/**
|
|
@@ -68733,11 +68873,29 @@ var SsoBridgeClaimsSchema = object({
|
|
|
68733
68873
|
codeChallenge: string().optional(),
|
|
68734
68874
|
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
68735
68875
|
* tokens so the verify path can check the session is not revoked. */
|
|
68736
|
-
sessionId: string().optional()
|
|
68876
|
+
sessionId: string().optional(),
|
|
68877
|
+
/**
|
|
68878
|
+
* The refresh lifetime this LINK was created with, in seconds, or `'never'`.
|
|
68879
|
+
* Baked into the code at `/authorize` from the integration's descriptor and
|
|
68880
|
+
* carried forward so `oauthRefresh` re-mints with the same lifetime. It rides
|
|
68881
|
+
* on the token rather than being re-read from the descriptor on purpose:
|
|
68882
|
+
* editing a descriptor must not retroactively extend or shorten a link the
|
|
68883
|
+
* operator already consented to.
|
|
68884
|
+
*/
|
|
68885
|
+
refreshTtl: union([number().int().positive(), literal("never")]).optional()
|
|
68737
68886
|
});
|
|
68738
68887
|
method(object({
|
|
68739
68888
|
claims: SsoBridgeClaimsSchema,
|
|
68740
|
-
|
|
68889
|
+
/**
|
|
68890
|
+
* Seconds, or `'never'` for a token minted with NO `exp` claim.
|
|
68891
|
+
*
|
|
68892
|
+
* `'never'` is a literal rather than `undefined`/`0` because omitting
|
|
68893
|
+
* this field already means "the 5-minute SSO hand-off default", and
|
|
68894
|
+
* `jwt.sign` THROWS on `{ expiresIn: undefined }` — a "no expiry" that
|
|
68895
|
+
* went through the numeric path would fail at mint time and break
|
|
68896
|
+
* linking rather than produce an eternal token.
|
|
68897
|
+
*/
|
|
68898
|
+
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
68741
68899
|
}), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
|
|
68742
68900
|
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
68743
68901
|
providerId: string().min(1),
|
|
@@ -68940,6 +69098,22 @@ var TerminalProfileInfoSchema = object({
|
|
|
68940
69098
|
label: string(),
|
|
68941
69099
|
description: string().optional()
|
|
68942
69100
|
});
|
|
69101
|
+
var TerminalOutputEventSchema = discriminatedUnion("kind", [object({
|
|
69102
|
+
seq: number().int().positive(),
|
|
69103
|
+
kind: literal("data"),
|
|
69104
|
+
data: string()
|
|
69105
|
+
}), object({
|
|
69106
|
+
seq: number().int().positive(),
|
|
69107
|
+
kind: literal("exit"),
|
|
69108
|
+
exitCode: number().int(),
|
|
69109
|
+
signal: number().int().optional()
|
|
69110
|
+
})]);
|
|
69111
|
+
var TerminalOutputBatchSchema = object({
|
|
69112
|
+
cursor: number().int().nonnegative(),
|
|
69113
|
+
reset: boolean(),
|
|
69114
|
+
snapshot: string().optional(),
|
|
69115
|
+
events: array(TerminalOutputEventSchema).readonly()
|
|
69116
|
+
});
|
|
68943
69117
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
68944
69118
|
profileId: string(),
|
|
68945
69119
|
cols: number().int().positive(),
|
|
@@ -68954,6 +69128,20 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
68954
69128
|
}), _void(), {
|
|
68955
69129
|
kind: "mutation",
|
|
68956
69130
|
auth: "admin"
|
|
69131
|
+
}), method(object({
|
|
69132
|
+
sessionId: string(),
|
|
69133
|
+
afterSeq: number().int().nonnegative(),
|
|
69134
|
+
waitMs: number().int().min(0).max(2e3).default(0)
|
|
69135
|
+
}), TerminalOutputBatchSchema, {
|
|
69136
|
+
kind: "mutation",
|
|
69137
|
+
auth: "admin",
|
|
69138
|
+
timeoutMs: 15e3
|
|
69139
|
+
}), method(object({
|
|
69140
|
+
sessionId: string(),
|
|
69141
|
+
data: string().max(64 * 1024)
|
|
69142
|
+
}), _void(), {
|
|
69143
|
+
kind: "mutation",
|
|
69144
|
+
auth: "admin"
|
|
68957
69145
|
}), method(object({ sessionId: string() }), _void(), {
|
|
68958
69146
|
kind: "mutation",
|
|
68959
69147
|
auth: "admin"
|
|
@@ -75954,7 +76142,12 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
75954
76142
|
hubUrl: string(),
|
|
75955
76143
|
/** PKCE (RFC 7636) S256 challenge. Baked into the signed code; a code
|
|
75956
76144
|
* that carries one can ONLY be exchanged with the matching verifier. */
|
|
75957
|
-
codeChallenge: string().optional()
|
|
76145
|
+
codeChallenge: string().optional(),
|
|
76146
|
+
/** The integration's declared refresh lifetime — seconds, or `'never'`.
|
|
76147
|
+
* From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
|
|
76148
|
+
* code so the link carries its own lifetime; omit for the 30-day
|
|
76149
|
+
* default. */
|
|
76150
|
+
refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
75958
76151
|
}), object({ code: string() }), {
|
|
75959
76152
|
kind: "mutation",
|
|
75960
76153
|
access: "create"
|
|
@@ -82572,12 +82765,24 @@ Object.freeze({
|
|
|
82572
82765
|
addonId: null,
|
|
82573
82766
|
access: "create"
|
|
82574
82767
|
},
|
|
82768
|
+
"terminalSession.pullOutput": {
|
|
82769
|
+
capName: "terminal-session",
|
|
82770
|
+
capScope: "system",
|
|
82771
|
+
addonId: null,
|
|
82772
|
+
access: "create"
|
|
82773
|
+
},
|
|
82575
82774
|
"terminalSession.resize": {
|
|
82576
82775
|
capName: "terminal-session",
|
|
82577
82776
|
capScope: "system",
|
|
82578
82777
|
addonId: null,
|
|
82579
82778
|
access: "create"
|
|
82580
82779
|
},
|
|
82780
|
+
"terminalSession.writeInput": {
|
|
82781
|
+
capName: "terminal-session",
|
|
82782
|
+
capScope: "system",
|
|
82783
|
+
addonId: null,
|
|
82784
|
+
access: "create"
|
|
82785
|
+
},
|
|
82581
82786
|
"toast.onToast": {
|
|
82582
82787
|
capName: "toast",
|
|
82583
82788
|
capScope: "system",
|
package/dist/addon.mjs
CHANGED
|
@@ -50504,7 +50504,7 @@ objectType({
|
|
|
50504
50504
|
})
|
|
50505
50505
|
});
|
|
50506
50506
|
//#endregion
|
|
50507
|
-
//#region ../types/dist/event-category-
|
|
50507
|
+
//#region ../types/dist/event-category-Cv9dO26A.mjs
|
|
50508
50508
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
50509
50509
|
EventCategory["SystemBoot"] = "system.boot";
|
|
50510
50510
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -50520,6 +50520,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
50520
50520
|
*/
|
|
50521
50521
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
50522
50522
|
/**
|
|
50523
|
+
* A newer addon or server-root package version was found by the
|
|
50524
|
+
* authoritative registry check. Emitted once per
|
|
50525
|
+
* `(target, packageName, currentVersion, latestVersion)` transition; repeated
|
|
50526
|
+
* polling of the same result is deduplicated by the checker.
|
|
50527
|
+
*/
|
|
50528
|
+
EventCategory["UpdateAvailable"] = "update.available";
|
|
50529
|
+
/**
|
|
50523
50530
|
* Readiness transition for a capability provider. Every producer emits
|
|
50524
50531
|
* this event on `onInitialize` completion, `onDestroy`, and
|
|
50525
50532
|
* `$node.reconnect`; every consumer that needs to gate on a cross-process
|
|
@@ -64574,8 +64581,35 @@ var NcDeliverySchema = _enum([
|
|
|
64574
64581
|
"immediate",
|
|
64575
64582
|
"track-end",
|
|
64576
64583
|
"device-event",
|
|
64577
|
-
"package-event"
|
|
64584
|
+
"package-event",
|
|
64585
|
+
"system-event"
|
|
64586
|
+
]);
|
|
64587
|
+
/**
|
|
64588
|
+
* Stable Notification Center vocabulary over infrastructure/liveness events.
|
|
64589
|
+
* Bus categories are normalized into these intent-level kinds so rules do not
|
|
64590
|
+
* depend on a provider's raw event name or payload shape.
|
|
64591
|
+
*/
|
|
64592
|
+
var NcSystemEventKindSchema = _enum([
|
|
64593
|
+
"camera-online",
|
|
64594
|
+
"camera-offline",
|
|
64595
|
+
"stream-online",
|
|
64596
|
+
"stream-offline",
|
|
64597
|
+
"node-online",
|
|
64598
|
+
"node-offline",
|
|
64599
|
+
"addon-update-available",
|
|
64600
|
+
"server-update-available"
|
|
64578
64601
|
]);
|
|
64602
|
+
/**
|
|
64603
|
+
* One coherent system-event condition. `kinds` is the required opt-in safety
|
|
64604
|
+
* gate; the remaining lists are optional narrowing filters relevant to the
|
|
64605
|
+
* selected kinds.
|
|
64606
|
+
*/
|
|
64607
|
+
var NcSystemEventConditionSchema = object({
|
|
64608
|
+
kinds: array(NcSystemEventKindSchema).min(1),
|
|
64609
|
+
deviceIds: array(number().int()).min(1).optional(),
|
|
64610
|
+
nodeIds: array(string().min(1)).min(1).optional(),
|
|
64611
|
+
packageNames: array(string().min(1)).min(1).optional()
|
|
64612
|
+
});
|
|
64579
64613
|
/** Weekly schedule — OR of windows; absence on the rule = always active. */
|
|
64580
64614
|
var NcScheduleSchema = object({
|
|
64581
64615
|
windows: array(object({
|
|
@@ -64880,6 +64914,8 @@ var NcConditionsSchema = object({
|
|
|
64880
64914
|
"picked-up",
|
|
64881
64915
|
"both"
|
|
64882
64916
|
]).optional(),
|
|
64917
|
+
/** Infrastructure/liveness/update event matcher (`system-event` delivery). */
|
|
64918
|
+
systemEvent: NcSystemEventConditionSchema.optional(),
|
|
64883
64919
|
/**
|
|
64884
64920
|
* PERSONAL-RULE custom zones (viewer-drawn). Inline normalized polygons
|
|
64885
64921
|
* (MaskShape vocabulary). A record passes when its bbox overlaps ANY
|
|
@@ -65099,7 +65135,8 @@ var NcTestResultSchema = object({
|
|
|
65099
65135
|
"object-event",
|
|
65100
65136
|
"track",
|
|
65101
65137
|
"device-event",
|
|
65102
|
-
"package-event"
|
|
65138
|
+
"package-event",
|
|
65139
|
+
"system-event"
|
|
65103
65140
|
]),
|
|
65104
65141
|
deviceId: number(),
|
|
65105
65142
|
timestamp: number(),
|
|
@@ -65121,7 +65158,8 @@ var NcConditionDescriptorSchema = object({
|
|
|
65121
65158
|
"schedule",
|
|
65122
65159
|
"device",
|
|
65123
65160
|
"package",
|
|
65124
|
-
"occupancy"
|
|
65161
|
+
"occupancy",
|
|
65162
|
+
"system"
|
|
65125
65163
|
]),
|
|
65126
65164
|
label: string(),
|
|
65127
65165
|
/** Editor widget the UI renders — never hardcode per-condition forms. */
|
|
@@ -65139,7 +65177,8 @@ var NcConditionDescriptorSchema = object({
|
|
|
65139
65177
|
"crossingSelect",
|
|
65140
65178
|
"polygonDraw",
|
|
65141
65179
|
"occupancy",
|
|
65142
|
-
"deviceState"
|
|
65180
|
+
"deviceState",
|
|
65181
|
+
"systemEvent"
|
|
65143
65182
|
]),
|
|
65144
65183
|
operator: _enum([
|
|
65145
65184
|
"in",
|
|
@@ -65198,7 +65237,8 @@ var NcHistoryRecordKindSchema = _enum([
|
|
|
65198
65237
|
"object-event",
|
|
65199
65238
|
"track-end",
|
|
65200
65239
|
"device-event",
|
|
65201
|
-
"package-event"
|
|
65240
|
+
"package-event",
|
|
65241
|
+
"system-event"
|
|
65202
65242
|
]);
|
|
65203
65243
|
/** Subject summary frozen on the row at fire time (survives rule/record edits). */
|
|
65204
65244
|
var NcHistorySubjectSchema = object({
|
|
@@ -65206,7 +65246,14 @@ var NcHistorySubjectSchema = object({
|
|
|
65206
65246
|
label: string().optional(),
|
|
65207
65247
|
confidence: number().optional(),
|
|
65208
65248
|
zones: array(string()),
|
|
65209
|
-
timestamp: number()
|
|
65249
|
+
timestamp: number(),
|
|
65250
|
+
systemEvent: object({
|
|
65251
|
+
kind: NcSystemEventKindSchema,
|
|
65252
|
+
subject: string(),
|
|
65253
|
+
title: string(),
|
|
65254
|
+
body: string(),
|
|
65255
|
+
data: record(string(), unknown())
|
|
65256
|
+
}).optional()
|
|
65210
65257
|
});
|
|
65211
65258
|
/**
|
|
65212
65259
|
* One delivery-history row. This is a read-only VIEW over the durable
|
|
@@ -65566,6 +65613,76 @@ object({
|
|
|
65566
65613
|
* Each provider returns a static descriptor; the core enumerates them
|
|
65567
65614
|
* to validate the `integration=` query param and resolve the consent
|
|
65568
65615
|
* label + the scopes baked into the issued token.
|
|
65616
|
+
*
|
|
65617
|
+
* ## Declaring one
|
|
65618
|
+
*
|
|
65619
|
+
* An OAuth client is integration-specific knowledge — who the client is, what
|
|
65620
|
+
* it may ask for, where it may be sent — so it is declared by the ADDON that
|
|
65621
|
+
* owns the integration, never by the kernel and never as a branch inside
|
|
65622
|
+
* `oauth2-routes.ts` ([D101](../../../../docs/decisions/adr-0101.md)). Three
|
|
65623
|
+
* steps, no others:
|
|
65624
|
+
*
|
|
65625
|
+
* 1. Add `{ "name": "oauth-integration" }` to the addon's `camstack.addons[]`
|
|
65626
|
+
* manifest entry. This is also what tells the hub, at addon-LOAD time, that
|
|
65627
|
+
* a descriptor is owed — see "the boot window" below.
|
|
65628
|
+
* 2. Return a provider from `onInitialize()`:
|
|
65629
|
+
*
|
|
65630
|
+
* ```ts
|
|
65631
|
+
* const provider: IOauthIntegrationProvider = {
|
|
65632
|
+
* getDescriptor: async () => ({
|
|
65633
|
+
* integrationId: 'my-thing', // the `integration=` query param
|
|
65634
|
+
* displayName: 'My Thing',
|
|
65635
|
+
* requestedScopes: [ … ], // see below
|
|
65636
|
+
* allowedRedirectPrefixes: ['https://callback.example/'],
|
|
65637
|
+
* }),
|
|
65638
|
+
* }
|
|
65639
|
+
* return [{ capability: oauthIntegrationCapability, provider }]
|
|
65640
|
+
* ```
|
|
65641
|
+
*
|
|
65642
|
+
* The descriptor must be **static** — it is read on the authorize path, so
|
|
65643
|
+
* never put an await on network or disk behind it, and never register it
|
|
65644
|
+
* behind one either (a provider is registered only once `onInitialize`
|
|
65645
|
+
* RETURNS, so anything awaited before the return delays linking).
|
|
65646
|
+
* 3. Nothing else. There is no allow-list to join, no id to register with the
|
|
65647
|
+
* core, and no per-integration branch anywhere: `/api/oauth2/authorize` and
|
|
65648
|
+
* `/api/oauth2/integrations` are built from this collection alone.
|
|
65649
|
+
*
|
|
65650
|
+
* **Scopes. `requestedScopes` has exactly ONE meaning: what the integration
|
|
65651
|
+
* NEEDS to function.** Not a blast radius, not a conservative
|
|
65652
|
+
* under-declaration, not a description of some other path the addon happens to
|
|
65653
|
+
* have. Derive it from what the client actually calls **with this token** —
|
|
65654
|
+
* every tRPC path against `METHOD_ACCESS_MAP`, plus an `addon:` grant for every
|
|
65655
|
+
* addon HTTP route it posts to — and write the call that justifies each entry
|
|
65656
|
+
* next to it. Two integrations once used this field to mean two different
|
|
65657
|
+
* things; the operator ruled there is one meaning, and any third integration
|
|
65658
|
+
* inherits it (2026-08-09).
|
|
65659
|
+
*
|
|
65660
|
+
* This is not documentation, it is the ENFORCEMENT INPUT. Since
|
|
65661
|
+
* [D103](../../../../docs/decisions/adr-0103.md) the `/addon/:addonId/*` gate
|
|
65662
|
+
* checks an integration token's grant before letting it reach an
|
|
65663
|
+
* `access: 'authenticated'` route, so an **under-declaration is an integration
|
|
65664
|
+
* that stops working** — a missing `addon:` entry means `403 Token scope
|
|
65665
|
+
* mismatch` on every control the client tries to actuate. Widen the descriptor
|
|
65666
|
+
* honestly rather than weakening a check to make a route pass.
|
|
65667
|
+
*
|
|
65668
|
+
* Prefer a narrow `capability:` scope to a `category:` one unless the client
|
|
65669
|
+
* genuinely needs a whole family; a category scope grants every future member
|
|
65670
|
+
* of that category too. `category:system [create]` has been rejected once and
|
|
65671
|
+
* should stay rejected: it hands `addons.installPackage` to an integration.
|
|
65672
|
+
*
|
|
65673
|
+
* Calls the ADDON itself makes over `ctx.api` run as the addon and are not
|
|
65674
|
+
* scope-checked, so they are not what this field describes — but reaching the
|
|
65675
|
+
* addon's route in the first place IS, and that is the entry to declare.
|
|
65676
|
+
*
|
|
65677
|
+
* **The boot window.** An addon registers its provider after its runner forks
|
|
65678
|
+
* and initialises, so between hub start and that moment this collection is
|
|
65679
|
+
* incomplete and an `integrationId` can be legitimately absent. The core does
|
|
65680
|
+
* not wait, poll or cache around this ([D3](../../../../docs/decisions/adr-0003.md)):
|
|
65681
|
+
* it compares the manifest declarers against the registered providers and
|
|
65682
|
+
* answers `503 temporarily_unavailable` (with `Retry-After` and the pending
|
|
65683
|
+
* addon ids) instead of `400 unknown integration`, and reports
|
|
65684
|
+
* `complete: false` on `GET /api/oauth2/integrations`. A client should retry
|
|
65685
|
+
* while the list is incomplete rather than conclude the hub cannot do OAuth.
|
|
65569
65686
|
*/
|
|
65570
65687
|
var OauthIntegrationDescriptorSchema = object({
|
|
65571
65688
|
/** Stable id used as the `integration=` query param, e.g. 'export-alexa'. */
|
|
@@ -65596,7 +65713,30 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
65596
65713
|
* present, /api/oauth2/authorize bakes THIS into the code instead of the
|
|
65597
65714
|
* hub-global `publicHubUrl()`, so a forked exporter addon (which can't set
|
|
65598
65715
|
* the hub's env) drives the claim that its cloud Lambda routes back on. */
|
|
65599
|
-
hubUrl: string().optional()
|
|
65716
|
+
hubUrl: string().optional(),
|
|
65717
|
+
/**
|
|
65718
|
+
* How long a REFRESH token issued for this integration lives — seconds, or
|
|
65719
|
+
* `'never'` for a token minted with no `exp` claim at all. Omit to keep the
|
|
65720
|
+
* 30-day default, which is what every link used before this field existed.
|
|
65721
|
+
*
|
|
65722
|
+
* Declared here for the same reason `requestedScopes` is: the integration
|
|
65723
|
+
* knows what it needs. Amazon's account linking and a Home Assistant config
|
|
65724
|
+
* entry are both meant to survive indefinitely, and re-linking is a manual
|
|
65725
|
+
* user action, so a 30-day expiry silently unlinks a working integration.
|
|
65726
|
+
*
|
|
65727
|
+
* **The security posture, stated so it is owned deliberately.** A refresh
|
|
65728
|
+
* token that never expires is permanent access if it leaks. What bounds it is
|
|
65729
|
+
* revocation, not time: `oauthRefresh` re-reads the session on every use and
|
|
65730
|
+
* returns `null` once `revokedAt` is set, as does `oauthVerifyAccessToken`.
|
|
65731
|
+
* The one gap is the ACCESS token — it is a plain signed JWT that nothing
|
|
65732
|
+
* re-checks against the session on the `/trpc` and `/addon/*` paths, so
|
|
65733
|
+
* revoking a link takes effect there only after its remaining hour. That hour
|
|
65734
|
+
* is why the access TTL is not configurable.
|
|
65735
|
+
*
|
|
65736
|
+
* The value is baked into the authorization code at `/authorize` and travels
|
|
65737
|
+
* on the tokens, so editing this field changes FUTURE links only.
|
|
65738
|
+
*/
|
|
65739
|
+
refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
65600
65740
|
});
|
|
65601
65741
|
method(_void(), OauthIntegrationDescriptorSchema);
|
|
65602
65742
|
/**
|
|
@@ -68733,11 +68873,29 @@ var SsoBridgeClaimsSchema = object({
|
|
|
68733
68873
|
codeChallenge: string().optional(),
|
|
68734
68874
|
/** OAuth session registry id — set on `oauth-access`/`oauth-refresh`
|
|
68735
68875
|
* tokens so the verify path can check the session is not revoked. */
|
|
68736
|
-
sessionId: string().optional()
|
|
68876
|
+
sessionId: string().optional(),
|
|
68877
|
+
/**
|
|
68878
|
+
* The refresh lifetime this LINK was created with, in seconds, or `'never'`.
|
|
68879
|
+
* Baked into the code at `/authorize` from the integration's descriptor and
|
|
68880
|
+
* carried forward so `oauthRefresh` re-mints with the same lifetime. It rides
|
|
68881
|
+
* on the token rather than being re-read from the descriptor on purpose:
|
|
68882
|
+
* editing a descriptor must not retroactively extend or shorten a link the
|
|
68883
|
+
* operator already consented to.
|
|
68884
|
+
*/
|
|
68885
|
+
refreshTtl: union([number().int().positive(), literal("never")]).optional()
|
|
68737
68886
|
});
|
|
68738
68887
|
method(object({
|
|
68739
68888
|
claims: SsoBridgeClaimsSchema,
|
|
68740
|
-
|
|
68889
|
+
/**
|
|
68890
|
+
* Seconds, or `'never'` for a token minted with NO `exp` claim.
|
|
68891
|
+
*
|
|
68892
|
+
* `'never'` is a literal rather than `undefined`/`0` because omitting
|
|
68893
|
+
* this field already means "the 5-minute SSO hand-off default", and
|
|
68894
|
+
* `jwt.sign` THROWS on `{ expiresIn: undefined }` — a "no expiry" that
|
|
68895
|
+
* went through the numeric path would fail at mint time and break
|
|
68896
|
+
* linking rather than produce an eternal token.
|
|
68897
|
+
*/
|
|
68898
|
+
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
68741
68899
|
}), object({ token: string() })), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable());
|
|
68742
68900
|
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
68743
68901
|
providerId: string().min(1),
|
|
@@ -68940,6 +69098,22 @@ var TerminalProfileInfoSchema = object({
|
|
|
68940
69098
|
label: string(),
|
|
68941
69099
|
description: string().optional()
|
|
68942
69100
|
});
|
|
69101
|
+
var TerminalOutputEventSchema = discriminatedUnion("kind", [object({
|
|
69102
|
+
seq: number().int().positive(),
|
|
69103
|
+
kind: literal("data"),
|
|
69104
|
+
data: string()
|
|
69105
|
+
}), object({
|
|
69106
|
+
seq: number().int().positive(),
|
|
69107
|
+
kind: literal("exit"),
|
|
69108
|
+
exitCode: number().int(),
|
|
69109
|
+
signal: number().int().optional()
|
|
69110
|
+
})]);
|
|
69111
|
+
var TerminalOutputBatchSchema = object({
|
|
69112
|
+
cursor: number().int().nonnegative(),
|
|
69113
|
+
reset: boolean(),
|
|
69114
|
+
snapshot: string().optional(),
|
|
69115
|
+
events: array(TerminalOutputEventSchema).readonly()
|
|
69116
|
+
});
|
|
68943
69117
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
68944
69118
|
profileId: string(),
|
|
68945
69119
|
cols: number().int().positive(),
|
|
@@ -68954,6 +69128,20 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
68954
69128
|
}), _void(), {
|
|
68955
69129
|
kind: "mutation",
|
|
68956
69130
|
auth: "admin"
|
|
69131
|
+
}), method(object({
|
|
69132
|
+
sessionId: string(),
|
|
69133
|
+
afterSeq: number().int().nonnegative(),
|
|
69134
|
+
waitMs: number().int().min(0).max(2e3).default(0)
|
|
69135
|
+
}), TerminalOutputBatchSchema, {
|
|
69136
|
+
kind: "mutation",
|
|
69137
|
+
auth: "admin",
|
|
69138
|
+
timeoutMs: 15e3
|
|
69139
|
+
}), method(object({
|
|
69140
|
+
sessionId: string(),
|
|
69141
|
+
data: string().max(64 * 1024)
|
|
69142
|
+
}), _void(), {
|
|
69143
|
+
kind: "mutation",
|
|
69144
|
+
auth: "admin"
|
|
68957
69145
|
}), method(object({ sessionId: string() }), _void(), {
|
|
68958
69146
|
kind: "mutation",
|
|
68959
69147
|
auth: "admin"
|
|
@@ -75954,7 +76142,12 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
|
|
|
75954
76142
|
hubUrl: string(),
|
|
75955
76143
|
/** PKCE (RFC 7636) S256 challenge. Baked into the signed code; a code
|
|
75956
76144
|
* that carries one can ONLY be exchanged with the matching verifier. */
|
|
75957
|
-
codeChallenge: string().optional()
|
|
76145
|
+
codeChallenge: string().optional(),
|
|
76146
|
+
/** The integration's declared refresh lifetime — seconds, or `'never'`.
|
|
76147
|
+
* From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
|
|
76148
|
+
* code so the link carries its own lifetime; omit for the 30-day
|
|
76149
|
+
* default. */
|
|
76150
|
+
refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
|
|
75958
76151
|
}), object({ code: string() }), {
|
|
75959
76152
|
kind: "mutation",
|
|
75960
76153
|
access: "create"
|
|
@@ -82572,12 +82765,24 @@ Object.freeze({
|
|
|
82572
82765
|
addonId: null,
|
|
82573
82766
|
access: "create"
|
|
82574
82767
|
},
|
|
82768
|
+
"terminalSession.pullOutput": {
|
|
82769
|
+
capName: "terminal-session",
|
|
82770
|
+
capScope: "system",
|
|
82771
|
+
addonId: null,
|
|
82772
|
+
access: "create"
|
|
82773
|
+
},
|
|
82575
82774
|
"terminalSession.resize": {
|
|
82576
82775
|
capName: "terminal-session",
|
|
82577
82776
|
capScope: "system",
|
|
82578
82777
|
addonId: null,
|
|
82579
82778
|
access: "create"
|
|
82580
82779
|
},
|
|
82780
|
+
"terminalSession.writeInput": {
|
|
82781
|
+
capName: "terminal-session",
|
|
82782
|
+
capScope: "system",
|
|
82783
|
+
addonId: null,
|
|
82784
|
+
access: "create"
|
|
82785
|
+
},
|
|
82581
82786
|
"toast.onToast": {
|
|
82582
82787
|
capName: "toast",
|
|
82583
82788
|
capScope: "system",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-dreame",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"description": "Dreame robot-vacuum / lawn-mower device-provider addon for CamStack — wraps the @apocaliss92/nodedreame Dreamehome cloud client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|