@camstack/addon-post-analysis 1.2.320 → 1.2.321
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/{clip-model-registry-C_aKxHpy.js → clip-model-registry-BCx3qCL5.js} +89 -0
- package/dist/{clip-model-registry-C-IEAIQQ.mjs → clip-model-registry-Dnm9dfy0.mjs} +89 -0
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-D553qWb_.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CPcZN3mA.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CGUnHrR_.mjs +26 -0
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CVH5tT7I.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-ByDyL2zi.mjs → hostInit-Dt-jaGuG.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +648 -48
- package/dist/pipeline-analytics/index.mjs +648 -48
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-7e_3hsiC.mjs +0 -26
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C1V_UEyk.mjs +0 -26
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-BCx3qCL5.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -5448,7 +5448,7 @@ function isAdoptable(record, nowMs) {
|
|
|
5448
5448
|
* what `localDayMinute` does for the same input — one behaviour for a bad
|
|
5449
5449
|
* zone, not two.
|
|
5450
5450
|
*/
|
|
5451
|
-
function clockOf$
|
|
5451
|
+
function clockOf$6(atMs, timeZone) {
|
|
5452
5452
|
if (timeZone !== void 0) try {
|
|
5453
5453
|
return new Intl.DateTimeFormat("en-GB", {
|
|
5454
5454
|
timeZone,
|
|
@@ -5584,7 +5584,7 @@ var TimelapseRenderer = class {
|
|
|
5584
5584
|
includeAudio: false,
|
|
5585
5585
|
maxLifeMs: await this.ports.exportLifeMs(),
|
|
5586
5586
|
deleteAfterDownload: false,
|
|
5587
|
-
title: `${request.ruleName} ${clockOf$
|
|
5587
|
+
title: `${request.ruleName} ${clockOf$6(window.startMs, request.timezone)}→${clockOf$6(window.endMs, request.timezone)}`,
|
|
5588
5588
|
timelapse: {
|
|
5589
5589
|
everyMs,
|
|
5590
5590
|
outputFps: request.framerate,
|
|
@@ -5915,7 +5915,7 @@ var TimelapseRenderer = class {
|
|
|
5915
5915
|
...meta,
|
|
5916
5916
|
unservedWindowCount: verdict.unservedWindows.length,
|
|
5917
5917
|
unservedWindows: verdict.unservedWindows.slice(0, 12).map((w) => ({
|
|
5918
|
-
at: clockOf$
|
|
5918
|
+
at: clockOf$6(request.window.startMs + w.fromSec * 1e3, request.timezone),
|
|
5919
5919
|
fromSec: Math.round(w.fromSec),
|
|
5920
5920
|
toSec: Math.round(w.toSec),
|
|
5921
5921
|
expectedDenseFrames: w.expectedDenseFrames,
|
|
@@ -13900,7 +13900,7 @@ var NcPruneRecordSchema = require_clip_model_registry.object({
|
|
|
13900
13900
|
source: require_clip_model_registry._enum(["event", "reconcile"])
|
|
13901
13901
|
});
|
|
13902
13902
|
/** Rules × deleted subjects — small; a generous ceiling. */
|
|
13903
|
-
var LOAD_LIMIT$
|
|
13903
|
+
var LOAD_LIMIT$5 = 1e4;
|
|
13904
13904
|
function keyOf$1(row) {
|
|
13905
13905
|
return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
|
|
13906
13906
|
}
|
|
@@ -13919,7 +13919,7 @@ var NC_DEVICE_PRUNES_SPEC = {
|
|
|
13919
13919
|
return parsed.success ? parsed.data : null;
|
|
13920
13920
|
},
|
|
13921
13921
|
deviceIdOf: (row) => row.subjectKind === "device" ? Number(row.subject) : void 0,
|
|
13922
|
-
loadLimit: LOAD_LIMIT$
|
|
13922
|
+
loadLimit: LOAD_LIMIT$5
|
|
13923
13923
|
};
|
|
13924
13924
|
/** The outcomes that mean "this last reference was already dealt with". */
|
|
13925
13925
|
var KEPT_OUTCOMES = new Set(["kept-disabled", "kept"]);
|
|
@@ -13954,7 +13954,7 @@ var NcPruneLedger = class {
|
|
|
13954
13954
|
try {
|
|
13955
13955
|
const records = await this.store.query.query({
|
|
13956
13956
|
collection: NC_DEVICE_PRUNES_COLLECTION,
|
|
13957
|
-
filter: { limit: LOAD_LIMIT$
|
|
13957
|
+
filter: { limit: LOAD_LIMIT$5 }
|
|
13958
13958
|
});
|
|
13959
13959
|
const next = /* @__PURE__ */ new Map();
|
|
13960
13960
|
for (const record of records) {
|
|
@@ -17279,12 +17279,12 @@ var NcSummaryTemplateSchema = require_clip_model_registry.object({
|
|
|
17279
17279
|
title: require_clip_model_registry.string().max(500).optional(),
|
|
17280
17280
|
body: require_clip_model_registry.string().max(2e3).optional()
|
|
17281
17281
|
});
|
|
17282
|
-
var NameField = require_clip_model_registry.string().min(1).max(200);
|
|
17282
|
+
var NameField$1 = require_clip_model_registry.string().min(1).max(200);
|
|
17283
17283
|
/** MULTI-CAMERA is the point of the feature — a one-camera digest is a
|
|
17284
17284
|
* timelapse with extra steps, but it is allowed and it is cheap. */
|
|
17285
17285
|
var DeviceIdsField = require_clip_model_registry.array(require_clip_model_registry.number()).min(1).max(64);
|
|
17286
|
-
var TargetsField = require_clip_model_registry.array(require_clip_model_registry.NcRuleTargetSchema).min(1);
|
|
17287
|
-
var PriorityField = require_clip_model_registry.number().int().min(1).max(5);
|
|
17286
|
+
var TargetsField$1 = require_clip_model_registry.array(require_clip_model_registry.NcRuleTargetSchema).min(1);
|
|
17287
|
+
var PriorityField$1 = require_clip_model_registry.number().int().min(1).max(5);
|
|
17288
17288
|
/**
|
|
17289
17289
|
* The mosaic's tile ceiling. 9 (a 3×3 grid) is the shipped default: it is the
|
|
17290
17290
|
* most a phone lock-screen thumbnail can carry and still be read, and it is the
|
|
@@ -17449,7 +17449,7 @@ var NcSummaryAiSchema = require_clip_model_registry.object({
|
|
|
17449
17449
|
onFailure: require_clip_model_registry._enum(["deliver-without-text", "skip"]).optional()
|
|
17450
17450
|
});
|
|
17451
17451
|
var NcSummaryRuleInputSchema = require_clip_model_registry.object({
|
|
17452
|
-
name: NameField,
|
|
17452
|
+
name: NameField$1,
|
|
17453
17453
|
enabled: require_clip_model_registry.boolean().default(true),
|
|
17454
17454
|
deviceIds: DeviceIdsField,
|
|
17455
17455
|
window: NcSummaryWindowSchema,
|
|
@@ -17458,7 +17458,7 @@ var NcSummaryRuleInputSchema = require_clip_model_registry.object({
|
|
|
17458
17458
|
maxAiImages: MaxAiImagesField.default(NC_SUMMARY_DEFAULT_AI_IMAGES),
|
|
17459
17459
|
rank: NcSummaryRankSchema.default("importance"),
|
|
17460
17460
|
barrierSeconds: BarrierSecondsField.default(120),
|
|
17461
|
-
targets: TargetsField,
|
|
17461
|
+
targets: TargetsField$1,
|
|
17462
17462
|
template: NcSummaryTemplateSchema.optional(),
|
|
17463
17463
|
/** Burned onto the mosaic's title band — and ONLY when `captions` is `all`. */
|
|
17464
17464
|
captions: NcSummaryCaptionsSchema.default(NC_SUMMARY_DEFAULT_CAPTIONS),
|
|
@@ -17468,7 +17468,7 @@ var NcSummaryRuleInputSchema = require_clip_model_registry.object({
|
|
|
17468
17468
|
/** Deliver a window that matched NOTHING? Default no: a nightly "0 events"
|
|
17469
17469
|
* is a line nobody reads, and it costs the lines beside it their reader. */
|
|
17470
17470
|
deliverEmpty: require_clip_model_registry.boolean().default(false),
|
|
17471
|
-
priority: PriorityField.default(3),
|
|
17471
|
+
priority: PriorityField$1.default(3),
|
|
17472
17472
|
ai: NcSummaryAiSchema.optional()
|
|
17473
17473
|
});
|
|
17474
17474
|
/**
|
|
@@ -17480,7 +17480,7 @@ var NcSummaryRuleInputSchema = require_clip_model_registry.object({
|
|
|
17480
17480
|
* plain `.optional()`.
|
|
17481
17481
|
*/
|
|
17482
17482
|
var NcSummaryRulePatchSchema = require_clip_model_registry.object({
|
|
17483
|
-
name: NameField.optional(),
|
|
17483
|
+
name: NameField$1.optional(),
|
|
17484
17484
|
enabled: require_clip_model_registry.boolean().optional(),
|
|
17485
17485
|
deviceIds: DeviceIdsField.optional(),
|
|
17486
17486
|
window: NcSummaryWindowSchema.optional(),
|
|
@@ -17489,12 +17489,12 @@ var NcSummaryRulePatchSchema = require_clip_model_registry.object({
|
|
|
17489
17489
|
maxAiImages: MaxAiImagesField.optional(),
|
|
17490
17490
|
rank: NcSummaryRankSchema.optional(),
|
|
17491
17491
|
barrierSeconds: BarrierSecondsField.optional(),
|
|
17492
|
-
targets: TargetsField.optional(),
|
|
17492
|
+
targets: TargetsField$1.optional(),
|
|
17493
17493
|
template: NcSummaryTemplateSchema.nullable().optional(),
|
|
17494
17494
|
captions: NcSummaryCaptionsSchema.optional(),
|
|
17495
17495
|
captionText: require_clip_model_registry.string().max(200).optional(),
|
|
17496
17496
|
deliverEmpty: require_clip_model_registry.boolean().optional(),
|
|
17497
|
-
priority: PriorityField.optional(),
|
|
17497
|
+
priority: PriorityField$1.optional(),
|
|
17498
17498
|
ai: NcSummaryAiSchema.nullable().optional()
|
|
17499
17499
|
});
|
|
17500
17500
|
/**
|
|
@@ -17527,6 +17527,89 @@ function assertSummaryBudget(rule) {
|
|
|
17527
17527
|
if (rule.maxAiImages > rule.maxTiles) throw new Error(`maxAiImages (${rule.maxAiImages}) cannot exceed maxTiles (${rule.maxTiles}) — the model cannot judge frames the mosaic does not contain`);
|
|
17528
17528
|
}
|
|
17529
17529
|
//#endregion
|
|
17530
|
+
//#region src/notification-center/calendar-events/calendar-events-window.ts
|
|
17531
|
+
/** One day in milliseconds. */
|
|
17532
|
+
var CALENDAR_EVENTS_DAY_MS = 1440 * 60 * 1e3;
|
|
17533
|
+
/**
|
|
17534
|
+
* Floor / ceiling on the horizon days a rule may declare.
|
|
17535
|
+
* 1 day is the minimum useful digest; 30 keeps a single notification bounded.
|
|
17536
|
+
*/
|
|
17537
|
+
var CALENDAR_EVENTS_MIN_HORIZON_DAYS = 1;
|
|
17538
|
+
var CALENDAR_EVENTS_MAX_HORIZON_DAYS = 30;
|
|
17539
|
+
var CALENDAR_EVENTS_DEFAULT_HORIZON_DAYS = 7;
|
|
17540
|
+
/**
|
|
17541
|
+
* Build the producer window for one fire.
|
|
17542
|
+
*
|
|
17543
|
+
* `horizonDays` is clamped into the legal range rather than refused — a
|
|
17544
|
+
* stored rule that predates a tighter bound must still fire.
|
|
17545
|
+
*/
|
|
17546
|
+
function calendarEventsWindow(nowMs, horizonDays) {
|
|
17547
|
+
const days = Math.min(CALENDAR_EVENTS_MAX_HORIZON_DAYS, Math.max(CALENDAR_EVENTS_MIN_HORIZON_DAYS, Math.floor(horizonDays)));
|
|
17548
|
+
return {
|
|
17549
|
+
startMs: nowMs,
|
|
17550
|
+
endMs: nowMs + days * CALENDAR_EVENTS_DAY_MS,
|
|
17551
|
+
horizonDays: days
|
|
17552
|
+
};
|
|
17553
|
+
}
|
|
17554
|
+
//#endregion
|
|
17555
|
+
//#region src/notification-center/calendar-events/calendar-events-rule.ts
|
|
17556
|
+
/**
|
|
17557
|
+
* NcCalendarEventsRule — scheduled "upcoming calendar events" digest.
|
|
17558
|
+
*
|
|
17559
|
+
* Same ownership model as Summaries/Timelapses: lives in the addon (not
|
|
17560
|
+
* `@camstack/types`) so a field is live on the next post-analysis deploy.
|
|
17561
|
+
* Rides no `NcDelivery` member; reaches the outside world through
|
|
17562
|
+
* `nc.*CalendarEvents*` bridge actions.
|
|
17563
|
+
*
|
|
17564
|
+
* Calendar selection mirrors detection classes: `calendars` = Only,
|
|
17565
|
+
* `calendarsExclude` = Except, both empty = All.
|
|
17566
|
+
*/
|
|
17567
|
+
var NcCalendarEventsTemplateSchema = require_clip_model_registry.object({
|
|
17568
|
+
title: require_clip_model_registry.string().max(500).optional(),
|
|
17569
|
+
body: require_clip_model_registry.string().max(4e3).optional()
|
|
17570
|
+
});
|
|
17571
|
+
var NameField = require_clip_model_registry.string().min(1).max(200);
|
|
17572
|
+
var TargetsField = require_clip_model_registry.array(require_clip_model_registry.NcRuleTargetSchema).min(1);
|
|
17573
|
+
var PriorityField = require_clip_model_registry.number().int().min(1).max(5);
|
|
17574
|
+
var HorizonDaysField = require_clip_model_registry.number().int().min(CALENDAR_EVENTS_MIN_HORIZON_DAYS).max(CALENDAR_EVENTS_MAX_HORIZON_DAYS);
|
|
17575
|
+
var CalendarIdsField = require_clip_model_registry.array(require_clip_model_registry.string().min(1).max(200)).max(64);
|
|
17576
|
+
var NcCalendarEventsRuleInputSchema = require_clip_model_registry.object({
|
|
17577
|
+
name: NameField,
|
|
17578
|
+
enabled: require_clip_model_registry.boolean().default(true),
|
|
17579
|
+
schedule: require_clip_model_registry.NcScheduleSchema,
|
|
17580
|
+
/** Only these calendars. Empty/absent with no exclude = All. */
|
|
17581
|
+
calendars: CalendarIdsField.optional(),
|
|
17582
|
+
/** All except these. Ignored when `calendars` is non-empty. */
|
|
17583
|
+
calendarsExclude: CalendarIdsField.optional(),
|
|
17584
|
+
horizonDays: HorizonDaysField.default(CALENDAR_EVENTS_DEFAULT_HORIZON_DAYS),
|
|
17585
|
+
/** Deliver when the horizon holds no events? Default no. */
|
|
17586
|
+
deliverEmpty: require_clip_model_registry.boolean().default(false),
|
|
17587
|
+
targets: TargetsField,
|
|
17588
|
+
template: NcCalendarEventsTemplateSchema.optional(),
|
|
17589
|
+
priority: PriorityField.default(3)
|
|
17590
|
+
});
|
|
17591
|
+
var NcCalendarEventsRulePatchSchema = require_clip_model_registry.object({
|
|
17592
|
+
name: NameField.optional(),
|
|
17593
|
+
enabled: require_clip_model_registry.boolean().optional(),
|
|
17594
|
+
schedule: require_clip_model_registry.NcScheduleSchema.optional(),
|
|
17595
|
+
calendars: CalendarIdsField.nullable().optional(),
|
|
17596
|
+
calendarsExclude: CalendarIdsField.nullable().optional(),
|
|
17597
|
+
horizonDays: HorizonDaysField.optional(),
|
|
17598
|
+
deliverEmpty: require_clip_model_registry.boolean().optional(),
|
|
17599
|
+
targets: TargetsField.optional(),
|
|
17600
|
+
template: NcCalendarEventsTemplateSchema.nullable().optional(),
|
|
17601
|
+
priority: PriorityField.optional()
|
|
17602
|
+
});
|
|
17603
|
+
var NcCalendarEventsRuleSchema = NcCalendarEventsRuleInputSchema.extend({
|
|
17604
|
+
id: require_clip_model_registry.string(),
|
|
17605
|
+
ownerUserId: require_clip_model_registry.string().optional(),
|
|
17606
|
+
/** Epoch-ms of the schedule window end that was last delivered. */
|
|
17607
|
+
generatedAt: require_clip_model_registry.number().optional(),
|
|
17608
|
+
createdBy: require_clip_model_registry.string(),
|
|
17609
|
+
createdAt: require_clip_model_registry.number(),
|
|
17610
|
+
updatedAt: require_clip_model_registry.number()
|
|
17611
|
+
});
|
|
17612
|
+
//#endregion
|
|
17530
17613
|
//#region src/notification-center/unstored-media-id.ts
|
|
17531
17614
|
/**
|
|
17532
17615
|
* An id for media bytes that were NEVER STORED.
|
|
@@ -18687,6 +18770,22 @@ var ncActions = require_clip_model_registry.defineCustomActions({
|
|
|
18687
18770
|
kind: "mutation",
|
|
18688
18771
|
caller: "required"
|
|
18689
18772
|
}),
|
|
18773
|
+
"nc.listCalendarEventsRules": require_clip_model_registry.customAction(require_clip_model_registry.object({}), require_clip_model_registry.object({
|
|
18774
|
+
rules: require_clip_model_registry.array(NcCalendarEventsRuleSchema.extend({ readOnly: require_clip_model_registry.boolean() })),
|
|
18775
|
+
canAuthor: require_clip_model_registry.boolean()
|
|
18776
|
+
}), { caller: "required" }),
|
|
18777
|
+
"nc.upsertCalendarEventsRule": require_clip_model_registry.customAction(require_clip_model_registry.object({
|
|
18778
|
+
ruleId: require_clip_model_registry.string().optional(),
|
|
18779
|
+
rule: NcCalendarEventsRuleInputSchema.optional(),
|
|
18780
|
+
patch: NcCalendarEventsRulePatchSchema.optional()
|
|
18781
|
+
}), require_clip_model_registry.object({ rule: NcCalendarEventsRuleSchema }), {
|
|
18782
|
+
kind: "mutation",
|
|
18783
|
+
caller: "required"
|
|
18784
|
+
}),
|
|
18785
|
+
"nc.deleteCalendarEventsRule": require_clip_model_registry.customAction(require_clip_model_registry.object({ ruleId: require_clip_model_registry.string() }), require_clip_model_registry.object({ success: require_clip_model_registry.literal(true) }), {
|
|
18786
|
+
kind: "mutation",
|
|
18787
|
+
caller: "required"
|
|
18788
|
+
}),
|
|
18690
18789
|
/**
|
|
18691
18790
|
* Produce one digest window RIGHT NOW — the operator's Test button.
|
|
18692
18791
|
*
|
|
@@ -18973,6 +19072,11 @@ function makeNcActionHandlers(deps) {
|
|
|
18973
19072
|
if (sum === void 0) throw new Error("this node does not host the summary producer — use the hub");
|
|
18974
19073
|
return sum;
|
|
18975
19074
|
};
|
|
19075
|
+
const requireCalendarEvents = () => {
|
|
19076
|
+
const ce = deps.calendarEvents;
|
|
19077
|
+
if (ce === void 0) throw new Error("this node does not host calendar-events rules — use the hub");
|
|
19078
|
+
return ce;
|
|
19079
|
+
};
|
|
18976
19080
|
/** The artefact index, or an honest refusal. */
|
|
18977
19081
|
const requireArtifacts = () => {
|
|
18978
19082
|
const index = deps.artifacts;
|
|
@@ -19001,6 +19105,13 @@ function makeNcActionHandlers(deps) {
|
|
|
19001
19105
|
if (rule.ownerUserId !== caller.userId) throw new Error(`forbidden: summary rule not owned: ${ruleId}`);
|
|
19002
19106
|
return rule;
|
|
19003
19107
|
};
|
|
19108
|
+
const assertCalendarEventsEditable = (ruleId, caller, ce) => {
|
|
19109
|
+
const rule = ce.store.get(ruleId);
|
|
19110
|
+
if (rule === null) throw new Error(`forbidden: calendar-events rule not found: ${ruleId}`);
|
|
19111
|
+
if (caller.isAdmin) return rule;
|
|
19112
|
+
if (rule.ownerUserId !== caller.userId) throw new Error(`forbidden: calendar-events rule not owned: ${ruleId}`);
|
|
19113
|
+
return rule;
|
|
19114
|
+
};
|
|
19004
19115
|
/** A timelapse rule the caller may EDIT. Same admin bypass as `NcRule`. */
|
|
19005
19116
|
const assertTimelapseEditable = (ruleId, caller, tl) => {
|
|
19006
19117
|
const rule = tl.store.get(ruleId);
|
|
@@ -19542,6 +19653,53 @@ function makeNcActionHandlers(deps) {
|
|
|
19542
19653
|
...out.skippedByAi === true ? { skippedByAi: true } : {}
|
|
19543
19654
|
};
|
|
19544
19655
|
},
|
|
19656
|
+
"nc.listCalendarEventsRules": async (_input, caller) => {
|
|
19657
|
+
const c = requireCaller$1(caller);
|
|
19658
|
+
const ce = requireCalendarEvents();
|
|
19659
|
+
return {
|
|
19660
|
+
rules: (c.isAdmin ? ce.store.list() : ce.store.listForOwner(c.userId)).map((r) => ({
|
|
19661
|
+
...r,
|
|
19662
|
+
readOnly: !c.isAdmin && r.ownerUserId !== c.userId
|
|
19663
|
+
})),
|
|
19664
|
+
canAuthor: true
|
|
19665
|
+
};
|
|
19666
|
+
},
|
|
19667
|
+
"nc.upsertCalendarEventsRule": async (input, caller) => {
|
|
19668
|
+
const c = requireCaller$1(caller);
|
|
19669
|
+
const ce = requireCalendarEvents();
|
|
19670
|
+
if (input.ruleId === void 0) {
|
|
19671
|
+
if (input.rule === void 0) throw new Error("upsertCalendarEventsRule: rule or ruleId required");
|
|
19672
|
+
const parsed = NcCalendarEventsRuleInputSchema.parse(input.rule);
|
|
19673
|
+
await assertTargetsOwned(parsed.targets.map((t) => t.targetId), c);
|
|
19674
|
+
const rule = await ce.store.create(parsed, c.userId, c.isAdmin ? void 0 : c.userId);
|
|
19675
|
+
deps.logger.info("calendar-events rule created", { meta: {
|
|
19676
|
+
ruleId: rule.id,
|
|
19677
|
+
owner: c.userId
|
|
19678
|
+
} });
|
|
19679
|
+
return { rule };
|
|
19680
|
+
}
|
|
19681
|
+
assertCalendarEventsEditable(input.ruleId, c, ce);
|
|
19682
|
+
if (input.patch === void 0) throw new Error("upsertCalendarEventsRule: patch required");
|
|
19683
|
+
const patch = NcCalendarEventsRulePatchSchema.parse(input.patch);
|
|
19684
|
+
if (patch.targets !== void 0) await assertTargetsOwned(patch.targets.map((t) => t.targetId), c);
|
|
19685
|
+
const rule = await ce.store.update(input.ruleId, patch);
|
|
19686
|
+
deps.logger.info("calendar-events rule updated", { meta: {
|
|
19687
|
+
ruleId: rule.id,
|
|
19688
|
+
owner: c.userId
|
|
19689
|
+
} });
|
|
19690
|
+
return { rule };
|
|
19691
|
+
},
|
|
19692
|
+
"nc.deleteCalendarEventsRule": async (input, caller) => {
|
|
19693
|
+
const c = requireCaller$1(caller);
|
|
19694
|
+
const ce = requireCalendarEvents();
|
|
19695
|
+
assertCalendarEventsEditable(input.ruleId, c, ce);
|
|
19696
|
+
await ce.store.delete(input.ruleId);
|
|
19697
|
+
deps.logger.info("calendar-events rule deleted", { meta: {
|
|
19698
|
+
ruleId: input.ruleId,
|
|
19699
|
+
owner: c.userId
|
|
19700
|
+
} });
|
|
19701
|
+
return { success: true };
|
|
19702
|
+
},
|
|
19545
19703
|
"nc.getTexts": async (input) => {
|
|
19546
19704
|
const texts = requireTexts();
|
|
19547
19705
|
return input.language === void 0 ? texts.view() : texts.view(input.language);
|
|
@@ -28405,7 +28563,8 @@ var OUTBOX_RECORD_KINDS = new Set([
|
|
|
28405
28563
|
"audio-window",
|
|
28406
28564
|
"system-event",
|
|
28407
28565
|
"timelapse",
|
|
28408
|
-
"ai-summary"
|
|
28566
|
+
"ai-summary",
|
|
28567
|
+
"calendar-events"
|
|
28409
28568
|
]);
|
|
28410
28569
|
function isOutboxRecordKind(x) {
|
|
28411
28570
|
return typeof x === "string" && OUTBOX_RECORD_KINDS.has(x);
|
|
@@ -28758,7 +28917,7 @@ function buildRuleAiSystemPrompt(shape, language, maxWords, grouped = false) {
|
|
|
28758
28917
|
].join("");
|
|
28759
28918
|
}
|
|
28760
28919
|
/** `HH:MM` in the host timezone. */
|
|
28761
|
-
function clockOf$
|
|
28920
|
+
function clockOf$5(atMs) {
|
|
28762
28921
|
const d = new Date(atMs);
|
|
28763
28922
|
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
28764
28923
|
}
|
|
@@ -28796,7 +28955,7 @@ function buildRuleAiPrompt(input) {
|
|
|
28796
28955
|
if (shape === "detection-track") {
|
|
28797
28956
|
lines.push(`Camera: ${camera}.${located} Subject: ${subject}. The stills, in order:`);
|
|
28798
28957
|
for (const [i, frame] of input.frames.entries()) {
|
|
28799
|
-
const when = frame.atMs !== void 0 ? ` · ${clockOf$
|
|
28958
|
+
const when = frame.atMs !== void 0 ? ` · ${clockOf$5(frame.atMs)}` : "";
|
|
28800
28959
|
lines.push(`${String(i + 1)}.${when}`);
|
|
28801
28960
|
}
|
|
28802
28961
|
return lines.join("\n");
|
|
@@ -29093,7 +29252,7 @@ var NcRuleAiRowSchema = require_clip_model_registry.object({
|
|
|
29093
29252
|
updatedAt: require_clip_model_registry.number()
|
|
29094
29253
|
});
|
|
29095
29254
|
/** Operator-authored and tiny; a generous ceiling. */
|
|
29096
|
-
var LOAD_LIMIT$
|
|
29255
|
+
var LOAD_LIMIT$4 = 1e4;
|
|
29097
29256
|
var NC_RULE_AI_SPEC = {
|
|
29098
29257
|
collection: NC_RULE_AI_COLLECTION,
|
|
29099
29258
|
columns: [...NC_RULE_AI_COLUMNS],
|
|
@@ -29109,7 +29268,7 @@ var NC_RULE_AI_SPEC = {
|
|
|
29109
29268
|
const parsed = NcRuleAiRowSchema.safeParse(data["row"]);
|
|
29110
29269
|
return parsed.success ? parsed.data : null;
|
|
29111
29270
|
},
|
|
29112
|
-
loadLimit: LOAD_LIMIT$
|
|
29271
|
+
loadLimit: LOAD_LIMIT$4
|
|
29113
29272
|
};
|
|
29114
29273
|
var NcRuleAiStore = class {
|
|
29115
29274
|
ledger;
|
|
@@ -29446,7 +29605,7 @@ var NC_RULES_INDEXES = [{
|
|
|
29446
29605
|
columns: ["enabled"]
|
|
29447
29606
|
}];
|
|
29448
29607
|
/** Query cap — the rule set is operator-authored and tiny; a generous ceiling. */
|
|
29449
|
-
var LOAD_LIMIT$
|
|
29608
|
+
var LOAD_LIMIT$3 = 1e4;
|
|
29450
29609
|
function isRecord$3(value) {
|
|
29451
29610
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29452
29611
|
}
|
|
@@ -29495,7 +29654,7 @@ function makeRulesSpec(onProjection) {
|
|
|
29495
29654
|
if (projection.kind === "projected") onProjection?.(key, projection);
|
|
29496
29655
|
return parsed.data;
|
|
29497
29656
|
},
|
|
29498
|
-
loadLimit: LOAD_LIMIT$
|
|
29657
|
+
loadLimit: LOAD_LIMIT$3
|
|
29499
29658
|
};
|
|
29500
29659
|
}
|
|
29501
29660
|
/** The spec `declare` needs — no sink: declaring a collection projects nothing. */
|
|
@@ -30567,7 +30726,7 @@ var DEFAULT_PROMPT_EN = "What happened during this period? Summarise it for a ho
|
|
|
30567
30726
|
*/
|
|
30568
30727
|
var SummaryAnswerSchema = require_clip_model_registry.object({ summary: require_clip_model_registry.string() });
|
|
30569
30728
|
/** `HH:MM` in the host timezone — the two numbers a frame label needs. */
|
|
30570
|
-
function clockOf$
|
|
30729
|
+
function clockOf$4(atMs) {
|
|
30571
30730
|
const d = new Date(atMs);
|
|
30572
30731
|
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
30573
30732
|
}
|
|
@@ -30590,7 +30749,7 @@ function buildSummaryAiPrompt(input, frames) {
|
|
|
30590
30749
|
const roster = frames.map((f, i) => {
|
|
30591
30750
|
const location = plainLabel(f.location ?? "");
|
|
30592
30751
|
const where = location.length > 0 ? ` (${location})` : "";
|
|
30593
|
-
return `${String(i + 1)}. ${plainLabel(f.deviceName)}${where} · ${clockOf$
|
|
30752
|
+
return `${String(i + 1)}. ${plainLabel(f.deviceName)}${where} · ${clockOf$4(f.atMs)} · ${plainVocabulary(f.className) || "subject"}`;
|
|
30594
30753
|
}).join("\n");
|
|
30595
30754
|
const described = /* @__PURE__ */ new Map();
|
|
30596
30755
|
for (const f of frames) {
|
|
@@ -31193,7 +31352,7 @@ var NO_DETECTIONS$1 = {
|
|
|
31193
31352
|
* has to check is a key somebody can get wrong. */
|
|
31194
31353
|
var BODY_SEPARATOR$1 = " · ";
|
|
31195
31354
|
/** `HH:MM` in the host timezone. */
|
|
31196
|
-
function clockOf$
|
|
31355
|
+
function clockOf$3(atMs) {
|
|
31197
31356
|
const d = new Date(atMs);
|
|
31198
31357
|
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
31199
31358
|
}
|
|
@@ -31207,8 +31366,8 @@ function clockOf$2(atMs) {
|
|
|
31207
31366
|
function summaryTemplateVars(input) {
|
|
31208
31367
|
return {
|
|
31209
31368
|
rule: input.ruleName,
|
|
31210
|
-
from: clockOf$
|
|
31211
|
-
to: clockOf$
|
|
31369
|
+
from: clockOf$3(input.window.startMs),
|
|
31370
|
+
to: clockOf$3(input.window.endMs),
|
|
31212
31371
|
time: ncClockText(input.texts.language, input.generatedAt),
|
|
31213
31372
|
events: String(input.tileCount),
|
|
31214
31373
|
cameras: String(input.cameraCount),
|
|
@@ -31252,7 +31411,7 @@ function composeSummaryBody(input) {
|
|
|
31252
31411
|
* string, for the reason the timelapse learned: a hand-rolled `n === 1` shipped
|
|
31253
31412
|
* "1 intervalli ripresi" to a live phone.
|
|
31254
31413
|
*/
|
|
31255
|
-
function derivedBody$
|
|
31414
|
+
function derivedBody$2(input, vars) {
|
|
31256
31415
|
const texts = input.texts;
|
|
31257
31416
|
const parts = [texts.text({
|
|
31258
31417
|
key: "summary.body.window",
|
|
@@ -31287,7 +31446,7 @@ function derivedBody$1(input, vars) {
|
|
|
31287
31446
|
}
|
|
31288
31447
|
/** Per-target params with the derived `clickUrl` filled in where absent. A
|
|
31289
31448
|
* value the operator set on the target WINS — he chose it. */
|
|
31290
|
-
function paramsFor$
|
|
31449
|
+
function paramsFor$2(target, clickUrl) {
|
|
31291
31450
|
if (clickUrl === void 0) return target.params;
|
|
31292
31451
|
if (target.params === void 0) return { clickUrl };
|
|
31293
31452
|
if (typeof target.params["clickUrl"] === "string") return target.params;
|
|
@@ -31303,11 +31462,11 @@ function summaryRecordId(ruleId, windowEndMs) {
|
|
|
31303
31462
|
/** The caption burned onto the mosaic's title band, with the same `{{var}}`
|
|
31304
31463
|
* vocabulary the title and body use. `''` is a real answer ("no caption"). */
|
|
31305
31464
|
function summaryCaptionText(input) {
|
|
31306
|
-
if (input.captionText === void 0) return `${input.ruleName} — ${clockOf$
|
|
31465
|
+
if (input.captionText === void 0) return `${input.ruleName} — ${clockOf$3(input.window.startMs)}→${clockOf$3(input.window.endMs)}`;
|
|
31307
31466
|
return renderTemplate(input.captionText, {
|
|
31308
31467
|
rule: input.ruleName,
|
|
31309
|
-
from: clockOf$
|
|
31310
|
-
to: clockOf$
|
|
31468
|
+
from: clockOf$3(input.window.startMs),
|
|
31469
|
+
to: clockOf$3(input.window.endMs)
|
|
31311
31470
|
}) ?? "";
|
|
31312
31471
|
}
|
|
31313
31472
|
/**
|
|
@@ -31325,7 +31484,7 @@ function buildSummaryOutboxInputs(input) {
|
|
|
31325
31484
|
const body = composeSummaryBody({
|
|
31326
31485
|
aiText: aiSentence(input),
|
|
31327
31486
|
renderedTemplateBody: renderTemplate(input.template?.body, vars),
|
|
31328
|
-
derivedBody: derivedBody$
|
|
31487
|
+
derivedBody: derivedBody$2(input, vars),
|
|
31329
31488
|
templateNamesAi: templatePlaceholders(input.template?.body ?? "").includes("ai")
|
|
31330
31489
|
});
|
|
31331
31490
|
const recordId = summaryRecordId(input.ruleId, input.window.endMs);
|
|
@@ -31344,7 +31503,7 @@ function buildSummaryOutboxInputs(input) {
|
|
|
31344
31503
|
},
|
|
31345
31504
|
media: "none",
|
|
31346
31505
|
...artifacts.length > 0 ? { artifacts } : {},
|
|
31347
|
-
...paramsFor$
|
|
31506
|
+
...paramsFor$2(target, input.clickUrl) !== void 0 ? { params: paramsFor$2(target, input.clickUrl) } : {},
|
|
31348
31507
|
subject: {
|
|
31349
31508
|
deviceId: input.primaryDeviceId,
|
|
31350
31509
|
className: "summary",
|
|
@@ -32239,7 +32398,7 @@ var NcSummaryProducer = class {
|
|
|
32239
32398
|
purpose: "summary",
|
|
32240
32399
|
tiles: tiles.map(({ candidate, jpeg }) => ({
|
|
32241
32400
|
jpeg,
|
|
32242
|
-
...plan.tileLabels ? { label: `${names.get(candidate.deviceId) ?? candidate.deviceId} · ${clockOf$
|
|
32401
|
+
...plan.tileLabels ? { label: `${names.get(candidate.deviceId) ?? candidate.deviceId} · ${clockOf$2(candidate.firstSeen)} · ${candidate.className}` } : {},
|
|
32243
32402
|
deviceId: candidate.deviceId,
|
|
32244
32403
|
sourceId: candidate.trackId
|
|
32245
32404
|
})),
|
|
@@ -32462,7 +32621,7 @@ var NcSummaryProducer = class {
|
|
|
32462
32621
|
}
|
|
32463
32622
|
};
|
|
32464
32623
|
/** `HH:MM` in the host timezone — the two numbers a tile label needs. */
|
|
32465
|
-
function clockOf$
|
|
32624
|
+
function clockOf$2(atMs) {
|
|
32466
32625
|
const d = new Date(atMs);
|
|
32467
32626
|
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
32468
32627
|
}
|
|
@@ -32538,7 +32697,7 @@ var NC_SUMMARY_RULES_INDEXES = [{
|
|
|
32538
32697
|
columns: ["enabled"]
|
|
32539
32698
|
}];
|
|
32540
32699
|
/** The rule set is operator-authored and tiny; a generous ceiling. */
|
|
32541
|
-
var LOAD_LIMIT$
|
|
32700
|
+
var LOAD_LIMIT$2 = 1e4;
|
|
32542
32701
|
/**
|
|
32543
32702
|
* The ledger contract for the digest rule set.
|
|
32544
32703
|
*
|
|
@@ -32568,7 +32727,7 @@ var NC_SUMMARY_RULES_SPEC = {
|
|
|
32568
32727
|
const parsed = NcSummaryRuleSchema.safeParse(data["rule"]);
|
|
32569
32728
|
return parsed.success ? parsed.data : null;
|
|
32570
32729
|
},
|
|
32571
|
-
loadLimit: LOAD_LIMIT$
|
|
32730
|
+
loadLimit: LOAD_LIMIT$2
|
|
32572
32731
|
};
|
|
32573
32732
|
/**
|
|
32574
32733
|
* The three-way CLEARABLE patch signal, one function per field: `undefined`
|
|
@@ -32579,7 +32738,7 @@ var NC_SUMMARY_RULES_SPEC = {
|
|
|
32579
32738
|
* cast here would be a cast on the exact path that decides whether a persisted
|
|
32580
32739
|
* rule keeps a field. Three literal destructurings are three provable ones.
|
|
32581
32740
|
*/
|
|
32582
|
-
function applyTemplatePatch$
|
|
32741
|
+
function applyTemplatePatch$2(merged, template) {
|
|
32583
32742
|
if (template === void 0) return merged;
|
|
32584
32743
|
if (template !== null) return {
|
|
32585
32744
|
...merged,
|
|
@@ -32725,7 +32884,7 @@ var NcSummaryStore = class {
|
|
|
32725
32884
|
createdAt: existing.createdAt,
|
|
32726
32885
|
updatedAt: this.now()
|
|
32727
32886
|
};
|
|
32728
|
-
return this.write(applyAiPatch(applyFiltersPatch(applyTemplatePatch$
|
|
32887
|
+
return this.write(applyAiPatch(applyFiltersPatch(applyTemplatePatch$2(merged, template), filters), ai));
|
|
32729
32888
|
}
|
|
32730
32889
|
async setEnabled(ruleId, enabled) {
|
|
32731
32890
|
return this.update(ruleId, { enabled });
|
|
@@ -32777,6 +32936,408 @@ var NcSummaryStore = class {
|
|
|
32777
32936
|
}
|
|
32778
32937
|
};
|
|
32779
32938
|
//#endregion
|
|
32939
|
+
//#region src/notification-center/calendar-events/calendar-events-store.ts
|
|
32940
|
+
/**
|
|
32941
|
+
* Durable calendar-events rule set — same DurableLedger mechanics as
|
|
32942
|
+
* Summaries, with a per-rule `generatedAt` stamp (one fire per schedule window).
|
|
32943
|
+
*/
|
|
32944
|
+
/**
|
|
32945
|
+
* @durable class=config owner=notification-center
|
|
32946
|
+
* write="an operator creates or edits a calendar-events rule, or the producer stamps a delivered window via markGenerated"
|
|
32947
|
+
* retention="none — a row goes only when the operator deletes the rule."
|
|
32948
|
+
*/
|
|
32949
|
+
var NC_CALENDAR_EVENTS_RULES_COLLECTION = "notification-center:calendar-events-rules";
|
|
32950
|
+
var NC_CALENDAR_EVENTS_RULES_COLUMNS = [
|
|
32951
|
+
{
|
|
32952
|
+
name: "id",
|
|
32953
|
+
type: "TEXT",
|
|
32954
|
+
primaryKey: true,
|
|
32955
|
+
notNull: true
|
|
32956
|
+
},
|
|
32957
|
+
{
|
|
32958
|
+
name: "name",
|
|
32959
|
+
type: "TEXT",
|
|
32960
|
+
notNull: true
|
|
32961
|
+
},
|
|
32962
|
+
{
|
|
32963
|
+
name: "enabled",
|
|
32964
|
+
type: "BOOLEAN",
|
|
32965
|
+
notNull: true
|
|
32966
|
+
},
|
|
32967
|
+
{
|
|
32968
|
+
name: "updatedAt",
|
|
32969
|
+
type: "INTEGER",
|
|
32970
|
+
notNull: true
|
|
32971
|
+
},
|
|
32972
|
+
{
|
|
32973
|
+
name: "rule",
|
|
32974
|
+
type: "JSON",
|
|
32975
|
+
notNull: true
|
|
32976
|
+
}
|
|
32977
|
+
];
|
|
32978
|
+
var NC_CALENDAR_EVENTS_RULES_INDEXES = [{
|
|
32979
|
+
name: "idx_nc_calendar_events_rules_enabled",
|
|
32980
|
+
columns: ["enabled"]
|
|
32981
|
+
}];
|
|
32982
|
+
var LOAD_LIMIT$1 = 1e4;
|
|
32983
|
+
var NC_CALENDAR_EVENTS_RULES_SPEC = {
|
|
32984
|
+
collection: NC_CALENDAR_EVENTS_RULES_COLLECTION,
|
|
32985
|
+
columns: [...NC_CALENDAR_EVENTS_RULES_COLUMNS],
|
|
32986
|
+
indexes: [...NC_CALENDAR_EVENTS_RULES_INDEXES],
|
|
32987
|
+
writeMode: "write-through",
|
|
32988
|
+
keyOf: (rule) => rule.id,
|
|
32989
|
+
toValue: (rule) => ({
|
|
32990
|
+
name: rule.name,
|
|
32991
|
+
enabled: rule.enabled,
|
|
32992
|
+
updatedAt: rule.updatedAt,
|
|
32993
|
+
rule
|
|
32994
|
+
}),
|
|
32995
|
+
fromRecord: (_key, data) => {
|
|
32996
|
+
const parsed = NcCalendarEventsRuleSchema.safeParse(data["rule"]);
|
|
32997
|
+
return parsed.success ? parsed.data : null;
|
|
32998
|
+
},
|
|
32999
|
+
loadLimit: LOAD_LIMIT$1
|
|
33000
|
+
};
|
|
33001
|
+
function applyTemplatePatch$1(merged, template) {
|
|
33002
|
+
if (template === void 0) return merged;
|
|
33003
|
+
if (template !== null) return {
|
|
33004
|
+
...merged,
|
|
33005
|
+
template
|
|
33006
|
+
};
|
|
33007
|
+
const { template: _cleared, ...rest } = merged;
|
|
33008
|
+
return rest;
|
|
33009
|
+
}
|
|
33010
|
+
function applyOptionalStringList(merged, key, value) {
|
|
33011
|
+
if (value === void 0) return merged;
|
|
33012
|
+
if (value !== null) return {
|
|
33013
|
+
...merged,
|
|
33014
|
+
[key]: [...value]
|
|
33015
|
+
};
|
|
33016
|
+
const { [key]: _cleared, ...rest } = merged;
|
|
33017
|
+
return rest;
|
|
33018
|
+
}
|
|
33019
|
+
var NcCalendarEventsStore = class {
|
|
33020
|
+
ledger;
|
|
33021
|
+
store;
|
|
33022
|
+
logger;
|
|
33023
|
+
now;
|
|
33024
|
+
newId;
|
|
33025
|
+
constructor(deps) {
|
|
33026
|
+
this.store = deps.store;
|
|
33027
|
+
this.ledger = new DurableLedger({
|
|
33028
|
+
spec: NC_CALENDAR_EVENTS_RULES_SPEC,
|
|
33029
|
+
store: deps.store,
|
|
33030
|
+
logger: deps.logger
|
|
33031
|
+
});
|
|
33032
|
+
this.logger = deps.logger;
|
|
33033
|
+
this.now = deps.now ?? (() => Date.now());
|
|
33034
|
+
this.newId = deps.newId ?? (() => (0, node_crypto.randomUUID)());
|
|
33035
|
+
}
|
|
33036
|
+
static async declare(store) {
|
|
33037
|
+
await DurableLedger.declare(store, NC_CALENDAR_EVENTS_RULES_SPEC);
|
|
33038
|
+
}
|
|
33039
|
+
async load() {
|
|
33040
|
+
await this.ledger.load();
|
|
33041
|
+
this.logger.debug("calendar-events rules loaded", { meta: { rules: this.ledger.size } });
|
|
33042
|
+
}
|
|
33043
|
+
list() {
|
|
33044
|
+
return this.ledger.snapshot().toSorted((a, b) => b.updatedAt - a.updatedAt);
|
|
33045
|
+
}
|
|
33046
|
+
listEnabled() {
|
|
33047
|
+
return this.list().filter((r) => r.enabled);
|
|
33048
|
+
}
|
|
33049
|
+
get(ruleId) {
|
|
33050
|
+
return this.ledger.get(ruleId) ?? null;
|
|
33051
|
+
}
|
|
33052
|
+
listForOwner(userId) {
|
|
33053
|
+
return this.list().filter((r) => r.ownerUserId === void 0 || r.ownerUserId === userId);
|
|
33054
|
+
}
|
|
33055
|
+
async create(input, createdBy, ownerUserId) {
|
|
33056
|
+
const now = this.now();
|
|
33057
|
+
const rule = NcCalendarEventsRuleSchema.parse({
|
|
33058
|
+
...NcCalendarEventsRuleInputSchema.parse(input),
|
|
33059
|
+
id: this.newId(),
|
|
33060
|
+
...ownerUserId !== void 0 ? { ownerUserId } : {},
|
|
33061
|
+
createdBy,
|
|
33062
|
+
createdAt: now,
|
|
33063
|
+
updatedAt: now
|
|
33064
|
+
});
|
|
33065
|
+
await this.ledger.put(rule);
|
|
33066
|
+
return rule;
|
|
33067
|
+
}
|
|
33068
|
+
async update(ruleId, patch) {
|
|
33069
|
+
const existing = this.ledger.get(ruleId);
|
|
33070
|
+
if (!existing) throw new Error(`calendar-events rule not found: ${ruleId}`);
|
|
33071
|
+
const { template, calendars, calendarsExclude, ...rest } = patch;
|
|
33072
|
+
const merged = {
|
|
33073
|
+
...existing,
|
|
33074
|
+
...rest,
|
|
33075
|
+
id: existing.id,
|
|
33076
|
+
...existing.ownerUserId !== void 0 ? { ownerUserId: existing.ownerUserId } : {},
|
|
33077
|
+
...existing.generatedAt !== void 0 ? { generatedAt: existing.generatedAt } : {},
|
|
33078
|
+
createdBy: existing.createdBy,
|
|
33079
|
+
createdAt: existing.createdAt,
|
|
33080
|
+
updatedAt: this.now()
|
|
33081
|
+
};
|
|
33082
|
+
return this.write(applyOptionalStringList(applyOptionalStringList(applyTemplatePatch$1(merged, template), "calendars", calendars), "calendarsExclude", calendarsExclude));
|
|
33083
|
+
}
|
|
33084
|
+
async setEnabled(ruleId, enabled) {
|
|
33085
|
+
return this.update(ruleId, { enabled });
|
|
33086
|
+
}
|
|
33087
|
+
async markGenerated(ruleId, windowEndMs) {
|
|
33088
|
+
const existing = this.ledger.get(ruleId);
|
|
33089
|
+
if (!existing) throw new Error(`calendar-events rule not found: ${ruleId}`);
|
|
33090
|
+
return this.write({
|
|
33091
|
+
...existing,
|
|
33092
|
+
generatedAt: Math.max(existing.generatedAt ?? 0, windowEndMs)
|
|
33093
|
+
});
|
|
33094
|
+
}
|
|
33095
|
+
/**
|
|
33096
|
+
* Idempotent delete — unknown ids are a no-op in RAM, still attempted on disk.
|
|
33097
|
+
*
|
|
33098
|
+
* Deliberately NOT {@link DurableLedger.forget}: that swallows a failed
|
|
33099
|
+
* durable delete. Mirror eviction first; durable half re-throws on failure
|
|
33100
|
+
* (same shape as Summaries).
|
|
33101
|
+
*/
|
|
33102
|
+
async delete(ruleId) {
|
|
33103
|
+
this.ledger.evict(ruleId);
|
|
33104
|
+
try {
|
|
33105
|
+
await this.store.delete.mutate({
|
|
33106
|
+
collection: NC_CALENDAR_EVENTS_RULES_COLLECTION,
|
|
33107
|
+
key: ruleId
|
|
33108
|
+
});
|
|
33109
|
+
} catch (err) {
|
|
33110
|
+
this.logger.warn("calendar-events rule durable delete failed", { meta: {
|
|
33111
|
+
ruleId,
|
|
33112
|
+
error: err instanceof Error ? err.message : String(err)
|
|
33113
|
+
} });
|
|
33114
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
33115
|
+
}
|
|
33116
|
+
}
|
|
33117
|
+
async write(rule) {
|
|
33118
|
+
const validated = NcCalendarEventsRuleSchema.parse(rule);
|
|
33119
|
+
await this.ledger.put(validated);
|
|
33120
|
+
return validated;
|
|
33121
|
+
}
|
|
33122
|
+
};
|
|
33123
|
+
//#endregion
|
|
33124
|
+
//#region src/notification-center/calendar-events/calendar-events-delivery.ts
|
|
33125
|
+
function calendarEventsRecordId(ruleId, windowEndMs) {
|
|
33126
|
+
return `calendar-events:${ruleId}:${windowEndMs}`;
|
|
33127
|
+
}
|
|
33128
|
+
/** `YYYY-MM-DD HH:MM` in the host timezone. */
|
|
33129
|
+
function clockOf$1(atMs) {
|
|
33130
|
+
const d = new Date(atMs);
|
|
33131
|
+
return `${`${String(d.getFullYear())}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`} ${`${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`}`;
|
|
33132
|
+
}
|
|
33133
|
+
function formatEventLine(event) {
|
|
33134
|
+
const when = event.allDay ? clockOf$1(event.startMs).slice(0, 10) : `${clockOf$1(event.startMs)}–${clockOf$1(event.endMs)}`;
|
|
33135
|
+
const where = event.location !== void 0 && event.location.length > 0 ? ` @ ${event.location}` : "";
|
|
33136
|
+
return `• ${when} ${event.title}${where}`;
|
|
33137
|
+
}
|
|
33138
|
+
function derivedBody$1(input) {
|
|
33139
|
+
if (input.events.length === 0) return `No events in the next ${String(input.horizonDays)} day(s).`;
|
|
33140
|
+
const lines = input.events.slice(0, 40).map(formatEventLine);
|
|
33141
|
+
const extra = input.events.length > 40 ? `\n…and ${String(input.events.length - 40)} more` : "";
|
|
33142
|
+
return lines.join("\n") + extra;
|
|
33143
|
+
}
|
|
33144
|
+
function templateVars(input) {
|
|
33145
|
+
return {
|
|
33146
|
+
rule: input.ruleName,
|
|
33147
|
+
count: String(input.events.length),
|
|
33148
|
+
horizonDays: String(input.horizonDays),
|
|
33149
|
+
from: clockOf$1(input.eventWindow.startMs),
|
|
33150
|
+
to: clockOf$1(input.eventWindow.endMs),
|
|
33151
|
+
events: derivedBody$1(input),
|
|
33152
|
+
calendars: String(input.calendarIds.length)
|
|
33153
|
+
};
|
|
33154
|
+
}
|
|
33155
|
+
function paramsFor$1(target) {
|
|
33156
|
+
const params = target.params;
|
|
33157
|
+
if (params === void 0) return void 0;
|
|
33158
|
+
const out = {};
|
|
33159
|
+
for (const [k, v] of Object.entries(params)) if (typeof v === "string") out[k] = v;
|
|
33160
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
33161
|
+
}
|
|
33162
|
+
/**
|
|
33163
|
+
* One outbox row per enabled target. Ready for `NcOutbox.enqueue`.
|
|
33164
|
+
*/
|
|
33165
|
+
function buildCalendarEventsOutboxInputs(input) {
|
|
33166
|
+
const vars = templateVars(input);
|
|
33167
|
+
const optedOut = new Set(input.disabledTargetIds ?? []);
|
|
33168
|
+
const title = renderTemplate(input.template?.title, vars) ?? `${input.ruleName}: ${String(input.events.length)} upcoming`;
|
|
33169
|
+
const body = renderTemplate(input.template?.body, vars) ?? derivedBody$1(input);
|
|
33170
|
+
const recordId = calendarEventsRecordId(input.ruleId, input.scheduleWindow.endMs);
|
|
33171
|
+
return input.targets.filter((t) => !optedOut.has(t.targetId)).map((target) => {
|
|
33172
|
+
const payload = {
|
|
33173
|
+
ruleName: input.ruleName,
|
|
33174
|
+
delivery: "calendar-events",
|
|
33175
|
+
priority: input.priority,
|
|
33176
|
+
template: {
|
|
33177
|
+
title,
|
|
33178
|
+
body
|
|
33179
|
+
},
|
|
33180
|
+
media: "none",
|
|
33181
|
+
...paramsFor$1(target) !== void 0 ? { params: paramsFor$1(target) } : {},
|
|
33182
|
+
subject: {
|
|
33183
|
+
deviceId: NC_SYSTEM_DEVICE_ID$1,
|
|
33184
|
+
className: "calendar-events",
|
|
33185
|
+
zones: [],
|
|
33186
|
+
timestamp: input.scheduleWindow.endMs
|
|
33187
|
+
}
|
|
33188
|
+
};
|
|
33189
|
+
return {
|
|
33190
|
+
ruleId: input.ruleId,
|
|
33191
|
+
targetId: target.targetId,
|
|
33192
|
+
deviceId: NC_SYSTEM_DEVICE_ID$1,
|
|
33193
|
+
recordKind: "calendar-events",
|
|
33194
|
+
recordId,
|
|
33195
|
+
payload
|
|
33196
|
+
};
|
|
33197
|
+
});
|
|
33198
|
+
}
|
|
33199
|
+
//#endregion
|
|
33200
|
+
//#region src/notification-center/calendar-events/calendar-events-selection.ts
|
|
33201
|
+
/**
|
|
33202
|
+
* Expand a rule's calendar scope against the known calendar ids.
|
|
33203
|
+
*
|
|
33204
|
+
* - All (both lists empty/absent) → every known id
|
|
33205
|
+
* - Only → the include list, intersected with known (unknown ids dropped)
|
|
33206
|
+
* - Except → known minus the exclude list
|
|
33207
|
+
*
|
|
33208
|
+
* An empty known set always yields empty — there is nothing to notify about.
|
|
33209
|
+
*/
|
|
33210
|
+
function expandCalendarScope(scope, knownIds) {
|
|
33211
|
+
const known = [...knownIds];
|
|
33212
|
+
if (known.length === 0) return [];
|
|
33213
|
+
const include = scope?.calendars ?? [];
|
|
33214
|
+
const exclude = scope?.calendarsExclude ?? [];
|
|
33215
|
+
if (include.length > 0) {
|
|
33216
|
+
const wanted = new Set(include);
|
|
33217
|
+
return known.filter((id) => wanted.has(id));
|
|
33218
|
+
}
|
|
33219
|
+
if (exclude.length > 0) {
|
|
33220
|
+
const banned = new Set(exclude);
|
|
33221
|
+
return known.filter((id) => !banned.has(id));
|
|
33222
|
+
}
|
|
33223
|
+
return known;
|
|
33224
|
+
}
|
|
33225
|
+
//#endregion
|
|
33226
|
+
//#region src/notification-center/calendar-events/calendar-events-producer.ts
|
|
33227
|
+
/** How far back a closed schedule window may still be owed. */
|
|
33228
|
+
var CALENDAR_EVENTS_LOOKBACK_MS = 2160 * 60 * 1e3;
|
|
33229
|
+
var NcCalendarEventsProducer = class {
|
|
33230
|
+
deps;
|
|
33231
|
+
inFlight = false;
|
|
33232
|
+
now;
|
|
33233
|
+
constructor(deps) {
|
|
33234
|
+
this.deps = deps;
|
|
33235
|
+
this.now = deps.now ?? (() => Date.now());
|
|
33236
|
+
}
|
|
33237
|
+
async tick() {
|
|
33238
|
+
if (this.inFlight) return {
|
|
33239
|
+
examined: 0,
|
|
33240
|
+
sent: 0,
|
|
33241
|
+
empty: 0,
|
|
33242
|
+
skipped: 0,
|
|
33243
|
+
failed: 0
|
|
33244
|
+
};
|
|
33245
|
+
this.inFlight = true;
|
|
33246
|
+
const report = {
|
|
33247
|
+
examined: 0,
|
|
33248
|
+
sent: 0,
|
|
33249
|
+
empty: 0,
|
|
33250
|
+
skipped: 0,
|
|
33251
|
+
failed: 0
|
|
33252
|
+
};
|
|
33253
|
+
try {
|
|
33254
|
+
const rules = this.deps.store.listEnabled();
|
|
33255
|
+
for (const rule of rules) {
|
|
33256
|
+
report.examined += 1;
|
|
33257
|
+
try {
|
|
33258
|
+
const outcome = await this.runRule(rule);
|
|
33259
|
+
if (outcome === "sent") report.sent += 1;
|
|
33260
|
+
else if (outcome === "empty") report.empty += 1;
|
|
33261
|
+
else report.skipped += 1;
|
|
33262
|
+
} catch (err) {
|
|
33263
|
+
report.failed += 1;
|
|
33264
|
+
this.deps.logger.warn("calendar-events rule failed", { meta: {
|
|
33265
|
+
ruleId: rule.id,
|
|
33266
|
+
error: err instanceof Error ? err.message : String(err)
|
|
33267
|
+
} });
|
|
33268
|
+
}
|
|
33269
|
+
}
|
|
33270
|
+
return report;
|
|
33271
|
+
} finally {
|
|
33272
|
+
this.inFlight = false;
|
|
33273
|
+
}
|
|
33274
|
+
}
|
|
33275
|
+
async runRule(rule) {
|
|
33276
|
+
const nowMs = this.now();
|
|
33277
|
+
const scheduleWindow = findLastClosedWindow({
|
|
33278
|
+
schedule: rule.schedule,
|
|
33279
|
+
nowMs,
|
|
33280
|
+
lookbackMs: CALENDAR_EVENTS_LOOKBACK_MS
|
|
33281
|
+
});
|
|
33282
|
+
if (scheduleWindow === null) return "skipped";
|
|
33283
|
+
if ((rule.generatedAt ?? 0) >= scheduleWindow.endMs) return "skipped";
|
|
33284
|
+
const known = await this.deps.calendars.listCalendars();
|
|
33285
|
+
const calendarIds = expandCalendarScope({
|
|
33286
|
+
calendars: rule.calendars,
|
|
33287
|
+
calendarsExclude: rule.calendarsExclude
|
|
33288
|
+
}, known.map((c) => c.id));
|
|
33289
|
+
if (calendarIds.length === 0) {
|
|
33290
|
+
this.deps.logger.info("calendar-events: no calendars in scope", { meta: { ruleId: rule.id } });
|
|
33291
|
+
if (!rule.deliverEmpty) {
|
|
33292
|
+
await this.deps.store.markGenerated(rule.id, scheduleWindow.endMs);
|
|
33293
|
+
return "empty";
|
|
33294
|
+
}
|
|
33295
|
+
}
|
|
33296
|
+
const eventWindow = calendarEventsWindow(nowMs, rule.horizonDays);
|
|
33297
|
+
const events = calendarIds.length === 0 ? [] : await this.deps.calendars.listEvents({
|
|
33298
|
+
calendarIds,
|
|
33299
|
+
startMs: eventWindow.startMs,
|
|
33300
|
+
endMs: eventWindow.endMs
|
|
33301
|
+
});
|
|
33302
|
+
if (events.length === 0 && !rule.deliverEmpty) {
|
|
33303
|
+
this.deps.logger.info("calendar-events: empty horizon, not delivering", { meta: {
|
|
33304
|
+
ruleId: rule.id,
|
|
33305
|
+
horizonDays: eventWindow.horizonDays
|
|
33306
|
+
} });
|
|
33307
|
+
await this.deps.store.markGenerated(rule.id, scheduleWindow.endMs);
|
|
33308
|
+
return "empty";
|
|
33309
|
+
}
|
|
33310
|
+
const sorted = [...events].toSorted((a, b) => a.startMs - b.startMs);
|
|
33311
|
+
await this.enqueue(rule, scheduleWindow, eventWindow, calendarIds, sorted);
|
|
33312
|
+
await this.deps.store.markGenerated(rule.id, scheduleWindow.endMs);
|
|
33313
|
+
return "sent";
|
|
33314
|
+
}
|
|
33315
|
+
async enqueue(rule, scheduleWindow, eventWindow, calendarIds, events) {
|
|
33316
|
+
const inputs = buildCalendarEventsOutboxInputs({
|
|
33317
|
+
ruleId: rule.id,
|
|
33318
|
+
ruleName: rule.name,
|
|
33319
|
+
scheduleWindow,
|
|
33320
|
+
eventWindow: {
|
|
33321
|
+
startMs: eventWindow.startMs,
|
|
33322
|
+
endMs: eventWindow.endMs
|
|
33323
|
+
},
|
|
33324
|
+
horizonDays: eventWindow.horizonDays,
|
|
33325
|
+
events,
|
|
33326
|
+
calendarIds,
|
|
33327
|
+
targets: rule.targets,
|
|
33328
|
+
priority: rule.priority,
|
|
33329
|
+
...rule.template !== void 0 ? { template: rule.template } : {}
|
|
33330
|
+
});
|
|
33331
|
+
for (const input of inputs) await this.deps.outbox.enqueue(input);
|
|
33332
|
+
this.deps.logger.info("calendar-events delivered", { meta: {
|
|
33333
|
+
ruleId: rule.id,
|
|
33334
|
+
events: events.length,
|
|
33335
|
+
targets: inputs.length,
|
|
33336
|
+
windowEndMs: scheduleWindow.endMs
|
|
33337
|
+
} });
|
|
33338
|
+
}
|
|
33339
|
+
};
|
|
33340
|
+
//#endregion
|
|
32780
33341
|
//#region src/notification-center/text-catalog-editor.ts
|
|
32781
33342
|
var NcTextCatalogEditor = class {
|
|
32782
33343
|
store;
|
|
@@ -35860,7 +36421,7 @@ function packagePhaseOf(ev) {
|
|
|
35860
36421
|
*/
|
|
35861
36422
|
function historyRecordKind(kind) {
|
|
35862
36423
|
if (kind === "audio-event" || kind === "audio-window") return "object-event";
|
|
35863
|
-
if (kind === "timelapse" || kind === "ai-summary") return "system-event";
|
|
36424
|
+
if (kind === "timelapse" || kind === "ai-summary" || kind === "calendar-events") return "system-event";
|
|
35864
36425
|
return kind;
|
|
35865
36426
|
}
|
|
35866
36427
|
/**
|
|
@@ -35875,7 +36436,8 @@ function historyRecordKind(kind) {
|
|
|
35875
36436
|
function cooldownSubjectKind(kind) {
|
|
35876
36437
|
switch (kind) {
|
|
35877
36438
|
case "timelapse":
|
|
35878
|
-
case "ai-summary":
|
|
36439
|
+
case "ai-summary":
|
|
36440
|
+
case "calendar-events": return "device-event";
|
|
35879
36441
|
default: return kind;
|
|
35880
36442
|
}
|
|
35881
36443
|
}
|
|
@@ -35975,7 +36537,7 @@ function outboxEntryToHistory(texts, entry) {
|
|
|
35975
36537
|
id: entry.id,
|
|
35976
36538
|
ruleId: entry.ruleId,
|
|
35977
36539
|
ruleName: entry.payload.ruleName,
|
|
35978
|
-
delivery: entry.payload.delivery === "timelapse" || entry.payload.delivery === "ai-summary" ? "system-event" : entry.payload.delivery,
|
|
36540
|
+
delivery: entry.payload.delivery === "timelapse" || entry.payload.delivery === "ai-summary" || entry.payload.delivery === "calendar-events" ? "system-event" : entry.payload.delivery,
|
|
35979
36541
|
targetId: entry.targetId,
|
|
35980
36542
|
deviceId: entry.deviceId,
|
|
35981
36543
|
recordKind: historyRecordKind(entry.recordKind),
|
|
@@ -36185,6 +36747,9 @@ var NotificationCenter = class {
|
|
|
36185
36747
|
*/
|
|
36186
36748
|
summaryRules;
|
|
36187
36749
|
summaryProducer = null;
|
|
36750
|
+
/** Calendar-events digest rules + producer (hub-only producer). */
|
|
36751
|
+
calendarEventsRules;
|
|
36752
|
+
calendarEventsProducer = null;
|
|
36188
36753
|
/**
|
|
36189
36754
|
* What the deleted-device / deleted-target prune did, and why — its
|
|
36190
36755
|
* disable-once guard and the source of both UIs' banner. Loaded on EVERY
|
|
@@ -37175,6 +37740,11 @@ var NotificationCenter = class {
|
|
|
37175
37740
|
logger: this.logger.child("summary"),
|
|
37176
37741
|
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
37177
37742
|
});
|
|
37743
|
+
this.calendarEventsRules = new NcCalendarEventsStore({
|
|
37744
|
+
store: deps.store,
|
|
37745
|
+
logger: this.logger.child("calendar-events"),
|
|
37746
|
+
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
37747
|
+
});
|
|
37178
37748
|
this.pruneLedger = new NcPruneLedger({
|
|
37179
37749
|
store: deps.store,
|
|
37180
37750
|
logger: this.logger.child("device-prune")
|
|
@@ -37221,6 +37791,14 @@ var NotificationCenter = class {
|
|
|
37221
37791
|
zoneOwner: this.zoneOwners.lookup()
|
|
37222
37792
|
});
|
|
37223
37793
|
}
|
|
37794
|
+
const calendarsPort = deps.calendars;
|
|
37795
|
+
if (calendarsPort !== void 0) this.calendarEventsProducer = new NcCalendarEventsProducer({
|
|
37796
|
+
store: this.calendarEventsRules,
|
|
37797
|
+
outbox: this.outbox,
|
|
37798
|
+
calendars: calendarsPort,
|
|
37799
|
+
logger: this.logger.child("calendar-events"),
|
|
37800
|
+
now: this.now
|
|
37801
|
+
});
|
|
37224
37802
|
}
|
|
37225
37803
|
/**
|
|
37226
37804
|
* The durable timelapse rule set — exposed for the `nc.*Timelapse*` bridge
|
|
@@ -37234,6 +37812,10 @@ var NotificationCenter = class {
|
|
|
37234
37812
|
get summaryStore() {
|
|
37235
37813
|
return this.summaryRules;
|
|
37236
37814
|
}
|
|
37815
|
+
/** The durable calendar-events rule set — exposed for the `nc.*CalendarEvents*` bridge. */
|
|
37816
|
+
get calendarEventsStore() {
|
|
37817
|
+
return this.calendarEventsRules;
|
|
37818
|
+
}
|
|
37237
37819
|
/**
|
|
37238
37820
|
* Produce one digest window for one rule, right now.
|
|
37239
37821
|
*
|
|
@@ -37307,6 +37889,7 @@ var NotificationCenter = class {
|
|
|
37307
37889
|
await NcDeviceMuteStore.declare(store);
|
|
37308
37890
|
await TimelapseStore.declare(store);
|
|
37309
37891
|
await NcSummaryStore.declare(store);
|
|
37892
|
+
await NcCalendarEventsStore.declare(store);
|
|
37310
37893
|
await NcRuleAiStore.declare(store);
|
|
37311
37894
|
await NcTextCatalogStore.declare(store);
|
|
37312
37895
|
await NcArtifactIndex.declare(store);
|
|
@@ -40929,11 +41512,17 @@ var NotificationCenter = class {
|
|
|
40929
41512
|
await this.rules.load();
|
|
40930
41513
|
this.noteInertGroupedRules();
|
|
40931
41514
|
if (this.evaluationActive) this.deviceAlerts.tick();
|
|
40932
|
-
if (this.evaluationActive)
|
|
40933
|
-
this.
|
|
40934
|
-
|
|
41515
|
+
if (this.evaluationActive) {
|
|
41516
|
+
this.stateSummary.tick().catch((err) => {
|
|
41517
|
+
this.logger.warn("state summary tick failed", { meta: { error: String(err) } });
|
|
41518
|
+
});
|
|
41519
|
+
this.calendarEventsProducer?.tick().catch((err) => {
|
|
41520
|
+
this.logger.warn("calendar-events tick failed", { meta: { error: String(err) } });
|
|
41521
|
+
});
|
|
41522
|
+
}
|
|
40935
41523
|
await this.timelapseRules.load();
|
|
40936
41524
|
await this.summaryRules.load();
|
|
41525
|
+
await this.calendarEventsRules.load();
|
|
40937
41526
|
await this.ruleAi.load();
|
|
40938
41527
|
await this.reloadTexts();
|
|
40939
41528
|
this.refreshOccupancyWatch();
|
|
@@ -104816,6 +105405,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
104816
105405
|
store: center.summaryStore,
|
|
104817
105406
|
runNow: (input) => center.runSummaryNow(input)
|
|
104818
105407
|
},
|
|
105408
|
+
calendarEvents: { store: center.calendarEventsStore },
|
|
104819
105409
|
texts: center.textCatalogEditor,
|
|
104820
105410
|
previewTexts: center.textCatalog,
|
|
104821
105411
|
...this.ncArtifactIndex !== null ? { artifacts: this.ncArtifactIndex } : {},
|
|
@@ -106003,7 +106593,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
106003
106593
|
})).tracks;
|
|
106004
106594
|
},
|
|
106005
106595
|
...this.isPostProcessingNode ? { timelapse: this.buildTimelapsePorts(api, stores) } : {},
|
|
106006
|
-
...this.isPostProcessingNode ? { summary: this.buildSummaryPorts(api, stores) } : {}
|
|
106596
|
+
...this.isPostProcessingNode ? { summary: this.buildSummaryPorts(api, stores) } : {},
|
|
106597
|
+
...this.isPostProcessingNode ? { calendars: {
|
|
106598
|
+
listCalendars: async () => {
|
|
106599
|
+
return (await api.calendars.listCalendars.query()).map((c) => ({ id: c.id }));
|
|
106600
|
+
},
|
|
106601
|
+
listEvents: async (input) => api.calendars.listEvents.query({
|
|
106602
|
+
calendarIds: [...input.calendarIds],
|
|
106603
|
+
startMs: input.startMs,
|
|
106604
|
+
endMs: input.endMs
|
|
106605
|
+
})
|
|
106606
|
+
} } : {}
|
|
106007
106607
|
});
|
|
106008
106608
|
this.wireAlarmPanel(this.notificationCenter);
|
|
106009
106609
|
await this.serveNcActionPlane(this.notificationCenter);
|