@camstack/addon-benchmark 1.2.194 → 1.2.196
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/{MotionZonesSettings-DgxFIEOT.mjs → MotionZonesSettings-YVl_5ifN.mjs} +19 -19
- package/dist/{PrivacyMaskSettings-C6LOtk8a.mjs → PrivacyMaskSettings-IsSFUgZT.mjs} +6 -6
- package/dist/{SceneMonitorEditor-af5EWFF8.mjs → SceneMonitorEditor-C6CN_Dd5.mjs} +304 -304
- package/dist/_stub.js +5852 -8046
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-BxlQX0Er.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-Dh7yN6kA.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_benchmark_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BjHHTPgv.mjs +26 -0
- package/dist/addon-benchmark.css +1 -1
- package/dist/{hostInit-C2lWuMo-.mjs → hostInit-CSIVvOiW.mjs} +2 -2
- package/dist/index.js +47 -1797
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -1800
- package/dist/index.mjs.map +1 -1
- package/dist/{player-overlays-Bd3_pIO9.mjs → player-overlays-CYe1ImiF.mjs} +5 -5
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-VI5WFdFh.mjs → responsive-8b-T948b.mjs} +3 -3
- package/dist/{scene-monitor-copy-D57V9t_V.mjs → scene-monitor-copy-BMIeJLtU.mjs} +1 -1
- package/dist/{square-EOouUJm_.mjs → square-m9Cdamuf.mjs} +1 -1
- package/dist/{trash-2-pWdn99SC.mjs → trash-2-CWiiPcjD.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-C8X7xy7Z.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-1YmYXS3x.mjs} +1 -1
- package/package.json +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_benchmark_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-xRXRRMbm.mjs +0 -26
package/dist/index.js
CHANGED
|
@@ -26,7 +26,10 @@ __export(src_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(src_exports);
|
|
27
27
|
|
|
28
28
|
// src/benchmark-addon.ts
|
|
29
|
-
var
|
|
29
|
+
var import_types3 = require("@camstack/types");
|
|
30
|
+
|
|
31
|
+
// src/benchmark-actions.ts
|
|
32
|
+
var import_types2 = require("@camstack/types");
|
|
30
33
|
|
|
31
34
|
// ../../node_modules/zod/v4/classic/external.js
|
|
32
35
|
var external_exports = {};
|
|
@@ -11518,42 +11521,42 @@ function extractDefs(ctx, schema) {
|
|
|
11518
11521
|
if (!root)
|
|
11519
11522
|
throw new Error("Unprocessed schema. This is a bug in Zod.");
|
|
11520
11523
|
const idToSchema = /* @__PURE__ */ new Map();
|
|
11521
|
-
for (const
|
|
11522
|
-
const id = ctx.metadataRegistry.get(
|
|
11524
|
+
for (const entry of ctx.seen.entries()) {
|
|
11525
|
+
const id = ctx.metadataRegistry.get(entry[0])?.id;
|
|
11523
11526
|
if (id) {
|
|
11524
11527
|
const existing = idToSchema.get(id);
|
|
11525
|
-
if (existing && existing !==
|
|
11528
|
+
if (existing && existing !== entry[0]) {
|
|
11526
11529
|
throw new Error(`Duplicate schema id "${id}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);
|
|
11527
11530
|
}
|
|
11528
|
-
idToSchema.set(id,
|
|
11531
|
+
idToSchema.set(id, entry[0]);
|
|
11529
11532
|
}
|
|
11530
11533
|
}
|
|
11531
|
-
const makeURI = (
|
|
11534
|
+
const makeURI = (entry) => {
|
|
11532
11535
|
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
11533
11536
|
if (ctx.external) {
|
|
11534
|
-
const externalId = ctx.external.registry.get(
|
|
11537
|
+
const externalId = ctx.external.registry.get(entry[0])?.id;
|
|
11535
11538
|
const uriGenerator = ctx.external.uri ?? ((id2) => id2);
|
|
11536
11539
|
if (externalId) {
|
|
11537
11540
|
return { ref: uriGenerator(externalId) };
|
|
11538
11541
|
}
|
|
11539
|
-
const id =
|
|
11540
|
-
|
|
11542
|
+
const id = entry[1].defId ?? entry[1].schema.id ?? `schema${ctx.counter++}`;
|
|
11543
|
+
entry[1].defId = id;
|
|
11541
11544
|
return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` };
|
|
11542
11545
|
}
|
|
11543
|
-
if (
|
|
11546
|
+
if (entry[1] === root) {
|
|
11544
11547
|
return { ref: "#" };
|
|
11545
11548
|
}
|
|
11546
11549
|
const uriPrefix = `#`;
|
|
11547
11550
|
const defUriPrefix = `${uriPrefix}/${defsSegment}/`;
|
|
11548
|
-
const defId =
|
|
11551
|
+
const defId = entry[1].schema.id ?? `__schema${ctx.counter++}`;
|
|
11549
11552
|
return { defId, ref: defUriPrefix + defId };
|
|
11550
11553
|
};
|
|
11551
|
-
const extractToDef = (
|
|
11552
|
-
if (
|
|
11554
|
+
const extractToDef = (entry) => {
|
|
11555
|
+
if (entry[1].schema.$ref) {
|
|
11553
11556
|
return;
|
|
11554
11557
|
}
|
|
11555
|
-
const seen =
|
|
11556
|
-
const { ref, defId } = makeURI(
|
|
11558
|
+
const seen = entry[1];
|
|
11559
|
+
const { ref, defId } = makeURI(entry);
|
|
11557
11560
|
seen.def = { ...seen.schema };
|
|
11558
11561
|
if (defId)
|
|
11559
11562
|
seen.defId = defId;
|
|
@@ -11564,8 +11567,8 @@ function extractDefs(ctx, schema) {
|
|
|
11564
11567
|
schema2.$ref = ref;
|
|
11565
11568
|
};
|
|
11566
11569
|
if (ctx.cycles === "throw") {
|
|
11567
|
-
for (const
|
|
11568
|
-
const seen =
|
|
11570
|
+
for (const entry of ctx.seen.entries()) {
|
|
11571
|
+
const seen = entry[1];
|
|
11569
11572
|
if (seen.cycle) {
|
|
11570
11573
|
throw new Error(`Cycle detected: #/${seen.cycle?.join("/")}/<root>
|
|
11571
11574
|
|
|
@@ -11573,31 +11576,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
11573
11576
|
}
|
|
11574
11577
|
}
|
|
11575
11578
|
}
|
|
11576
|
-
for (const
|
|
11577
|
-
const seen =
|
|
11578
|
-
if (schema ===
|
|
11579
|
-
extractToDef(
|
|
11579
|
+
for (const entry of ctx.seen.entries()) {
|
|
11580
|
+
const seen = entry[1];
|
|
11581
|
+
if (schema === entry[0]) {
|
|
11582
|
+
extractToDef(entry);
|
|
11580
11583
|
continue;
|
|
11581
11584
|
}
|
|
11582
11585
|
if (ctx.external) {
|
|
11583
|
-
const ext = ctx.external.registry.get(
|
|
11584
|
-
if (schema !==
|
|
11585
|
-
extractToDef(
|
|
11586
|
+
const ext = ctx.external.registry.get(entry[0])?.id;
|
|
11587
|
+
if (schema !== entry[0] && ext) {
|
|
11588
|
+
extractToDef(entry);
|
|
11586
11589
|
continue;
|
|
11587
11590
|
}
|
|
11588
11591
|
}
|
|
11589
|
-
const id = ctx.metadataRegistry.get(
|
|
11592
|
+
const id = ctx.metadataRegistry.get(entry[0])?.id;
|
|
11590
11593
|
if (id) {
|
|
11591
|
-
extractToDef(
|
|
11594
|
+
extractToDef(entry);
|
|
11592
11595
|
continue;
|
|
11593
11596
|
}
|
|
11594
11597
|
if (seen.cycle) {
|
|
11595
|
-
extractToDef(
|
|
11598
|
+
extractToDef(entry);
|
|
11596
11599
|
continue;
|
|
11597
11600
|
}
|
|
11598
11601
|
if (seen.count > 1) {
|
|
11599
11602
|
if (ctx.reused === "ref") {
|
|
11600
|
-
extractToDef(
|
|
11603
|
+
extractToDef(entry);
|
|
11601
11604
|
continue;
|
|
11602
11605
|
}
|
|
11603
11606
|
}
|
|
@@ -11669,8 +11672,8 @@ function finalize(ctx, schema) {
|
|
|
11669
11672
|
path: seen.path ?? []
|
|
11670
11673
|
});
|
|
11671
11674
|
};
|
|
11672
|
-
for (const
|
|
11673
|
-
flattenRef(
|
|
11675
|
+
for (const entry of [...ctx.seen.entries()].reverse()) {
|
|
11676
|
+
flattenRef(entry[0]);
|
|
11674
11677
|
}
|
|
11675
11678
|
const result = {};
|
|
11676
11679
|
if (ctx.target === "draft-2020-12") {
|
|
@@ -11693,8 +11696,8 @@ function finalize(ctx, schema) {
|
|
|
11693
11696
|
if (rootMetaId !== void 0 && result.id === rootMetaId)
|
|
11694
11697
|
delete result.id;
|
|
11695
11698
|
const defs = ctx.external?.defs ?? {};
|
|
11696
|
-
for (const
|
|
11697
|
-
const seen =
|
|
11699
|
+
for (const entry of ctx.seen.entries()) {
|
|
11700
|
+
const seen = entry[1];
|
|
11698
11701
|
if (seen.def && seen.defId) {
|
|
11699
11702
|
if (seen.def.id === seen.defId)
|
|
11700
11703
|
delete seen.def.id;
|
|
@@ -12310,8 +12313,8 @@ function toJSONSchema(input, params) {
|
|
|
12310
12313
|
const registry2 = input;
|
|
12311
12314
|
const ctx2 = initializeContext({ ...params, processors: allProcessors });
|
|
12312
12315
|
const defs = {};
|
|
12313
|
-
for (const
|
|
12314
|
-
const [_, schema] =
|
|
12316
|
+
for (const entry of registry2._idmap.entries()) {
|
|
12317
|
+
const [_, schema] = entry;
|
|
12315
12318
|
process(schema, ctx2);
|
|
12316
12319
|
}
|
|
12317
12320
|
const schemas = {};
|
|
@@ -12321,8 +12324,8 @@ function toJSONSchema(input, params) {
|
|
|
12321
12324
|
defs
|
|
12322
12325
|
};
|
|
12323
12326
|
ctx2.external = external;
|
|
12324
|
-
for (const
|
|
12325
|
-
const [key, schema] =
|
|
12327
|
+
for (const entry of registry2._idmap.entries()) {
|
|
12328
|
+
const [key, schema] = entry;
|
|
12326
12329
|
extractDefs(ctx2, schema);
|
|
12327
12330
|
schemas[key] = finalize(ctx2, schema);
|
|
12328
12331
|
}
|
|
@@ -14542,205 +14545,10 @@ function date4(params) {
|
|
|
14542
14545
|
// ../../node_modules/zod/v4/classic/external.js
|
|
14543
14546
|
config(en_default());
|
|
14544
14547
|
|
|
14545
|
-
// src/benchmark-actions.ts
|
|
14546
|
-
var import_types3 = require("@camstack/types");
|
|
14547
|
-
|
|
14548
14548
|
// src/benchmark-schemas.ts
|
|
14549
|
-
var import_types2 = require("@camstack/types");
|
|
14550
|
-
|
|
14551
|
-
// src/session-replay/schemas.ts
|
|
14552
14549
|
var import_types = require("@camstack/types");
|
|
14553
|
-
var NormBoxSchema = external_exports.object({
|
|
14554
|
-
x: external_exports.number(),
|
|
14555
|
-
y: external_exports.number(),
|
|
14556
|
-
w: external_exports.number(),
|
|
14557
|
-
h: external_exports.number()
|
|
14558
|
-
});
|
|
14559
|
-
var ReplayBoxSchema = external_exports.object({
|
|
14560
|
-
className: external_exports.string(),
|
|
14561
|
-
score: external_exports.number(),
|
|
14562
|
-
box: NormBoxSchema
|
|
14563
|
-
});
|
|
14564
|
-
var StoredPositionSchema = external_exports.object({
|
|
14565
|
-
trackId: external_exports.string(),
|
|
14566
|
-
className: external_exports.string(),
|
|
14567
|
-
t: external_exports.number(),
|
|
14568
|
-
box: NormBoxSchema,
|
|
14569
|
-
score: external_exports.number().nullable()
|
|
14570
|
-
});
|
|
14571
|
-
var ReferenceBasisSchema = external_exports.enum(["exact", "interpolated", "uncertain"]);
|
|
14572
|
-
var ReferenceEntrySchema = StoredPositionSchema.extend({ basis: ReferenceBasisSchema });
|
|
14573
|
-
var TimelineReplayBoxSchema = ReplayBoxSchema.extend({
|
|
14574
|
-
/** `unreferenced`: no stored track was alive at this instant — nothing to contradict. */
|
|
14575
|
-
outcome: external_exports.enum(["matched", "class-changed", "added", "unreferenced"])
|
|
14576
|
-
});
|
|
14577
|
-
var TimelineStoredBoxSchema = ReferenceEntrySchema.extend({
|
|
14578
|
-
outcome: external_exports.enum(["matched", "class-changed", "missed"])
|
|
14579
|
-
});
|
|
14580
|
-
var TimelineFrameSchema = external_exports.object({
|
|
14581
|
-
t: external_exports.number(),
|
|
14582
|
-
/** Some stored track was alive at `t` — the instant HAS a reference. */
|
|
14583
|
-
referenced: external_exports.boolean(),
|
|
14584
|
-
replay: external_exports.array(TimelineReplayBoxSchema).readonly(),
|
|
14585
|
-
stored: external_exports.array(TimelineStoredBoxSchema).readonly(),
|
|
14586
|
-
inferenceMs: external_exports.number().nullable()
|
|
14587
|
-
});
|
|
14588
|
-
var ClassCountsSchema = external_exports.object({
|
|
14589
|
-
replay: external_exports.number(),
|
|
14590
|
-
stored: external_exports.number(),
|
|
14591
|
-
matched: external_exports.number(),
|
|
14592
|
-
added: external_exports.number(),
|
|
14593
|
-
missed: external_exports.number(),
|
|
14594
|
-
unreferenced: external_exports.number(),
|
|
14595
|
-
/** Comparisons (matched / class-changed / missed) made on a non-exact stored box. */
|
|
14596
|
-
interpolated: external_exports.number()
|
|
14597
|
-
});
|
|
14598
|
-
var ReplaySummarySchema = external_exports.object({
|
|
14599
|
-
totals: external_exports.object({
|
|
14600
|
-
matched: external_exports.number(),
|
|
14601
|
-
classChanged: external_exports.number(),
|
|
14602
|
-
added: external_exports.number(),
|
|
14603
|
-
missed: external_exports.number(),
|
|
14604
|
-
unreferenced: external_exports.number(),
|
|
14605
|
-
interpolated: external_exports.number()
|
|
14606
|
-
}),
|
|
14607
|
-
perClass: external_exports.record(external_exports.string(), ClassCountsSchema),
|
|
14608
|
-
scoreDelta: external_exports.object({ mean: external_exports.number(), p10: external_exports.number(), p50: external_exports.number(), p90: external_exports.number() }).nullable(),
|
|
14609
|
-
inferenceMs: external_exports.object({ p50: external_exports.number(), p95: external_exports.number(), count: external_exports.number() }).nullable()
|
|
14610
|
-
});
|
|
14611
|
-
var CadenceSourceSchema = external_exports.enum(["operator", "stored-positions", "default"]);
|
|
14612
|
-
var ReplayCadenceSchema = external_exports.object({ fps: external_exports.number(), source: CadenceSourceSchema });
|
|
14613
|
-
var SessionReplayReportSchema = external_exports.object({
|
|
14614
|
-
deviceId: external_exports.number(),
|
|
14615
|
-
motionEventId: external_exports.number(),
|
|
14616
|
-
window: external_exports.object({
|
|
14617
|
-
startMs: external_exports.number(),
|
|
14618
|
-
endMs: external_exports.number(),
|
|
14619
|
-
motionStartMs: external_exports.number(),
|
|
14620
|
-
motionEndMs: external_exports.number(),
|
|
14621
|
-
/** Where the pre-roll / tail came from: the camera's own settings, or the fleet default. */
|
|
14622
|
-
prerollSource: external_exports.enum(["camera", "default"]),
|
|
14623
|
-
tailSource: external_exports.enum(["camera", "default"])
|
|
14624
|
-
}),
|
|
14625
|
-
pin: external_exports.object({
|
|
14626
|
-
nodeId: external_exports.string(),
|
|
14627
|
-
deviceKey: external_exports.string().nullable(),
|
|
14628
|
-
modelId: external_exports.string(),
|
|
14629
|
-
cameraModelId: external_exports.string().nullable()
|
|
14630
|
-
}),
|
|
14631
|
-
rootStep: import_types.PipelineStepInputSchema,
|
|
14632
|
-
cadence: ReplayCadenceSchema,
|
|
14633
|
-
frameSize: external_exports.object({ width: external_exports.number(), source: external_exports.enum(["stored-tracks", "default"]) }),
|
|
14634
|
-
frames: external_exports.object({
|
|
14635
|
-
planned: external_exports.number(),
|
|
14636
|
-
decoded: external_exports.number(),
|
|
14637
|
-
ran: external_exports.number(),
|
|
14638
|
-
failed: external_exports.number(),
|
|
14639
|
-
withoutFootage: external_exports.number(),
|
|
14640
|
-
notDecoded: external_exports.number(),
|
|
14641
|
-
truncated: external_exports.boolean(),
|
|
14642
|
-
truncatedBySafetyCap: external_exports.boolean(),
|
|
14643
|
-
aborted: external_exports.boolean()
|
|
14644
|
-
}),
|
|
14645
|
-
reference: external_exports.object({
|
|
14646
|
-
tracks: external_exports.number(),
|
|
14647
|
-
positionsCompared: external_exports.number(),
|
|
14648
|
-
recoveryExcluded: external_exports.number(),
|
|
14649
|
-
withoutGeometry: external_exports.number(),
|
|
14650
|
-
models: external_exports.record(external_exports.string(), external_exports.number()),
|
|
14651
|
-
/** The stored-track read: how many came back, and whether its limit cut it short. */
|
|
14652
|
-
read: external_exports.object({ tracks: external_exports.number(), truncated: external_exports.boolean() })
|
|
14653
|
-
}),
|
|
14654
|
-
confirmation: external_exports.object({
|
|
14655
|
-
liveVerdicts: external_exports.record(external_exports.string(), external_exports.number()),
|
|
14656
|
-
/** Added or unreferenced detections near a live SUPPRESSED birth of their class. */
|
|
14657
|
-
nearSuppressedBirth: external_exports.number()
|
|
14658
|
-
}),
|
|
14659
|
-
summary: ReplaySummarySchema,
|
|
14660
|
-
timeline: external_exports.array(TimelineFrameSchema).readonly(),
|
|
14661
|
-
failures: external_exports.array(external_exports.object({ t: external_exports.number(), error: external_exports.string() })).readonly()
|
|
14662
|
-
});
|
|
14663
|
-
var SessionReplayRefusalSchema = external_exports.enum([
|
|
14664
|
-
"session-not-found",
|
|
14665
|
-
"session-open",
|
|
14666
|
-
"window-too-long",
|
|
14667
|
-
"no-root-step",
|
|
14668
|
-
"model-unpinned",
|
|
14669
|
-
"model-not-servable",
|
|
14670
|
-
"device-not-servable",
|
|
14671
|
-
"no-recording",
|
|
14672
|
-
"no-footage",
|
|
14673
|
-
"session-length-unknown",
|
|
14674
|
-
"pool-failing"
|
|
14675
|
-
]);
|
|
14676
|
-
var SessionReplayOutcomeSchema = external_exports.discriminatedUnion("kind", [
|
|
14677
|
-
external_exports.object({
|
|
14678
|
-
kind: external_exports.literal("refused"),
|
|
14679
|
-
reason: SessionReplayRefusalSchema,
|
|
14680
|
-
detail: external_exports.string()
|
|
14681
|
-
}),
|
|
14682
|
-
external_exports.object({ kind: external_exports.literal("ran"), report: SessionReplayReportSchema })
|
|
14683
|
-
]);
|
|
14684
|
-
var SessionReplayProgressSchema = external_exports.object({
|
|
14685
|
-
phase: external_exports.enum(["preparing", "replaying", "done"]),
|
|
14686
|
-
framesPlanned: external_exports.number(),
|
|
14687
|
-
framesDone: external_exports.number()
|
|
14688
|
-
});
|
|
14689
|
-
|
|
14690
|
-
// src/benchmark-schemas.ts
|
|
14691
|
-
var BenchModeSchema = external_exports.enum(["inference", "full-tree", "live-frame"]);
|
|
14692
|
-
var BenchPoolTuningSchema = external_exports.object({
|
|
14693
|
-
runtime: external_exports.string().nullable(),
|
|
14694
|
-
numWorkers: external_exports.number().nullable(),
|
|
14695
|
-
concurrency: external_exports.number().nullable(),
|
|
14696
|
-
batchMode: external_exports.string().nullable(),
|
|
14697
|
-
maxBatchSize: external_exports.number().nullable()
|
|
14698
|
-
});
|
|
14699
|
-
var HistoryMeasurementFields = {
|
|
14700
|
-
stepId: external_exports.string(),
|
|
14701
|
-
modelId: external_exports.string(),
|
|
14702
|
-
iterations: external_exports.number(),
|
|
14703
|
-
warmup: external_exports.number(),
|
|
14704
|
-
fps: external_exports.number(),
|
|
14705
|
-
avgMs: external_exports.number(),
|
|
14706
|
-
medianMs: external_exports.number(),
|
|
14707
|
-
p95Ms: external_exports.number(),
|
|
14708
|
-
p99Ms: external_exports.number(),
|
|
14709
|
-
/**
|
|
14710
|
-
* OPTIONAL, and absent on every entry the page writes: the runner reports
|
|
14711
|
-
* mean / p50 / p95 / p99 and measures no per-call min or max. A fabricated
|
|
14712
|
-
* latency in the history table is worse than a blank one.
|
|
14713
|
-
*/
|
|
14714
|
-
minMs: external_exports.number().optional(),
|
|
14715
|
-
maxMs: external_exports.number().optional(),
|
|
14716
|
-
avgDetections: external_exports.number(),
|
|
14717
|
-
referenceImage: external_exports.string().optional(),
|
|
14718
|
-
/** Concurrent in-flight requests (simulated cameras) — NOT pool workers. */
|
|
14719
|
-
parallelRuns: external_exports.number().min(1).default(1)
|
|
14720
|
-
};
|
|
14721
|
-
var PipelineBenchmarkHistoryEntrySchema = external_exports.object({
|
|
14722
|
-
kind: external_exports.literal("pipeline").default("pipeline"),
|
|
14723
|
-
id: external_exports.string(),
|
|
14724
|
-
date: external_exports.string(),
|
|
14725
|
-
...HistoryMeasurementFields,
|
|
14726
|
-
nodeId: external_exports.string().optional(),
|
|
14727
|
-
deviceKey: external_exports.string().nullable().optional(),
|
|
14728
|
-
plane: external_exports.enum(["full", "frame"]).nullable().optional(),
|
|
14729
|
-
mode: BenchModeSchema.optional(),
|
|
14730
|
-
pool: BenchPoolTuningSchema.optional(),
|
|
14731
|
-
runtime: external_exports.string().optional()
|
|
14732
|
-
});
|
|
14733
|
-
var PipelineBenchmarkHistoryEntryInputSchema = external_exports.object({
|
|
14734
|
-
...HistoryMeasurementFields,
|
|
14735
|
-
nodeId: external_exports.string(),
|
|
14736
|
-
deviceKey: external_exports.string().nullable(),
|
|
14737
|
-
plane: external_exports.enum(["full", "frame"]).nullable(),
|
|
14738
|
-
mode: BenchModeSchema,
|
|
14739
|
-
pool: BenchPoolTuningSchema
|
|
14740
|
-
});
|
|
14741
|
-
var HistoryEntrySchema = PipelineBenchmarkHistoryEntrySchema;
|
|
14742
14550
|
var PipelineRunInputSchema = external_exports.object({
|
|
14743
|
-
steps: external_exports.array(
|
|
14551
|
+
steps: external_exports.array(import_types.PipelineStepInputSchema).readonly(),
|
|
14744
14552
|
imageBase64: external_exports.string().optional(),
|
|
14745
14553
|
referenceImage: external_exports.string().optional(),
|
|
14746
14554
|
/** Target node. Forwarded to `pipelineExecutor.runPipeline`. Omit to run on the hub. */
|
|
@@ -14750,1242 +14558,17 @@ var PipelineRunInputSchema = external_exports.object({
|
|
|
14750
14558
|
deviceKey: external_exports.string().optional(),
|
|
14751
14559
|
plane: external_exports.enum(["full", "frame"]).optional()
|
|
14752
14560
|
});
|
|
14753
|
-
var SyntheticBenchPoolSchema = external_exports.object({
|
|
14754
|
-
nodeId: external_exports.string(),
|
|
14755
|
-
deviceKey: external_exports.string().nullable(),
|
|
14756
|
-
runtime: external_exports.string().nullable(),
|
|
14757
|
-
numWorkers: external_exports.number().nullable(),
|
|
14758
|
-
concurrency: external_exports.number().nullable(),
|
|
14759
|
-
batchMode: external_exports.string().nullable(),
|
|
14760
|
-
maxBatchSize: external_exports.number().nullable()
|
|
14761
|
-
});
|
|
14762
|
-
var SyntheticBenchStatsSchema = external_exports.object({
|
|
14763
|
-
runs: external_exports.number(),
|
|
14764
|
-
wallSec: external_exports.number(),
|
|
14765
|
-
fps: external_exports.number(),
|
|
14766
|
-
detectionsPerSec: external_exports.number(),
|
|
14767
|
-
avgDetections: external_exports.number(),
|
|
14768
|
-
callMs: external_exports.object({ mean: external_exports.number(), p50: external_exports.number(), p95: external_exports.number(), p99: external_exports.number() }),
|
|
14769
|
-
inferMs: external_exports.number(),
|
|
14770
|
-
preprocessMs: external_exports.number(),
|
|
14771
|
-
predictMs: external_exports.number(),
|
|
14772
|
-
batchSizeMean: external_exports.number(),
|
|
14773
|
-
batchSizeMax: external_exports.number(),
|
|
14774
|
-
pool: SyntheticBenchPoolSchema,
|
|
14775
|
-
path: external_exports.string()
|
|
14776
|
-
});
|
|
14777
|
-
var SyntheticBenchStatusSchema = external_exports.object({
|
|
14778
|
-
sessionId: external_exports.string(),
|
|
14779
|
-
state: external_exports.enum(["running", "done", "failed", "aborted"]),
|
|
14780
|
-
startedAtMs: external_exports.number(),
|
|
14781
|
-
finishedAtMs: external_exports.number().nullable(),
|
|
14782
|
-
progress: external_exports.object({ runs: external_exports.number(), totalRuns: external_exports.number() }).nullable(),
|
|
14783
|
-
result: SyntheticBenchStatsSchema.nullable(),
|
|
14784
|
-
error: external_exports.string().nullable()
|
|
14785
|
-
}).nullable();
|
|
14786
|
-
var MotionSessionSchema = external_exports.object({
|
|
14787
|
-
motionEventId: external_exports.number(),
|
|
14788
|
-
deviceId: external_exports.number(),
|
|
14789
|
-
startMs: external_exports.number(),
|
|
14790
|
-
/** `null` while the session is still open. */
|
|
14791
|
-
durationMs: external_exports.number().nullable(),
|
|
14792
|
-
open: external_exports.boolean(),
|
|
14793
|
-
/** Why a replay of this session would be refused (`session-open`, `session-length-unknown`, …); `null` = replayable. */
|
|
14794
|
-
refusal: external_exports.string().nullable(),
|
|
14795
|
-
/** Stored tracks born inside the session window (slim read). */
|
|
14796
|
-
storedTracks: external_exports.number()
|
|
14797
|
-
});
|
|
14798
|
-
var ListMotionSessionsInputSchema = external_exports.object({
|
|
14799
|
-
deviceId: external_exports.number(),
|
|
14800
|
-
/** Default: the last 24 h. */
|
|
14801
|
-
sinceMs: external_exports.number().optional(),
|
|
14802
|
-
untilMs: external_exports.number().optional(),
|
|
14803
|
-
limit: external_exports.number().int().min(1).max(200).default(50)
|
|
14804
|
-
});
|
|
14805
|
-
var SESSION_REPLAY_MAX_FRAMES = 1200;
|
|
14806
|
-
var StartSessionReplayInputSchema = external_exports.object({
|
|
14807
|
-
deviceId: external_exports.number(),
|
|
14808
|
-
motionEventId: external_exports.number(),
|
|
14809
|
-
/** The session's start (`getMotionEvents.timestamp`) — the lookup is windowed on it. */
|
|
14810
|
-
motionStartMs: external_exports.number(),
|
|
14811
|
-
nodeId: external_exports.string(),
|
|
14812
|
-
/** Absent ⇒ the node-default pool. */
|
|
14813
|
-
deviceKey: external_exports.string().optional(),
|
|
14814
|
-
/** Absent ⇒ the model the camera's resolved tree names. */
|
|
14815
|
-
modelId: external_exports.string().optional(),
|
|
14816
|
-
/** Absent ⇒ measured from the stored positions (the live cadence). */
|
|
14817
|
-
fps: external_exports.number().min(1).max(30).optional(),
|
|
14818
|
-
maxFrames: external_exports.number().int().min(1).max(SESSION_REPLAY_MAX_FRAMES).default(600)
|
|
14819
|
-
});
|
|
14820
|
-
var StartSessionReplayAcceptedSchema = external_exports.object({
|
|
14821
|
-
accepted: external_exports.literal(true),
|
|
14822
|
-
runId: external_exports.string()
|
|
14823
|
-
});
|
|
14824
|
-
var SessionReplayKeySchema = external_exports.object({ runId: external_exports.string() });
|
|
14825
|
-
var SessionReplayStatusSchema = external_exports.object({
|
|
14826
|
-
runId: external_exports.string(),
|
|
14827
|
-
state: external_exports.enum(["running", "done", "failed", "aborted"]),
|
|
14828
|
-
startedAtMs: external_exports.number(),
|
|
14829
|
-
finishedAtMs: external_exports.number().nullable(),
|
|
14830
|
-
progress: SessionReplayProgressSchema.nullable(),
|
|
14831
|
-
outcome: SessionReplayOutcomeSchema.nullable(),
|
|
14832
|
-
error: external_exports.string().nullable()
|
|
14833
|
-
}).nullable();
|
|
14834
|
-
var StoredSessionReplaySummarySchema = external_exports.object({
|
|
14835
|
-
id: external_exports.string(),
|
|
14836
|
-
deviceId: external_exports.number(),
|
|
14837
|
-
nodeId: external_exports.string(),
|
|
14838
|
-
createdAtMs: external_exports.number(),
|
|
14839
|
-
report: SessionReplayReportSchema.omit({ timeline: true })
|
|
14840
|
-
});
|
|
14841
|
-
var ListSessionReplaysInputSchema = external_exports.object({ deviceId: external_exports.number() });
|
|
14842
|
-
var ListSessionReplaysResultSchema = external_exports.object({
|
|
14843
|
-
runs: external_exports.array(StoredSessionReplaySummarySchema).readonly(),
|
|
14844
|
-
/** Rows that did not read as a session replay (the removed clip replays). */
|
|
14845
|
-
skipped: external_exports.number()
|
|
14846
|
-
});
|
|
14847
|
-
var GetSessionReplayInputSchema = external_exports.object({ id: external_exports.string() });
|
|
14848
|
-
var StoredSessionReplaySchema = external_exports.object({
|
|
14849
|
-
id: external_exports.string(),
|
|
14850
|
-
deviceId: external_exports.number(),
|
|
14851
|
-
nodeId: external_exports.string(),
|
|
14852
|
-
createdAtMs: external_exports.number(),
|
|
14853
|
-
report: SessionReplayReportSchema
|
|
14854
|
-
});
|
|
14855
14561
|
|
|
14856
14562
|
// src/benchmark-actions.ts
|
|
14857
|
-
var benchmarkActions = (0,
|
|
14858
|
-
//
|
|
14859
|
-
runPipelineTest: (0,
|
|
14860
|
-
kind: "mutation"
|
|
14861
|
-
}),
|
|
14862
|
-
/**
|
|
14863
|
-
* History.
|
|
14864
|
-
*
|
|
14865
|
-
* `z.object({})`, never `z.void()`: the `addons.custom` envelope insists
|
|
14866
|
-
* on an `input` object, and a `z.void()` schema REJECTS `{}`.
|
|
14867
|
-
*/
|
|
14868
|
-
listBenchmarkHistory: (0, import_types3.customAction)(external_exports.object({}), external_exports.array(HistoryEntrySchema).readonly()),
|
|
14869
|
-
saveBenchmarkResult: (0, import_types3.customAction)(
|
|
14870
|
-
PipelineBenchmarkHistoryEntryInputSchema,
|
|
14871
|
-
PipelineBenchmarkHistoryEntrySchema,
|
|
14872
|
-
{ kind: "mutation" }
|
|
14873
|
-
),
|
|
14874
|
-
deleteBenchmarkResult: (0, import_types3.customAction)(
|
|
14875
|
-
external_exports.object({ id: external_exports.string() }),
|
|
14876
|
-
external_exports.object({ success: external_exports.literal(true) }),
|
|
14877
|
-
{ kind: "mutation" }
|
|
14878
|
-
),
|
|
14879
|
-
clearBenchmarkHistory: (0, import_types3.customAction)(external_exports.object({}), external_exports.object({ success: external_exports.literal(true) }), {
|
|
14563
|
+
var benchmarkActions = (0, import_types2.defineCustomActions)({
|
|
14564
|
+
// Image — one run of a step tree on a (node, deviceKey).
|
|
14565
|
+
runPipelineTest: (0, import_types2.customAction)(PipelineRunInputSchema, import_types2.PipelineRunResultBridge, {
|
|
14880
14566
|
kind: "mutation"
|
|
14881
|
-
})
|
|
14882
|
-
// Session replay (D646) — the stored sessions to pick from, then start / poll / abort.
|
|
14883
|
-
listMotionSessions: (0, import_types3.customAction)(
|
|
14884
|
-
ListMotionSessionsInputSchema,
|
|
14885
|
-
external_exports.array(MotionSessionSchema).readonly()
|
|
14886
|
-
),
|
|
14887
|
-
startSessionReplay: (0, import_types3.customAction)(
|
|
14888
|
-
StartSessionReplayInputSchema,
|
|
14889
|
-
StartSessionReplayAcceptedSchema,
|
|
14890
|
-
{ kind: "mutation", auth: "admin" }
|
|
14891
|
-
),
|
|
14892
|
-
getSessionReplayStatus: (0, import_types3.customAction)(SessionReplayKeySchema, SessionReplayStatusSchema),
|
|
14893
|
-
abortSessionReplay: (0, import_types3.customAction)(SessionReplayKeySchema, external_exports.object({ aborted: external_exports.boolean() }), {
|
|
14894
|
-
kind: "mutation",
|
|
14895
|
-
auth: "admin"
|
|
14896
|
-
}),
|
|
14897
|
-
listSessionReplays: (0, import_types3.customAction)(ListSessionReplaysInputSchema, ListSessionReplaysResultSchema),
|
|
14898
|
-
getSessionReplay: (0, import_types3.customAction)(GetSessionReplayInputSchema, StoredSessionReplaySchema.nullable())
|
|
14899
|
-
});
|
|
14900
|
-
|
|
14901
|
-
// src/replay/mjpeg-split.ts
|
|
14902
|
-
var SOI = Buffer.from([255, 216]);
|
|
14903
|
-
var EOI = Buffer.from([255, 217]);
|
|
14904
|
-
function splitMjpegStream(bytes) {
|
|
14905
|
-
const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
14906
|
-
const out = [];
|
|
14907
|
-
let pos = 0;
|
|
14908
|
-
for (; ; ) {
|
|
14909
|
-
const soi = buf.indexOf(SOI, pos);
|
|
14910
|
-
if (soi === -1) break;
|
|
14911
|
-
const eoi = buf.indexOf(EOI, soi + SOI.length);
|
|
14912
|
-
if (eoi === -1) break;
|
|
14913
|
-
out.push(buf.subarray(soi, eoi + EOI.length));
|
|
14914
|
-
pos = eoi + EOI.length;
|
|
14915
|
-
}
|
|
14916
|
-
return out;
|
|
14917
|
-
}
|
|
14918
|
-
|
|
14919
|
-
// src/replay/replay-decode.ts
|
|
14920
|
-
var import_node_child_process = require("child_process");
|
|
14921
|
-
function buildReplaySequenceArgs(opts) {
|
|
14922
|
-
const { startSec, durationSec, fps, maxWidth } = opts;
|
|
14923
|
-
return [
|
|
14924
|
-
"-hide_banner",
|
|
14925
|
-
"-loglevel",
|
|
14926
|
-
"error",
|
|
14927
|
-
"-nostdin",
|
|
14928
|
-
"-ss",
|
|
14929
|
-
String(Math.max(0, startSec)),
|
|
14930
|
-
"-i",
|
|
14931
|
-
"pipe:0",
|
|
14932
|
-
"-t",
|
|
14933
|
-
String(Math.max(0, durationSec)),
|
|
14934
|
-
"-vf",
|
|
14935
|
-
`fps=${fps},scale='min(${maxWidth},iw)':-2`,
|
|
14936
|
-
"-f",
|
|
14937
|
-
"mjpeg",
|
|
14938
|
-
"pipe:1"
|
|
14939
|
-
];
|
|
14940
|
-
}
|
|
14941
|
-
var REPLAY_DECODE_MAX_CONCURRENT = 2;
|
|
14942
|
-
var REPLAY_DECODE_TIMEOUT_MS = 15e3;
|
|
14943
|
-
var KILL_GRACE_MS = 500;
|
|
14944
|
-
var ReplayDecoder = class {
|
|
14945
|
-
resolveFfmpegPath;
|
|
14946
|
-
spawnFn;
|
|
14947
|
-
maxConcurrent;
|
|
14948
|
-
timeoutMs;
|
|
14949
|
-
inFlight = 0;
|
|
14950
|
-
constructor(deps) {
|
|
14951
|
-
this.resolveFfmpegPath = deps.resolveFfmpegPath;
|
|
14952
|
-
this.spawnFn = deps.spawnFn ?? ((bin, args) => (0, import_node_child_process.spawn)(bin, [...args]));
|
|
14953
|
-
this.maxConcurrent = deps.maxConcurrent ?? REPLAY_DECODE_MAX_CONCURRENT;
|
|
14954
|
-
this.timeoutMs = deps.timeoutMs ?? REPLAY_DECODE_TIMEOUT_MS;
|
|
14955
|
-
}
|
|
14956
|
-
/** Decodes currently running (tests / metrics). */
|
|
14957
|
-
get running() {
|
|
14958
|
-
return this.inFlight;
|
|
14959
|
-
}
|
|
14960
|
-
/**
|
|
14961
|
-
* Run the sequence once, feeding `input` on stdin (`-i pipe:0` in
|
|
14962
|
-
* {@link buildReplaySequenceArgs}) — the same shape as
|
|
14963
|
-
* `RecordedStillService`, which pipes a GOP rather than pointing ffmpeg at
|
|
14964
|
-
* a file path, because the source bytes (a recorder GOP window, or an
|
|
14965
|
-
* uploaded clip) are fetched through a capability call, not a local path.
|
|
14966
|
-
* Resolves with the whole MJPEG stdout stream.
|
|
14967
|
-
*/
|
|
14968
|
-
async decodeSequence(args, input, opts = {}) {
|
|
14969
|
-
if (this.inFlight >= this.maxConcurrent) {
|
|
14970
|
-
throw new Error(
|
|
14971
|
-
`replay decode busy: node already running ${this.inFlight}/${this.maxConcurrent} decodes`
|
|
14972
|
-
);
|
|
14973
|
-
}
|
|
14974
|
-
this.inFlight += 1;
|
|
14975
|
-
try {
|
|
14976
|
-
const ffmpegPath = await this.resolveFfmpegPath();
|
|
14977
|
-
return await this.runOnce(ffmpegPath, args, input, opts.timeoutMs ?? this.timeoutMs);
|
|
14978
|
-
} finally {
|
|
14979
|
-
this.inFlight -= 1;
|
|
14980
|
-
}
|
|
14981
|
-
}
|
|
14982
|
-
runOnce(ffmpegPath, args, input, timeoutMs) {
|
|
14983
|
-
return new Promise((resolve, reject) => {
|
|
14984
|
-
const chunks = [];
|
|
14985
|
-
const stderrTail = [];
|
|
14986
|
-
let settled = false;
|
|
14987
|
-
const child = (() => {
|
|
14988
|
-
try {
|
|
14989
|
-
return this.spawnFn(ffmpegPath, args);
|
|
14990
|
-
} catch (err) {
|
|
14991
|
-
reject(new Error(`replay decode: ffmpeg spawn failed: ${String(err)}`));
|
|
14992
|
-
return null;
|
|
14993
|
-
}
|
|
14994
|
-
})();
|
|
14995
|
-
if (child === null) return;
|
|
14996
|
-
const settle = (fn) => {
|
|
14997
|
-
if (settled) return;
|
|
14998
|
-
settled = true;
|
|
14999
|
-
clearTimeout(timer);
|
|
15000
|
-
fn();
|
|
15001
|
-
};
|
|
15002
|
-
const kill = () => {
|
|
15003
|
-
try {
|
|
15004
|
-
child.kill("SIGTERM");
|
|
15005
|
-
} catch {
|
|
15006
|
-
}
|
|
15007
|
-
const t = setTimeout(() => {
|
|
15008
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
15009
|
-
try {
|
|
15010
|
-
child.kill("SIGKILL");
|
|
15011
|
-
} catch {
|
|
15012
|
-
}
|
|
15013
|
-
}
|
|
15014
|
-
}, KILL_GRACE_MS);
|
|
15015
|
-
t.unref?.();
|
|
15016
|
-
};
|
|
15017
|
-
const timer = setTimeout(() => {
|
|
15018
|
-
settle(() => reject(new Error("replay decode: timed out")));
|
|
15019
|
-
kill();
|
|
15020
|
-
}, timeoutMs);
|
|
15021
|
-
timer.unref?.();
|
|
15022
|
-
child.stdout?.on("data", (chunk) => chunks.push(chunk));
|
|
15023
|
-
child.stderr?.on("data", (data) => {
|
|
15024
|
-
const line = data.toString().trim();
|
|
15025
|
-
if (line) {
|
|
15026
|
-
stderrTail.push(line);
|
|
15027
|
-
if (stderrTail.length > 8) stderrTail.shift();
|
|
15028
|
-
}
|
|
15029
|
-
});
|
|
15030
|
-
child.on("error", (err) => {
|
|
15031
|
-
settle(() => reject(new Error(`replay decode: ffmpeg process error: ${err.message}`)));
|
|
15032
|
-
});
|
|
15033
|
-
child.on("exit", (code, signal) => {
|
|
15034
|
-
const stdout = Buffer.concat(chunks);
|
|
15035
|
-
if (code === 0) {
|
|
15036
|
-
settle(() => resolve(stdout));
|
|
15037
|
-
return;
|
|
15038
|
-
}
|
|
15039
|
-
settle(
|
|
15040
|
-
() => reject(
|
|
15041
|
-
new Error(
|
|
15042
|
-
`replay decode: ffmpeg exited code=${String(code)} signal=${String(signal)}: ${stderrTail.join(" | ") || "(no stderr)"}`
|
|
15043
|
-
)
|
|
15044
|
-
)
|
|
15045
|
-
);
|
|
15046
|
-
});
|
|
15047
|
-
try {
|
|
15048
|
-
child.stdin?.write(Buffer.from(input.buffer, input.byteOffset, input.byteLength));
|
|
15049
|
-
child.stdin?.end();
|
|
15050
|
-
} catch {
|
|
15051
|
-
}
|
|
15052
|
-
});
|
|
15053
|
-
}
|
|
15054
|
-
};
|
|
15055
|
-
|
|
15056
|
-
// src/replay/replay-store.ts
|
|
15057
|
-
var REPLAY_RUNS_COLLECTION = "benchmark:replay-runs";
|
|
15058
|
-
var REPLAY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
15059
|
-
var REPLAY_RUNS_COLUMNS = [
|
|
15060
|
-
{ name: "id", type: "TEXT", primaryKey: true, notNull: true },
|
|
15061
|
-
{ name: "deviceId", type: "INTEGER", notNull: true },
|
|
15062
|
-
{ name: "nodeId", type: "TEXT", notNull: true },
|
|
15063
|
-
{ name: "clipId", type: "TEXT", notNull: true },
|
|
15064
|
-
{ name: "createdAtMs", type: "INTEGER", notNull: true },
|
|
15065
|
-
{ name: "source", type: "JSON", notNull: true },
|
|
15066
|
-
{ name: "plan", type: "JSON", notNull: true },
|
|
15067
|
-
{ name: "verdict", type: "JSON", notNull: true }
|
|
15068
|
-
];
|
|
15069
|
-
var REPLAY_RUNS_INDEXES = [
|
|
15070
|
-
{ name: "idx_replay_runs_device_created", columns: ["deviceId", "createdAtMs"] },
|
|
15071
|
-
{ name: "idx_replay_runs_created", columns: ["createdAtMs"] }
|
|
15072
|
-
];
|
|
15073
|
-
var StoredRowSchema = external_exports.object({
|
|
15074
|
-
deviceId: external_exports.number(),
|
|
15075
|
-
nodeId: external_exports.string(),
|
|
15076
|
-
createdAtMs: external_exports.number(),
|
|
15077
|
-
source: external_exports.object({ kind: external_exports.literal("motion-session") }).loose(),
|
|
15078
|
-
verdict: SessionReplayReportSchema
|
|
15079
|
-
});
|
|
15080
|
-
var ReplayStore = class _ReplayStore {
|
|
15081
|
-
constructor(store) {
|
|
15082
|
-
this.store = store;
|
|
15083
|
-
}
|
|
15084
|
-
store;
|
|
15085
|
-
/** Declare the collection. Call in `onInitialize`, before the first
|
|
15086
|
-
* `save` — an undeclared collection crash-loops the runner. */
|
|
15087
|
-
static async declare(store) {
|
|
15088
|
-
await store.declareCollection.mutate({
|
|
15089
|
-
collection: REPLAY_RUNS_COLLECTION,
|
|
15090
|
-
columns: [...REPLAY_RUNS_COLUMNS],
|
|
15091
|
-
indexes: [...REPLAY_RUNS_INDEXES]
|
|
15092
|
-
});
|
|
15093
|
-
}
|
|
15094
|
-
async save(run) {
|
|
15095
|
-
const { report } = run;
|
|
15096
|
-
await this.store.insert.mutate({
|
|
15097
|
-
collection: REPLAY_RUNS_COLLECTION,
|
|
15098
|
-
record: {
|
|
15099
|
-
id: run.id,
|
|
15100
|
-
data: {
|
|
15101
|
-
deviceId: run.deviceId,
|
|
15102
|
-
nodeId: run.nodeId,
|
|
15103
|
-
clipId: `session:${report.deviceId}:${report.motionEventId}`,
|
|
15104
|
-
createdAtMs: run.createdAtMs,
|
|
15105
|
-
source: { kind: "motion-session", motionEventId: report.motionEventId, ...report.window },
|
|
15106
|
-
plan: {
|
|
15107
|
-
pin: report.pin,
|
|
15108
|
-
rootStep: report.rootStep,
|
|
15109
|
-
cadence: report.cadence,
|
|
15110
|
-
frameSize: report.frameSize
|
|
15111
|
-
},
|
|
15112
|
-
verdict: report
|
|
15113
|
-
}
|
|
15114
|
-
}
|
|
15115
|
-
});
|
|
15116
|
-
}
|
|
15117
|
-
/** Session replays newest-first, optionally scoped to one camera. */
|
|
15118
|
-
async list(query) {
|
|
15119
|
-
const rows = await this.store.query.query({
|
|
15120
|
-
collection: REPLAY_RUNS_COLLECTION,
|
|
15121
|
-
filter: {
|
|
15122
|
-
orderBy: { field: "createdAtMs", direction: "desc" },
|
|
15123
|
-
...query.deviceId !== void 0 ? { where: { deviceId: query.deviceId } } : {},
|
|
15124
|
-
...query.limit !== void 0 ? { limit: query.limit } : {}
|
|
15125
|
-
}
|
|
15126
|
-
});
|
|
15127
|
-
const runs = [];
|
|
15128
|
-
let skipped = 0;
|
|
15129
|
-
for (const row of rows) {
|
|
15130
|
-
const parsed = _ReplayStore.readRow(row.id, row.data);
|
|
15131
|
-
if (parsed === null) skipped += 1;
|
|
15132
|
-
else runs.push(parsed);
|
|
15133
|
-
}
|
|
15134
|
-
return { runs, skipped };
|
|
15135
|
-
}
|
|
15136
|
-
async get(id) {
|
|
15137
|
-
const rows = await this.store.query.query({
|
|
15138
|
-
collection: REPLAY_RUNS_COLLECTION,
|
|
15139
|
-
filter: { where: { id }, limit: 1 }
|
|
15140
|
-
});
|
|
15141
|
-
const row = rows[0];
|
|
15142
|
-
return row === void 0 ? null : _ReplayStore.readRow(row.id, row.data);
|
|
15143
|
-
}
|
|
15144
|
-
/** Delete every run created before `cutoffMs`. This store's OWN retention
|
|
15145
|
-
* clock — see the file header for why that matters. */
|
|
15146
|
-
async pruneBefore(cutoffMs) {
|
|
15147
|
-
const { deleted } = await this.store.deleteWhere.mutate({
|
|
15148
|
-
collection: REPLAY_RUNS_COLLECTION,
|
|
15149
|
-
filter: { whereBetween: { createdAtMs: [0, cutoffMs] } }
|
|
15150
|
-
});
|
|
15151
|
-
return deleted;
|
|
15152
|
-
}
|
|
15153
|
-
static readRow(id, data) {
|
|
15154
|
-
const parsed = StoredRowSchema.safeParse(data);
|
|
15155
|
-
if (!parsed.success) return null;
|
|
15156
|
-
return {
|
|
15157
|
-
id,
|
|
15158
|
-
deviceId: parsed.data.deviceId,
|
|
15159
|
-
nodeId: parsed.data.nodeId,
|
|
15160
|
-
createdAtMs: parsed.data.createdAtMs,
|
|
15161
|
-
report: parsed.data.verdict
|
|
15162
|
-
};
|
|
15163
|
-
}
|
|
15164
|
-
};
|
|
15165
|
-
|
|
15166
|
-
// src/session-replay/recording-reader.ts
|
|
15167
|
-
async function readRecordingForInstants(deps, instants, stepMs) {
|
|
15168
|
-
const parts = [];
|
|
15169
|
-
let withoutFootage = 0;
|
|
15170
|
-
let truncated = false;
|
|
15171
|
-
let i = 0;
|
|
15172
|
-
while (i < instants.length) {
|
|
15173
|
-
const t = instants[i];
|
|
15174
|
-
if (t === void 0) break;
|
|
15175
|
-
const located = await deps.locateSegment(t);
|
|
15176
|
-
if (located.kind !== "segment") {
|
|
15177
|
-
const edge = located.nearestEdgeMs;
|
|
15178
|
-
let j2 = i + 1;
|
|
15179
|
-
if (edge === null) j2 = instants.length;
|
|
15180
|
-
else if (edge > t) while (j2 < instants.length && (instants[j2] ?? edge) < edge) j2++;
|
|
15181
|
-
withoutFootage += j2 - i;
|
|
15182
|
-
i = j2;
|
|
15183
|
-
continue;
|
|
15184
|
-
}
|
|
15185
|
-
const segEnd = located.startMs + located.durMs;
|
|
15186
|
-
let j = i;
|
|
15187
|
-
while (j < instants.length && (instants[j] ?? segEnd) < segEnd) j++;
|
|
15188
|
-
const covered = instants.slice(i, j);
|
|
15189
|
-
const lastCovered = covered[covered.length - 1] ?? t;
|
|
15190
|
-
const window = await deps.readWindowBytes({
|
|
15191
|
-
startMs: located.startMs,
|
|
15192
|
-
fromMs: t,
|
|
15193
|
-
toMs: Math.min(segEnd, lastCovered + Math.round(stepMs))
|
|
15194
|
-
});
|
|
15195
|
-
if (window.kind === "ok") {
|
|
15196
|
-
if (!window.reachesRequestedEnd) truncated = true;
|
|
15197
|
-
parts.push({ bytes: window.data, gopStartMs: window.gopStartMs, instants: covered });
|
|
15198
|
-
} else {
|
|
15199
|
-
withoutFootage += covered.length;
|
|
15200
|
-
}
|
|
15201
|
-
i = j === i ? i + 1 : j;
|
|
15202
|
-
}
|
|
15203
|
-
if (parts.length === 0) {
|
|
15204
|
-
return {
|
|
15205
|
-
kind: "refused",
|
|
15206
|
-
reason: "no-footage",
|
|
15207
|
-
detail: `no recording covers any of the ${instants.length} planned instants`
|
|
15208
|
-
};
|
|
15209
|
-
}
|
|
15210
|
-
return {
|
|
15211
|
-
kind: "ok",
|
|
15212
|
-
parts,
|
|
15213
|
-
instantsWithoutFootage: withoutFootage,
|
|
15214
|
-
truncatedBySafetyCap: truncated
|
|
15215
|
-
};
|
|
15216
|
-
}
|
|
15217
|
-
|
|
15218
|
-
// src/session-replay/cadence.ts
|
|
15219
|
-
var DEFAULT_DETECTION_FPS = 10;
|
|
15220
|
-
var MIN_DETECTION_FPS = 1;
|
|
15221
|
-
var MAX_DETECTION_FPS = 30;
|
|
15222
|
-
var MIN_GAPS_TO_MEASURE = 3;
|
|
15223
|
-
function clampFps(fps) {
|
|
15224
|
-
return Math.min(MAX_DETECTION_FPS, Math.max(MIN_DETECTION_FPS, fps));
|
|
15225
|
-
}
|
|
15226
|
-
function resolveReplayCadence(requestedFps, positionTimesByTrack) {
|
|
15227
|
-
if (requestedFps !== void 0) return { fps: clampFps(requestedFps), source: "operator" };
|
|
15228
|
-
const gaps = [];
|
|
15229
|
-
for (const times of positionTimesByTrack) {
|
|
15230
|
-
for (let i = 1; i < times.length; i++) {
|
|
15231
|
-
const prev = times[i - 1];
|
|
15232
|
-
const cur = times[i];
|
|
15233
|
-
if (prev !== void 0 && cur !== void 0 && cur > prev) gaps.push(cur - prev);
|
|
15234
|
-
}
|
|
15235
|
-
}
|
|
15236
|
-
if (gaps.length < MIN_GAPS_TO_MEASURE) return { fps: DEFAULT_DETECTION_FPS, source: "default" };
|
|
15237
|
-
const sorted = gaps.toSorted((a, b) => a - b);
|
|
15238
|
-
const quartile = sorted[Math.floor((sorted.length - 1) / 4)] ?? 0;
|
|
15239
|
-
if (!(quartile > 0)) return { fps: DEFAULT_DETECTION_FPS, source: "default" };
|
|
15240
|
-
return { fps: clampFps(Math.round(1e3 / quartile)), source: "stored-positions" };
|
|
15241
|
-
}
|
|
15242
|
-
|
|
15243
|
-
// src/session-replay/compare.ts
|
|
15244
|
-
var MATCH_IOU_MIN = 0.5;
|
|
15245
|
-
var RELAXED_IOU_MIN = 0.3;
|
|
15246
|
-
var MAX_INTERPOLATION_GAP_MS = 5e3;
|
|
15247
|
-
function iou(a, b) {
|
|
15248
|
-
const x1 = Math.max(a.x, b.x);
|
|
15249
|
-
const y1 = Math.max(a.y, b.y);
|
|
15250
|
-
const x2 = Math.min(a.x + a.w, b.x + b.w);
|
|
15251
|
-
const y2 = Math.min(a.y + a.h, b.y + b.h);
|
|
15252
|
-
const inter = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);
|
|
15253
|
-
if (inter === 0) return 0;
|
|
15254
|
-
const union2 = a.w * a.h + b.w * b.h - inter;
|
|
15255
|
-
return union2 > 0 ? inter / union2 : 0;
|
|
15256
|
-
}
|
|
15257
|
-
function lerp(a, b, f) {
|
|
15258
|
-
return a + (b - a) * f;
|
|
15259
|
-
}
|
|
15260
|
-
function entry(p, t, basis) {
|
|
15261
|
-
return { ...p, t, basis };
|
|
15262
|
-
}
|
|
15263
|
-
function trackAt(track, t, halfStepMs) {
|
|
15264
|
-
let prev;
|
|
15265
|
-
let next;
|
|
15266
|
-
let nearest;
|
|
15267
|
-
for (const p of track.positions) {
|
|
15268
|
-
if (p.t <= t) prev = p;
|
|
15269
|
-
if (p.t >= t && next === void 0) next = p;
|
|
15270
|
-
if (nearest === void 0 || Math.abs(p.t - t) < Math.abs(nearest.t - t)) nearest = p;
|
|
15271
|
-
}
|
|
15272
|
-
if (nearest !== void 0 && Math.abs(nearest.t - t) <= halfStepMs) {
|
|
15273
|
-
return entry(nearest, nearest.t, "exact");
|
|
15274
|
-
}
|
|
15275
|
-
if (t < track.firstSeen || t > track.lastSeen || nearest === void 0) return null;
|
|
15276
|
-
if (prev !== void 0 && next !== void 0 && next.t - prev.t <= MAX_INTERPOLATION_GAP_MS) {
|
|
15277
|
-
const f = next.t === prev.t ? 0 : (t - prev.t) / (next.t - prev.t);
|
|
15278
|
-
return {
|
|
15279
|
-
trackId: track.trackId,
|
|
15280
|
-
className: track.className,
|
|
15281
|
-
t,
|
|
15282
|
-
box: {
|
|
15283
|
-
x: lerp(prev.box.x, next.box.x, f),
|
|
15284
|
-
y: lerp(prev.box.y, next.box.y, f),
|
|
15285
|
-
w: lerp(prev.box.w, next.box.w, f),
|
|
15286
|
-
h: lerp(prev.box.h, next.box.h, f)
|
|
15287
|
-
},
|
|
15288
|
-
score: prev.score !== null && next.score !== null ? lerp(prev.score, next.score, f) : null,
|
|
15289
|
-
basis: "interpolated"
|
|
15290
|
-
};
|
|
15291
|
-
}
|
|
15292
|
-
return entry(nearest, t, "uncertain");
|
|
15293
|
-
}
|
|
15294
|
-
function referenceAt(tracks, t, halfStepMs) {
|
|
15295
|
-
const entries = [];
|
|
15296
|
-
let live = false;
|
|
15297
|
-
for (const track of tracks) {
|
|
15298
|
-
if (t >= track.firstSeen && t <= track.lastSeen) live = true;
|
|
15299
|
-
const e = trackAt(track, t, halfStepMs);
|
|
15300
|
-
if (e !== null) {
|
|
15301
|
-
entries.push(e);
|
|
15302
|
-
live = true;
|
|
15303
|
-
}
|
|
15304
|
-
}
|
|
15305
|
-
return { live, entries };
|
|
15306
|
-
}
|
|
15307
|
-
function sameClass(a, b) {
|
|
15308
|
-
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
15309
|
-
}
|
|
15310
|
-
function barFor(basis, iouMin) {
|
|
15311
|
-
if (basis === "exact") return iouMin;
|
|
15312
|
-
if (basis === "interpolated") return Math.min(iouMin, RELAXED_IOU_MIN);
|
|
15313
|
-
return 0;
|
|
15314
|
-
}
|
|
15315
|
-
function pairUp(replay, stored, accept, bar) {
|
|
15316
|
-
const candidates = [];
|
|
15317
|
-
replay.forEach((r, ri) => {
|
|
15318
|
-
stored.forEach((s, si) => {
|
|
15319
|
-
if (!accept(r, s)) return;
|
|
15320
|
-
const v = iou(r.box, s.box);
|
|
15321
|
-
if (v >= bar(s)) candidates.push({ r: ri, s: si, iou: v });
|
|
15322
|
-
});
|
|
15323
|
-
});
|
|
15324
|
-
const ranked = candidates.toSorted((a, b) => b.iou - a.iou);
|
|
15325
|
-
const usedR = /* @__PURE__ */ new Set();
|
|
15326
|
-
const usedS = /* @__PURE__ */ new Set();
|
|
15327
|
-
const pairs = [];
|
|
15328
|
-
for (const c of ranked) {
|
|
15329
|
-
if (usedR.has(c.r) || usedS.has(c.s)) continue;
|
|
15330
|
-
const r = replay[c.r];
|
|
15331
|
-
const s = stored[c.s];
|
|
15332
|
-
if (r === void 0 || s === void 0) continue;
|
|
15333
|
-
usedR.add(c.r);
|
|
15334
|
-
usedS.add(c.s);
|
|
15335
|
-
pairs.push({
|
|
15336
|
-
replay: r,
|
|
15337
|
-
stored: s,
|
|
15338
|
-
iou: c.iou,
|
|
15339
|
-
scoreDelta: s.score === null ? null : r.score - s.score
|
|
15340
|
-
});
|
|
15341
|
-
}
|
|
15342
|
-
return {
|
|
15343
|
-
pairs,
|
|
15344
|
-
restReplay: replay.filter((_r, i) => !usedR.has(i)),
|
|
15345
|
-
restStored: stored.filter((_s, i) => !usedS.has(i))
|
|
15346
|
-
};
|
|
15347
|
-
}
|
|
15348
|
-
function compareFrame(t, replay, reference, iouMin = MATCH_IOU_MIN) {
|
|
15349
|
-
const same = pairUp(
|
|
15350
|
-
replay,
|
|
15351
|
-
reference.entries,
|
|
15352
|
-
(r, s) => sameClass(r.className, s.className),
|
|
15353
|
-
(s) => barFor(s.basis, iouMin)
|
|
15354
|
-
);
|
|
15355
|
-
const changed = pairUp(
|
|
15356
|
-
same.restReplay,
|
|
15357
|
-
same.restStored,
|
|
15358
|
-
(_r, s) => s.basis !== "uncertain",
|
|
15359
|
-
(s) => barFor(s.basis, iouMin)
|
|
15360
|
-
);
|
|
15361
|
-
return {
|
|
15362
|
-
t,
|
|
15363
|
-
referenced: reference.live,
|
|
15364
|
-
matched: same.pairs,
|
|
15365
|
-
classChanged: changed.pairs,
|
|
15366
|
-
added: reference.live ? changed.restReplay : [],
|
|
15367
|
-
unreferenced: reference.live ? [] : changed.restReplay,
|
|
15368
|
-
missed: changed.restStored
|
|
15369
|
-
};
|
|
15370
|
-
}
|
|
15371
|
-
function percentile(sorted, q) {
|
|
15372
|
-
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(q * sorted.length) - 1));
|
|
15373
|
-
return sorted[idx] ?? 0;
|
|
15374
|
-
}
|
|
15375
|
-
function summarizeReplay(frames, inferenceMs) {
|
|
15376
|
-
const perClass = /* @__PURE__ */ new Map();
|
|
15377
|
-
const bump = (cls, field) => {
|
|
15378
|
-
const key = cls.trim().toLowerCase();
|
|
15379
|
-
const e = perClass.get(key) ?? {
|
|
15380
|
-
replay: 0,
|
|
15381
|
-
stored: 0,
|
|
15382
|
-
matched: 0,
|
|
15383
|
-
added: 0,
|
|
15384
|
-
missed: 0,
|
|
15385
|
-
unreferenced: 0,
|
|
15386
|
-
interpolated: 0
|
|
15387
|
-
};
|
|
15388
|
-
e[field] += 1;
|
|
15389
|
-
perClass.set(key, e);
|
|
15390
|
-
};
|
|
15391
|
-
const deltas = [];
|
|
15392
|
-
let matched = 0;
|
|
15393
|
-
let classChanged = 0;
|
|
15394
|
-
let added = 0;
|
|
15395
|
-
let missed = 0;
|
|
15396
|
-
let unreferenced = 0;
|
|
15397
|
-
let interpolated = 0;
|
|
15398
|
-
const noteBasis = (s) => {
|
|
15399
|
-
if (s.basis === "exact") return;
|
|
15400
|
-
interpolated += 1;
|
|
15401
|
-
bump(s.className, "interpolated");
|
|
15402
|
-
};
|
|
15403
|
-
for (const f of frames) {
|
|
15404
|
-
for (const p of f.matched) {
|
|
15405
|
-
matched += 1;
|
|
15406
|
-
bump(p.replay.className, "replay");
|
|
15407
|
-
bump(p.stored.className, "stored");
|
|
15408
|
-
bump(p.replay.className, "matched");
|
|
15409
|
-
noteBasis(p.stored);
|
|
15410
|
-
if (p.scoreDelta !== null) deltas.push(p.scoreDelta);
|
|
15411
|
-
}
|
|
15412
|
-
for (const p of f.classChanged) {
|
|
15413
|
-
classChanged += 1;
|
|
15414
|
-
bump(p.replay.className, "replay");
|
|
15415
|
-
bump(p.stored.className, "stored");
|
|
15416
|
-
noteBasis(p.stored);
|
|
15417
|
-
}
|
|
15418
|
-
for (const r of f.added) {
|
|
15419
|
-
added += 1;
|
|
15420
|
-
bump(r.className, "replay");
|
|
15421
|
-
bump(r.className, "added");
|
|
15422
|
-
}
|
|
15423
|
-
for (const r of f.unreferenced) {
|
|
15424
|
-
unreferenced += 1;
|
|
15425
|
-
bump(r.className, "replay");
|
|
15426
|
-
bump(r.className, "unreferenced");
|
|
15427
|
-
}
|
|
15428
|
-
for (const s of f.missed) {
|
|
15429
|
-
missed += 1;
|
|
15430
|
-
bump(s.className, "stored");
|
|
15431
|
-
bump(s.className, "missed");
|
|
15432
|
-
noteBasis(s);
|
|
15433
|
-
}
|
|
15434
|
-
}
|
|
15435
|
-
const sortedDeltas = deltas.toSorted((a, b) => a - b);
|
|
15436
|
-
const sortedInfer = [...inferenceMs].toSorted((a, b) => a - b);
|
|
15437
|
-
return {
|
|
15438
|
-
totals: { matched, classChanged, added, missed, unreferenced, interpolated },
|
|
15439
|
-
perClass: Object.fromEntries(
|
|
15440
|
-
[...perClass.entries()].toSorted(([a], [b]) => a.localeCompare(b))
|
|
15441
|
-
),
|
|
15442
|
-
scoreDelta: sortedDeltas.length === 0 ? null : {
|
|
15443
|
-
mean: sortedDeltas.reduce((s, v) => s + v, 0) / sortedDeltas.length,
|
|
15444
|
-
p10: percentile(sortedDeltas, 0.1),
|
|
15445
|
-
p50: percentile(sortedDeltas, 0.5),
|
|
15446
|
-
p90: percentile(sortedDeltas, 0.9)
|
|
15447
|
-
},
|
|
15448
|
-
inferenceMs: sortedInfer.length === 0 ? null : {
|
|
15449
|
-
p50: percentile(sortedInfer, 0.5),
|
|
15450
|
-
p95: percentile(sortedInfer, 0.95),
|
|
15451
|
-
count: sortedInfer.length
|
|
15452
|
-
}
|
|
15453
|
-
};
|
|
15454
|
-
}
|
|
15455
|
-
|
|
15456
|
-
// src/session-replay/frame-plan.ts
|
|
15457
|
-
var REPLAY_CHUNK_MS = 1e4;
|
|
15458
|
-
function planReplayFrames(window, fps, maxFrames, chunkMs = REPLAY_CHUNK_MS) {
|
|
15459
|
-
const stepMs = 1e3 / fps;
|
|
15460
|
-
const instants = [];
|
|
15461
|
-
let truncated = false;
|
|
15462
|
-
for (let k = 0; ; k++) {
|
|
15463
|
-
const t = window.startMs + Math.round(k * stepMs);
|
|
15464
|
-
if (t >= window.endMs) break;
|
|
15465
|
-
if (instants.length >= maxFrames) {
|
|
15466
|
-
truncated = true;
|
|
15467
|
-
break;
|
|
15468
|
-
}
|
|
15469
|
-
instants.push(t);
|
|
15470
|
-
}
|
|
15471
|
-
const chunks = [];
|
|
15472
|
-
let current = [];
|
|
15473
|
-
let chunkStart = window.startMs;
|
|
15474
|
-
const flush = (toMs) => {
|
|
15475
|
-
if (current.length > 0) chunks.push({ fromMs: chunkStart, toMs, instants: current });
|
|
15476
|
-
current = [];
|
|
15477
|
-
};
|
|
15478
|
-
for (const t of instants) {
|
|
15479
|
-
while (t >= chunkStart + chunkMs) {
|
|
15480
|
-
flush(chunkStart + chunkMs);
|
|
15481
|
-
chunkStart += chunkMs;
|
|
15482
|
-
}
|
|
15483
|
-
current.push(t);
|
|
15484
|
-
}
|
|
15485
|
-
const last = instants[instants.length - 1];
|
|
15486
|
-
flush(
|
|
15487
|
-
truncated && last !== void 0 ? last + Math.round(stepMs) : Math.min(window.endMs, chunkStart + chunkMs)
|
|
15488
|
-
);
|
|
15489
|
-
return { chunks, planned: instants.length, truncated };
|
|
15490
|
-
}
|
|
15491
|
-
|
|
15492
|
-
// src/session-replay/replay-steps.ts
|
|
15493
|
-
var REPLAY_ROOT_STEP = "object-detection";
|
|
15494
|
-
function selectRootReplayStep(tree, requestedModelId) {
|
|
15495
|
-
const root = tree.steps.find((s) => s.addonId === REPLAY_ROOT_STEP);
|
|
15496
|
-
if (root === void 0 || !root.enabled) {
|
|
15497
|
-
return {
|
|
15498
|
-
kind: "refused",
|
|
15499
|
-
reason: "no-root-step",
|
|
15500
|
-
detail: `the camera does not run ${REPLAY_ROOT_STEP} as an enabled root step`
|
|
15501
|
-
};
|
|
15502
|
-
}
|
|
15503
|
-
const cameraModelId = root.modelId ?? null;
|
|
15504
|
-
const modelId = requestedModelId ?? cameraModelId;
|
|
15505
|
-
if (modelId === null || modelId.length === 0) {
|
|
15506
|
-
return {
|
|
15507
|
-
kind: "refused",
|
|
15508
|
-
reason: "model-unpinned",
|
|
15509
|
-
detail: `the camera's ${REPLAY_ROOT_STEP} names no model (it runs the node's format default); pick the model to replay explicitly`
|
|
15510
|
-
};
|
|
15511
|
-
}
|
|
15512
|
-
return {
|
|
15513
|
-
kind: "ok",
|
|
15514
|
-
cameraModelId,
|
|
15515
|
-
step: {
|
|
15516
|
-
addonId: root.addonId,
|
|
15517
|
-
modelId,
|
|
15518
|
-
enabled: true,
|
|
15519
|
-
...root.settings !== void 0 ? { settings: root.settings } : {},
|
|
15520
|
-
children: []
|
|
15521
|
-
}
|
|
15522
|
-
};
|
|
15523
|
-
}
|
|
15524
|
-
|
|
15525
|
-
// src/session-replay/session-window.ts
|
|
15526
|
-
var SESSION_PREROLL_MS = 5e3;
|
|
15527
|
-
var SESSION_TAIL_MS = 3e4;
|
|
15528
|
-
var SESSION_MAX_WINDOW_MS = 10 * 6e4;
|
|
15529
|
-
function motionSessionWindow(session, camera = {}, opts = {}) {
|
|
15530
|
-
if (session.durationMs === null) {
|
|
15531
|
-
return {
|
|
15532
|
-
kind: "refused",
|
|
15533
|
-
reason: "session-open",
|
|
15534
|
-
detail: `motion session ${session.id} is still open \u2014 its end is not known yet`
|
|
15535
|
-
};
|
|
15536
|
-
}
|
|
15537
|
-
if (session.durationMs === void 0) {
|
|
15538
|
-
return {
|
|
15539
|
-
kind: "refused",
|
|
15540
|
-
reason: "session-length-unknown",
|
|
15541
|
-
detail: `motion session ${session.id} predates the episode duration column \u2014 its length is unknown`
|
|
15542
|
-
};
|
|
15543
|
-
}
|
|
15544
|
-
const motionEndMs = session.timestamp + session.durationMs;
|
|
15545
|
-
const prerollSource = camera.preRollInferenceEnabled === false ? "camera" : "default";
|
|
15546
|
-
const prerollMs = camera.preRollInferenceEnabled === false ? 0 : SESSION_PREROLL_MS;
|
|
15547
|
-
const tailSource = camera.motionCooldownMs !== void 0 ? "camera" : "default";
|
|
15548
|
-
const tailMs = camera.motionCooldownMs ?? SESSION_TAIL_MS;
|
|
15549
|
-
const startMs = session.timestamp - prerollMs;
|
|
15550
|
-
const endMs = motionEndMs + tailMs;
|
|
15551
|
-
const maxWindowMs = opts.maxWindowMs ?? SESSION_MAX_WINDOW_MS;
|
|
15552
|
-
if (endMs - startMs > maxWindowMs) {
|
|
15553
|
-
return {
|
|
15554
|
-
kind: "refused",
|
|
15555
|
-
reason: "window-too-long",
|
|
15556
|
-
detail: `motion session ${session.id} spans ${Math.round((endMs - startMs) / 1e3)} s \u2014 longer than the ${Math.round(maxWindowMs / 1e3)} s a replay reads`
|
|
15557
|
-
};
|
|
15558
|
-
}
|
|
15559
|
-
return {
|
|
15560
|
-
kind: "window",
|
|
15561
|
-
motionEventId: session.id,
|
|
15562
|
-
motionStartMs: session.timestamp,
|
|
15563
|
-
motionEndMs,
|
|
15564
|
-
startMs,
|
|
15565
|
-
endMs,
|
|
15566
|
-
prerollSource,
|
|
15567
|
-
tailSource
|
|
15568
|
-
};
|
|
15569
|
-
}
|
|
15570
|
-
|
|
15571
|
-
// src/session-replay/stored-reference.ts
|
|
15572
|
-
var UNKNOWN_MODEL = "unknown";
|
|
15573
|
-
function buildStoredReference(tracks, window) {
|
|
15574
|
-
const refs = [];
|
|
15575
|
-
const models = /* @__PURE__ */ new Map();
|
|
15576
|
-
const sizes = /* @__PURE__ */ new Map();
|
|
15577
|
-
const timesByTrack = [];
|
|
15578
|
-
let recoveryExcluded = 0;
|
|
15579
|
-
let withoutGeometry = 0;
|
|
15580
|
-
let trackCount = 0;
|
|
15581
|
-
for (const track of tracks) {
|
|
15582
|
-
const inWindow = track.positions.filter(
|
|
15583
|
-
(p) => p.timestamp >= window.startMs && p.timestamp < window.endMs
|
|
15584
|
-
);
|
|
15585
|
-
const aliveInWindow = track.firstSeen < window.endMs && track.lastSeen >= window.startMs;
|
|
15586
|
-
if (inWindow.length === 0 && !aliveInWindow) continue;
|
|
15587
|
-
trackCount += 1;
|
|
15588
|
-
const model = track.modelId ?? UNKNOWN_MODEL;
|
|
15589
|
-
models.set(model, (models.get(model) ?? 0) + 1);
|
|
15590
|
-
const fw = track.frameWidth;
|
|
15591
|
-
const fh = track.frameHeight;
|
|
15592
|
-
const hasGeometry = fw !== void 0 && fh !== void 0 && fw > 0 && fh > 0;
|
|
15593
|
-
if (hasGeometry) {
|
|
15594
|
-
const key = `${fw}x${fh}`;
|
|
15595
|
-
const entry2 = sizes.get(key) ?? { width: fw, height: fh, count: 0 };
|
|
15596
|
-
entry2.count += 1;
|
|
15597
|
-
sizes.set(key, entry2);
|
|
15598
|
-
}
|
|
15599
|
-
const times = [];
|
|
15600
|
-
const positions = [];
|
|
15601
|
-
for (const p of inWindow) {
|
|
15602
|
-
if (p.source === "recovery") {
|
|
15603
|
-
recoveryExcluded += 1;
|
|
15604
|
-
continue;
|
|
15605
|
-
}
|
|
15606
|
-
times.push(p.timestamp);
|
|
15607
|
-
if (!hasGeometry) {
|
|
15608
|
-
withoutGeometry += 1;
|
|
15609
|
-
continue;
|
|
15610
|
-
}
|
|
15611
|
-
positions.push({
|
|
15612
|
-
trackId: track.trackId,
|
|
15613
|
-
className: track.className,
|
|
15614
|
-
t: p.timestamp,
|
|
15615
|
-
box: { x: p.bbox.x / fw, y: p.bbox.y / fh, w: p.bbox.w / fw, h: p.bbox.h / fh },
|
|
15616
|
-
score: p.score ?? null
|
|
15617
|
-
});
|
|
15618
|
-
}
|
|
15619
|
-
timesByTrack.push(times.toSorted((a, b) => a - b));
|
|
15620
|
-
if (hasGeometry) {
|
|
15621
|
-
const sorted = positions.toSorted((a, b) => a.t - b.t);
|
|
15622
|
-
refs.push({
|
|
15623
|
-
trackId: track.trackId,
|
|
15624
|
-
className: track.className,
|
|
15625
|
-
firstSeen: Math.min(track.firstSeen, sorted[0]?.t ?? track.firstSeen),
|
|
15626
|
-
lastSeen: Math.max(track.lastSeen, sorted[sorted.length - 1]?.t ?? track.lastSeen),
|
|
15627
|
-
positions: sorted
|
|
15628
|
-
});
|
|
15629
|
-
}
|
|
15630
|
-
}
|
|
15631
|
-
const commonest = [...sizes.values()].toSorted((a, b) => b.count - a.count)[0];
|
|
15632
|
-
return {
|
|
15633
|
-
tracks: refs,
|
|
15634
|
-
trackCount,
|
|
15635
|
-
recoveryExcluded,
|
|
15636
|
-
withoutGeometry,
|
|
15637
|
-
models: Object.fromEntries(models),
|
|
15638
|
-
frameSize: commonest ? { width: commonest.width, height: commonest.height } : null,
|
|
15639
|
-
positionTimesByTrack: timesByTrack
|
|
15640
|
-
};
|
|
15641
|
-
}
|
|
15642
|
-
|
|
15643
|
-
// src/session-replay/run-session-replay.ts
|
|
15644
|
-
var TRACK_LOOKBACK_MS = 12e4;
|
|
15645
|
-
var DEFAULT_REPLAY_WIDTH = 1280;
|
|
15646
|
-
var SUPPRESSED_BIRTH_PROXIMITY_MS = 2e3;
|
|
15647
|
-
var MAX_REPORTED_FAILURES = 20;
|
|
15648
|
-
var MAX_CONSECUTIVE_FAILURES = 10;
|
|
15649
|
-
var STORED_TRACKS_READ_LIMIT = 1e3;
|
|
15650
|
-
var REAL_PACE = {
|
|
15651
|
-
now: () => Date.now(),
|
|
15652
|
-
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
15653
|
-
};
|
|
15654
|
-
var PIN_REFUSALS = ["model-not-servable", "device-not-servable"];
|
|
15655
|
-
function pinRefusalOf(err) {
|
|
15656
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
15657
|
-
for (const reason of PIN_REFUSALS) {
|
|
15658
|
-
if (message.includes(`${reason}:`)) return { reason, detail: message };
|
|
15659
|
-
}
|
|
15660
|
-
return null;
|
|
15661
|
-
}
|
|
15662
|
-
function toReplayBoxes(frame) {
|
|
15663
|
-
const w = frame.width;
|
|
15664
|
-
const h = frame.height;
|
|
15665
|
-
if (!(w > 0 && h > 0)) return [];
|
|
15666
|
-
return frame.detections.filter((d) => d.kind === "first-level").map((d) => ({
|
|
15667
|
-
className: d.macroClass,
|
|
15668
|
-
score: d.score,
|
|
15669
|
-
box: { x: d.bbox.x / w, y: d.bbox.y / h, w: d.bbox.width / w, h: d.bbox.height / h }
|
|
15670
|
-
}));
|
|
15671
|
-
}
|
|
15672
|
-
function sameClass2(a, b) {
|
|
15673
|
-
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
15674
|
-
}
|
|
15675
|
-
async function runSessionReplay(deps, request, run) {
|
|
15676
|
-
const tags = { deviceId: request.deviceId };
|
|
15677
|
-
const refuse = (reason, detail) => {
|
|
15678
|
-
deps.logger.warn("session replay refused", {
|
|
15679
|
-
tags,
|
|
15680
|
-
meta: { reason, detail, motionEventId: request.motionEventId, nodeId: request.nodeId }
|
|
15681
|
-
});
|
|
15682
|
-
return { kind: "refused", reason, detail };
|
|
15683
|
-
};
|
|
15684
|
-
run.report({ phase: "preparing", framesPlanned: 0, framesDone: 0 });
|
|
15685
|
-
const session = await deps.findMotionSession(
|
|
15686
|
-
request.deviceId,
|
|
15687
|
-
request.motionEventId,
|
|
15688
|
-
request.motionStartMs
|
|
15689
|
-
);
|
|
15690
|
-
if (session === null) {
|
|
15691
|
-
return refuse(
|
|
15692
|
-
"session-not-found",
|
|
15693
|
-
`no stored motion session ${request.motionEventId} for camera ${request.deviceId}`
|
|
15694
|
-
);
|
|
15695
|
-
}
|
|
15696
|
-
const window = motionSessionWindow(session, await deps.cameraSessionSettings(request.deviceId));
|
|
15697
|
-
if (window.kind === "refused") return refuse(window.reason, window.detail);
|
|
15698
|
-
const tree = await deps.resolveCameraTree(request.deviceId, request.nodeId);
|
|
15699
|
-
const selection = selectRootReplayStep(tree, request.modelId);
|
|
15700
|
-
if (selection.kind === "refused") return refuse(selection.reason, selection.detail);
|
|
15701
|
-
const step = selection.step;
|
|
15702
|
-
const modelId = step.modelId ?? "";
|
|
15703
|
-
const validation = await deps.validate([step], request.nodeId, request.deviceKey);
|
|
15704
|
-
const pool = `${request.nodeId}/${request.deviceKey ?? "node default"}`;
|
|
15705
|
-
if (!validation.ok) {
|
|
15706
|
-
return refuse(
|
|
15707
|
-
"device-not-servable",
|
|
15708
|
-
`${pool} cannot run ${step.addonId}: ${validation.issues.map((i) => i.detail).join("; ")}`
|
|
15709
|
-
);
|
|
15710
|
-
}
|
|
15711
|
-
const substitution = validation.substitutions[0];
|
|
15712
|
-
if (substitution !== void 0) {
|
|
15713
|
-
return refuse(
|
|
15714
|
-
"model-not-servable",
|
|
15715
|
-
`${pool} would run ${substitution.running} instead of ${substitution.chosen} (${substitution.format})`
|
|
15716
|
-
);
|
|
15717
|
-
}
|
|
15718
|
-
if (!await deps.isRecording(request.deviceId)) {
|
|
15719
|
-
return refuse("no-recording", `camera ${request.deviceId} does not have recording enabled`);
|
|
15720
|
-
}
|
|
15721
|
-
const [tracks, births] = await Promise.all([
|
|
15722
|
-
deps.listStoredTracks(request.deviceId, window.startMs - TRACK_LOOKBACK_MS, window.endMs),
|
|
15723
|
-
deps.listBirthDecisions(request.deviceId, window.startMs)
|
|
15724
|
-
]);
|
|
15725
|
-
const reference = buildStoredReference(tracks, window);
|
|
15726
|
-
const readTruncated = tracks.length >= STORED_TRACKS_READ_LIMIT;
|
|
15727
|
-
if (readTruncated) {
|
|
15728
|
-
deps.logger.warn(
|
|
15729
|
-
"session replay: stored-track read hit its limit \u2014 the reference may be incomplete",
|
|
15730
|
-
{
|
|
15731
|
-
tags,
|
|
15732
|
-
meta: { limit: STORED_TRACKS_READ_LIMIT, motionEventId: request.motionEventId }
|
|
15733
|
-
}
|
|
15734
|
-
);
|
|
15735
|
-
}
|
|
15736
|
-
const cadence = resolveReplayCadence(request.fps, reference.positionTimesByTrack);
|
|
15737
|
-
const frameSize = reference.frameSize !== null ? { width: reference.frameSize.width, source: "stored-tracks" } : { width: DEFAULT_REPLAY_WIDTH, source: "default" };
|
|
15738
|
-
const windowBirths = births.filter((b) => b.at >= window.startMs && b.at < window.endMs);
|
|
15739
|
-
const suppressedBirths = windowBirths.filter((b) => b.verdict === "suppressed");
|
|
15740
|
-
const liveVerdicts = {};
|
|
15741
|
-
for (const b of windowBirths) liveVerdicts[b.verdict] = (liveVerdicts[b.verdict] ?? 0) + 1;
|
|
15742
|
-
const plan = planReplayFrames(window, cadence.fps, request.maxFrames);
|
|
15743
|
-
if (plan.truncated) {
|
|
15744
|
-
deps.logger.warn("session replay: frame plan truncated by maxFrames", {
|
|
15745
|
-
tags,
|
|
15746
|
-
meta: { maxFrames: request.maxFrames, motionEventId: request.motionEventId }
|
|
15747
|
-
});
|
|
15748
|
-
}
|
|
15749
|
-
const stepMs = 1e3 / cadence.fps;
|
|
15750
|
-
const halfStepMs = stepMs / 2;
|
|
15751
|
-
const timeline = [];
|
|
15752
|
-
const comparisons = [];
|
|
15753
|
-
const inferenceMs = [];
|
|
15754
|
-
const failures = [];
|
|
15755
|
-
let decoded = 0;
|
|
15756
|
-
let ran = 0;
|
|
15757
|
-
let failed = 0;
|
|
15758
|
-
let consecutiveFailures = 0;
|
|
15759
|
-
let lastCallAt = null;
|
|
15760
|
-
const pace = deps.pace ?? REAL_PACE;
|
|
15761
|
-
let withoutFootage = 0;
|
|
15762
|
-
let notDecoded = 0;
|
|
15763
|
-
let truncatedBySafetyCap = false;
|
|
15764
|
-
let nearSuppressedBirth = 0;
|
|
15765
|
-
let positionsCompared = 0;
|
|
15766
|
-
let partsRead = 0;
|
|
15767
|
-
const progress = () => run.report({ phase: "replaying", framesPlanned: plan.planned, framesDone: ran + failed });
|
|
15768
|
-
progress();
|
|
15769
|
-
for (const chunk of plan.chunks) {
|
|
15770
|
-
if (run.signal.aborted) break;
|
|
15771
|
-
const recording = await deps.readRecording(request.deviceId, chunk.instants, stepMs);
|
|
15772
|
-
if (recording.kind === "refused") {
|
|
15773
|
-
withoutFootage += chunk.instants.length;
|
|
15774
|
-
deps.logger.warn("session replay: no footage for a stretch of the session", {
|
|
15775
|
-
tags,
|
|
15776
|
-
meta: { fromMs: chunk.fromMs, toMs: chunk.toMs, detail: recording.detail }
|
|
15777
|
-
});
|
|
15778
|
-
continue;
|
|
15779
|
-
}
|
|
15780
|
-
withoutFootage += recording.instantsWithoutFootage;
|
|
15781
|
-
partsRead += recording.parts.length;
|
|
15782
|
-
if (recording.truncatedBySafetyCap) truncatedBySafetyCap = true;
|
|
15783
|
-
for (const part of recording.parts) {
|
|
15784
|
-
if (run.signal.aborted) break;
|
|
15785
|
-
const first = part.instants[0];
|
|
15786
|
-
if (first === void 0) continue;
|
|
15787
|
-
const frames = await deps.decode(part.bytes, {
|
|
15788
|
-
startSec: (first - part.gopStartMs) / 1e3,
|
|
15789
|
-
durationSec: part.instants.length / cadence.fps,
|
|
15790
|
-
fps: cadence.fps,
|
|
15791
|
-
maxWidth: frameSize.width
|
|
15792
|
-
});
|
|
15793
|
-
decoded += Math.min(frames.length, part.instants.length);
|
|
15794
|
-
if (frames.length < part.instants.length) {
|
|
15795
|
-
notDecoded += part.instants.length - frames.length;
|
|
15796
|
-
deps.logger.warn("session replay: decode produced fewer frames than planned", {
|
|
15797
|
-
tags,
|
|
15798
|
-
meta: { planned: part.instants.length, decoded: frames.length, fromMs: first }
|
|
15799
|
-
});
|
|
15800
|
-
}
|
|
15801
|
-
for (let k = 0; k < part.instants.length; k++) {
|
|
15802
|
-
if (run.signal.aborted) break;
|
|
15803
|
-
const t = part.instants[k];
|
|
15804
|
-
const image = frames[k];
|
|
15805
|
-
if (t === void 0 || image === void 0) break;
|
|
15806
|
-
if (lastCallAt !== null) {
|
|
15807
|
-
const wait = lastCallAt + stepMs - pace.now();
|
|
15808
|
-
if (wait > 0) await pace.sleep(wait);
|
|
15809
|
-
}
|
|
15810
|
-
lastCallAt = pace.now();
|
|
15811
|
-
let result;
|
|
15812
|
-
try {
|
|
15813
|
-
result = await deps.runRootDetector({
|
|
15814
|
-
nodeId: request.nodeId,
|
|
15815
|
-
...request.deviceKey !== void 0 ? { deviceKey: request.deviceKey } : {},
|
|
15816
|
-
steps: [step],
|
|
15817
|
-
image,
|
|
15818
|
-
deviceId: request.deviceId,
|
|
15819
|
-
t
|
|
15820
|
-
});
|
|
15821
|
-
} catch (err) {
|
|
15822
|
-
const pinRefusal = pinRefusalOf(err);
|
|
15823
|
-
if (pinRefusal !== null) return refuse(pinRefusal.reason, pinRefusal.detail);
|
|
15824
|
-
failed += 1;
|
|
15825
|
-
consecutiveFailures += 1;
|
|
15826
|
-
const error51 = err instanceof Error ? err.message : String(err);
|
|
15827
|
-
if (failures.length < MAX_REPORTED_FAILURES) failures.push({ t, error: error51 });
|
|
15828
|
-
deps.logger.warn("session replay: frame failed \u2014 counted, the run continues", {
|
|
15829
|
-
tags,
|
|
15830
|
-
meta: { t, error: error51, consecutiveFailures }
|
|
15831
|
-
});
|
|
15832
|
-
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
15833
|
-
return refuse(
|
|
15834
|
-
"pool-failing",
|
|
15835
|
-
`${pool} failed ${consecutiveFailures} frames in a row (last: ${error51}) \u2014 stopped rather than keep loading a live pool`
|
|
15836
|
-
);
|
|
15837
|
-
}
|
|
15838
|
-
progress();
|
|
15839
|
-
continue;
|
|
15840
|
-
}
|
|
15841
|
-
consecutiveFailures = 0;
|
|
15842
|
-
if (result.rootModelId !== modelId) {
|
|
15843
|
-
return refuse(
|
|
15844
|
-
"model-not-servable",
|
|
15845
|
-
result.rootModelId === void 0 ? `${pool} does not report the model it ran (the executor does not report it) \u2014 the pin cannot be verified` : `${pool} reported ${result.rootModelId} for a frame pinned to ${modelId}`
|
|
15846
|
-
);
|
|
15847
|
-
}
|
|
15848
|
-
ran += 1;
|
|
15849
|
-
const frameInferMs = result.debug?.totalInferenceMs;
|
|
15850
|
-
if (frameInferMs !== void 0) inferenceMs.push(frameInferMs);
|
|
15851
|
-
const replayBoxes = toReplayBoxes(result);
|
|
15852
|
-
const instantRef = referenceAt(reference.tracks, t, halfStepMs);
|
|
15853
|
-
positionsCompared += instantRef.entries.length;
|
|
15854
|
-
const cmp = compareFrame(t, replayBoxes, instantRef);
|
|
15855
|
-
comparisons.push(cmp);
|
|
15856
|
-
for (const unmatched of [...cmp.added, ...cmp.unreferenced]) {
|
|
15857
|
-
if (suppressedBirths.some(
|
|
15858
|
-
(b) => sameClass2(b.className, unmatched.className) && Math.abs(b.at - t) <= SUPPRESSED_BIRTH_PROXIMITY_MS
|
|
15859
|
-
)) {
|
|
15860
|
-
nearSuppressedBirth += 1;
|
|
15861
|
-
}
|
|
15862
|
-
}
|
|
15863
|
-
timeline.push({
|
|
15864
|
-
t,
|
|
15865
|
-
referenced: cmp.referenced,
|
|
15866
|
-
inferenceMs: frameInferMs ?? null,
|
|
15867
|
-
replay: [
|
|
15868
|
-
...cmp.matched.map((p) => ({ ...p.replay, outcome: "matched" })),
|
|
15869
|
-
...cmp.classChanged.map((p) => ({ ...p.replay, outcome: "class-changed" })),
|
|
15870
|
-
...cmp.added.map((r) => ({ ...r, outcome: "added" })),
|
|
15871
|
-
...cmp.unreferenced.map((r) => ({ ...r, outcome: "unreferenced" }))
|
|
15872
|
-
],
|
|
15873
|
-
stored: [
|
|
15874
|
-
...cmp.matched.map((p) => ({ ...p.stored, outcome: "matched" })),
|
|
15875
|
-
...cmp.classChanged.map((p) => ({ ...p.stored, outcome: "class-changed" })),
|
|
15876
|
-
...cmp.missed.map((s) => ({ ...s, outcome: "missed" }))
|
|
15877
|
-
]
|
|
15878
|
-
});
|
|
15879
|
-
progress();
|
|
15880
|
-
}
|
|
15881
|
-
}
|
|
15882
|
-
}
|
|
15883
|
-
const aborted2 = run.signal.aborted;
|
|
15884
|
-
if (partsRead === 0 && !aborted2) {
|
|
15885
|
-
return refuse(
|
|
15886
|
-
"no-footage",
|
|
15887
|
-
`no recording covers any of the ${plan.planned} planned instants of motion session ${request.motionEventId}`
|
|
15888
|
-
);
|
|
15889
|
-
}
|
|
15890
|
-
if (aborted2) {
|
|
15891
|
-
deps.logger.info("session replay aborted \u2014 the report covers the frames that ran", {
|
|
15892
|
-
tags,
|
|
15893
|
-
meta: { ran, planned: plan.planned }
|
|
15894
|
-
});
|
|
15895
|
-
}
|
|
15896
|
-
run.report({ phase: "done", framesPlanned: plan.planned, framesDone: ran + failed });
|
|
15897
|
-
return {
|
|
15898
|
-
kind: "ran",
|
|
15899
|
-
report: {
|
|
15900
|
-
deviceId: request.deviceId,
|
|
15901
|
-
motionEventId: request.motionEventId,
|
|
15902
|
-
window: {
|
|
15903
|
-
startMs: window.startMs,
|
|
15904
|
-
endMs: window.endMs,
|
|
15905
|
-
motionStartMs: window.motionStartMs,
|
|
15906
|
-
motionEndMs: window.motionEndMs,
|
|
15907
|
-
prerollSource: window.prerollSource,
|
|
15908
|
-
tailSource: window.tailSource
|
|
15909
|
-
},
|
|
15910
|
-
pin: {
|
|
15911
|
-
nodeId: request.nodeId,
|
|
15912
|
-
deviceKey: request.deviceKey ?? null,
|
|
15913
|
-
modelId,
|
|
15914
|
-
cameraModelId: selection.cameraModelId
|
|
15915
|
-
},
|
|
15916
|
-
rootStep: step,
|
|
15917
|
-
cadence,
|
|
15918
|
-
frameSize,
|
|
15919
|
-
frames: {
|
|
15920
|
-
planned: plan.planned,
|
|
15921
|
-
decoded,
|
|
15922
|
-
ran,
|
|
15923
|
-
failed,
|
|
15924
|
-
withoutFootage,
|
|
15925
|
-
notDecoded,
|
|
15926
|
-
truncated: plan.truncated,
|
|
15927
|
-
truncatedBySafetyCap,
|
|
15928
|
-
aborted: aborted2
|
|
15929
|
-
},
|
|
15930
|
-
reference: {
|
|
15931
|
-
tracks: reference.trackCount,
|
|
15932
|
-
positionsCompared,
|
|
15933
|
-
recoveryExcluded: reference.recoveryExcluded,
|
|
15934
|
-
withoutGeometry: reference.withoutGeometry,
|
|
15935
|
-
models: reference.models,
|
|
15936
|
-
read: { tracks: tracks.length, truncated: readTruncated }
|
|
15937
|
-
},
|
|
15938
|
-
confirmation: { liveVerdicts, nearSuppressedBirth },
|
|
15939
|
-
summary: summarizeReplay(comparisons, inferenceMs),
|
|
15940
|
-
timeline,
|
|
15941
|
-
failures
|
|
15942
|
-
}
|
|
15943
|
-
};
|
|
15944
|
-
}
|
|
15945
|
-
|
|
15946
|
-
// src/session-replay/camera-settings.ts
|
|
15947
|
-
var FieldSchema = external_exports.object({
|
|
15948
|
-
key: external_exports.string().optional(),
|
|
15949
|
-
value: external_exports.unknown().optional(),
|
|
15950
|
-
fields: external_exports.array(external_exports.unknown()).optional(),
|
|
15951
|
-
tabs: external_exports.array(external_exports.object({ fields: external_exports.array(external_exports.unknown()).optional() }).loose()).optional()
|
|
15952
|
-
}).loose();
|
|
15953
|
-
var FormSchema = external_exports.object({
|
|
15954
|
-
sections: external_exports.array(external_exports.object({ fields: external_exports.array(external_exports.unknown()) }).loose())
|
|
14567
|
+
})
|
|
15955
14568
|
});
|
|
15956
|
-
function collect(fields, out) {
|
|
15957
|
-
for (const raw of fields) {
|
|
15958
|
-
const field = FieldSchema.safeParse(raw);
|
|
15959
|
-
if (!field.success) continue;
|
|
15960
|
-
if (field.data.key !== void 0 && "value" in field.data)
|
|
15961
|
-
out.set(field.data.key, field.data.value);
|
|
15962
|
-
if (field.data.fields !== void 0) collect(field.data.fields, out);
|
|
15963
|
-
for (const tab of field.data.tabs ?? []) collect(tab.fields ?? [], out);
|
|
15964
|
-
}
|
|
15965
|
-
}
|
|
15966
|
-
function readCameraSessionSettings(raw) {
|
|
15967
|
-
const form = FormSchema.safeParse(raw);
|
|
15968
|
-
if (!form.success) return {};
|
|
15969
|
-
const values = /* @__PURE__ */ new Map();
|
|
15970
|
-
for (const section of form.data.sections) collect(section.fields, values);
|
|
15971
|
-
const cooldown = values.get("motionCooldownMs");
|
|
15972
|
-
const preRoll = values.get("preRollInferenceEnabled");
|
|
15973
|
-
return {
|
|
15974
|
-
...typeof cooldown === "number" && Number.isFinite(cooldown) && cooldown >= 0 ? { motionCooldownMs: cooldown } : {},
|
|
15975
|
-
...typeof preRoll === "boolean" ? { preRollInferenceEnabled: preRoll } : {}
|
|
15976
|
-
};
|
|
15977
|
-
}
|
|
15978
14569
|
|
|
15979
14570
|
// src/benchmark-addon.ts
|
|
15980
|
-
var KEY_BENCHMARK_HISTORY = "benchmarkHistory";
|
|
15981
|
-
var MAX_HISTORY_ENTRIES = 100;
|
|
15982
|
-
var EMPTY_HISTORY_JSON = "[]";
|
|
15983
14571
|
var BENCH_RUN_TIMEOUT_MS = 55e3;
|
|
15984
|
-
var REPLAY_RECORDING_PROFILE = "high";
|
|
15985
|
-
var REPLAY_BIRTHS_LIMIT = 1e3;
|
|
15986
|
-
var REPLAY_DECODE_TIMEOUT_FLOOR_MS = 15e3;
|
|
15987
|
-
var REPLAY_DECODE_TIMEOUT_PER_SEC_MS = 3e3;
|
|
15988
|
-
var MOTION_SESSIONS_DEFAULT_SPAN_MS = 24 * 60 * 60 * 1e3;
|
|
15989
14572
|
async function withHardTimeout(promise2, ms, label) {
|
|
15990
14573
|
let timer;
|
|
15991
14574
|
const timeout = new Promise((_resolve, reject) => {
|
|
@@ -16001,36 +14584,8 @@ async function withHardTimeout(promise2, ms, label) {
|
|
|
16001
14584
|
if (timer) clearTimeout(timer);
|
|
16002
14585
|
}
|
|
16003
14586
|
}
|
|
16004
|
-
var
|
|
16005
|
-
var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
14587
|
+
var BenchmarkAddon = class extends import_types3.BaseAddon {
|
|
16006
14588
|
id = "benchmark";
|
|
16007
|
-
replayStore = null;
|
|
16008
|
-
/** The replay's ffmpeg spawns — the PINNED binary (`ctx.deps.ensureFfmpeg`, D540). */
|
|
16009
|
-
replayDecoder = new ReplayDecoder({
|
|
16010
|
-
resolveFfmpegPath: () => this.ctx.deps.ensureFfmpeg()
|
|
16011
|
-
});
|
|
16012
|
-
/**
|
|
16013
|
-
* Session replays, start-then-poll (D646). One at a time — a replay drives
|
|
16014
|
-
* one inference call after another on a node that also serves live
|
|
16015
|
-
* cameras — and the last few finished runs kept for a late poll.
|
|
16016
|
-
*/
|
|
16017
|
-
replays = new import_types4.BackgroundRunRegistry(
|
|
16018
|
-
{
|
|
16019
|
-
maxConcurrent: 1,
|
|
16020
|
-
keepFinished: 10,
|
|
16021
|
-
onSettled: (status) => {
|
|
16022
|
-
if (status.state === "failed") {
|
|
16023
|
-
this.ctx.logger.warn("session replay failed", {
|
|
16024
|
-
meta: { runId: status.runId, error: status.error }
|
|
16025
|
-
});
|
|
16026
|
-
return;
|
|
16027
|
-
}
|
|
16028
|
-
const outcome = status.result;
|
|
16029
|
-
if (outcome === null || outcome.kind !== "ran") return;
|
|
16030
|
-
void this.persistReplay(status.runId, outcome.report.pin.nodeId, outcome);
|
|
16031
|
-
}
|
|
16032
|
-
}
|
|
16033
|
-
);
|
|
16034
14589
|
pages = [
|
|
16035
14590
|
{
|
|
16036
14591
|
id: "benchmark",
|
|
@@ -16051,29 +14606,15 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
16051
14606
|
id: this.id,
|
|
16052
14607
|
listPages: () => this.pages
|
|
16053
14608
|
};
|
|
16054
|
-
await ReplayStore.declare(this.api.settingsStore);
|
|
16055
|
-
this.replayStore = new ReplayStore(this.api.settingsStore);
|
|
16056
|
-
await this.pruneReplays();
|
|
16057
14609
|
return {
|
|
16058
|
-
providers: [{ capability:
|
|
14610
|
+
providers: [{ capability: import_types3.addonPagesSourceCapability, provider }],
|
|
16059
14611
|
customActions: benchmarkActions,
|
|
16060
14612
|
actionHandlers: {
|
|
16061
|
-
runPipelineTest: async (input) => this.runPipelineTest(input)
|
|
16062
|
-
listBenchmarkHistory: async () => this.loadHistory(),
|
|
16063
|
-
saveBenchmarkResult: async (input) => this.saveBenchmarkResult(input),
|
|
16064
|
-
deleteBenchmarkResult: async (input) => this.deleteBenchmarkResult(input),
|
|
16065
|
-
clearBenchmarkHistory: async () => this.clearBenchmarkHistory(),
|
|
16066
|
-
listMotionSessions: async (input) => this.listMotionSessions(input),
|
|
16067
|
-
startSessionReplay: async (input) => this.startSessionReplay(input),
|
|
16068
|
-
getSessionReplayStatus: async (input) => this.getSessionReplayStatus(input),
|
|
16069
|
-
abortSessionReplay: async (input) => ({ aborted: this.replays.abort(input.runId) }),
|
|
16070
|
-
listSessionReplays: async (input) => this.listSessionReplays(input),
|
|
16071
|
-
getSessionReplay: async (input) => this.replayStore === null ? null : this.replayStore.get(input.id)
|
|
14613
|
+
runPipelineTest: async (input) => this.runPipelineTest(input)
|
|
16072
14614
|
}
|
|
16073
14615
|
};
|
|
16074
14616
|
}
|
|
16075
14617
|
async onShutdown() {
|
|
16076
|
-
this.replays.abortAll();
|
|
16077
14618
|
}
|
|
16078
14619
|
get api() {
|
|
16079
14620
|
const api = this.ctx.api;
|
|
@@ -16127,297 +14668,6 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
16127
14668
|
"runPipelineTest"
|
|
16128
14669
|
);
|
|
16129
14670
|
}
|
|
16130
|
-
// ── Session replay (D646) ─────────────────────────────────────────────────
|
|
16131
|
-
async listMotionSessions(input) {
|
|
16132
|
-
const untilMs = input.untilMs ?? Date.now();
|
|
16133
|
-
const sinceMs = input.sinceMs ?? untilMs - MOTION_SESSIONS_DEFAULT_SPAN_MS;
|
|
16134
|
-
const events = await this.api.pipelineAnalytics.getMotionEvents.query({
|
|
16135
|
-
deviceId: input.deviceId,
|
|
16136
|
-
since: sinceMs,
|
|
16137
|
-
until: untilMs,
|
|
16138
|
-
limit: input.limit,
|
|
16139
|
-
projection: "full"
|
|
16140
|
-
});
|
|
16141
|
-
const camera = await this.cameraSessionSettings(input.deviceId);
|
|
16142
|
-
const windows = events.map((ev) => ({ ev, window: motionSessionWindow(ev, camera) }));
|
|
16143
|
-
const starts = windows.map(
|
|
16144
|
-
({ ev, window }) => window.kind === "window" ? window.startMs : ev.timestamp
|
|
16145
|
-
);
|
|
16146
|
-
const ends = windows.map(
|
|
16147
|
-
({ ev, window }) => window.kind === "window" ? window.endMs : ev.timestamp
|
|
16148
|
-
);
|
|
16149
|
-
const tracks = events.length === 0 ? [] : await this.api.pipelineAnalytics.listTracks.query({
|
|
16150
|
-
deviceId: input.deviceId,
|
|
16151
|
-
since: Math.min(...starts),
|
|
16152
|
-
until: Math.max(...ends),
|
|
16153
|
-
projection: "slim",
|
|
16154
|
-
excludeSources: ["sensor", "audio"],
|
|
16155
|
-
limit: STORED_TRACKS_READ_LIMIT
|
|
16156
|
-
});
|
|
16157
|
-
return windows.map(({ ev, window }) => ({
|
|
16158
|
-
motionEventId: ev.id,
|
|
16159
|
-
deviceId: ev.deviceId,
|
|
16160
|
-
startMs: ev.timestamp,
|
|
16161
|
-
durationMs: ev.durationMs ?? null,
|
|
16162
|
-
open: ev.durationMs === null,
|
|
16163
|
-
refusal: window.kind === "refused" ? window.reason : null,
|
|
16164
|
-
storedTracks: window.kind === "window" ? tracks.filter((t) => t.firstSeen >= window.startMs && t.firstSeen < window.endMs).length : 0
|
|
16165
|
-
}));
|
|
16166
|
-
}
|
|
16167
|
-
startSessionReplay(input) {
|
|
16168
|
-
const runId = `replay-${input.deviceId}-${input.motionEventId}-${Date.now().toString(36)}`;
|
|
16169
|
-
this.ctx.logger.info("session replay started", {
|
|
16170
|
-
tags: { deviceId: input.deviceId },
|
|
16171
|
-
meta: {
|
|
16172
|
-
runId,
|
|
16173
|
-
motionEventId: input.motionEventId,
|
|
16174
|
-
nodeId: input.nodeId,
|
|
16175
|
-
deviceKey: input.deviceKey ?? null,
|
|
16176
|
-
modelId: input.modelId ?? null,
|
|
16177
|
-
fps: input.fps ?? null,
|
|
16178
|
-
maxFrames: input.maxFrames
|
|
16179
|
-
}
|
|
16180
|
-
});
|
|
16181
|
-
this.replays.start(
|
|
16182
|
-
runId,
|
|
16183
|
-
(run) => runSessionReplay(
|
|
16184
|
-
this.sessionReplayDeps(),
|
|
16185
|
-
{
|
|
16186
|
-
deviceId: input.deviceId,
|
|
16187
|
-
motionEventId: input.motionEventId,
|
|
16188
|
-
motionStartMs: input.motionStartMs,
|
|
16189
|
-
nodeId: input.nodeId,
|
|
16190
|
-
maxFrames: input.maxFrames,
|
|
16191
|
-
...input.deviceKey !== void 0 ? { deviceKey: input.deviceKey } : {},
|
|
16192
|
-
...input.modelId !== void 0 ? { modelId: input.modelId } : {},
|
|
16193
|
-
...input.fps !== void 0 ? { fps: input.fps } : {}
|
|
16194
|
-
},
|
|
16195
|
-
run
|
|
16196
|
-
)
|
|
16197
|
-
);
|
|
16198
|
-
return { accepted: true, runId };
|
|
16199
|
-
}
|
|
16200
|
-
getSessionReplayStatus(input) {
|
|
16201
|
-
const status = this.replays.status(input.runId);
|
|
16202
|
-
if (status === null) return null;
|
|
16203
|
-
return {
|
|
16204
|
-
runId: status.runId,
|
|
16205
|
-
state: status.state,
|
|
16206
|
-
startedAtMs: status.startedAtMs,
|
|
16207
|
-
finishedAtMs: status.finishedAtMs,
|
|
16208
|
-
progress: status.progress,
|
|
16209
|
-
outcome: status.result,
|
|
16210
|
-
error: status.error
|
|
16211
|
-
};
|
|
16212
|
-
}
|
|
16213
|
-
async listSessionReplays(input) {
|
|
16214
|
-
if (this.replayStore === null) return { runs: [], skipped: 0 };
|
|
16215
|
-
const { runs, skipped } = await this.replayStore.list({ deviceId: input.deviceId, limit: 50 });
|
|
16216
|
-
if (skipped > 0) {
|
|
16217
|
-
this.ctx.logger.info("session replays: rows skipped that are not session replays", {
|
|
16218
|
-
tags: { deviceId: input.deviceId },
|
|
16219
|
-
meta: { skipped }
|
|
16220
|
-
});
|
|
16221
|
-
}
|
|
16222
|
-
return {
|
|
16223
|
-
runs: runs.map((r) => {
|
|
16224
|
-
const { timeline: _timeline, ...report } = r.report;
|
|
16225
|
-
return {
|
|
16226
|
-
id: r.id,
|
|
16227
|
-
deviceId: r.deviceId,
|
|
16228
|
-
nodeId: r.nodeId,
|
|
16229
|
-
createdAtMs: r.createdAtMs,
|
|
16230
|
-
report
|
|
16231
|
-
};
|
|
16232
|
-
}),
|
|
16233
|
-
skipped
|
|
16234
|
-
};
|
|
16235
|
-
}
|
|
16236
|
-
async persistReplay(runId, nodeId, outcome) {
|
|
16237
|
-
const store = this.replayStore;
|
|
16238
|
-
if (store === null) return;
|
|
16239
|
-
const tags = { deviceId: outcome.report.deviceId };
|
|
16240
|
-
try {
|
|
16241
|
-
await store.save({
|
|
16242
|
-
id: runId,
|
|
16243
|
-
deviceId: outcome.report.deviceId,
|
|
16244
|
-
nodeId,
|
|
16245
|
-
createdAtMs: Date.now(),
|
|
16246
|
-
report: outcome.report
|
|
16247
|
-
});
|
|
16248
|
-
await this.pruneReplays();
|
|
16249
|
-
} catch (err) {
|
|
16250
|
-
this.ctx.logger.warn("session replay: report NOT saved", {
|
|
16251
|
-
tags,
|
|
16252
|
-
meta: { runId, error: (0, import_types4.errMsg)(err) }
|
|
16253
|
-
});
|
|
16254
|
-
}
|
|
16255
|
-
}
|
|
16256
|
-
async pruneReplays() {
|
|
16257
|
-
const store = this.replayStore;
|
|
16258
|
-
if (store === null) return;
|
|
16259
|
-
try {
|
|
16260
|
-
const deleted = await store.pruneBefore(Date.now() - REPLAY_RETENTION_MS);
|
|
16261
|
-
if (deleted > 0) this.ctx.logger.info("session replays pruned", { meta: { deleted } });
|
|
16262
|
-
} catch (err) {
|
|
16263
|
-
this.ctx.logger.warn("session replays: prune failed", { meta: { error: (0, import_types4.errMsg)(err) } });
|
|
16264
|
-
}
|
|
16265
|
-
}
|
|
16266
|
-
/**
|
|
16267
|
-
* The camera's own cooldown + pre-roll switch, read from the orchestrator's
|
|
16268
|
-
* hydrated device settings (the authority, not a copy). A failed read is
|
|
16269
|
-
* `{}` — the fleet defaults apply and the report says `default`.
|
|
16270
|
-
*/
|
|
16271
|
-
async cameraSessionSettings(deviceId) {
|
|
16272
|
-
try {
|
|
16273
|
-
const raw = await this.api.addonSettings.getDeviceSettings.query({
|
|
16274
|
-
addonId: "pipeline-orchestrator",
|
|
16275
|
-
deviceId
|
|
16276
|
-
});
|
|
16277
|
-
return readCameraSessionSettings(raw);
|
|
16278
|
-
} catch (err) {
|
|
16279
|
-
this.ctx.logger.warn("session replay: camera session settings unreadable \u2014 fleet defaults", {
|
|
16280
|
-
tags: { deviceId },
|
|
16281
|
-
meta: { error: (0, import_types4.errMsg)(err) }
|
|
16282
|
-
});
|
|
16283
|
-
return {};
|
|
16284
|
-
}
|
|
16285
|
-
}
|
|
16286
|
-
/** The replay's outside world — every call through `ctx.api`, node-pinned where it matters. */
|
|
16287
|
-
sessionReplayDeps() {
|
|
16288
|
-
const api = this.api;
|
|
16289
|
-
const profile = REPLAY_RECORDING_PROFILE;
|
|
16290
|
-
return {
|
|
16291
|
-
logger: this.ctx.logger,
|
|
16292
|
-
cameraSessionSettings: (deviceId) => this.cameraSessionSettings(deviceId),
|
|
16293
|
-
findMotionSession: async (deviceId, motionEventId, motionStartMs) => {
|
|
16294
|
-
const events = await api.pipelineAnalytics.getMotionEvents.query({
|
|
16295
|
-
deviceId,
|
|
16296
|
-
since: motionStartMs - 1,
|
|
16297
|
-
until: motionStartMs + 1,
|
|
16298
|
-
limit: 50,
|
|
16299
|
-
projection: "full"
|
|
16300
|
-
});
|
|
16301
|
-
return events.find((e) => e.id === motionEventId) ?? null;
|
|
16302
|
-
},
|
|
16303
|
-
resolveCameraTree: (deviceId, nodeId) => api.pipelineOrchestrator.resolvePipeline.query({ deviceId, agentNodeId: nodeId }),
|
|
16304
|
-
validate: (steps, nodeId, deviceKey) => api.pipelineExecutor.validatePipeline.query(
|
|
16305
|
-
{ steps: [...steps], ...deviceKey !== void 0 ? { deviceKey } : {} },
|
|
16306
|
-
(0, import_types4.nodePin)(nodeId)
|
|
16307
|
-
),
|
|
16308
|
-
isRecording: async (deviceId) => {
|
|
16309
|
-
const config2 = await api.recordingArchive.getDeviceConfig.query({ deviceId });
|
|
16310
|
-
return config2.enabled !== false;
|
|
16311
|
-
},
|
|
16312
|
-
listStoredTracks: async (deviceId, sinceMs, untilMs) => api.pipelineAnalytics.listTracks.query({
|
|
16313
|
-
deviceId,
|
|
16314
|
-
since: sinceMs,
|
|
16315
|
-
until: untilMs,
|
|
16316
|
-
projection: "full",
|
|
16317
|
-
excludeSources: ["sensor", "audio"],
|
|
16318
|
-
limit: STORED_TRACKS_READ_LIMIT
|
|
16319
|
-
}),
|
|
16320
|
-
listBirthDecisions: (deviceId, sinceMs) => api.pipelineAnalytics.listBirthDecisions.query({
|
|
16321
|
-
deviceId,
|
|
16322
|
-
since: sinceMs,
|
|
16323
|
-
limit: REPLAY_BIRTHS_LIMIT
|
|
16324
|
-
}),
|
|
16325
|
-
readRecording: (deviceId, instants, stepMs) => readRecordingForInstants(
|
|
16326
|
-
{
|
|
16327
|
-
locateSegment: (epochMs) => api.recordingArchive.locateSegment.query({ deviceId, profile, epochMs }),
|
|
16328
|
-
readWindowBytes: (q) => api.recordingArchive.readWindowBytes.query({ deviceId, profile, ...q })
|
|
16329
|
-
},
|
|
16330
|
-
instants,
|
|
16331
|
-
stepMs
|
|
16332
|
-
),
|
|
16333
|
-
decode: async (bytes, args) => {
|
|
16334
|
-
const mjpeg = await this.replayDecoder.decodeSequence(
|
|
16335
|
-
buildReplaySequenceArgs(args),
|
|
16336
|
-
bytes,
|
|
16337
|
-
{
|
|
16338
|
-
timeoutMs: Math.max(
|
|
16339
|
-
REPLAY_DECODE_TIMEOUT_FLOOR_MS,
|
|
16340
|
-
Math.round(args.durationSec * REPLAY_DECODE_TIMEOUT_PER_SEC_MS)
|
|
16341
|
-
)
|
|
16342
|
-
}
|
|
16343
|
-
);
|
|
16344
|
-
return splitMjpegStream(mjpeg);
|
|
16345
|
-
},
|
|
16346
|
-
runRootDetector: (call) => api.pipelineExecutor.runPipeline.mutate({
|
|
16347
|
-
nodeId: call.nodeId,
|
|
16348
|
-
steps: [...call.steps],
|
|
16349
|
-
// A fresh `Uint8Array<ArrayBuffer>`: `mjpeg-split` hands back a
|
|
16350
|
-
// `Buffer` view, and the generated cap input is typed against the
|
|
16351
|
-
// narrower `ArrayBuffer` backing.
|
|
16352
|
-
image: new Uint8Array(call.image),
|
|
16353
|
-
deviceId: call.deviceId,
|
|
16354
|
-
plane: "frame",
|
|
16355
|
-
replay: true,
|
|
16356
|
-
...call.deviceKey !== void 0 ? { deviceKey: call.deviceKey } : {}
|
|
16357
|
-
})
|
|
16358
|
-
};
|
|
16359
|
-
}
|
|
16360
|
-
// ── History persistence (per-addon store on the hub) ──────────────────
|
|
16361
|
-
/**
|
|
16362
|
-
* Durable handle over the persisted history blob. LEGACY SHAPE, PRESERVED
|
|
16363
|
-
* VERBATIM: the value under `benchmarkHistory` is a JSON **string** (not
|
|
16364
|
-
* the array itself), so the handle is typed over the string — the
|
|
16365
|
-
* encode/decode + entry-level recovery below stays the codec, the handle
|
|
16366
|
-
* owns store access (retry budget + validated fallback).
|
|
16367
|
-
*/
|
|
16368
|
-
historyState() {
|
|
16369
|
-
return this.state(KEY_BENCHMARK_HISTORY, external_exports.string(), EMPTY_HISTORY_JSON);
|
|
16370
|
-
}
|
|
16371
|
-
async loadHistory() {
|
|
16372
|
-
const raw = await this.historyState().get();
|
|
16373
|
-
try {
|
|
16374
|
-
const parsed = (0, import_types4.parseJsonUnknown)(raw);
|
|
16375
|
-
const result = HistoryArraySchema.safeParse(parsed);
|
|
16376
|
-
if (result.success) return [...result.data];
|
|
16377
|
-
const arr = Array.isArray(parsed) ? parsed : [];
|
|
16378
|
-
const accepted = [];
|
|
16379
|
-
let rejected = 0;
|
|
16380
|
-
for (const entry2 of arr) {
|
|
16381
|
-
const parsedEntry = HistoryEntrySchema.safeParse(entry2);
|
|
16382
|
-
if (parsedEntry.success) accepted.push(parsedEntry.data);
|
|
16383
|
-
else rejected += 1;
|
|
16384
|
-
}
|
|
16385
|
-
if (rejected > 0) {
|
|
16386
|
-
this.ctx.logger.warn("benchmark history: dropped unparseable entries", {
|
|
16387
|
-
meta: { rejected, accepted: accepted.length, error: result.error.message }
|
|
16388
|
-
});
|
|
16389
|
-
}
|
|
16390
|
-
return accepted;
|
|
16391
|
-
} catch (err) {
|
|
16392
|
-
this.ctx.logger.warn("benchmark history parse failed", { meta: { error: (0, import_types4.errMsg)(err) } });
|
|
16393
|
-
return [];
|
|
16394
|
-
}
|
|
16395
|
-
}
|
|
16396
|
-
async writeHistory(history) {
|
|
16397
|
-
await this.historyState().set(JSON.stringify(history));
|
|
16398
|
-
}
|
|
16399
|
-
async saveBenchmarkResult(entry2) {
|
|
16400
|
-
const history = await this.loadHistory();
|
|
16401
|
-
const saved = {
|
|
16402
|
-
...entry2,
|
|
16403
|
-
kind: "pipeline",
|
|
16404
|
-
id: `bench-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
16405
|
-
date: (/* @__PURE__ */ new Date()).toISOString()
|
|
16406
|
-
};
|
|
16407
|
-
const next = [saved, ...history];
|
|
16408
|
-
if (next.length > MAX_HISTORY_ENTRIES) next.length = MAX_HISTORY_ENTRIES;
|
|
16409
|
-
await this.writeHistory(next);
|
|
16410
|
-
return saved;
|
|
16411
|
-
}
|
|
16412
|
-
async deleteBenchmarkResult(input) {
|
|
16413
|
-
const history = (await this.loadHistory()).filter((e) => e.id !== input.id);
|
|
16414
|
-
await this.writeHistory(history);
|
|
16415
|
-
return { success: true };
|
|
16416
|
-
}
|
|
16417
|
-
async clearBenchmarkHistory() {
|
|
16418
|
-
await this.writeHistory([]);
|
|
16419
|
-
return { success: true };
|
|
16420
|
-
}
|
|
16421
14671
|
};
|
|
16422
14672
|
// Annotate the CommonJS export names for ESM import in node:
|
|
16423
14673
|
0 && (module.exports = {
|