@camstack/addon-post-analysis 1.2.315 → 1.2.319
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-8Ss3XHG0.js → clip-model-registry-BjYpZuuI.js} +33 -7
- package/dist/{clip-model-registry-60cLfjwb.mjs → clip-model-registry-DSqobJfx.mjs} +28 -8
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BoFERv6x.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CKHzDA_B.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bz37odzX.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-V7A5cH1T.mjs → hostInit-DDDjJDA6.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +565 -85
- package/dist/pipeline-analytics/index.mjs +565 -85
- 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-BejCg41J.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-BjYpZuuI.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");
|
|
@@ -16996,7 +16996,7 @@ var NcStateSummaryTestSchema = require_clip_model_registry.object({
|
|
|
16996
16996
|
/** Nothing open and every section read: nothing was enqueued. */
|
|
16997
16997
|
nothingToReport: require_clip_model_registry.boolean(),
|
|
16998
16998
|
sections: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
16999
|
-
id: require_clip_model_registry.
|
|
16999
|
+
id: require_clip_model_registry.NcStateSummarySectionIdSchema,
|
|
17000
17000
|
status: require_clip_model_registry._enum(["read", "unavailable"]),
|
|
17001
17001
|
items: require_clip_model_registry.number().int().nonnegative(),
|
|
17002
17002
|
reason: require_clip_model_registry.string().optional()
|
|
@@ -17004,7 +17004,7 @@ var NcStateSummaryTestSchema = require_clip_model_registry.object({
|
|
|
17004
17004
|
rows: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
17005
17005
|
recordId: require_clip_model_registry.string(),
|
|
17006
17006
|
/** The section of a per-section row. */
|
|
17007
|
-
section: require_clip_model_registry.
|
|
17007
|
+
section: require_clip_model_registry.NcStateSummarySectionIdSchema.optional(),
|
|
17008
17008
|
enqueued: require_clip_model_registry.number().int().nonnegative(),
|
|
17009
17009
|
targetIds: require_clip_model_registry.array(require_clip_model_registry.string())
|
|
17010
17010
|
}))
|
|
@@ -20730,17 +20730,8 @@ var NcDeviceStateCache = class {
|
|
|
20730
20730
|
* a job that COMPLETED, and the state it leaves behind is not a condition that
|
|
20731
20731
|
* is still true. The ADR records why it is excluded rather than approximated.
|
|
20732
20732
|
*/
|
|
20733
|
-
|
|
20734
|
-
|
|
20735
|
-
"streams",
|
|
20736
|
-
"battery",
|
|
20737
|
-
"consumables",
|
|
20738
|
-
"tanks",
|
|
20739
|
-
"detection-blind",
|
|
20740
|
-
"nodes",
|
|
20741
|
-
"addons",
|
|
20742
|
-
"updates"
|
|
20743
|
-
];
|
|
20733
|
+
/** One source: the cap's Zod enum (D698 LOW-6). */
|
|
20734
|
+
var NC_STATE_SUMMARY_SECTION_IDS = require_clip_model_registry.NcStateSummarySectionIdSchema.options;
|
|
20744
20735
|
/** The kind groups that have no section, and why — said on the preview. */
|
|
20745
20736
|
var NC_STATE_SUMMARY_EXCLUDED_GROUPS = { "backups-exports": "a backup or an export is a completed job, not a state that is still true" };
|
|
20746
20737
|
function isStateSectionId(value) {
|
|
@@ -35209,10 +35200,10 @@ var NcStateSummaryService = class {
|
|
|
35209
35200
|
const rows = [];
|
|
35210
35201
|
for (const subject of subjects) {
|
|
35211
35202
|
const enqueued = await this.deps.enqueue(rule, subject, "synthetic");
|
|
35212
|
-
const section = subject
|
|
35203
|
+
const section = stateSummarySectionOf(subject);
|
|
35213
35204
|
rows.push({
|
|
35214
35205
|
recordId: subject.recordId,
|
|
35215
|
-
...
|
|
35206
|
+
...section !== void 0 ? { section } : {},
|
|
35216
35207
|
enqueued: enqueued.inserted,
|
|
35217
35208
|
targetIds: [...enqueued.targetIds]
|
|
35218
35209
|
});
|
|
@@ -51371,6 +51362,11 @@ var MEDIA_ID_SEGMENT = "m";
|
|
|
51371
51362
|
/** Path segment that introduces an OWNER question ("best media of"). */
|
|
51372
51363
|
var MEDIA_OWNER_SEGMENT = "o";
|
|
51373
51364
|
/**
|
|
51365
|
+
* Path segment that introduces an event-owner GENERATION (D694) —
|
|
51366
|
+
* `/g/<deviceId>/<recordAt>` after the owner id.
|
|
51367
|
+
*/
|
|
51368
|
+
var MEDIA_GENERATION_SEGMENT = "g";
|
|
51369
|
+
/**
|
|
51374
51370
|
* The plane path for one media row.
|
|
51375
51371
|
*
|
|
51376
51372
|
* `prefix` is `/addon/<addonId>/event-media` — a pure function of the addon
|
|
@@ -51387,6 +51383,23 @@ function mediaFileUrl(prefix, mediaId) {
|
|
|
51387
51383
|
return `${prefix}/${MEDIA_ID_SEGMENT}/${mediaId}`;
|
|
51388
51384
|
}
|
|
51389
51385
|
/**
|
|
51386
|
+
* The plane path for "whatever media best represents this owner".
|
|
51387
|
+
*
|
|
51388
|
+
* The owner id IS encoded: a trackId is a UUID today but an owner id is an
|
|
51389
|
+
* arbitrary row key (the summary mosaic's is `summary-<ruleId>-<windowEndMs>`),
|
|
51390
|
+
* and one slash in it would silently re-cut the path.
|
|
51391
|
+
*
|
|
51392
|
+
* When `generation` is set and `ownerType` is an event table, the path carries
|
|
51393
|
+
* `/g/<deviceId>/<recordAt>` so a reused rowid cannot resolve another record's
|
|
51394
|
+
* crop. Generation is ignored (and not minted) for non-event owners — a track
|
|
51395
|
+
* id is random and needs none.
|
|
51396
|
+
*/
|
|
51397
|
+
function mediaOwnerUrl(prefix, ownerType, ownerId, generation) {
|
|
51398
|
+
const base = `${prefix}/${MEDIA_OWNER_SEGMENT}/${ownerType}/${encodeURIComponent(ownerId)}`;
|
|
51399
|
+
if (generation === void 0 || !require_clip_model_registry.isEventOwnerType(ownerType)) return base;
|
|
51400
|
+
return `${base}/${MEDIA_GENERATION_SEGMENT}/${generation.deviceId}/${generation.recordAt}`;
|
|
51401
|
+
}
|
|
51402
|
+
/**
|
|
51390
51403
|
* A stored row as the wire sees it: the ref, the address, and — transitionally
|
|
51391
51404
|
* — the bytes.
|
|
51392
51405
|
*
|
|
@@ -59239,6 +59252,12 @@ function openGzipStream(out) {
|
|
|
59239
59252
|
onceDrain: (listener) => {
|
|
59240
59253
|
gzip.once("drain", listener);
|
|
59241
59254
|
},
|
|
59255
|
+
flush: () => new Promise((resolve, reject) => {
|
|
59256
|
+
gzip.flush((err) => {
|
|
59257
|
+
if (err !== null && err !== void 0) reject(err);
|
|
59258
|
+
else resolve();
|
|
59259
|
+
});
|
|
59260
|
+
}),
|
|
59242
59261
|
finish: async () => {
|
|
59243
59262
|
gzip.end();
|
|
59244
59263
|
await closed;
|
|
@@ -59334,6 +59353,25 @@ var TapeSegmentWriter = class {
|
|
|
59334
59353
|
});
|
|
59335
59354
|
}
|
|
59336
59355
|
/**
|
|
59356
|
+
* Push the sink's pending gzip output to disk without ending the segment.
|
|
59357
|
+
* A quiet / silent camera otherwise keeps its tail only in the zlib
|
|
59358
|
+
* buffer until the next rotation (D681 follow-up). Never throws into the
|
|
59359
|
+
* caller's frame path: a flush fault is logged and swallowed here.
|
|
59360
|
+
*/
|
|
59361
|
+
async flushToDisk() {
|
|
59362
|
+
try {
|
|
59363
|
+
await this.deps.sink.flush();
|
|
59364
|
+
} catch (err) {
|
|
59365
|
+
this.deps.logger.warn("tracking tape segment flush failed", {
|
|
59366
|
+
tags: { deviceId: this.deps.deviceId },
|
|
59367
|
+
meta: {
|
|
59368
|
+
segmentOriginMs: this.deps.segmentOriginMs,
|
|
59369
|
+
error: require_clip_model_registry.errMsg(err)
|
|
59370
|
+
}
|
|
59371
|
+
});
|
|
59372
|
+
}
|
|
59373
|
+
}
|
|
59374
|
+
/**
|
|
59337
59375
|
* Always ends the segment's records (queue flush, lost-span gap, `end`
|
|
59338
59376
|
* record) before touching the sink, so a sink that cannot finish still
|
|
59339
59377
|
* leaves a decodable body behind it. `sink.finish()`'s rejection is caught
|
|
@@ -59660,6 +59698,21 @@ var TapeRecorder = class {
|
|
|
59660
59698
|
if (cam !== void 0) await this.closeSegment(deviceId, cam, "segment-end");
|
|
59661
59699
|
}
|
|
59662
59700
|
/**
|
|
59701
|
+
* Push every open segment's pending gzip output to disk without rotating
|
|
59702
|
+
* (D681 follow-up). A silent / quiet camera otherwise keeps its tail only
|
|
59703
|
+
* in the zlib buffer until the next frame or the 10-minute cut — a crash
|
|
59704
|
+
* loses it, and `bytesLastHour` lags behind what was already recorded.
|
|
59705
|
+
* Never throws: a per-camera flush fault is logged inside the writer.
|
|
59706
|
+
*/
|
|
59707
|
+
async flushOpenSegments() {
|
|
59708
|
+
const pending = [];
|
|
59709
|
+
for (const cam of this.cameras.values()) {
|
|
59710
|
+
const writer = cam.segment?.writer;
|
|
59711
|
+
if (writer !== void 0 && writer !== null) pending.push(writer.flushToDisk());
|
|
59712
|
+
}
|
|
59713
|
+
await Promise.all(pending);
|
|
59714
|
+
}
|
|
59715
|
+
/**
|
|
59663
59716
|
* Close the camera's open segment only if it holds time at or before
|
|
59664
59717
|
* `atMs` — what a promotion of a window ending at `atMs` needs on disk
|
|
59665
59718
|
* (Task 12 fix round, H1). A window wholly before the open segment leaves
|
|
@@ -60159,6 +60212,29 @@ var TapeShelf = class {
|
|
|
60159
60212
|
};
|
|
60160
60213
|
}
|
|
60161
60214
|
};
|
|
60215
|
+
/**
|
|
60216
|
+
* Log every refusal the sweep already measured (D391 / D681 follow-up).
|
|
60217
|
+
* `sweep()` itself never throws for `rootUnreadable` / per-camera `failed`:
|
|
60218
|
+
* those land on the report, and a caller that only `.catch`es would drop them
|
|
60219
|
+
* silently even when the shelf already warned at the `readdir` site. One
|
|
60220
|
+
* tagged line per refusal at the sweep boundary closes that gap.
|
|
60221
|
+
*/
|
|
60222
|
+
function logTapeSweepReport(report, logger) {
|
|
60223
|
+
if (report.rootUnreadable !== null) logger.warn("tracking tape sweep refused", { meta: {
|
|
60224
|
+
reason: report.rootUnreadable,
|
|
60225
|
+
scope: "root"
|
|
60226
|
+
} });
|
|
60227
|
+
for (const [deviceId, outcome] of report.perDevice) {
|
|
60228
|
+
if (!("failed" in outcome) || outcome.failed !== true) continue;
|
|
60229
|
+
logger.warn("tracking tape sweep refused", {
|
|
60230
|
+
tags: { deviceId },
|
|
60231
|
+
meta: {
|
|
60232
|
+
reason: outcome.reason,
|
|
60233
|
+
scope: "camera"
|
|
60234
|
+
}
|
|
60235
|
+
});
|
|
60236
|
+
}
|
|
60237
|
+
}
|
|
60162
60238
|
//#endregion
|
|
60163
60239
|
//#region src/pipeline-analytics/tracking-bench/recorder/tape-operation-queue.ts
|
|
60164
60240
|
/** Generous: a promotion reads its window's segments and walks back through
|
|
@@ -61212,6 +61288,17 @@ function shelfFigures(listing, now) {
|
|
|
61212
61288
|
...last !== void 0 ? { heldToMs: last.originMs } : {}
|
|
61213
61289
|
};
|
|
61214
61290
|
}
|
|
61291
|
+
/**
|
|
61292
|
+
* Default `debug.tape.status` (no `deviceIds`): drop the noise. An Off camera
|
|
61293
|
+
* with a measured empty shelf is not a cost row and is not promotable; keep
|
|
61294
|
+
* everything else — Recording / Refused / Failing / Unknown, Off with held
|
|
61295
|
+
* segments (still promotable after the switch went off), and Off whose shelf
|
|
61296
|
+
* could not be read (D393: never silently hide an unreadable camera). Explicit
|
|
61297
|
+
* `deviceIds` still return every asked-about camera, Off included (D62).
|
|
61298
|
+
*/
|
|
61299
|
+
function defaultTapeStatusCameras(rows) {
|
|
61300
|
+
return rows.filter((row) => row.state !== "off" || row.segmentsHeld === null || row.segmentsHeld > 0);
|
|
61301
|
+
}
|
|
61215
61302
|
function describeTapeStatus(input) {
|
|
61216
61303
|
return input.deviceIds.map((deviceId) => {
|
|
61217
61304
|
const shelf = {
|
|
@@ -66122,6 +66209,51 @@ var EventStore = class {
|
|
|
66122
66209
|
return found;
|
|
66123
66210
|
}
|
|
66124
66211
|
/**
|
|
66212
|
+
* The GENERATION of each live event in `table` among `eventIds` — its camera
|
|
66213
|
+
* and its own timestamp (D694). Same throw-on-failure contract as
|
|
66214
|
+
* {@link existingEventIdsIn}: a quiet under-report here would let the orphan
|
|
66215
|
+
* audit keep treating another camera's leftovers as owned.
|
|
66216
|
+
*/
|
|
66217
|
+
async eventGenerationsIn(table, eventIds) {
|
|
66218
|
+
const collection = EVENT_COLLECTION_BY_TABLE[table];
|
|
66219
|
+
const found = /* @__PURE__ */ new Map();
|
|
66220
|
+
for (let i = 0; i < eventIds.length; i += PRUNE_PAGE_SIZE) {
|
|
66221
|
+
const chunk = eventIds.slice(i, i + PRUNE_PAGE_SIZE);
|
|
66222
|
+
if (chunk.length === 0) continue;
|
|
66223
|
+
const rows = await this.store.query.query({
|
|
66224
|
+
collection,
|
|
66225
|
+
columns: [
|
|
66226
|
+
"id",
|
|
66227
|
+
"deviceId",
|
|
66228
|
+
"timestamp"
|
|
66229
|
+
],
|
|
66230
|
+
filter: {
|
|
66231
|
+
whereIn: { id: [...chunk] },
|
|
66232
|
+
limit: chunk.length
|
|
66233
|
+
}
|
|
66234
|
+
});
|
|
66235
|
+
for (const row of rows) {
|
|
66236
|
+
const deviceId = row.data["deviceId"];
|
|
66237
|
+
const timestamp = row.data["timestamp"];
|
|
66238
|
+
if (typeof deviceId !== "number" || !Number.isFinite(deviceId)) continue;
|
|
66239
|
+
if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) continue;
|
|
66240
|
+
found.set(row.id, {
|
|
66241
|
+
deviceId,
|
|
66242
|
+
timestamp
|
|
66243
|
+
});
|
|
66244
|
+
}
|
|
66245
|
+
}
|
|
66246
|
+
return found;
|
|
66247
|
+
}
|
|
66248
|
+
/**
|
|
66249
|
+
* One event's generation, or `null` when the id is not live in `table`.
|
|
66250
|
+
* Used by the `/o/<eventOwner>/<id>` plane ladder to scope media to the
|
|
66251
|
+
* current holder (D694).
|
|
66252
|
+
*/
|
|
66253
|
+
async eventGenerationOf(table, eventId) {
|
|
66254
|
+
return (await this.eventGenerationsIn(table, [eventId])).get(eventId) ?? null;
|
|
66255
|
+
}
|
|
66256
|
+
/**
|
|
66125
66257
|
* The SUBSET of `eventIds` that are MOTION events specifically.
|
|
66126
66258
|
*
|
|
66127
66259
|
* {@link existingEventIds} answers "is this event alive", which is the
|
|
@@ -73076,7 +73208,7 @@ var AnalyticsQueryFacade = class {
|
|
|
73076
73208
|
async getMotionEvents(input) {
|
|
73077
73209
|
const events = await (this.deps.eventStore()?.queryMotion(input) ?? Promise.resolve([]));
|
|
73078
73210
|
if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
|
|
73079
|
-
return this.withMediaUrl(events);
|
|
73211
|
+
return this.withMediaUrl(events, "motion");
|
|
73080
73212
|
}
|
|
73081
73213
|
async getEventDensity(input) {
|
|
73082
73214
|
return this.deps.eventStore()?.densityByKind(input) ?? [];
|
|
@@ -73133,12 +73265,12 @@ var AnalyticsQueryFacade = class {
|
|
|
73133
73265
|
async getObjectEvents(input) {
|
|
73134
73266
|
const events = await (this.deps.eventStore()?.queryObject(input) ?? Promise.resolve([]));
|
|
73135
73267
|
if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
|
|
73136
|
-
return this.withMediaUrl(events);
|
|
73268
|
+
return this.withMediaUrl(events, "object");
|
|
73137
73269
|
}
|
|
73138
73270
|
async getAudioEvents(input) {
|
|
73139
73271
|
const events = await (this.deps.eventStore()?.queryAudio(input) ?? Promise.resolve([]));
|
|
73140
73272
|
if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
|
|
73141
|
-
return this.withMediaUrl(events);
|
|
73273
|
+
return this.withMediaUrl(events, "audio");
|
|
73142
73274
|
}
|
|
73143
73275
|
async getEventMedia(input) {
|
|
73144
73276
|
const store = this.deps.mediaStore();
|
|
@@ -73405,13 +73537,20 @@ var AnalyticsQueryFacade = class {
|
|
|
73405
73537
|
*
|
|
73406
73538
|
* Only called when `eventMediaBaseUrl()` is non-null (guard sits in
|
|
73407
73539
|
* the callers), so the template literal is safe.
|
|
73540
|
+
*
|
|
73541
|
+
* The URL names the owner TABLE and the event's GENERATION (D482 + D694):
|
|
73542
|
+
* a bare `${base}/${id}` was both ambiguous across tables and unable to
|
|
73543
|
+
* survive a reused rowid. New stamps always carry `/o/<type>/<id>/g/…`.
|
|
73408
73544
|
*/
|
|
73409
|
-
withMediaUrl(events) {
|
|
73545
|
+
withMediaUrl(events, ownerType) {
|
|
73410
73546
|
const base = this.deps.eventMediaBaseUrl();
|
|
73411
73547
|
if (base === null) return events;
|
|
73412
73548
|
return events.map((e) => ({
|
|
73413
73549
|
...e,
|
|
73414
|
-
mediaUrl:
|
|
73550
|
+
mediaUrl: mediaOwnerUrl(base, ownerType, String(e.id), {
|
|
73551
|
+
deviceId: e.deviceId,
|
|
73552
|
+
recordAt: e.timestamp
|
|
73553
|
+
})
|
|
73415
73554
|
}));
|
|
73416
73555
|
}
|
|
73417
73556
|
};
|
|
@@ -76243,18 +76382,26 @@ function eventMediaEtag(media) {
|
|
|
76243
76382
|
}
|
|
76244
76383
|
/** Digits only — `Number('')` is 0 and `Number(' ')` is 0, so neither is used. */
|
|
76245
76384
|
var DIGITS = /^\d+$/;
|
|
76385
|
+
/** One run of digits, for composing the event-owner remainder pattern. */
|
|
76386
|
+
var DIGIT_RUN = "\\d+";
|
|
76387
|
+
/**
|
|
76388
|
+
* Event-owner remainder with an optional generation suffix (D694):
|
|
76389
|
+
* `<ownerId>` or `<ownerId>/g/<deviceId>/<recordAt>`. Owner ids for event
|
|
76390
|
+
* tables are decimal rowids; the generation segment is the literal `g`.
|
|
76391
|
+
*/
|
|
76392
|
+
var EVENT_OWNER_REMAINDER = new RegExp(`^(${DIGIT_RUN})(?:/${MEDIA_GENERATION_SEGMENT}/(${DIGIT_RUN})/(${DIGIT_RUN}))?$`);
|
|
76246
76393
|
/**
|
|
76247
76394
|
* Read a plane path. `null` for anything that is not one of the two forms —
|
|
76248
76395
|
* including the BARE id the plane used to accept, which is gone: a bare number
|
|
76249
76396
|
* could be a media id or an event id, and after D474 an event id could be one
|
|
76250
76397
|
* of three rows. Ambiguity at the door is what this record removes.
|
|
76251
76398
|
*
|
|
76252
|
-
*
|
|
76253
|
-
* hub's wildcard param is percent-decoded by find-my-way before the
|
|
76254
|
-
* is proxied, so a `%2F` an owner id carried would arrive here as a
|
|
76255
|
-
* slash, and re-cutting on it would silently address a different owner.
|
|
76256
|
-
*
|
|
76257
|
-
*
|
|
76399
|
+
* For non-event owners the id is the whole remainder rather than one segment:
|
|
76400
|
+
* the hub's wildcard param is percent-decoded by find-my-way before the
|
|
76401
|
+
* sub-path is proxied, so a `%2F` an owner id carried would arrive here as a
|
|
76402
|
+
* real slash, and re-cutting on it would silently address a different owner.
|
|
76403
|
+
* Event owners (decimal rowids) may end with `/g/<deviceId>/<recordAt>` —
|
|
76404
|
+
* that suffix is the generation, not part of the id (D694).
|
|
76258
76405
|
*/
|
|
76259
76406
|
function parseMediaAddress(rawPath) {
|
|
76260
76407
|
const path = rawPath.replace(/^\/+/, "");
|
|
@@ -76274,13 +76421,35 @@ function parseMediaAddress(rawPath) {
|
|
|
76274
76421
|
const typeSlash = rest.indexOf("/");
|
|
76275
76422
|
if (typeSlash <= 0) return null;
|
|
76276
76423
|
const ownerType = rest.slice(0, typeSlash);
|
|
76277
|
-
const
|
|
76278
|
-
if (
|
|
76424
|
+
const remainder = rest.slice(typeSlash + 1);
|
|
76425
|
+
if (remainder.length === 0) return null;
|
|
76279
76426
|
if (!require_clip_model_registry.isMediaOwnerType(ownerType)) return null;
|
|
76427
|
+
if (require_clip_model_registry.isEventOwnerType(ownerType)) {
|
|
76428
|
+
const match = EVENT_OWNER_REMAINDER.exec(remainder);
|
|
76429
|
+
if (match === null) return null;
|
|
76430
|
+
const ownerId = match[1];
|
|
76431
|
+
const deviceIdRaw = match[2];
|
|
76432
|
+
const recordAtRaw = match[3];
|
|
76433
|
+
if (ownerId === void 0) return null;
|
|
76434
|
+
if (deviceIdRaw !== void 0 && recordAtRaw !== void 0) return {
|
|
76435
|
+
kind: "owner",
|
|
76436
|
+
ownerType,
|
|
76437
|
+
ownerId,
|
|
76438
|
+
generation: {
|
|
76439
|
+
deviceId: Number(deviceIdRaw),
|
|
76440
|
+
recordAt: Number(recordAtRaw)
|
|
76441
|
+
}
|
|
76442
|
+
};
|
|
76443
|
+
return {
|
|
76444
|
+
kind: "owner",
|
|
76445
|
+
ownerType,
|
|
76446
|
+
ownerId
|
|
76447
|
+
};
|
|
76448
|
+
}
|
|
76280
76449
|
return {
|
|
76281
76450
|
kind: "owner",
|
|
76282
76451
|
ownerType,
|
|
76283
|
-
ownerId
|
|
76452
|
+
ownerId: remainder
|
|
76284
76453
|
};
|
|
76285
76454
|
}
|
|
76286
76455
|
/** Set by the HUB for a `csv_*` share token admitted on this plane; never by a client. */
|
|
@@ -78957,6 +79126,218 @@ function createRetrainExportHandler(deps) {
|
|
|
78957
79126
|
};
|
|
78958
79127
|
}
|
|
78959
79128
|
//#endregion
|
|
79129
|
+
//#region src/pipeline-analytics/retrain/retrain-collection-store.ts
|
|
79130
|
+
/**
|
|
79131
|
+
* @durable class=ledger owner=pipeline-analytics
|
|
79132
|
+
* write="one row per named training collection the operator creates. Names are
|
|
79133
|
+
* unique on nameKey (lower(trim)); empty names never become rows."
|
|
79134
|
+
* retention="none — operator-owned corpus metadata. Rows leave only when an
|
|
79135
|
+
* explicit delete API lands (not V1)."
|
|
79136
|
+
*/
|
|
79137
|
+
var RETRAIN_COLLECTIONS_COLLECTION = "pipeline-analytics:retrain-collections";
|
|
79138
|
+
var RETRAIN_COLLECTION_COLUMNS = [
|
|
79139
|
+
{
|
|
79140
|
+
name: "id",
|
|
79141
|
+
type: "TEXT",
|
|
79142
|
+
primaryKey: true,
|
|
79143
|
+
notNull: true
|
|
79144
|
+
},
|
|
79145
|
+
{
|
|
79146
|
+
name: "name",
|
|
79147
|
+
type: "TEXT",
|
|
79148
|
+
notNull: true
|
|
79149
|
+
},
|
|
79150
|
+
{
|
|
79151
|
+
name: "nameKey",
|
|
79152
|
+
type: "TEXT",
|
|
79153
|
+
notNull: true
|
|
79154
|
+
},
|
|
79155
|
+
{
|
|
79156
|
+
name: "createdAt",
|
|
79157
|
+
type: "INTEGER",
|
|
79158
|
+
notNull: true
|
|
79159
|
+
},
|
|
79160
|
+
{
|
|
79161
|
+
name: "updatedAt",
|
|
79162
|
+
type: "INTEGER",
|
|
79163
|
+
notNull: true
|
|
79164
|
+
}
|
|
79165
|
+
];
|
|
79166
|
+
var RETRAIN_COLLECTION_INDEXES = [{
|
|
79167
|
+
name: "idx_retrain_collections_name_key",
|
|
79168
|
+
columns: ["nameKey"],
|
|
79169
|
+
unique: true
|
|
79170
|
+
}, {
|
|
79171
|
+
name: "idx_retrain_collections_created",
|
|
79172
|
+
columns: ["createdAt"]
|
|
79173
|
+
}];
|
|
79174
|
+
function normalizeCollectionName(raw) {
|
|
79175
|
+
const name = raw.trim();
|
|
79176
|
+
if (name.length === 0) return null;
|
|
79177
|
+
return {
|
|
79178
|
+
name,
|
|
79179
|
+
nameKey: name.toLowerCase()
|
|
79180
|
+
};
|
|
79181
|
+
}
|
|
79182
|
+
var RetrainCollectionStore = class {
|
|
79183
|
+
store;
|
|
79184
|
+
logger;
|
|
79185
|
+
now;
|
|
79186
|
+
newId;
|
|
79187
|
+
countFrames;
|
|
79188
|
+
constructor(deps) {
|
|
79189
|
+
this.store = deps.store;
|
|
79190
|
+
this.logger = deps.logger;
|
|
79191
|
+
this.now = deps.now;
|
|
79192
|
+
this.newId = deps.newId;
|
|
79193
|
+
this.countFrames = deps.countFrames;
|
|
79194
|
+
}
|
|
79195
|
+
static async declare(store) {
|
|
79196
|
+
await store.declareCollection.mutate({
|
|
79197
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79198
|
+
columns: [...RETRAIN_COLLECTION_COLUMNS],
|
|
79199
|
+
indexes: [...RETRAIN_COLLECTION_INDEXES]
|
|
79200
|
+
});
|
|
79201
|
+
}
|
|
79202
|
+
async create(input) {
|
|
79203
|
+
const normalized = normalizeCollectionName(input.name);
|
|
79204
|
+
if (normalized === null) return {
|
|
79205
|
+
kind: "refused",
|
|
79206
|
+
reason: "empty-name"
|
|
79207
|
+
};
|
|
79208
|
+
if (await this.findByNameKey(normalized.nameKey) !== null) return {
|
|
79209
|
+
kind: "refused",
|
|
79210
|
+
reason: "duplicate-name"
|
|
79211
|
+
};
|
|
79212
|
+
const createdAt = this.now();
|
|
79213
|
+
const id = this.newId();
|
|
79214
|
+
await this.store.set.mutate({
|
|
79215
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79216
|
+
key: id,
|
|
79217
|
+
value: {
|
|
79218
|
+
name: normalized.name,
|
|
79219
|
+
nameKey: normalized.nameKey,
|
|
79220
|
+
createdAt,
|
|
79221
|
+
updatedAt: createdAt
|
|
79222
|
+
}
|
|
79223
|
+
});
|
|
79224
|
+
return {
|
|
79225
|
+
kind: "created",
|
|
79226
|
+
collection: {
|
|
79227
|
+
id,
|
|
79228
|
+
name: normalized.name,
|
|
79229
|
+
createdAt,
|
|
79230
|
+
frameCount: 0
|
|
79231
|
+
}
|
|
79232
|
+
};
|
|
79233
|
+
}
|
|
79234
|
+
async list() {
|
|
79235
|
+
let rows;
|
|
79236
|
+
try {
|
|
79237
|
+
rows = await this.store.query.query({
|
|
79238
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79239
|
+
filter: { orderBy: {
|
|
79240
|
+
field: "createdAt",
|
|
79241
|
+
direction: "desc"
|
|
79242
|
+
} }
|
|
79243
|
+
});
|
|
79244
|
+
} catch (err) {
|
|
79245
|
+
this.logger.warn("retrain collections list failed", { meta: { error: String(err) } });
|
|
79246
|
+
return [];
|
|
79247
|
+
}
|
|
79248
|
+
const out = [];
|
|
79249
|
+
for (const row of rows) {
|
|
79250
|
+
const name = typeof row.data["name"] === "string" ? row.data["name"] : "";
|
|
79251
|
+
const createdAt = Number(row.data["createdAt"] ?? 0);
|
|
79252
|
+
const frameCount = this.countFrames !== void 0 ? await this.countFrames(row.id) : 0;
|
|
79253
|
+
out.push({
|
|
79254
|
+
id: row.id,
|
|
79255
|
+
name,
|
|
79256
|
+
createdAt,
|
|
79257
|
+
frameCount
|
|
79258
|
+
});
|
|
79259
|
+
}
|
|
79260
|
+
return out;
|
|
79261
|
+
}
|
|
79262
|
+
async getById(id) {
|
|
79263
|
+
try {
|
|
79264
|
+
const row = await this.store.get.query({
|
|
79265
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79266
|
+
key: id
|
|
79267
|
+
});
|
|
79268
|
+
if (row === null || row === void 0) return null;
|
|
79269
|
+
const data = row;
|
|
79270
|
+
return {
|
|
79271
|
+
id,
|
|
79272
|
+
name: typeof data["name"] === "string" ? data["name"] : "",
|
|
79273
|
+
createdAt: Number(data["createdAt"] ?? 0),
|
|
79274
|
+
frameCount: this.countFrames !== void 0 ? await this.countFrames(id) : 0
|
|
79275
|
+
};
|
|
79276
|
+
} catch (err) {
|
|
79277
|
+
this.logger.warn("retrain collection get failed", { meta: {
|
|
79278
|
+
collectionId: id,
|
|
79279
|
+
error: String(err)
|
|
79280
|
+
} });
|
|
79281
|
+
return null;
|
|
79282
|
+
}
|
|
79283
|
+
}
|
|
79284
|
+
/**
|
|
79285
|
+
* Stub until still ingest lands. Validates the collection exists, then
|
|
79286
|
+
* refuses with a named reason so callers can wire UI against a stable shape.
|
|
79287
|
+
*/
|
|
79288
|
+
async addRetrainFrameFromRecording(input) {
|
|
79289
|
+
if (await this.getById(input.collectionId) === null) {
|
|
79290
|
+
this.logger.info("retrain collection addFrame refused: unknown-collection", {
|
|
79291
|
+
tags: { deviceId: input.deviceId },
|
|
79292
|
+
meta: {
|
|
79293
|
+
collectionId: input.collectionId,
|
|
79294
|
+
epochMs: input.epochMs
|
|
79295
|
+
}
|
|
79296
|
+
});
|
|
79297
|
+
return {
|
|
79298
|
+
kind: "refused",
|
|
79299
|
+
reason: "unknown-collection"
|
|
79300
|
+
};
|
|
79301
|
+
}
|
|
79302
|
+
this.logger.info("retrain collection addFrame stub: still-capture-unimplemented", {
|
|
79303
|
+
tags: { deviceId: input.deviceId },
|
|
79304
|
+
meta: {
|
|
79305
|
+
collectionId: input.collectionId,
|
|
79306
|
+
epochMs: input.epochMs
|
|
79307
|
+
}
|
|
79308
|
+
});
|
|
79309
|
+
return {
|
|
79310
|
+
kind: "refused",
|
|
79311
|
+
reason: "still-capture-unimplemented",
|
|
79312
|
+
detail: "Still ingest lands in plan Task 3"
|
|
79313
|
+
};
|
|
79314
|
+
}
|
|
79315
|
+
async findByNameKey(nameKey) {
|
|
79316
|
+
try {
|
|
79317
|
+
const row = (await this.store.query.query({
|
|
79318
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79319
|
+
filter: {
|
|
79320
|
+
where: { nameKey },
|
|
79321
|
+
limit: 1
|
|
79322
|
+
}
|
|
79323
|
+
}))[0];
|
|
79324
|
+
if (row === void 0) return null;
|
|
79325
|
+
return {
|
|
79326
|
+
id: row.id,
|
|
79327
|
+
name: typeof row.data["name"] === "string" ? row.data["name"] : "",
|
|
79328
|
+
createdAt: Number(row.data["createdAt"] ?? 0),
|
|
79329
|
+
frameCount: 0
|
|
79330
|
+
};
|
|
79331
|
+
} catch (err) {
|
|
79332
|
+
this.logger.warn("retrain collection findByNameKey failed", { meta: {
|
|
79333
|
+
nameKey,
|
|
79334
|
+
error: String(err)
|
|
79335
|
+
} });
|
|
79336
|
+
return null;
|
|
79337
|
+
}
|
|
79338
|
+
}
|
|
79339
|
+
};
|
|
79340
|
+
//#endregion
|
|
78960
79341
|
//#region src/pipeline-analytics/retrain/retrain-assist.ts
|
|
78961
79342
|
/** The whole frame, as a normalised rectangle. */
|
|
78962
79343
|
var WHOLE_FRAME = {
|
|
@@ -82366,7 +82747,8 @@ async function rankKeyEvents(candidates, options, peakLookup) {
|
|
|
82366
82747
|
...t.markForTrain !== void 0 ? { markForTrain: t.markForTrain } : {},
|
|
82367
82748
|
...t.retrainStatus !== void 0 ? { retrainStatus: t.retrainStatus } : {},
|
|
82368
82749
|
...t.debug !== void 0 ? { debug: t.debug } : {},
|
|
82369
|
-
...t.favourited !== void 0 ? { favourited: t.favourited } : {}
|
|
82750
|
+
...t.favourited !== void 0 ? { favourited: t.favourited } : {},
|
|
82751
|
+
...t.audioLabels !== void 0 && t.audioLabels.length > 0 ? { audioLabels: t.audioLabels } : {}
|
|
82370
82752
|
});
|
|
82371
82753
|
}
|
|
82372
82754
|
scored.sort((a, b) => b.importance - a.importance);
|
|
@@ -82793,16 +83175,19 @@ var MotionOnsetRegistry = class {
|
|
|
82793
83175
|
* - `resolveSearchThumbnailUrl` — the search-hit thumbnail resolver. It points
|
|
82794
83176
|
* a hit at the CLIP embedding row's OWN crop (the crop captured on the
|
|
82795
83177
|
* embedding's peak frame → the hit thumbnail IS the embedded crop), and only
|
|
82796
|
-
* falls back to the representative event's
|
|
82797
|
-
* crop yet, so a hit never renders blank.
|
|
83178
|
+
* falls back to the representative event's owner ladder when the embedding
|
|
83179
|
+
* row has no crop yet, so a hit never renders blank.
|
|
82798
83180
|
*/
|
|
82799
83181
|
function isClipObjectEmbedding(t, registry = require_clip_model_registry.BUILTIN_CLIP_MODELS) {
|
|
82800
83182
|
return Array.isArray(t.embedding) && t.embedding.length > 0 && typeof t.embeddingModelId === "string" && registry.resolve(t.embeddingModelId) !== null;
|
|
82801
83183
|
}
|
|
82802
83184
|
function resolveSearchThumbnailUrl(input) {
|
|
82803
83185
|
if (input.baseUrl === null) return void 0;
|
|
82804
|
-
|
|
82805
|
-
return
|
|
83186
|
+
if (input.embeddingMediaId !== void 0) return mediaFileUrl(input.baseUrl, input.embeddingMediaId);
|
|
83187
|
+
return mediaOwnerUrl(input.baseUrl, "object", input.eventId, {
|
|
83188
|
+
deviceId: input.deviceId,
|
|
83189
|
+
recordAt: input.recordAt
|
|
83190
|
+
});
|
|
82806
83191
|
}
|
|
82807
83192
|
//#endregion
|
|
82808
83193
|
//#region src/pipeline-analytics/pipeline/onboard-birth-authority.ts
|
|
@@ -93492,6 +93877,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
93492
93877
|
const { trackIds, eventIdsByTable } = questionsFor(refs);
|
|
93493
93878
|
let liveTracks;
|
|
93494
93879
|
const liveEvents = /* @__PURE__ */ new Map();
|
|
93880
|
+
const eventGens = /* @__PURE__ */ new Map();
|
|
93495
93881
|
let keptMedia = /* @__PURE__ */ new Set();
|
|
93496
93882
|
try {
|
|
93497
93883
|
liveTracks = trackIds.length > 0 ? await deps.liveTrackIds(trackIds) : /* @__PURE__ */ new Set();
|
|
@@ -93499,6 +93885,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
93499
93885
|
for (const [table, ids] of eventIdsByTable) {
|
|
93500
93886
|
if (ids.length === 0) continue;
|
|
93501
93887
|
liveEvents.set(table, await deps.liveEventIds(table, ids));
|
|
93888
|
+
if (deps.eventGenerations !== void 0 && scope.collection === MEDIA_COLLECTION) eventGens.set(table, await deps.eventGenerations(table, ids));
|
|
93502
93889
|
}
|
|
93503
93890
|
} catch (err) {
|
|
93504
93891
|
deps.logger.warn("orphan audit: liveness probe failed — scope abandoned for this run", { meta: {
|
|
@@ -93527,7 +93914,13 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
93527
93914
|
tally.unresolvable += 1;
|
|
93528
93915
|
continue;
|
|
93529
93916
|
}
|
|
93530
|
-
|
|
93917
|
+
let live = ref.kind === "track" ? liveTracks.has(ref.trackId) || keptMedia.has(row.key) : ref.kind === "event" ? liveEvents.get(ref.table)?.has(ref.eventId) ?? false : false;
|
|
93918
|
+
if (live && ref.kind === "event" && scope.collection === MEDIA_COLLECTION) {
|
|
93919
|
+
const gen = eventGens.get(ref.table)?.get(ref.eventId);
|
|
93920
|
+
const mediaDeviceId = num(row.data, scope.deviceIdField);
|
|
93921
|
+
if (gen !== void 0 && mediaDeviceId !== null && mediaDeviceId !== gen.deviceId) live = false;
|
|
93922
|
+
}
|
|
93923
|
+
if (live) continue;
|
|
93531
93924
|
const ts = num(row.data, scope.timestampField);
|
|
93532
93925
|
if (ts !== null && ts > graceFloor) {
|
|
93533
93926
|
tally.withinGrace += 1;
|
|
@@ -93542,7 +93935,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
93542
93935
|
break;
|
|
93543
93936
|
}
|
|
93544
93937
|
try {
|
|
93545
|
-
const outcome = await deps.reclaim(scope.collection, row.key);
|
|
93938
|
+
const outcome = await deps.reclaim(scope.collection, row.key, row);
|
|
93546
93939
|
if (outcome.rows === 0) continue;
|
|
93547
93940
|
tally.reclaimed += 1;
|
|
93548
93941
|
reclaimedInPage += 1;
|
|
@@ -93698,7 +94091,10 @@ var ORPHAN_AUDIT_DEFAULTS = {
|
|
|
93698
94091
|
intervalHours: OrphanAuditIntervalHoursSchema.parse(void 0),
|
|
93699
94092
|
pageSize: OrphanAuditPageSizeSchema.parse(void 0)
|
|
93700
94093
|
};
|
|
93701
|
-
|
|
94094
|
+
//#endregion
|
|
94095
|
+
//#region src/pipeline-analytics/retention/orphan-reclaimers.ts
|
|
94096
|
+
var OBJECT_EVENTS_COLLECTION = "pipeline-analytics:object-events";
|
|
94097
|
+
var RAW_DELETE_COLLECTIONS = new Set([OBJECT_EVENTS_COLLECTION, "pipeline-analytics:plates"]);
|
|
93702
94098
|
/**
|
|
93703
94099
|
* The `deleteRow` port: ONE row by its primary key `id` (what every
|
|
93704
94100
|
* raw-delete collection above declares), answering whether a row went.
|
|
@@ -93707,12 +94103,19 @@ var RAW_DELETE_COLLECTIONS = new Set(["pipeline-analytics:object-events", "pipel
|
|
|
93707
94103
|
* is `z.void()`, and reading it as `!== undefined` answered `false` for every
|
|
93708
94104
|
* row it really deleted — the audit counted every plates/object-events reclaim
|
|
93709
94105
|
* as "found nothing", and the per-scope reclaim ceiling never tripped.
|
|
94106
|
+
*
|
|
94107
|
+
* When `assert.trackId` is set, the delete also requires that column: an
|
|
94108
|
+
* object-events rowid alone is not an identity across the row's life (D694).
|
|
93710
94109
|
*/
|
|
93711
94110
|
function settingsRowDeleter(store) {
|
|
93712
|
-
return async (collection, key) => {
|
|
94111
|
+
return async (collection, key, assert) => {
|
|
94112
|
+
const where = assert !== void 0 ? {
|
|
94113
|
+
id: key,
|
|
94114
|
+
trackId: assert.trackId
|
|
94115
|
+
} : { id: key };
|
|
93713
94116
|
const { deleted } = await store.deleteWhere.mutate({
|
|
93714
94117
|
collection,
|
|
93715
|
-
filter: { where
|
|
94118
|
+
filter: { where }
|
|
93716
94119
|
});
|
|
93717
94120
|
return deleted > 0;
|
|
93718
94121
|
};
|
|
@@ -93727,14 +94130,19 @@ function settingsRowDeleter(store) {
|
|
|
93727
94130
|
* `scripts/check-orphan-reclaimers.ts` checks `index.ts` uses it.
|
|
93728
94131
|
*/
|
|
93729
94132
|
function settingsRowDeleterOf(getStore) {
|
|
93730
|
-
return async (collection, key) => {
|
|
94133
|
+
return async (collection, key, assert) => {
|
|
93731
94134
|
const store = getStore();
|
|
93732
94135
|
if (store === null) throw new Error("orphan audit: settings store not bound");
|
|
93733
|
-
return await settingsRowDeleter(store)(collection, key);
|
|
94136
|
+
return await settingsRowDeleter(store)(collection, key, assert);
|
|
93734
94137
|
};
|
|
93735
94138
|
}
|
|
94139
|
+
/** Pull the trackId the raw delete must re-assert, or `null` when absent. */
|
|
94140
|
+
function trackIdAssertOf(data) {
|
|
94141
|
+
const trackId = data["trackId"];
|
|
94142
|
+
return typeof trackId === "string" && trackId.length > 0 ? { trackId } : null;
|
|
94143
|
+
}
|
|
93736
94144
|
function buildOrphanReclaimer(ports) {
|
|
93737
|
-
return async (collection, key) => {
|
|
94145
|
+
return async (collection, key, data = {}) => {
|
|
93738
94146
|
if (collection === MEDIA_COLLECTION) {
|
|
93739
94147
|
const mediaId = Number(key);
|
|
93740
94148
|
if (!Number.isInteger(mediaId) || mediaId <= 0) {
|
|
@@ -93760,10 +94168,23 @@ function buildOrphanReclaimer(ports) {
|
|
|
93760
94168
|
rows: await ports.deleteTrackAlias(key) ? 1 : 0,
|
|
93761
94169
|
bytes: null
|
|
93762
94170
|
};
|
|
93763
|
-
if (RAW_DELETE_COLLECTIONS.has(collection))
|
|
93764
|
-
|
|
93765
|
-
|
|
93766
|
-
|
|
94171
|
+
if (RAW_DELETE_COLLECTIONS.has(collection)) {
|
|
94172
|
+
const assert = trackIdAssertOf(data);
|
|
94173
|
+
if (collection === OBJECT_EVENTS_COLLECTION && assert === null) {
|
|
94174
|
+
ports.logger.warn("orphan audit: object-events reclaim without trackId — row kept (id-only delete refused)", { meta: {
|
|
94175
|
+
collection,
|
|
94176
|
+
key
|
|
94177
|
+
} });
|
|
94178
|
+
return {
|
|
94179
|
+
rows: 0,
|
|
94180
|
+
bytes: null
|
|
94181
|
+
};
|
|
94182
|
+
}
|
|
94183
|
+
return {
|
|
94184
|
+
rows: await ports.deleteRow(collection, key, assert === null ? void 0 : assert) ? 1 : 0,
|
|
94185
|
+
bytes: null
|
|
94186
|
+
};
|
|
94187
|
+
}
|
|
93767
94188
|
throw new Error(`orphan audit: '${collection}' is not declared in RECLAIMED_COLLECTIONS — a collection walked by the audit must say how its rows are deleted, because the raw path leaves behind anything its store owns outside the row`);
|
|
93768
94189
|
};
|
|
93769
94190
|
}
|
|
@@ -100926,7 +101347,10 @@ function ofGeneration(files, scope) {
|
|
|
100926
101347
|
function makeNcOwnerMediaReader(deps) {
|
|
100927
101348
|
return async (ownerKind, ownerId, eventScope) => {
|
|
100928
101349
|
if (ownerKind === "track") return deps.media.listByOwner("track", await deps.resolveTrackOwnerId(ownerId));
|
|
100929
|
-
if (eventScope === void 0)
|
|
101350
|
+
if (eventScope === void 0) {
|
|
101351
|
+
deps.logger.error("object media read refused — NcEventMediaScope required", { meta: { ownerId } });
|
|
101352
|
+
return [];
|
|
101353
|
+
}
|
|
100930
101354
|
const onCamera = await deps.media.listByOwner("object", ownerId, eventScope.deviceId);
|
|
100931
101355
|
const kept = ofGeneration(onCamera, eventScope);
|
|
100932
101356
|
if (kept.length < onCamera.length) deps.logger.warn("event media of another record on a reused event id — not used", {
|
|
@@ -101163,8 +101587,14 @@ async function buildTrainingExportPlan(readers, input) {
|
|
|
101163
101587
|
const eventsByTrack = /* @__PURE__ */ new Map();
|
|
101164
101588
|
for (const link of eventLinks) {
|
|
101165
101589
|
const list = eventsByTrack.get(link.trackId);
|
|
101166
|
-
if (list) list.push(
|
|
101167
|
-
|
|
101590
|
+
if (list) list.push({
|
|
101591
|
+
eventId: link.eventId,
|
|
101592
|
+
recordAt: link.recordAt
|
|
101593
|
+
});
|
|
101594
|
+
else eventsByTrack.set(link.trackId, [{
|
|
101595
|
+
eventId: link.eventId,
|
|
101596
|
+
recordAt: link.recordAt
|
|
101597
|
+
}]);
|
|
101168
101598
|
}
|
|
101169
101599
|
for (const track of take) {
|
|
101170
101600
|
const files = [];
|
|
@@ -101184,15 +101614,17 @@ async function buildTrainingExportPlan(readers, input) {
|
|
|
101184
101614
|
ownerId: `${PLATE_OWNER_PREFIX}${track.trackId}`,
|
|
101185
101615
|
scope: "plate"
|
|
101186
101616
|
},
|
|
101187
|
-
...(eventsByTrack.get(track.trackId) ?? []).map((
|
|
101617
|
+
...(eventsByTrack.get(track.trackId) ?? []).map((ev) => ({
|
|
101188
101618
|
ownerKind: "object",
|
|
101189
|
-
ownerId: eventId,
|
|
101190
|
-
scope: `events/${eventId}
|
|
101619
|
+
ownerId: ev.eventId,
|
|
101620
|
+
scope: `events/${ev.eventId}`,
|
|
101621
|
+
recordAt: ev.recordAt
|
|
101191
101622
|
}))
|
|
101192
101623
|
];
|
|
101193
101624
|
for (const owner of owners) {
|
|
101194
|
-
const infos = await readers.listMediaInfo(owner.ownerKind, owner.ownerId);
|
|
101195
|
-
|
|
101625
|
+
const infos = await readers.listMediaInfo(owner.ownerKind, owner.ownerId, owner.ownerKind === "object" ? track.deviceId : void 0);
|
|
101626
|
+
const kept = owner.recordAt === void 0 ? infos : infos.filter((info) => info.timestamp === owner.recordAt);
|
|
101627
|
+
for (const info of kept) {
|
|
101196
101628
|
const archivePath = archivePathFor({
|
|
101197
101629
|
deviceId: track.deviceId,
|
|
101198
101630
|
trackId: track.trackId,
|
|
@@ -102011,6 +102443,10 @@ var NC_ARTIFACT_PRUNE_INTERVAL_MS = 36e5;
|
|
|
102011
102443
|
* (every camera switched off): the bound is 24 h, not "24 h after the next
|
|
102012
102444
|
* close" (D681). */
|
|
102013
102445
|
var TAPE_SWEEP_INTERVAL_MS = 36e5;
|
|
102446
|
+
/** How often open tape segments push their pending gzip output to disk
|
|
102447
|
+
* without rotating (D681 follow-up): a silent camera otherwise keeps its
|
|
102448
|
+
* tail only in the zlib buffer until the next frame or the 10-minute cut. */
|
|
102449
|
+
var TAPE_FLUSH_INTERVAL_MS = 6e4;
|
|
102014
102450
|
/** What a publish answers on a node whose artifact plane never came up: no
|
|
102015
102451
|
* link, no index row — and, exactly as before, bytes-only delivery. */
|
|
102016
102452
|
var NO_PUBLICATION = {
|
|
@@ -102544,6 +102980,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
102544
102980
|
ncArtifactReadDataPlane = null;
|
|
102545
102981
|
ncArtifactPruneTimer = null;
|
|
102546
102982
|
tapeSweepTimer = null;
|
|
102983
|
+
tapeFlushTimer = null;
|
|
102547
102984
|
/** Why `startTrackingTapes` threw on a node that was allowed to record —
|
|
102548
102985
|
* reported `failing` per switched-on camera, never as `recording`. */
|
|
102549
102986
|
tapeStartFailure = null;
|
|
@@ -104348,6 +104785,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
104348
104785
|
StationaryObjectRegistry.declare(api.settingsStore),
|
|
104349
104786
|
RetrainFrameStore.declare(api.settingsStore),
|
|
104350
104787
|
RetrainAnnotationStore.declare(api.settingsStore),
|
|
104788
|
+
RetrainCollectionStore.declare(api.settingsStore),
|
|
104351
104789
|
OpsLogStore.declare(api.settingsStore),
|
|
104352
104790
|
DebugNoteArchiveStore.declare(api.settingsStore),
|
|
104353
104791
|
TrackingTapeIndexStore.declare(api.settingsStore),
|
|
@@ -105703,15 +106141,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
105703
106141
|
if (!trackStore || !mediaStore) throw new Error("training export: analytics stores unavailable");
|
|
105704
106142
|
return buildTrainingExportPlan({
|
|
105705
106143
|
listMarkedTracks: (deviceId, limit) => trackStore.listStaging(deviceId, limit),
|
|
105706
|
-
listMediaInfo: async (ownerKind, ownerId) => {
|
|
106144
|
+
listMediaInfo: async (ownerKind, ownerId, deviceId) => {
|
|
105707
106145
|
if (!isMediaOwnerKind(ownerKind)) return [];
|
|
105708
|
-
return mediaStore.listInfoByOwner(ownerKind, ownerId);
|
|
106146
|
+
return mediaStore.listInfoByOwner(ownerKind, ownerId, deviceId);
|
|
105709
106147
|
},
|
|
105710
106148
|
listEventIdsForTracks: async (trackIds) => {
|
|
105711
106149
|
if (!eventStore || trackIds.length === 0) return [];
|
|
105712
106150
|
return (await eventStore.queryObjectByTrackIds(trackIds)).flatMap((e) => e.trackId === void 0 ? [] : [{
|
|
105713
106151
|
eventId: String(e.id),
|
|
105714
|
-
trackId: e.trackId
|
|
106152
|
+
trackId: e.trackId,
|
|
106153
|
+
recordAt: e.timestamp
|
|
105715
106154
|
}]);
|
|
105716
106155
|
},
|
|
105717
106156
|
now: () => Date.now()
|
|
@@ -106795,6 +107234,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
106795
107234
|
clearInterval(this.tapeSweepTimer);
|
|
106796
107235
|
this.tapeSweepTimer = null;
|
|
106797
107236
|
}
|
|
107237
|
+
if (this.tapeFlushTimer !== null) {
|
|
107238
|
+
clearInterval(this.tapeFlushTimer);
|
|
107239
|
+
this.tapeFlushTimer = null;
|
|
107240
|
+
}
|
|
106798
107241
|
if (this.ncArtifactPruneTimer !== null) {
|
|
106799
107242
|
clearInterval(this.ncArtifactPruneTimer);
|
|
106800
107243
|
this.ncArtifactPruneTimer = null;
|
|
@@ -111912,26 +112355,30 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
111912
112355
|
},
|
|
111913
112356
|
liveTrackIds: (ids) => this.liveTrackIdsThroughAliases(ids, trackStore),
|
|
111914
112357
|
liveEventIds: (table, ids) => eventStore.existingEventIdsIn(table, ids),
|
|
112358
|
+
eventGenerations: (table, ids) => eventStore.eventGenerationsIn(table, ids),
|
|
111915
112359
|
keptMediaKeys: async (keys) => this.keptStationaryMediaKeys(keys),
|
|
111916
|
-
reclaim:
|
|
111917
|
-
|
|
111918
|
-
|
|
111919
|
-
|
|
111920
|
-
|
|
111921
|
-
|
|
111922
|
-
|
|
111923
|
-
|
|
111924
|
-
|
|
111925
|
-
|
|
111926
|
-
|
|
111927
|
-
|
|
111928
|
-
|
|
111929
|
-
|
|
111930
|
-
|
|
111931
|
-
|
|
111932
|
-
|
|
111933
|
-
|
|
111934
|
-
|
|
112360
|
+
reclaim: (() => {
|
|
112361
|
+
const reclaimOne = buildOrphanReclaimer({
|
|
112362
|
+
deleteFace: async (faceId) => {
|
|
112363
|
+
await this.faceStore?.delete(faceId);
|
|
112364
|
+
},
|
|
112365
|
+
deleteMediaConfirmed: async (mediaId) => {
|
|
112366
|
+
const count = await mediaStore.deleteConfirmed(mediaId);
|
|
112367
|
+
return {
|
|
112368
|
+
rows: count.rows,
|
|
112369
|
+
bytes: count.bytes
|
|
112370
|
+
};
|
|
112371
|
+
},
|
|
112372
|
+
deleteTrackAlias: async (aliasId) => {
|
|
112373
|
+
const aliasStore = this.trackAliasStore;
|
|
112374
|
+
if (aliasStore === null) throw new Error("track alias store not bound");
|
|
112375
|
+
return await aliasStore.deleteAlias(aliasId);
|
|
112376
|
+
},
|
|
112377
|
+
deleteRow: settingsRowDeleterOf(() => this.settingsStore),
|
|
112378
|
+
logger: this.ctx.logger
|
|
112379
|
+
});
|
|
112380
|
+
return (collection, key, row) => reclaimOne(collection, key, row.data);
|
|
112381
|
+
})(),
|
|
111935
112382
|
now: () => Date.now(),
|
|
111936
112383
|
logger: this.ctx.logger,
|
|
111937
112384
|
breathe: () => new Promise((resolve) => setImmediate(resolve))
|
|
@@ -113564,10 +114011,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
113564
114011
|
});
|
|
113565
114012
|
const scored = [];
|
|
113566
114013
|
for (const { event, score } of bestEventByTrackId.values()) {
|
|
114014
|
+
const embeddingMediaId = event.trackId !== void 0 ? embeddingMediaIdByTrackId.get(event.trackId) : void 0;
|
|
113567
114015
|
const mediaUrl = resolveSearchThumbnailUrl({
|
|
113568
114016
|
baseUrl: this.eventMediaBaseUrl,
|
|
113569
114017
|
eventId: String(event.id),
|
|
113570
|
-
|
|
114018
|
+
deviceId: event.deviceId,
|
|
114019
|
+
recordAt: event.timestamp,
|
|
114020
|
+
...embeddingMediaId !== void 0 ? { embeddingMediaId } : {}
|
|
113571
114021
|
});
|
|
113572
114022
|
const keyFrameMediaId = event.trackId !== void 0 ? keyFrameMediaIdByTrackId.get(event.trackId) : void 0;
|
|
113573
114023
|
scored.push({
|
|
@@ -113954,7 +114404,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
113954
114404
|
logger
|
|
113955
114405
|
});
|
|
113956
114406
|
const sweep = (deviceId) => {
|
|
113957
|
-
shelf.sweep().
|
|
114407
|
+
shelf.sweep().then((report) => {
|
|
114408
|
+
logTapeSweepReport(report, logger);
|
|
114409
|
+
}).catch((err) => {
|
|
113958
114410
|
logger.warn("tracking tape sweep failed", {
|
|
113959
114411
|
...deviceId !== null ? { tags: { deviceId } } : {},
|
|
113960
114412
|
meta: { error: require_clip_model_registry.errMsg(err) }
|
|
@@ -113986,6 +114438,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
113986
114438
|
sweep(null);
|
|
113987
114439
|
this.tapeSweepTimer = setInterval(() => sweep(null), TAPE_SWEEP_INTERVAL_MS);
|
|
113988
114440
|
this.tapeSweepTimer.unref?.();
|
|
114441
|
+
this.tapeFlushTimer = setInterval(() => {
|
|
114442
|
+
this.tapeRecorder?.flushOpenSegments();
|
|
114443
|
+
}, TAPE_FLUSH_INTERVAL_MS);
|
|
114444
|
+
this.tapeFlushTimer.unref?.();
|
|
113989
114445
|
this.ctx.logger.info("tracking tapes ready", { meta: { seededProcessors: this.processorInputs.size } });
|
|
113990
114446
|
} catch (err) {
|
|
113991
114447
|
this.tapeStartFailure = require_clip_model_registry.errMsg(err);
|
|
@@ -114178,7 +114634,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114178
114634
|
segments.set(deviceId, await this.tapeListingOf(shelf, deviceId));
|
|
114179
114635
|
}
|
|
114180
114636
|
const recorder = this.tapeRecorder;
|
|
114181
|
-
|
|
114637
|
+
const cameras = describeTapeStatus({
|
|
114182
114638
|
deviceIds,
|
|
114183
114639
|
switchOn,
|
|
114184
114640
|
switchUnreadable,
|
|
@@ -114187,7 +114643,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114187
114643
|
startFailure: this.tapeStartFailure,
|
|
114188
114644
|
segments,
|
|
114189
114645
|
now: Date.now()
|
|
114190
|
-
})
|
|
114646
|
+
});
|
|
114647
|
+
return { cameras: input.deviceIds === void 0 ? defaultTapeStatusCameras(cameras) : cameras };
|
|
114191
114648
|
},
|
|
114192
114649
|
"debug.tape.promote": async (input) => {
|
|
114193
114650
|
const deps = this.tapePromotionDeps();
|
|
@@ -114636,7 +115093,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114636
115093
|
* owner table, so the motion/object coin-flip is gone from here too.
|
|
114637
115094
|
*/
|
|
114638
115095
|
async readMediaByAddress(address, variant, preferKind) {
|
|
114639
|
-
const base = address.kind === "row" ? await this.readMediaById(address.mediaId) : await this.readOwnerThumbnail(address.ownerType, address.ownerId, preferKind);
|
|
115096
|
+
const base = address.kind === "row" ? await this.readMediaById(address.mediaId) : await this.readOwnerThumbnail(address.ownerType, address.ownerId, preferKind, address.generation);
|
|
114640
115097
|
if (base === null || variant === void 0) return base;
|
|
114641
115098
|
return this.applyThumbVariant(base, variant);
|
|
114642
115099
|
}
|
|
@@ -114694,9 +115151,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114694
115151
|
* ~144 MB and allocated ~190 MB of base64 to deliver ~1.2 MB, all of it on
|
|
114695
115152
|
* the single-threaded post-analysis runner.
|
|
114696
115153
|
*/
|
|
114697
|
-
async readOwnerThumbnail(ownerType, rawOwnerId, preferKind) {
|
|
115154
|
+
async readOwnerThumbnail(ownerType, rawOwnerId, preferKind, addressGeneration) {
|
|
114698
115155
|
const ownerId = ownerType === "track" ? await resolveMediaOwnerId(ownerType, rawOwnerId, (id) => this.trackAliasStore?.resolve(id) ?? Promise.resolve(null), (failure) => this.aliasResolveFailureLogger?.(failure), "readOwnerThumbnail") : rawOwnerId;
|
|
114699
|
-
const listInfo = (ownerKind, id) => this.mediaStore?.listInfoByOwner(ownerKind, id) ?? Promise.resolve([]);
|
|
115156
|
+
const listInfo = (ownerKind, id, deviceId) => this.mediaStore?.listInfoByOwner(ownerKind, id, deviceId) ?? Promise.resolve([]);
|
|
114700
115157
|
const getTrackIdForEvent = (eventId) => require_clip_model_registry.isEventOwnerType(ownerType) ? this.eventStore?.getTrackIdForEvent(eventId, ownerType) ?? Promise.resolve(null) : Promise.resolve(null);
|
|
114701
115158
|
const readBlob = async (mediaId) => {
|
|
114702
115159
|
const file = await (this.mediaStore?.getById(mediaId) ?? Promise.resolve(null));
|
|
@@ -114707,7 +115164,30 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114707
115164
|
deviceId: file.deviceId
|
|
114708
115165
|
};
|
|
114709
115166
|
};
|
|
114710
|
-
|
|
115167
|
+
let ownFiles = await listInfo(ownerType, ownerId);
|
|
115168
|
+
if (require_clip_model_registry.isEventOwnerType(ownerType)) {
|
|
115169
|
+
const gen = addressGeneration !== void 0 ? {
|
|
115170
|
+
deviceId: addressGeneration.deviceId,
|
|
115171
|
+
timestamp: addressGeneration.recordAt
|
|
115172
|
+
} : this.eventStore !== null ? await this.eventStore.eventGenerationOf(ownerType, ownerId) : null;
|
|
115173
|
+
if (gen !== null) {
|
|
115174
|
+
const onCamera = await listInfo(ownerType, ownerId, gen.deviceId);
|
|
115175
|
+
const kept = onCamera.filter((f) => f.timestamp === gen.timestamp);
|
|
115176
|
+
if (kept.length < onCamera.length) this.ctx.logger.warn("event media of another record on a reused event id — not used", {
|
|
115177
|
+
tags: { deviceId: gen.deviceId },
|
|
115178
|
+
meta: {
|
|
115179
|
+
ownerId,
|
|
115180
|
+
ownerType,
|
|
115181
|
+
recordAt: gen.timestamp,
|
|
115182
|
+
refused: onCamera.length - kept.length,
|
|
115183
|
+
kept: kept.length,
|
|
115184
|
+
via: "readOwnerThumbnail",
|
|
115185
|
+
scopedBy: addressGeneration !== void 0 ? "url" : "live-row"
|
|
115186
|
+
}
|
|
115187
|
+
});
|
|
115188
|
+
ownFiles = kept;
|
|
115189
|
+
}
|
|
115190
|
+
}
|
|
114711
115191
|
if (preferKind !== void 0 && preferKind.length > 0) {
|
|
114712
115192
|
const layers = await collectCropForcedMedia({
|
|
114713
115193
|
listByOwner: listInfo,
|