@camstack/addon-pipeline 1.2.39 → 1.2.41
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/audio-analyzer/index.js +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +2 -2
- package/dist/detection-pipeline/index.mjs +2 -2
- package/dist/{dist-BtAvtroH.js → dist-BS7wLqoV.js} +499 -10
- package/dist/{dist-COgGdFMv.mjs → dist-DvUTPZ8J.mjs} +499 -10
- package/dist/{event-loop-stall-monitor-shfwi1_b.js → event-loop-stall-monitor-C5P2bcwP.js} +1 -1
- package/dist/{event-loop-stall-monitor-naFYvcOO.mjs → event-loop-stall-monitor-a2u9e1qx.mjs} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +2 -2
- package/dist/pipeline-runner/index.mjs +2 -2
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/session-decode/decode-worker-child.js +39 -2
- package/dist/session-decode/decode-worker-child.mjs +39 -2
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CjrKd4xX.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CS82x0lt.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CDzxDzcv.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CciPNnL7.mjs +26 -0
- package/dist/stream-broker/{hostInit-BFH17kSk.mjs → hostInit-lj4kKHrb.mjs} +3 -3
- package/dist/stream-broker/index.js +7 -4
- package/dist/stream-broker/index.mjs +7 -4
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-yeSHfpUy.js → MaskShapeCanvas-DI4BY7W2-BRQ5u_mo.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-Df4WxM6N.js → MotionZonesSettings-NcxxQN8r-KbOg5nD9.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-COHSiTGq.js → PrivacyMaskSettings-APgPLF7p-CKSPn3_D.js} +1 -1
- package/embed-dist/assets/index-CGdwTcwE.css +2 -0
- package/embed-dist/assets/{index-Ct9M2v5n.js → index-CbrEyYGW.js} +13 -13
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B59GNAJq.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-83qNzMSL.mjs +0 -26
- package/embed-dist/assets/index-4fJzjj4H.css +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../types/dist/event-category-
|
|
1
|
+
//#region ../types/dist/event-category-41fKf-q9.mjs
|
|
2
2
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
3
3
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4
4
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -58,6 +58,26 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
58
58
|
*/
|
|
59
59
|
EventCategory["AddonRetryScheduled"] = "addon.retry-scheduled";
|
|
60
60
|
/**
|
|
61
|
+
* A liveness invariant of this node is false — it has no devices, every
|
|
62
|
+
* camera is offline, or a camera that was recording has stopped producing
|
|
63
|
+
* segments. Emitted by the `liveness-monitor` builtin, once per fault (the
|
|
64
|
+
* `findingId` is stable across ticks), only after its boot grace period.
|
|
65
|
+
* AlertCenter raises a persistent operator-visible alert.
|
|
66
|
+
*
|
|
67
|
+
* It exists because on 2026-08-03 the hub ran four hours with zero devices
|
|
68
|
+
* and zero recordings while every surface stayed quiet.
|
|
69
|
+
*
|
|
70
|
+
* Payload: `{ findingId, severity, title, message, deviceId? }`.
|
|
71
|
+
*/
|
|
72
|
+
EventCategory["SystemLivenessFailed"] = "system.liveness-failed";
|
|
73
|
+
/**
|
|
74
|
+
* A previously reported liveness fault is true again. AlertCenter dismisses
|
|
75
|
+
* the matching `SystemLivenessFailed` alert.
|
|
76
|
+
*
|
|
77
|
+
* Payload: `{ findingId }`.
|
|
78
|
+
*/
|
|
79
|
+
EventCategory["SystemLivenessRecovered"] = "system.liveness-recovered";
|
|
80
|
+
/**
|
|
61
81
|
* Monitor is attempting to reload a failed addon NOW. UI uses this
|
|
62
82
|
* to show a spinner during the retry attempt. Same transient nature
|
|
63
83
|
* as AddonRetryScheduled.
|
|
@@ -9646,7 +9666,68 @@ var NcZoneConditionSchema = object({
|
|
|
9646
9666
|
* The P1 condition set — a flat AND of groups; absent group = pass;
|
|
9647
9667
|
* membership lists are OR within the list (spec §2.3).
|
|
9648
9668
|
*/
|
|
9669
|
+
/**
|
|
9670
|
+
* What a rule may actuate.
|
|
9671
|
+
*
|
|
9672
|
+
* **No hand-maintained allowlist** (operator decision, and the right one — a
|
|
9673
|
+
* written list of methods is a third parallel map to keep aligned, and this
|
|
9674
|
+
* repo has paid for those). The boundary instead comes from a property the
|
|
9675
|
+
* capabilities already carry: an action may target only a **device-scoped**
|
|
9676
|
+
* capability method.
|
|
9677
|
+
*
|
|
9678
|
+
* That is not decoration. A rule can be authored by a NON-ADMIN — personal
|
|
9679
|
+
* rules are a supported flow — and the executor runs with the addon's
|
|
9680
|
+
* privileges, so an unbounded action is an arbitrary RPC channel with a
|
|
9681
|
+
* privilege escalation attached. Restricting to device scope excludes the
|
|
9682
|
+
* system caps (`device-manager.removeDevice` and friends) by construction,
|
|
9683
|
+
* costs nothing to maintain, and cannot rot: a cap that stops being
|
|
9684
|
+
* device-scoped stops being actuatable in the same change.
|
|
9685
|
+
*
|
|
9686
|
+
* The executor enforces it; {@link NcRuleActionSchema} carries the intent.
|
|
9687
|
+
*/
|
|
9688
|
+
/**
|
|
9689
|
+
* One step of a sequence.
|
|
9690
|
+
*
|
|
9691
|
+
* `wait` is a first-class step rather than a property of the next action: it is
|
|
9692
|
+
* what makes a sequence a SEQUENCE and not a list — "unlock, wait 5s, open"
|
|
9693
|
+
* cannot be expressed otherwise.
|
|
9694
|
+
*/
|
|
9695
|
+
var NcRuleActionSchema = discriminatedUnion("kind", [object({
|
|
9696
|
+
kind: literal("wait"),
|
|
9697
|
+
seconds: number().min(0).max(300)
|
|
9698
|
+
}), object({
|
|
9699
|
+
kind: literal("cap"),
|
|
9700
|
+
deviceId: number().int(),
|
|
9701
|
+
/** Capability name, e.g. `alarm-panel`. */
|
|
9702
|
+
cap: string().min(1),
|
|
9703
|
+
/** Method on it. The executor refuses a non-device-scoped cap. */
|
|
9704
|
+
method: string().min(1),
|
|
9705
|
+
/** Method arguments, minus `deviceId` (the executor injects it). */
|
|
9706
|
+
args: record(string(), unknown()).optional()
|
|
9707
|
+
})]);
|
|
9708
|
+
/**
|
|
9709
|
+
* Sequences a rule runs, by hook point.
|
|
9710
|
+
*
|
|
9711
|
+
* ONLY `onTrigger` is here, deliberately. The reference also has activation /
|
|
9712
|
+
* deactivation / reset / post-generation hooks, and they are wanted — but this
|
|
9713
|
+
* repo's expensive failure mode is declaring a surface nothing produces, so a
|
|
9714
|
+
* hook appears here in the same change that produces its edge, never before.
|
|
9715
|
+
*/
|
|
9716
|
+
var NcRuleActionsSchema = object({
|
|
9717
|
+
/** Runs when the rule MATCHES. */
|
|
9718
|
+
onTrigger: array(object({
|
|
9719
|
+
name: string().min(1).max(120),
|
|
9720
|
+
enabled: boolean(),
|
|
9721
|
+
minDelaySec: number().int().min(0).max(86400).optional(),
|
|
9722
|
+
actions: array(NcRuleActionSchema).min(1)
|
|
9723
|
+
})).optional() });
|
|
9649
9724
|
var NcConditionsSchema = object({
|
|
9725
|
+
/** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
|
|
9726
|
+
deviceState: object({
|
|
9727
|
+
deviceId: number().int(),
|
|
9728
|
+
/** Any of these matches. */
|
|
9729
|
+
states: array(string().min(1)).min(1)
|
|
9730
|
+
}).optional(),
|
|
9650
9731
|
/** Device scope — absent = all devices. */
|
|
9651
9732
|
devices: array(number()).optional(),
|
|
9652
9733
|
/** Detector class names (any overlap with the record's class set). */
|
|
@@ -9847,6 +9928,14 @@ var NcMediaPolicySchema = object({
|
|
|
9847
9928
|
clipPreRollSec: number().int().min(0).max(30).optional(),
|
|
9848
9929
|
clipPostRollSec: number().int().min(0).max(30).optional(),
|
|
9849
9930
|
/**
|
|
9931
|
+
* Playback rate of the attached gif / clip. Absent = 2x.
|
|
9932
|
+
*
|
|
9933
|
+
* A notification clip is GLANCED at on a lock screen, not watched: at real
|
|
9934
|
+
* time an eight-second passage is eight seconds of the recipient's attention
|
|
9935
|
+
* and twice the bytes. 1 is real time for the operator who wants it.
|
|
9936
|
+
*/
|
|
9937
|
+
clipSpeed: number().min(1).max(8).optional(),
|
|
9938
|
+
/**
|
|
9850
9939
|
* Which stream profile the footage is cut from. Absent = the CHEAPEST
|
|
9851
9940
|
* assigned profile: a notification is watched on a phone, so the 4K
|
|
9852
9941
|
* rendition would burn CPU to produce a file the client downscales anyway.
|
|
@@ -9918,7 +10007,30 @@ var NcRuleInputSchema = object({
|
|
|
9918
10007
|
* behaviour, visible to all, read-only in the viewer). Present = personal
|
|
9919
10008
|
* rule owned by this userId. Server-stamped; never trusted from a client.
|
|
9920
10009
|
*/
|
|
9921
|
-
ownerUserId: string().optional()
|
|
10010
|
+
ownerUserId: string().optional(),
|
|
10011
|
+
/**
|
|
10012
|
+
* May a non-admin snooze this rule for EVERYONE, not just themselves?
|
|
10013
|
+
*
|
|
10014
|
+
* A snooze is personal by default — it silences the person who set it. This
|
|
10015
|
+
* opts THIS rule into the "the gardener is here all afternoon" case, where
|
|
10016
|
+
* silencing the camera for the whole household is legitimate. It silences
|
|
10017
|
+
* other people, so it is off unless a rule deliberately allows it.
|
|
10018
|
+
*
|
|
10019
|
+
* `.optional()`, deliberately NOT `.default()`: a Zod default does not run on
|
|
10020
|
+
* the addon cap path (three production failures in one day), so absent is
|
|
10021
|
+
* read as `false` by {@link canSetGlobal} in the engine. Admins are not bound
|
|
10022
|
+
* by this flag — see the scope rules on that function.
|
|
10023
|
+
*/
|
|
10024
|
+
snoozeAllowGlobal: boolean().optional(),
|
|
10025
|
+
/**
|
|
10026
|
+
* Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
|
|
10027
|
+
*
|
|
10028
|
+
* This is what makes the rule set the alarm's trigger set without the alarm
|
|
10029
|
+
* being a special case: arming is
|
|
10030
|
+
* `{ cap: 'alarm-panel', method: 'arm', args: { mode: 'away' } }`, the same
|
|
10031
|
+
* shape as every other actuation.
|
|
10032
|
+
*/
|
|
10033
|
+
actions: NcRuleActionsSchema.optional()
|
|
9922
10034
|
});
|
|
9923
10035
|
/**
|
|
9924
10036
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -9989,7 +10101,8 @@ var NcConditionDescriptorSchema = object({
|
|
|
9989
10101
|
"packagePhase",
|
|
9990
10102
|
"crossingSelect",
|
|
9991
10103
|
"polygonDraw",
|
|
9992
|
-
"occupancy"
|
|
10104
|
+
"occupancy",
|
|
10105
|
+
"deviceState"
|
|
9993
10106
|
]),
|
|
9994
10107
|
operator: _enum([
|
|
9995
10108
|
"in",
|
|
@@ -10003,7 +10116,28 @@ var NcConditionDescriptorSchema = object({
|
|
|
10003
10116
|
/** Which delivery kinds the condition applies to. */
|
|
10004
10117
|
appliesTo: array(NcDeliverySchema),
|
|
10005
10118
|
phase: string(),
|
|
10006
|
-
description: string().optional()
|
|
10119
|
+
description: string().optional(),
|
|
10120
|
+
/**
|
|
10121
|
+
* The CHOICES for a single-choice widget (`sourceSelect`, `crossingSelect`,
|
|
10122
|
+
* `packagePhase`, …), served with the descriptor.
|
|
10123
|
+
*
|
|
10124
|
+
* Before this the descriptor said which widget to render and not what to put
|
|
10125
|
+
* in it, so every option list lived in three places: this file's enums, the
|
|
10126
|
+
* admin's `NC_*_OPTIONS` and the viewer's `NC_*_VALUES`. That triple mirror
|
|
10127
|
+
* is the drift that emptied the viewer's rule editor on 2026-08-04 — the app
|
|
10128
|
+
* mirrors the cap by hand, so it is only ever as current as its last build.
|
|
10129
|
+
*
|
|
10130
|
+
* With the options on the wire, a condition of an EXISTING `valueType` costs
|
|
10131
|
+
* zero client changes. Clients keep a local fallback for an older hub that
|
|
10132
|
+
* does not send them; absent here is "use your own list", not "no choices".
|
|
10133
|
+
*/
|
|
10134
|
+
options: array(object({
|
|
10135
|
+
/** Written to the rule verbatim. `''` means the ABSENT state. */
|
|
10136
|
+
value: string(),
|
|
10137
|
+
label: string(),
|
|
10138
|
+
/** What THIS choice matches — shown one at a time, under the control. */
|
|
10139
|
+
hint: string().optional()
|
|
10140
|
+
})).readonly().optional()
|
|
10007
10141
|
});
|
|
10008
10142
|
/**
|
|
10009
10143
|
* The delivery lifecycle status of a history row — a straight read of the
|
|
@@ -10088,6 +10222,74 @@ var NcHistoryFilterSchema = object({
|
|
|
10088
10222
|
until: number().optional(),
|
|
10089
10223
|
limit: number().int().min(1).max(500).default(100)
|
|
10090
10224
|
});
|
|
10225
|
+
/**
|
|
10226
|
+
* What a snooze covers. Broader scopes win when several overlap, so one window
|
|
10227
|
+
* leaves ONE digest rather than a rule snooze and a whole-feed snooze both
|
|
10228
|
+
* summarising the same silence.
|
|
10229
|
+
*/
|
|
10230
|
+
var NcSnoozeScopeSchema = _enum([
|
|
10231
|
+
"rule",
|
|
10232
|
+
"device",
|
|
10233
|
+
"all"
|
|
10234
|
+
]);
|
|
10235
|
+
/**
|
|
10236
|
+
* Client-authored snooze. The server stamps `userId`, `startedAt` and
|
|
10237
|
+
* `expiresAt` — a DURATION is sent rather than an instant so a client with a
|
|
10238
|
+
* skewed clock cannot author a window that is already over, or never ends.
|
|
10239
|
+
*/
|
|
10240
|
+
var NcSnoozeInputSchema = object({
|
|
10241
|
+
scope: NcSnoozeScopeSchema,
|
|
10242
|
+
/** Required when `scope: 'rule'` — a scoped snooze with no id matches
|
|
10243
|
+
* NOTHING rather than degrading to "everything". */
|
|
10244
|
+
ruleId: string().optional(),
|
|
10245
|
+
/** Required when `scope: 'device'`. */
|
|
10246
|
+
deviceId: number().int().optional(),
|
|
10247
|
+
durationMinutes: number().int().min(1).max(1440),
|
|
10248
|
+
/**
|
|
10249
|
+
* Silence this for EVERY recipient, not just the caller. Permission is
|
|
10250
|
+
* checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
|
|
10251
|
+
* broader scopes). Absent = personal.
|
|
10252
|
+
*/
|
|
10253
|
+
global: boolean().optional(),
|
|
10254
|
+
/**
|
|
10255
|
+
* Deliver a summary of what was suppressed when the window ends. Absent =
|
|
10256
|
+
* ON: someone silencing a nuisance camera wants it off, someone silencing a
|
|
10257
|
+
* SECURITY camera wants to know what they missed, and choosing "off" for
|
|
10258
|
+
* everybody is how a snooze becomes an outage. Resolved to a concrete
|
|
10259
|
+
* boolean by the server at create time — never left to a Zod default, which
|
|
10260
|
+
* does not run on the addon cap path.
|
|
10261
|
+
*/
|
|
10262
|
+
summary: boolean().optional()
|
|
10263
|
+
});
|
|
10264
|
+
/** A persisted snooze window. */
|
|
10265
|
+
var NcSnoozeSchema = object({
|
|
10266
|
+
id: string(),
|
|
10267
|
+
/** Who set it. Also who it silences, unless `global`. */
|
|
10268
|
+
userId: string(),
|
|
10269
|
+
scope: NcSnoozeScopeSchema,
|
|
10270
|
+
ruleId: string().optional(),
|
|
10271
|
+
deviceId: number().int().optional(),
|
|
10272
|
+
startedAt: number(),
|
|
10273
|
+
/** Exclusive: at exactly this instant the snooze is over. Expiry is a
|
|
10274
|
+
* COMPARISON, not a job — no sweeper can leave the operator silenced. */
|
|
10275
|
+
expiresAt: number(),
|
|
10276
|
+
global: boolean(),
|
|
10277
|
+
summary: boolean(),
|
|
10278
|
+
/** When the end-of-window digest went out. Absent = not sent (yet, or the
|
|
10279
|
+
* window has not closed, or `summary` is false). */
|
|
10280
|
+
digestSentAt: number().optional()
|
|
10281
|
+
});
|
|
10282
|
+
object({
|
|
10283
|
+
snoozeId: string(),
|
|
10284
|
+
targetId: string(),
|
|
10285
|
+
ruleId: string(),
|
|
10286
|
+
ruleName: string(),
|
|
10287
|
+
deviceId: number().int(),
|
|
10288
|
+
/** How many notifications this snooze hid for that pair. */
|
|
10289
|
+
count: number().int(),
|
|
10290
|
+
firstAt: number(),
|
|
10291
|
+
lastAt: number()
|
|
10292
|
+
});
|
|
10091
10293
|
method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
|
|
10092
10294
|
kind: "mutation",
|
|
10093
10295
|
auth: "admin",
|
|
@@ -10117,7 +10319,13 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
10117
10319
|
}), method(object({}), object({
|
|
10118
10320
|
catalog: array(NcConditionDescriptorSchema),
|
|
10119
10321
|
taxonomy: NcTaxonomySchema.optional()
|
|
10120
|
-
})), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" })
|
|
10322
|
+
})), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
|
|
10323
|
+
kind: "mutation",
|
|
10324
|
+
caller: "required"
|
|
10325
|
+
}), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
|
|
10326
|
+
kind: "mutation",
|
|
10327
|
+
caller: "required"
|
|
10328
|
+
});
|
|
10121
10329
|
/**
|
|
10122
10330
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
10123
10331
|
*
|
|
@@ -10894,7 +11102,15 @@ var streamBrokerCapability = {
|
|
|
10894
11102
|
format: _enum(["gif", "mp4"]).default("gif"),
|
|
10895
11103
|
maxWidth: number().int().min(120).max(1920).default(480),
|
|
10896
11104
|
/** GIF only — MP4 keeps the source cadence. */
|
|
10897
|
-
fps: number().int().min(1).max(15).default(5)
|
|
11105
|
+
fps: number().int().min(1).max(15).default(5),
|
|
11106
|
+
/**
|
|
11107
|
+
* Playback rate. A notification clip is GLANCED at on a lock screen,
|
|
11108
|
+
* not watched, so 2x is the default: the recipient sees the whole
|
|
11109
|
+
* passage in half the time and the GIF is half the bytes. `1` is real
|
|
11110
|
+
* time. Applies to MP4 as well — the operator set a speed, not a GIF
|
|
11111
|
+
* speed.
|
|
11112
|
+
*/
|
|
11113
|
+
speed: number().min(1).max(8).default(2)
|
|
10898
11114
|
}), object({
|
|
10899
11115
|
base64: string(),
|
|
10900
11116
|
mime: string(),
|
|
@@ -16272,6 +16488,20 @@ var QueryFilterSchema = object({
|
|
|
16272
16488
|
limit: number().optional(),
|
|
16273
16489
|
offset: number().optional()
|
|
16274
16490
|
});
|
|
16491
|
+
/**
|
|
16492
|
+
* The predicate half of a filter, for BULK MUTATIONS.
|
|
16493
|
+
*
|
|
16494
|
+
* Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
|
|
16495
|
+
* meaning for a statement that rewrites a set, and accepting them would invite
|
|
16496
|
+
* a caller to believe `limit` bounds the damage. Every field is optional here
|
|
16497
|
+
* only so the shape stays composable — the implementation REJECTS a filter
|
|
16498
|
+
* that compiles to no predicate, because that is the whole collection.
|
|
16499
|
+
*/
|
|
16500
|
+
var MutationFilterSchema = object({
|
|
16501
|
+
where: record(string(), unknown()).optional(),
|
|
16502
|
+
whereIn: record(string(), array(unknown())).optional(),
|
|
16503
|
+
whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
|
|
16504
|
+
});
|
|
16275
16505
|
/** A single stored record: `{ id, data }`. */
|
|
16276
16506
|
var SettingsRecordSchema = object({
|
|
16277
16507
|
id: string(),
|
|
@@ -16297,7 +16527,18 @@ var CollectionColumnSchema = object({
|
|
|
16297
16527
|
]),
|
|
16298
16528
|
primaryKey: boolean().optional(),
|
|
16299
16529
|
notNull: boolean().optional(),
|
|
16300
|
-
unique: boolean().optional()
|
|
16530
|
+
unique: boolean().optional(),
|
|
16531
|
+
/**
|
|
16532
|
+
* Column DEFAULT. Required for B2: the four `ensureTable` consumers declare
|
|
16533
|
+
* columns like `enabled INTEGER NOT NULL DEFAULT 1`, and without this the
|
|
16534
|
+
* collection surface simply cannot express their existing tables — which is
|
|
16535
|
+
* why they were still on the uncapped `table*` API.
|
|
16536
|
+
*/
|
|
16537
|
+
defaultValue: union([
|
|
16538
|
+
string(),
|
|
16539
|
+
number(),
|
|
16540
|
+
boolean()
|
|
16541
|
+
]).optional()
|
|
16301
16542
|
});
|
|
16302
16543
|
var CollectionIndexSchema = object({
|
|
16303
16544
|
name: string(),
|
|
@@ -16331,6 +16572,15 @@ method(object({
|
|
|
16331
16572
|
collection: string(),
|
|
16332
16573
|
key: string()
|
|
16333
16574
|
}), _void(), { kind: "mutation" }), method(object({
|
|
16575
|
+
namespace: string().optional(),
|
|
16576
|
+
collection: string(),
|
|
16577
|
+
filter: MutationFilterSchema
|
|
16578
|
+
}), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
|
|
16579
|
+
namespace: string().optional(),
|
|
16580
|
+
collection: string(),
|
|
16581
|
+
filter: MutationFilterSchema,
|
|
16582
|
+
data: record(string(), unknown())
|
|
16583
|
+
}), object({ updated: number().int() }), { kind: "mutation" }), method(object({
|
|
16334
16584
|
namespace: string().optional(),
|
|
16335
16585
|
collection: string(),
|
|
16336
16586
|
filter: QueryFilterSchema.optional()
|
|
@@ -16397,6 +16647,15 @@ method(_void(), EngineInfoSchema), method(object({
|
|
|
16397
16647
|
collection: string(),
|
|
16398
16648
|
key: string()
|
|
16399
16649
|
}), _void(), { kind: "mutation" }), method(object({
|
|
16650
|
+
namespace: string().optional(),
|
|
16651
|
+
collection: string(),
|
|
16652
|
+
filter: MutationFilterSchema
|
|
16653
|
+
}), object({ deleted: number().int() }), { kind: "mutation" }), method(object({
|
|
16654
|
+
namespace: string().optional(),
|
|
16655
|
+
collection: string(),
|
|
16656
|
+
filter: MutationFilterSchema,
|
|
16657
|
+
data: record(string(), unknown())
|
|
16658
|
+
}), object({ updated: number().int() }), { kind: "mutation" }), method(object({
|
|
16400
16659
|
namespace: string().optional(),
|
|
16401
16660
|
collection: string(),
|
|
16402
16661
|
filter: QueryFilterSchema.optional()
|
|
@@ -17128,7 +17387,20 @@ method(object({
|
|
|
17128
17387
|
}), _void(), {
|
|
17129
17388
|
kind: "mutation",
|
|
17130
17389
|
auth: "admin"
|
|
17131
|
-
}), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({
|
|
17390
|
+
}), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({
|
|
17391
|
+
addonId: string().optional(),
|
|
17392
|
+
/**
|
|
17393
|
+
* `slim` omits `config` (returned `{}`), `metadata` (null) and the
|
|
17394
|
+
* `sourceInfo` derived from config — and skips the per-device settings
|
|
17395
|
+
* read that produces them. Everything identifying a device (id, name,
|
|
17396
|
+
* type, online, features, isCamera, parent/link ids) is unchanged.
|
|
17397
|
+
* Do not use it for dispatch routing, which needs `sourceInfo`.
|
|
17398
|
+
*/
|
|
17399
|
+
projection: _enum(["full", "slim"]).optional(),
|
|
17400
|
+
/** Return only camera devices. Filtering server-side instead of
|
|
17401
|
+
* shipping 293 rows to find 12. */
|
|
17402
|
+
isCamera: boolean().optional()
|
|
17403
|
+
}), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
|
|
17132
17404
|
mode: DeviceLinkModeSchema,
|
|
17133
17405
|
devices: array(LinkedDeviceSchema)
|
|
17134
17406
|
})), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
|
|
@@ -18327,6 +18599,103 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
18327
18599
|
enabled: boolean()
|
|
18328
18600
|
}), _void(), { kind: "mutation" });
|
|
18329
18601
|
/**
|
|
18602
|
+
* core-blocks — user-authored TypeScript, stored in the kernel and executed in
|
|
18603
|
+
* its own process.
|
|
18604
|
+
*
|
|
18605
|
+
* Spec: `docs/superpowers/specs/2026-08-04-core-blocks-and-synthetic-devices-design.md`.
|
|
18606
|
+
*
|
|
18607
|
+
* The first use is **owning devices without being a device provider**: a block
|
|
18608
|
+
* declares devices under a system or custom integration and drives their state,
|
|
18609
|
+
* with the same `ctx` an addon gets. Automations come later; nothing here
|
|
18610
|
+
* models a trigger.
|
|
18611
|
+
*
|
|
18612
|
+
* **Stated plainly, because it does not change by being true:** a block has an
|
|
18613
|
+
* addon's powers — devices, storage, the event bus, `ctx.api`. It is a plugin
|
|
18614
|
+
* with no review step. What makes that survivable is not a sandbox, it is
|
|
18615
|
+
* PROCESS ISOLATION: one process per block, supervised by `CrashSupervisor`,
|
|
18616
|
+
* so a block that throws or never returns is marked `failed` and visible
|
|
18617
|
+
* instead of taking the hub with it (D6). Every method here is admin-only, and
|
|
18618
|
+
* must stay so.
|
|
18619
|
+
*/
|
|
18620
|
+
/** Where a block runs. The operator chooses — a block driving a device on an
|
|
18621
|
+
* agent is the reason placement is not fixed to the hub. */
|
|
18622
|
+
var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
|
|
18623
|
+
/** What a block's process is doing. Mirrors the addon runner's own lifecycle so
|
|
18624
|
+
* a failing block reads the same way a failing addon does. */
|
|
18625
|
+
var CoreBlockStatusSchema = _enum([
|
|
18626
|
+
"stopped",
|
|
18627
|
+
"starting",
|
|
18628
|
+
"running",
|
|
18629
|
+
"failed"
|
|
18630
|
+
]);
|
|
18631
|
+
/** Client-authored fields. */
|
|
18632
|
+
var CoreBlockInputSchema = object({
|
|
18633
|
+
name: string().min(1).max(120),
|
|
18634
|
+
/** TypeScript source. Compiled server-side before it is ever stored — a
|
|
18635
|
+
* block that does not compile is a fork failure the operator would meet
|
|
18636
|
+
* minutes later, in a log, instead of in the editor. */
|
|
18637
|
+
code: string().max(2e5),
|
|
18638
|
+
enabled: boolean(),
|
|
18639
|
+
placement: CoreBlockPlacementSchema,
|
|
18640
|
+
/**
|
|
18641
|
+
* Integration the block's devices hang from. Absent = the system integration
|
|
18642
|
+
* blocks share. A block may declare its own instead.
|
|
18643
|
+
*/
|
|
18644
|
+
integrationId: string().optional()
|
|
18645
|
+
});
|
|
18646
|
+
/** A stored block. */
|
|
18647
|
+
var CoreBlockSchema = CoreBlockInputSchema.extend({
|
|
18648
|
+
id: string(),
|
|
18649
|
+
createdAt: number(),
|
|
18650
|
+
updatedAt: number(),
|
|
18651
|
+
/** Server-stamped author. */
|
|
18652
|
+
createdBy: string(),
|
|
18653
|
+
status: CoreBlockStatusSchema,
|
|
18654
|
+
/**
|
|
18655
|
+
* Why the block is not running, when it is not. The operator's ONLY window
|
|
18656
|
+
* into a block that failed at load — a block that is silently absent is the
|
|
18657
|
+
* failure mode this whole feature has to avoid.
|
|
18658
|
+
*/
|
|
18659
|
+
lastError: string().optional(),
|
|
18660
|
+
/** Ms epoch of the last state change. */
|
|
18661
|
+
lastChangedAt: number()
|
|
18662
|
+
});
|
|
18663
|
+
/** What a compile attempt produced. */
|
|
18664
|
+
var CoreBlockCompileResultSchema = object({
|
|
18665
|
+
ok: boolean(),
|
|
18666
|
+
/** Present when `ok` is false — the first error, in the author's words. */
|
|
18667
|
+
error: string().optional(),
|
|
18668
|
+
line: number().optional(),
|
|
18669
|
+
column: number().optional()
|
|
18670
|
+
});
|
|
18671
|
+
method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }), method(object({ blockId: string() }), object({ block: CoreBlockSchema.nullable() }), { auth: "admin" }), method(object({ block: CoreBlockInputSchema }), object({ block: CoreBlockSchema }), {
|
|
18672
|
+
kind: "mutation",
|
|
18673
|
+
auth: "admin",
|
|
18674
|
+
caller: "required"
|
|
18675
|
+
}), method(object({
|
|
18676
|
+
blockId: string(),
|
|
18677
|
+
block: CoreBlockInputSchema.partial()
|
|
18678
|
+
}), object({ block: CoreBlockSchema }), {
|
|
18679
|
+
kind: "mutation",
|
|
18680
|
+
auth: "admin",
|
|
18681
|
+
caller: "required"
|
|
18682
|
+
}), method(object({ blockId: string() }), object({ success: literal(true) }), {
|
|
18683
|
+
kind: "mutation",
|
|
18684
|
+
auth: "admin"
|
|
18685
|
+
}), method(object({
|
|
18686
|
+
blockId: string(),
|
|
18687
|
+
enabled: boolean()
|
|
18688
|
+
}), object({ block: CoreBlockSchema }), {
|
|
18689
|
+
kind: "mutation",
|
|
18690
|
+
auth: "admin"
|
|
18691
|
+
}), method(object({ code: string() }), CoreBlockCompileResultSchema, {
|
|
18692
|
+
kind: "mutation",
|
|
18693
|
+
auth: "admin"
|
|
18694
|
+
}), method(object({}), object({ libs: array(object({
|
|
18695
|
+
filePath: string(),
|
|
18696
|
+
content: string()
|
|
18697
|
+
})) }), { auth: "admin" });
|
|
18698
|
+
/**
|
|
18330
18699
|
* Zod schemas for persisted record types.
|
|
18331
18700
|
*
|
|
18332
18701
|
* These schemas serve as the single source of truth for types that are
|
|
@@ -18573,6 +18942,11 @@ var EventKindDescriptorSchema = object({
|
|
|
18573
18942
|
deviceId: number()
|
|
18574
18943
|
})
|
|
18575
18944
|
});
|
|
18945
|
+
/** One camera's event vocabulary, as returned by `listEventKindsBatch`. */
|
|
18946
|
+
var EventKindsForDeviceSchema = object({
|
|
18947
|
+
deviceId: number(),
|
|
18948
|
+
kinds: array(EventKindDescriptorSchema).readonly()
|
|
18949
|
+
});
|
|
18576
18950
|
var SensorEventSchema = object({
|
|
18577
18951
|
id: string(),
|
|
18578
18952
|
/** The CAMERA the event is attributed to (a sensor linked to N cameras
|
|
@@ -18829,6 +19203,19 @@ var MediaFileSchema = object({
|
|
|
18829
19203
|
sizeBytes: number(),
|
|
18830
19204
|
timestamp: number()
|
|
18831
19205
|
});
|
|
19206
|
+
/**
|
|
19207
|
+
* One media row WITHOUT its bytes.
|
|
19208
|
+
*
|
|
19209
|
+
* A track's media is 5-8 MB of base64 (measured: 7.67 MB across 23 files for a
|
|
19210
|
+
* 140 s track), and a client that renders tiles from the media data plane needs
|
|
19211
|
+
* to know only WHAT EXISTS — the bytes then arrive per tile, lazily, over HTTP
|
|
19212
|
+
* with an immutable cache, instead of all at once inside a tRPC response that
|
|
19213
|
+
* blocks the whole view.
|
|
19214
|
+
*
|
|
19215
|
+
* `sizeBytes` is carried because it is what lets a client decide between the
|
|
19216
|
+
* stored blob and a `?variant=thumb` rendering without fetching either.
|
|
19217
|
+
*/
|
|
19218
|
+
var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
|
|
18832
19219
|
var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
|
|
18833
19220
|
var MAX_EVENT_QUERY_LIMIT = 5e3;
|
|
18834
19221
|
var DeviceEventQueryInput = object({
|
|
@@ -18978,7 +19365,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18978
19365
|
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
|
|
18979
19366
|
kind: "mutation",
|
|
18980
19367
|
auth: "admin"
|
|
18981
|
-
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
|
|
19368
|
+
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
18982
19369
|
deviceId: number(),
|
|
18983
19370
|
since: number().optional(),
|
|
18984
19371
|
until: number().optional(),
|
|
@@ -19052,7 +19439,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19052
19439
|
}), array(MediaFileSchema).readonly()), method(object({
|
|
19053
19440
|
trackId: string(),
|
|
19054
19441
|
kinds: array(MediaFileKindEnum).optional()
|
|
19055
|
-
}), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
19442
|
+
}), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
19056
19443
|
deviceId: number(),
|
|
19057
19444
|
timestamp: number(),
|
|
19058
19445
|
frameWidth: number(),
|
|
@@ -23833,6 +24220,54 @@ Object.freeze({
|
|
|
23833
24220
|
addonId: null,
|
|
23834
24221
|
access: "create"
|
|
23835
24222
|
},
|
|
24223
|
+
"coreBlocks.compile": {
|
|
24224
|
+
capName: "core-blocks",
|
|
24225
|
+
capScope: "system",
|
|
24226
|
+
addonId: null,
|
|
24227
|
+
access: "create"
|
|
24228
|
+
},
|
|
24229
|
+
"coreBlocks.create": {
|
|
24230
|
+
capName: "core-blocks",
|
|
24231
|
+
capScope: "system",
|
|
24232
|
+
addonId: null,
|
|
24233
|
+
access: "create"
|
|
24234
|
+
},
|
|
24235
|
+
"coreBlocks.delete": {
|
|
24236
|
+
capName: "core-blocks",
|
|
24237
|
+
capScope: "system",
|
|
24238
|
+
addonId: null,
|
|
24239
|
+
access: "delete"
|
|
24240
|
+
},
|
|
24241
|
+
"coreBlocks.get": {
|
|
24242
|
+
capName: "core-blocks",
|
|
24243
|
+
capScope: "system",
|
|
24244
|
+
addonId: null,
|
|
24245
|
+
access: "view"
|
|
24246
|
+
},
|
|
24247
|
+
"coreBlocks.getTypeDefs": {
|
|
24248
|
+
capName: "core-blocks",
|
|
24249
|
+
capScope: "system",
|
|
24250
|
+
addonId: null,
|
|
24251
|
+
access: "view"
|
|
24252
|
+
},
|
|
24253
|
+
"coreBlocks.list": {
|
|
24254
|
+
capName: "core-blocks",
|
|
24255
|
+
capScope: "system",
|
|
24256
|
+
addonId: null,
|
|
24257
|
+
access: "view"
|
|
24258
|
+
},
|
|
24259
|
+
"coreBlocks.setEnabled": {
|
|
24260
|
+
capName: "core-blocks",
|
|
24261
|
+
capScope: "system",
|
|
24262
|
+
addonId: null,
|
|
24263
|
+
access: "create"
|
|
24264
|
+
},
|
|
24265
|
+
"coreBlocks.update": {
|
|
24266
|
+
capName: "core-blocks",
|
|
24267
|
+
capScope: "system",
|
|
24268
|
+
addonId: null,
|
|
24269
|
+
access: "create"
|
|
24270
|
+
},
|
|
23836
24271
|
"cover.close": {
|
|
23837
24272
|
capName: "cover",
|
|
23838
24273
|
capScope: "device",
|
|
@@ -23887,6 +24322,12 @@ Object.freeze({
|
|
|
23887
24322
|
addonId: null,
|
|
23888
24323
|
access: "delete"
|
|
23889
24324
|
},
|
|
24325
|
+
"dataStoreProvider.deleteWhere": {
|
|
24326
|
+
capName: "data-store-provider",
|
|
24327
|
+
capScope: "system",
|
|
24328
|
+
addonId: null,
|
|
24329
|
+
access: "delete"
|
|
24330
|
+
},
|
|
23890
24331
|
"dataStoreProvider.get": {
|
|
23891
24332
|
capName: "data-store-provider",
|
|
23892
24333
|
capScope: "system",
|
|
@@ -23935,6 +24376,12 @@ Object.freeze({
|
|
|
23935
24376
|
addonId: null,
|
|
23936
24377
|
access: "create"
|
|
23937
24378
|
},
|
|
24379
|
+
"dataStoreProvider.updateWhere": {
|
|
24380
|
+
capName: "data-store-provider",
|
|
24381
|
+
capScope: "system",
|
|
24382
|
+
addonId: null,
|
|
24383
|
+
access: "create"
|
|
24384
|
+
},
|
|
23938
24385
|
"dayNight.getOptions": {
|
|
23939
24386
|
capName: "day-night",
|
|
23940
24387
|
capScope: "device",
|
|
@@ -25729,12 +26176,24 @@ Object.freeze({
|
|
|
25729
26176
|
addonId: null,
|
|
25730
26177
|
access: "create"
|
|
25731
26178
|
},
|
|
26179
|
+
"notificationRules.cancelSnooze": {
|
|
26180
|
+
capName: "notification-rules",
|
|
26181
|
+
capScope: "system",
|
|
26182
|
+
addonId: null,
|
|
26183
|
+
access: "create"
|
|
26184
|
+
},
|
|
25732
26185
|
"notificationRules.createRule": {
|
|
25733
26186
|
capName: "notification-rules",
|
|
25734
26187
|
capScope: "system",
|
|
25735
26188
|
addonId: null,
|
|
25736
26189
|
access: "create"
|
|
25737
26190
|
},
|
|
26191
|
+
"notificationRules.createSnooze": {
|
|
26192
|
+
capName: "notification-rules",
|
|
26193
|
+
capScope: "system",
|
|
26194
|
+
addonId: null,
|
|
26195
|
+
access: "create"
|
|
26196
|
+
},
|
|
25738
26197
|
"notificationRules.deleteRule": {
|
|
25739
26198
|
capName: "notification-rules",
|
|
25740
26199
|
capScope: "system",
|
|
@@ -25765,6 +26224,12 @@ Object.freeze({
|
|
|
25765
26224
|
addonId: null,
|
|
25766
26225
|
access: "view"
|
|
25767
26226
|
},
|
|
26227
|
+
"notificationRules.listSnoozes": {
|
|
26228
|
+
capName: "notification-rules",
|
|
26229
|
+
capScope: "system",
|
|
26230
|
+
addonId: null,
|
|
26231
|
+
access: "view"
|
|
26232
|
+
},
|
|
25768
26233
|
"notificationRules.setRuleEnabled": {
|
|
25769
26234
|
capName: "notification-rules",
|
|
25770
26235
|
capScope: "system",
|
|
@@ -25969,6 +26434,12 @@ Object.freeze({
|
|
|
25969
26434
|
addonId: null,
|
|
25970
26435
|
access: "view"
|
|
25971
26436
|
},
|
|
26437
|
+
"pipelineAnalytics.listEventKindsBatch": {
|
|
26438
|
+
capName: "pipeline-analytics",
|
|
26439
|
+
capScope: "device",
|
|
26440
|
+
addonId: null,
|
|
26441
|
+
access: "view"
|
|
26442
|
+
},
|
|
25972
26443
|
"pipelineAnalytics.listOpsLog": {
|
|
25973
26444
|
capName: "pipeline-analytics",
|
|
25974
26445
|
capScope: "device",
|
|
@@ -25981,6 +26452,12 @@ Object.freeze({
|
|
|
25981
26452
|
addonId: null,
|
|
25982
26453
|
access: "view"
|
|
25983
26454
|
},
|
|
26455
|
+
"pipelineAnalytics.listTrackMedia": {
|
|
26456
|
+
capName: "pipeline-analytics",
|
|
26457
|
+
capScope: "device",
|
|
26458
|
+
addonId: null,
|
|
26459
|
+
access: "view"
|
|
26460
|
+
},
|
|
25984
26461
|
"pipelineAnalytics.listTracks": {
|
|
25985
26462
|
capName: "pipeline-analytics",
|
|
25986
26463
|
capScope: "device",
|
|
@@ -27013,6 +27490,12 @@ Object.freeze({
|
|
|
27013
27490
|
addonId: null,
|
|
27014
27491
|
access: "delete"
|
|
27015
27492
|
},
|
|
27493
|
+
"settingsStore.deleteWhere": {
|
|
27494
|
+
capName: "settings-store",
|
|
27495
|
+
capScope: "system",
|
|
27496
|
+
addonId: null,
|
|
27497
|
+
access: "delete"
|
|
27498
|
+
},
|
|
27016
27499
|
"settingsStore.get": {
|
|
27017
27500
|
capName: "settings-store",
|
|
27018
27501
|
capScope: "system",
|
|
@@ -27055,6 +27538,12 @@ Object.freeze({
|
|
|
27055
27538
|
addonId: null,
|
|
27056
27539
|
access: "create"
|
|
27057
27540
|
},
|
|
27541
|
+
"settingsStore.updateWhere": {
|
|
27542
|
+
capName: "settings-store",
|
|
27543
|
+
capScope: "system",
|
|
27544
|
+
addonId: null,
|
|
27545
|
+
access: "create"
|
|
27546
|
+
},
|
|
27058
27547
|
"smtpProvider.getStatus": {
|
|
27059
27548
|
capName: "smtp-provider",
|
|
27060
27549
|
capScope: "system",
|