@camstack/addon-pipeline 1.2.221 → 1.2.223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio-analyzer/index.js +55 -16
- package/dist/audio-analyzer/index.mjs +55 -16
- package/dist/{default-detection-model-B1NT1A38.js → default-detection-model-B7kn9xHR.js} +1 -1
- package/dist/{default-detection-model-DkVFgubU.mjs → default-detection-model-BSX8cVHY.mjs} +1 -1
- package/dist/detection-pipeline/index.js +4 -4
- package/dist/detection-pipeline/index.mjs +4 -4
- package/dist/{dist-CEFC3PkP.mjs → dist-BNzfbpF6.mjs} +31 -2
- package/dist/{dist-ATDfcxgL.js → dist-DZsjVFey.js} +31 -2
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/{node-C5jnTgpv.js → node-BhQ8dDQC.js} +1 -1
- package/dist/{node-DR3fHqUq.mjs → node-uYIx2aZF.mjs} +1 -1
- package/dist/pipeline-runner/index.js +139 -20
- package/dist/pipeline-runner/index.mjs +139 -20
- package/dist/{process-memory-DQbCPnMA.js → process-memory-BozKb5-g.js} +1 -1
- package/dist/{process-memory-DqYj5IAb.mjs → process-memory-CkoLpOV-.mjs} +1 -1
- package/dist/recorder/index.js +90 -27
- package/dist/recorder/index.mjs +90 -27
- package/dist/{segment-demux-js-o9KBAz9p.js → segment-demux-js-Cvszh55p.js} +1 -1
- package/dist/{segment-demux-js-CddDwD69.mjs → segment-demux-js-Dh1dn_AT.mjs} +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D5XnLZWR.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BZG3prqU.mjs} +3 -3
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-C-VdcJS2.mjs → hostInit-Cyo0CFo3.mjs} +3 -3
- package/dist/stream-broker/index.js +96 -54
- package/dist/stream-broker/index.mjs +96 -54
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-emK7D4bc.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_node = require("../node-
|
|
8
|
-
const require_default_detection_model = require("../default-detection-model-
|
|
6
|
+
const require_dist = require("../dist-DZsjVFey.js");
|
|
7
|
+
const require_node = require("../node-BhQ8dDQC.js");
|
|
8
|
+
const require_default_detection_model = require("../default-detection-model-B7kn9xHR.js");
|
|
9
9
|
const require_worker_protocol = require("../worker-protocol-B2MfQLlu.js");
|
|
10
10
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
11
11
|
const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
|
|
@@ -4782,9 +4782,23 @@ function shouldRetainDetectionFrame(input) {
|
|
|
4782
4782
|
if (input.continuousRetain) return true;
|
|
4783
4783
|
return input.hasDetection;
|
|
4784
4784
|
}
|
|
4785
|
+
/**
|
|
4786
|
+
* The ONE place the cross-node crop wire format is decided (D462).
|
|
4787
|
+
*
|
|
4788
|
+
* `jpeg` arrives as the bytes the encoder produced. Whether they leave as
|
|
4789
|
+
* those bytes or as a base64 string is not this function's preference — it is
|
|
4790
|
+
* the CALLER'S declaration, `acceptBytes`, and an absent declaration means the
|
|
4791
|
+
* old wire. A pre-D462 consumer does `Buffer.from(reply.jpeg, 'base64')`, so
|
|
4792
|
+
* reading a missing flag as consent would hand it a `Uint8Array` to parse as
|
|
4793
|
+
* base64 and it would decode to garbage rather than fail.
|
|
4794
|
+
*
|
|
4795
|
+
* Base64 cost ~400 MB/min of hub-main relay traffic and, on the decoded half
|
|
4796
|
+
* of that, a multi-megabyte string in old space per call.
|
|
4797
|
+
*/
|
|
4785
4798
|
function encodeCrossNodeCrop(input) {
|
|
4786
|
-
if (!input.jpeg) return null;
|
|
4787
|
-
return {
|
|
4799
|
+
if (!input.jpeg || input.jpeg.byteLength === 0) return null;
|
|
4800
|
+
if (input.acceptBytes === true) return { jpegBytes: input.jpeg };
|
|
4801
|
+
return { jpeg: Buffer.from(input.jpeg).toString("base64") };
|
|
4788
4802
|
}
|
|
4789
4803
|
function handleKey(handle) {
|
|
4790
4804
|
return `${handle.shmId}#${handle.slot}#${handle.seq}`;
|
|
@@ -6491,6 +6505,37 @@ function isUsable(bbox) {
|
|
|
6491
6505
|
return bbox.width > 0 && bbox.height > 0;
|
|
6492
6506
|
}
|
|
6493
6507
|
//#endregion
|
|
6508
|
+
//#region src/pipeline-runner/runtime-labels.ts
|
|
6509
|
+
/**
|
|
6510
|
+
* `null` means the read FAILED; `[]` means nothing is resident. Both omit the
|
|
6511
|
+
* fields, but they are different facts and the caller logs the failure.
|
|
6512
|
+
*
|
|
6513
|
+
* The `runtime` pool is named first because it is the one serving cameras; the
|
|
6514
|
+
* rest follow in arrival order. `python` is deliberately NOT printed: every
|
|
6515
|
+
* engine available on this cluster is python, and a token that cannot vary
|
|
6516
|
+
* costs a column and tells the reader nothing.
|
|
6517
|
+
*/
|
|
6518
|
+
function buildRuntimeLabels(engines) {
|
|
6519
|
+
if (engines === null || engines.length === 0) return {};
|
|
6520
|
+
const ordered = [...engines.filter((e) => e.kind === "runtime"), ...engines.filter((e) => e.kind !== "runtime")];
|
|
6521
|
+
const devices = [...new Set(ordered.map((e) => e.device).filter((d) => !!d))];
|
|
6522
|
+
const backends = [...new Set(ordered.map((e) => e.backend))];
|
|
6523
|
+
const backend = backends.length === 1 ? backends[0] : "mixed";
|
|
6524
|
+
const runtime = devices.length === 0 ? backend : `${backend}/${devices.join("+")}`;
|
|
6525
|
+
const primary = ordered[0];
|
|
6526
|
+
if (primary === void 0) return {};
|
|
6527
|
+
const models = [];
|
|
6528
|
+
for (const engine of ordered) for (const model of engine.modelsLoaded) {
|
|
6529
|
+
if (models.includes(model)) continue;
|
|
6530
|
+
if (engine === primary || primary.modelsLoaded.includes(model)) models.push(model);
|
|
6531
|
+
else models.push(engine.device === void 0 ? model : `${model}@${engine.device}`);
|
|
6532
|
+
}
|
|
6533
|
+
return {
|
|
6534
|
+
pipelineRuntime: runtime,
|
|
6535
|
+
pipelineModels: models
|
|
6536
|
+
};
|
|
6537
|
+
}
|
|
6538
|
+
//#endregion
|
|
6494
6539
|
//#region src/pipeline-runner/track-frame-park.ts
|
|
6495
6540
|
/** Park one frame for one track. Never throws; a refusal is an answer. */
|
|
6496
6541
|
async function parkTrackFrame(deps, input) {
|
|
@@ -6644,6 +6689,14 @@ var PACKAGE_ZONE_UNION_TTL_MS = 6e4;
|
|
|
6644
6689
|
var MAX_BOUND_FRAME_CUTTERS = 256;
|
|
6645
6690
|
/** How often a camera reports its undispatched cuts. */
|
|
6646
6691
|
var TILE_CUT_DROP_LOG_INTERVAL_MS = 6e4;
|
|
6692
|
+
/**
|
|
6693
|
+
* How often the `pipeline stats` runtime labels are re-read (D463). Slow on
|
|
6694
|
+
* purpose: this answers "which engine is this node on", which changes when an
|
|
6695
|
+
* operator re-selects one or lazy provisioning completes — not per frame.
|
|
6696
|
+
*/
|
|
6697
|
+
var RUNTIME_INFO_REFRESH_MS = 3e5;
|
|
6698
|
+
/** Sentinel for "no api" so it is one transition, not a per-refresh line. */
|
|
6699
|
+
var NO_API_FAILURE = "no addon api";
|
|
6647
6700
|
/** How often a camera reports parcels that left without a release (D422). */
|
|
6648
6701
|
var PARKED_EVICTION_LOG_INTERVAL_MS = 6e4;
|
|
6649
6702
|
/**
|
|
@@ -7080,6 +7133,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7080
7133
|
parkedDetail = /* @__PURE__ */ new Map();
|
|
7081
7134
|
nodeId = "unknown";
|
|
7082
7135
|
metricsSnapshotTimer = null;
|
|
7136
|
+
runtimeInfoTimer = null;
|
|
7137
|
+
/** Last runtime-label failure, so only a CHANGE is logged (D463). */
|
|
7138
|
+
lastRuntimeInfoFailure = null;
|
|
7083
7139
|
unsubMotionEvents = null;
|
|
7084
7140
|
/** Devices we have positively observed reporting `battery.sleeping: true`.
|
|
7085
7141
|
* Absent = unknown or awake; both mean "do not gate" (D49). Fed only by
|
|
@@ -7331,6 +7387,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7331
7387
|
workerFootprintMb: decodeWorkerFootprintMb(this.nativeLease.current())
|
|
7332
7388
|
});
|
|
7333
7389
|
this.runner.timingSampler.setLogger(this.ctx.logger.child("timing"));
|
|
7390
|
+
this.refreshRuntimeInfo();
|
|
7391
|
+
this.runtimeInfoTimer = setInterval(() => void this.refreshRuntimeInfo(), RUNTIME_INFO_REFRESH_MS);
|
|
7392
|
+
this.runtimeInfoTimer.unref?.();
|
|
7334
7393
|
this.runner.onDetectionStreamChange((deviceId, action) => {
|
|
7335
7394
|
this.handleDetectionStreamChange(deviceId, action);
|
|
7336
7395
|
});
|
|
@@ -7397,6 +7456,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7397
7456
|
};
|
|
7398
7457
|
}
|
|
7399
7458
|
async onShutdown() {
|
|
7459
|
+
if (this.runtimeInfoTimer !== null) {
|
|
7460
|
+
clearInterval(this.runtimeInfoTimer);
|
|
7461
|
+
this.runtimeInfoTimer = null;
|
|
7462
|
+
}
|
|
7400
7463
|
if (this.metricsSnapshotTimer) {
|
|
7401
7464
|
clearInterval(this.metricsSnapshotTimer);
|
|
7402
7465
|
this.metricsSnapshotTimer = null;
|
|
@@ -8204,7 +8267,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8204
8267
|
const resolved = await this.resolveNativeCropPixels(input);
|
|
8205
8268
|
if (resolved.tier === "native" && resolved.pixels !== null) {
|
|
8206
8269
|
this.frameLazy.noteHit(resolveNativeCropHitKind(resolved.pixels.source), deviceId);
|
|
8207
|
-
return this.encodeNativeCropResult(resolved.pixels, resolved.tier, input.encodeJpeg);
|
|
8270
|
+
return this.encodeNativeCropResult(resolved.pixels, resolved.tier, input.encodeJpeg, input.acceptJpegBytes);
|
|
8208
8271
|
}
|
|
8209
8272
|
this.frameLazy.noteMiss("native", deviceId);
|
|
8210
8273
|
if (resolved.tier !== "ram-fullframe") return null;
|
|
@@ -8230,7 +8293,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8230
8293
|
bytes: crop.bytes,
|
|
8231
8294
|
width: crop.width,
|
|
8232
8295
|
height: crop.height
|
|
8233
|
-
}, resolved.tier, input.encodeJpeg);
|
|
8296
|
+
}, resolved.tier, input.encodeJpeg, input.acceptJpegBytes);
|
|
8234
8297
|
}
|
|
8235
8298
|
/**
|
|
8236
8299
|
* PARK a frame for a track (D422) — the hub judged it relevant, and this node
|
|
@@ -8253,8 +8316,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8253
8316
|
store,
|
|
8254
8317
|
resolvePixels: (request) => this.resolveNativeCropPixels(request),
|
|
8255
8318
|
encodeJpeg: async (pixels) => {
|
|
8256
|
-
const encoded = await this.encodeNativeCropResult(pixels, "native", true);
|
|
8257
|
-
return encoded?.
|
|
8319
|
+
const encoded = await this.encodeNativeCropResult(pixels, "native", true, true);
|
|
8320
|
+
return encoded?.jpegBytes !== void 0 ? Buffer.from(encoded.jpegBytes) : null;
|
|
8258
8321
|
},
|
|
8259
8322
|
logger: this.ctx.logger
|
|
8260
8323
|
}, input);
|
|
@@ -8277,7 +8340,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8277
8340
|
return null;
|
|
8278
8341
|
}
|
|
8279
8342
|
return {
|
|
8280
|
-
jpeg: parcel.jpeg.toString("base64"),
|
|
8343
|
+
...input.acceptJpegBytes === true ? { jpegBytes: new Uint8Array(parcel.jpeg) } : { jpeg: parcel.jpeg.toString("base64") },
|
|
8281
8344
|
width: parcel.width,
|
|
8282
8345
|
height: parcel.height,
|
|
8283
8346
|
timestamp: parcel.timestamp,
|
|
@@ -8657,12 +8720,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8657
8720
|
* boundary (CLAUDE.md invariant #21). On an encode failure the crop is a miss
|
|
8658
8721
|
* (`null`) — the caller retries — never a silent raw-RGB cross-node fallback.
|
|
8659
8722
|
*/
|
|
8660
|
-
async encodeNativeCropResult(crop, tier, encodeJpeg) {
|
|
8723
|
+
async encodeNativeCropResult(crop, tier, encodeJpeg, acceptJpegBytes) {
|
|
8661
8724
|
if (crop.bytes.byteLength >= 2 && crop.bytes[0] === 255 && crop.bytes[1] === 216) {
|
|
8662
|
-
const encoded = encodeCrossNodeCrop({
|
|
8725
|
+
const encoded = encodeCrossNodeCrop({
|
|
8726
|
+
jpeg: crop.bytes,
|
|
8727
|
+
acceptBytes: acceptJpegBytes
|
|
8728
|
+
});
|
|
8663
8729
|
if (!encoded) return null;
|
|
8664
8730
|
return {
|
|
8665
|
-
|
|
8731
|
+
...encoded,
|
|
8666
8732
|
width: crop.width,
|
|
8667
8733
|
height: crop.height,
|
|
8668
8734
|
tier
|
|
@@ -8676,16 +8742,17 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8676
8742
|
};
|
|
8677
8743
|
try {
|
|
8678
8744
|
const encoded = encodeCrossNodeCrop({
|
|
8679
|
-
jpeg:
|
|
8745
|
+
jpeg: await (await require_lazy_sharp.getSharp())(Buffer.from(crop.bytes), { raw: {
|
|
8680
8746
|
width: crop.width,
|
|
8681
8747
|
height: crop.height,
|
|
8682
8748
|
channels: 3
|
|
8683
|
-
} }).jpeg({ quality: 90 }).toBuffer()
|
|
8684
|
-
bytes: crop.bytes
|
|
8749
|
+
} }).jpeg({ quality: 90 }).toBuffer(),
|
|
8750
|
+
bytes: crop.bytes,
|
|
8751
|
+
acceptBytes: acceptJpegBytes
|
|
8685
8752
|
});
|
|
8686
8753
|
if (!encoded) return null;
|
|
8687
8754
|
return {
|
|
8688
|
-
|
|
8755
|
+
...encoded,
|
|
8689
8756
|
width: crop.width,
|
|
8690
8757
|
height: crop.height,
|
|
8691
8758
|
tier
|
|
@@ -8742,6 +8809,58 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8742
8809
|
}, input);
|
|
8743
8810
|
}
|
|
8744
8811
|
/**
|
|
8812
|
+
* Re-read the labels the `pipeline stats` lines carry (D463).
|
|
8813
|
+
*
|
|
8814
|
+
* Each half is read INDEPENDENTLY and a failure omits only its own field:
|
|
8815
|
+
* an engine that will not answer must not also erase the model list. Both
|
|
8816
|
+
* reads are best-effort by design — this is a label on a telemetry line, and
|
|
8817
|
+
* it must never be able to take the runner down or to log a storm. A failure
|
|
8818
|
+
* is logged once per refresh at `debug`, deliberately: the ABSENCE of the
|
|
8819
|
+
* field in the line is the signal, and the hub writes `info`.
|
|
8820
|
+
*/
|
|
8821
|
+
async refreshRuntimeInfo() {
|
|
8822
|
+
const api = this.ctxIfReady?.api;
|
|
8823
|
+
const sampler = this.runner?.timingSampler;
|
|
8824
|
+
if (!api || sampler === void 0 || sampler === null) {
|
|
8825
|
+
this.noteRuntimeInfoFailure(NO_API_FAILURE);
|
|
8826
|
+
return;
|
|
8827
|
+
}
|
|
8828
|
+
let engines = null;
|
|
8829
|
+
let failure = null;
|
|
8830
|
+
try {
|
|
8831
|
+
engines = (await api.pipelineExecutor.listLoadedEngines.query()).map((r) => ({
|
|
8832
|
+
backend: r.engine.backend,
|
|
8833
|
+
...r.engine.device !== void 0 ? { device: r.engine.device } : {},
|
|
8834
|
+
kind: r.kind,
|
|
8835
|
+
modelsLoaded: r.modelsLoaded
|
|
8836
|
+
}));
|
|
8837
|
+
} catch (err) {
|
|
8838
|
+
failure = require_dist.errMsg(err);
|
|
8839
|
+
}
|
|
8840
|
+
this.noteRuntimeInfoFailure(failure);
|
|
8841
|
+
sampler.runtimeInfo = {
|
|
8842
|
+
...sampler.runtimeInfo,
|
|
8843
|
+
...buildRuntimeLabels(engines)
|
|
8844
|
+
};
|
|
8845
|
+
}
|
|
8846
|
+
/**
|
|
8847
|
+
* Log a runtime-label failure at its TRANSITION, and at `warn`.
|
|
8848
|
+
*
|
|
8849
|
+
* This branch first shipped at `debug`, on the argument that the absent field
|
|
8850
|
+
* in the line was signal enough. It was not: within the hour `little-unraid`
|
|
8851
|
+
* was the one node still logging `pipelineRuntime=null` and there was no way
|
|
8852
|
+
* to tell "this node has no engine" from "the read threw", because the hub
|
|
8853
|
+
* writes `info` and the agent forwards to it. That is D438's blind
|
|
8854
|
+
* instrument for the third time. Logging the transition rather than every
|
|
8855
|
+
* refresh is what keeps it from becoming a storm.
|
|
8856
|
+
*/
|
|
8857
|
+
noteRuntimeInfoFailure(failure) {
|
|
8858
|
+
if (failure === this.lastRuntimeInfoFailure) return;
|
|
8859
|
+
if (failure === null) this.ctxIfReady?.logger.info("runtime labels: readable again");
|
|
8860
|
+
else this.ctxIfReady?.logger.warn("runtime labels unreadable — this node cannot name its engines", { meta: { error: failure } });
|
|
8861
|
+
this.lastRuntimeInfoFailure = failure;
|
|
8862
|
+
}
|
|
8863
|
+
/**
|
|
8745
8864
|
* The model format this node dispatches on when no `deviceKey` is given —
|
|
8746
8865
|
* its selected engine's format. `null` on any failure, which the caller
|
|
8747
8866
|
* turns into a refusal rather than a guess: a model pin that cannot be
|
|
@@ -8970,13 +9089,13 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8970
9089
|
handleAgeMs: resolved.handleAgeMs,
|
|
8971
9090
|
...resolved.workerReason !== void 0 ? { workerReason: resolved.workerReason } : {}
|
|
8972
9091
|
};
|
|
8973
|
-
const encoded = await this.encodeNativeCropResult(resolved.pixels, "native", true);
|
|
8974
|
-
if (!encoded?.
|
|
9092
|
+
const encoded = await this.encodeNativeCropResult(resolved.pixels, "native", true, true);
|
|
9093
|
+
if (!encoded?.jpegBytes) return {
|
|
8975
9094
|
miss: "jpeg-encode-failed",
|
|
8976
9095
|
handleAgeMs: resolved.handleAgeMs
|
|
8977
9096
|
};
|
|
8978
9097
|
return {
|
|
8979
|
-
jpeg: Buffer.from(encoded.
|
|
9098
|
+
jpeg: Buffer.from(encoded.jpegBytes),
|
|
8980
9099
|
width: encoded.width,
|
|
8981
9100
|
height: encoded.height,
|
|
8982
9101
|
frameSpace: denormalizeRect(paddedBbox, handle.width, handle.height)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { H as defineCustomActions, Lt as errMsg, W as deriveDetailCropRect, Wt as BaseAddon, Yt as createEvent, _ as FailureCounters, at as loadContributionCapability, b as NativeLeaseAdmissionSchema, cn as boolean, dn as literal, et as failureContributionCapability, fn as number, gn as string, m as DEVICE_BACKEND_TO_FORMAT, on as _enum, p as DEFAULT_NATIVE_LEASE_SETTINGS, pn as object, sn as array, tn as nodePin, u as DEFAULT_DETAIL_CROP_CONVENTION, un as lazy, vn as EventCategory, vt as pickDetailCropConvention, x as NativeLeaseSettingsSchema, xt as pipelineRunnerCapability, yt as pickNativeLeaseOverride, z as customAction } from "../dist-
|
|
2
|
-
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-
|
|
3
|
-
import { i as resolveFrameViewGeometry, l as getStepDefinition, r as localFrameRegistry } from "../default-detection-model-
|
|
1
|
+
import { H as defineCustomActions, Lt as errMsg, W as deriveDetailCropRect, Wt as BaseAddon, Yt as createEvent, _ as FailureCounters, at as loadContributionCapability, b as NativeLeaseAdmissionSchema, cn as boolean, dn as literal, et as failureContributionCapability, fn as number, gn as string, m as DEVICE_BACKEND_TO_FORMAT, on as _enum, p as DEFAULT_NATIVE_LEASE_SETTINGS, pn as object, sn as array, tn as nodePin, u as DEFAULT_DETAIL_CROP_CONVENTION, un as lazy, vn as EventCategory, vt as pickDetailCropConvention, x as NativeLeaseSettingsSchema, xt as pipelineRunnerCapability, yt as pickNativeLeaseOverride, z as customAction } from "../dist-BNzfbpF6.mjs";
|
|
2
|
+
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-uYIx2aZF.mjs";
|
|
3
|
+
import { i as resolveFrameViewGeometry, l as getStepDefinition, r as localFrameRegistry } from "../default-detection-model-BSX8cVHY.mjs";
|
|
4
4
|
import { t as isWorkerReply } from "../worker-protocol-C_W-P_g-.mjs";
|
|
5
5
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
6
6
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
@@ -4774,9 +4774,23 @@ function shouldRetainDetectionFrame(input) {
|
|
|
4774
4774
|
if (input.continuousRetain) return true;
|
|
4775
4775
|
return input.hasDetection;
|
|
4776
4776
|
}
|
|
4777
|
+
/**
|
|
4778
|
+
* The ONE place the cross-node crop wire format is decided (D462).
|
|
4779
|
+
*
|
|
4780
|
+
* `jpeg` arrives as the bytes the encoder produced. Whether they leave as
|
|
4781
|
+
* those bytes or as a base64 string is not this function's preference — it is
|
|
4782
|
+
* the CALLER'S declaration, `acceptBytes`, and an absent declaration means the
|
|
4783
|
+
* old wire. A pre-D462 consumer does `Buffer.from(reply.jpeg, 'base64')`, so
|
|
4784
|
+
* reading a missing flag as consent would hand it a `Uint8Array` to parse as
|
|
4785
|
+
* base64 and it would decode to garbage rather than fail.
|
|
4786
|
+
*
|
|
4787
|
+
* Base64 cost ~400 MB/min of hub-main relay traffic and, on the decoded half
|
|
4788
|
+
* of that, a multi-megabyte string in old space per call.
|
|
4789
|
+
*/
|
|
4777
4790
|
function encodeCrossNodeCrop(input) {
|
|
4778
|
-
if (!input.jpeg) return null;
|
|
4779
|
-
return {
|
|
4791
|
+
if (!input.jpeg || input.jpeg.byteLength === 0) return null;
|
|
4792
|
+
if (input.acceptBytes === true) return { jpegBytes: input.jpeg };
|
|
4793
|
+
return { jpeg: Buffer.from(input.jpeg).toString("base64") };
|
|
4780
4794
|
}
|
|
4781
4795
|
function handleKey(handle) {
|
|
4782
4796
|
return `${handle.shmId}#${handle.slot}#${handle.seq}`;
|
|
@@ -6483,6 +6497,37 @@ function isUsable(bbox) {
|
|
|
6483
6497
|
return bbox.width > 0 && bbox.height > 0;
|
|
6484
6498
|
}
|
|
6485
6499
|
//#endregion
|
|
6500
|
+
//#region src/pipeline-runner/runtime-labels.ts
|
|
6501
|
+
/**
|
|
6502
|
+
* `null` means the read FAILED; `[]` means nothing is resident. Both omit the
|
|
6503
|
+
* fields, but they are different facts and the caller logs the failure.
|
|
6504
|
+
*
|
|
6505
|
+
* The `runtime` pool is named first because it is the one serving cameras; the
|
|
6506
|
+
* rest follow in arrival order. `python` is deliberately NOT printed: every
|
|
6507
|
+
* engine available on this cluster is python, and a token that cannot vary
|
|
6508
|
+
* costs a column and tells the reader nothing.
|
|
6509
|
+
*/
|
|
6510
|
+
function buildRuntimeLabels(engines) {
|
|
6511
|
+
if (engines === null || engines.length === 0) return {};
|
|
6512
|
+
const ordered = [...engines.filter((e) => e.kind === "runtime"), ...engines.filter((e) => e.kind !== "runtime")];
|
|
6513
|
+
const devices = [...new Set(ordered.map((e) => e.device).filter((d) => !!d))];
|
|
6514
|
+
const backends = [...new Set(ordered.map((e) => e.backend))];
|
|
6515
|
+
const backend = backends.length === 1 ? backends[0] : "mixed";
|
|
6516
|
+
const runtime = devices.length === 0 ? backend : `${backend}/${devices.join("+")}`;
|
|
6517
|
+
const primary = ordered[0];
|
|
6518
|
+
if (primary === void 0) return {};
|
|
6519
|
+
const models = [];
|
|
6520
|
+
for (const engine of ordered) for (const model of engine.modelsLoaded) {
|
|
6521
|
+
if (models.includes(model)) continue;
|
|
6522
|
+
if (engine === primary || primary.modelsLoaded.includes(model)) models.push(model);
|
|
6523
|
+
else models.push(engine.device === void 0 ? model : `${model}@${engine.device}`);
|
|
6524
|
+
}
|
|
6525
|
+
return {
|
|
6526
|
+
pipelineRuntime: runtime,
|
|
6527
|
+
pipelineModels: models
|
|
6528
|
+
};
|
|
6529
|
+
}
|
|
6530
|
+
//#endregion
|
|
6486
6531
|
//#region src/pipeline-runner/track-frame-park.ts
|
|
6487
6532
|
/** Park one frame for one track. Never throws; a refusal is an answer. */
|
|
6488
6533
|
async function parkTrackFrame(deps, input) {
|
|
@@ -6636,6 +6681,14 @@ var PACKAGE_ZONE_UNION_TTL_MS = 6e4;
|
|
|
6636
6681
|
var MAX_BOUND_FRAME_CUTTERS = 256;
|
|
6637
6682
|
/** How often a camera reports its undispatched cuts. */
|
|
6638
6683
|
var TILE_CUT_DROP_LOG_INTERVAL_MS = 6e4;
|
|
6684
|
+
/**
|
|
6685
|
+
* How often the `pipeline stats` runtime labels are re-read (D463). Slow on
|
|
6686
|
+
* purpose: this answers "which engine is this node on", which changes when an
|
|
6687
|
+
* operator re-selects one or lazy provisioning completes — not per frame.
|
|
6688
|
+
*/
|
|
6689
|
+
var RUNTIME_INFO_REFRESH_MS = 3e5;
|
|
6690
|
+
/** Sentinel for "no api" so it is one transition, not a per-refresh line. */
|
|
6691
|
+
var NO_API_FAILURE = "no addon api";
|
|
6639
6692
|
/** How often a camera reports parcels that left without a release (D422). */
|
|
6640
6693
|
var PARKED_EVICTION_LOG_INTERVAL_MS = 6e4;
|
|
6641
6694
|
/**
|
|
@@ -7072,6 +7125,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
7072
7125
|
parkedDetail = /* @__PURE__ */ new Map();
|
|
7073
7126
|
nodeId = "unknown";
|
|
7074
7127
|
metricsSnapshotTimer = null;
|
|
7128
|
+
runtimeInfoTimer = null;
|
|
7129
|
+
/** Last runtime-label failure, so only a CHANGE is logged (D463). */
|
|
7130
|
+
lastRuntimeInfoFailure = null;
|
|
7075
7131
|
unsubMotionEvents = null;
|
|
7076
7132
|
/** Devices we have positively observed reporting `battery.sleeping: true`.
|
|
7077
7133
|
* Absent = unknown or awake; both mean "do not gate" (D49). Fed only by
|
|
@@ -7323,6 +7379,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
7323
7379
|
workerFootprintMb: decodeWorkerFootprintMb(this.nativeLease.current())
|
|
7324
7380
|
});
|
|
7325
7381
|
this.runner.timingSampler.setLogger(this.ctx.logger.child("timing"));
|
|
7382
|
+
this.refreshRuntimeInfo();
|
|
7383
|
+
this.runtimeInfoTimer = setInterval(() => void this.refreshRuntimeInfo(), RUNTIME_INFO_REFRESH_MS);
|
|
7384
|
+
this.runtimeInfoTimer.unref?.();
|
|
7326
7385
|
this.runner.onDetectionStreamChange((deviceId, action) => {
|
|
7327
7386
|
this.handleDetectionStreamChange(deviceId, action);
|
|
7328
7387
|
});
|
|
@@ -7389,6 +7448,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
7389
7448
|
};
|
|
7390
7449
|
}
|
|
7391
7450
|
async onShutdown() {
|
|
7451
|
+
if (this.runtimeInfoTimer !== null) {
|
|
7452
|
+
clearInterval(this.runtimeInfoTimer);
|
|
7453
|
+
this.runtimeInfoTimer = null;
|
|
7454
|
+
}
|
|
7392
7455
|
if (this.metricsSnapshotTimer) {
|
|
7393
7456
|
clearInterval(this.metricsSnapshotTimer);
|
|
7394
7457
|
this.metricsSnapshotTimer = null;
|
|
@@ -8196,7 +8259,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8196
8259
|
const resolved = await this.resolveNativeCropPixels(input);
|
|
8197
8260
|
if (resolved.tier === "native" && resolved.pixels !== null) {
|
|
8198
8261
|
this.frameLazy.noteHit(resolveNativeCropHitKind(resolved.pixels.source), deviceId);
|
|
8199
|
-
return this.encodeNativeCropResult(resolved.pixels, resolved.tier, input.encodeJpeg);
|
|
8262
|
+
return this.encodeNativeCropResult(resolved.pixels, resolved.tier, input.encodeJpeg, input.acceptJpegBytes);
|
|
8200
8263
|
}
|
|
8201
8264
|
this.frameLazy.noteMiss("native", deviceId);
|
|
8202
8265
|
if (resolved.tier !== "ram-fullframe") return null;
|
|
@@ -8222,7 +8285,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8222
8285
|
bytes: crop.bytes,
|
|
8223
8286
|
width: crop.width,
|
|
8224
8287
|
height: crop.height
|
|
8225
|
-
}, resolved.tier, input.encodeJpeg);
|
|
8288
|
+
}, resolved.tier, input.encodeJpeg, input.acceptJpegBytes);
|
|
8226
8289
|
}
|
|
8227
8290
|
/**
|
|
8228
8291
|
* PARK a frame for a track (D422) — the hub judged it relevant, and this node
|
|
@@ -8245,8 +8308,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8245
8308
|
store,
|
|
8246
8309
|
resolvePixels: (request) => this.resolveNativeCropPixels(request),
|
|
8247
8310
|
encodeJpeg: async (pixels) => {
|
|
8248
|
-
const encoded = await this.encodeNativeCropResult(pixels, "native", true);
|
|
8249
|
-
return encoded?.
|
|
8311
|
+
const encoded = await this.encodeNativeCropResult(pixels, "native", true, true);
|
|
8312
|
+
return encoded?.jpegBytes !== void 0 ? Buffer.from(encoded.jpegBytes) : null;
|
|
8250
8313
|
},
|
|
8251
8314
|
logger: this.ctx.logger
|
|
8252
8315
|
}, input);
|
|
@@ -8269,7 +8332,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8269
8332
|
return null;
|
|
8270
8333
|
}
|
|
8271
8334
|
return {
|
|
8272
|
-
jpeg: parcel.jpeg.toString("base64"),
|
|
8335
|
+
...input.acceptJpegBytes === true ? { jpegBytes: new Uint8Array(parcel.jpeg) } : { jpeg: parcel.jpeg.toString("base64") },
|
|
8273
8336
|
width: parcel.width,
|
|
8274
8337
|
height: parcel.height,
|
|
8275
8338
|
timestamp: parcel.timestamp,
|
|
@@ -8649,12 +8712,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8649
8712
|
* boundary (CLAUDE.md invariant #21). On an encode failure the crop is a miss
|
|
8650
8713
|
* (`null`) — the caller retries — never a silent raw-RGB cross-node fallback.
|
|
8651
8714
|
*/
|
|
8652
|
-
async encodeNativeCropResult(crop, tier, encodeJpeg) {
|
|
8715
|
+
async encodeNativeCropResult(crop, tier, encodeJpeg, acceptJpegBytes) {
|
|
8653
8716
|
if (crop.bytes.byteLength >= 2 && crop.bytes[0] === 255 && crop.bytes[1] === 216) {
|
|
8654
|
-
const encoded = encodeCrossNodeCrop({
|
|
8717
|
+
const encoded = encodeCrossNodeCrop({
|
|
8718
|
+
jpeg: crop.bytes,
|
|
8719
|
+
acceptBytes: acceptJpegBytes
|
|
8720
|
+
});
|
|
8655
8721
|
if (!encoded) return null;
|
|
8656
8722
|
return {
|
|
8657
|
-
|
|
8723
|
+
...encoded,
|
|
8658
8724
|
width: crop.width,
|
|
8659
8725
|
height: crop.height,
|
|
8660
8726
|
tier
|
|
@@ -8668,16 +8734,17 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8668
8734
|
};
|
|
8669
8735
|
try {
|
|
8670
8736
|
const encoded = encodeCrossNodeCrop({
|
|
8671
|
-
jpeg:
|
|
8737
|
+
jpeg: await (await getSharp())(Buffer.from(crop.bytes), { raw: {
|
|
8672
8738
|
width: crop.width,
|
|
8673
8739
|
height: crop.height,
|
|
8674
8740
|
channels: 3
|
|
8675
|
-
} }).jpeg({ quality: 90 }).toBuffer()
|
|
8676
|
-
bytes: crop.bytes
|
|
8741
|
+
} }).jpeg({ quality: 90 }).toBuffer(),
|
|
8742
|
+
bytes: crop.bytes,
|
|
8743
|
+
acceptBytes: acceptJpegBytes
|
|
8677
8744
|
});
|
|
8678
8745
|
if (!encoded) return null;
|
|
8679
8746
|
return {
|
|
8680
|
-
|
|
8747
|
+
...encoded,
|
|
8681
8748
|
width: crop.width,
|
|
8682
8749
|
height: crop.height,
|
|
8683
8750
|
tier
|
|
@@ -8734,6 +8801,58 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8734
8801
|
}, input);
|
|
8735
8802
|
}
|
|
8736
8803
|
/**
|
|
8804
|
+
* Re-read the labels the `pipeline stats` lines carry (D463).
|
|
8805
|
+
*
|
|
8806
|
+
* Each half is read INDEPENDENTLY and a failure omits only its own field:
|
|
8807
|
+
* an engine that will not answer must not also erase the model list. Both
|
|
8808
|
+
* reads are best-effort by design — this is a label on a telemetry line, and
|
|
8809
|
+
* it must never be able to take the runner down or to log a storm. A failure
|
|
8810
|
+
* is logged once per refresh at `debug`, deliberately: the ABSENCE of the
|
|
8811
|
+
* field in the line is the signal, and the hub writes `info`.
|
|
8812
|
+
*/
|
|
8813
|
+
async refreshRuntimeInfo() {
|
|
8814
|
+
const api = this.ctxIfReady?.api;
|
|
8815
|
+
const sampler = this.runner?.timingSampler;
|
|
8816
|
+
if (!api || sampler === void 0 || sampler === null) {
|
|
8817
|
+
this.noteRuntimeInfoFailure(NO_API_FAILURE);
|
|
8818
|
+
return;
|
|
8819
|
+
}
|
|
8820
|
+
let engines = null;
|
|
8821
|
+
let failure = null;
|
|
8822
|
+
try {
|
|
8823
|
+
engines = (await api.pipelineExecutor.listLoadedEngines.query()).map((r) => ({
|
|
8824
|
+
backend: r.engine.backend,
|
|
8825
|
+
...r.engine.device !== void 0 ? { device: r.engine.device } : {},
|
|
8826
|
+
kind: r.kind,
|
|
8827
|
+
modelsLoaded: r.modelsLoaded
|
|
8828
|
+
}));
|
|
8829
|
+
} catch (err) {
|
|
8830
|
+
failure = errMsg(err);
|
|
8831
|
+
}
|
|
8832
|
+
this.noteRuntimeInfoFailure(failure);
|
|
8833
|
+
sampler.runtimeInfo = {
|
|
8834
|
+
...sampler.runtimeInfo,
|
|
8835
|
+
...buildRuntimeLabels(engines)
|
|
8836
|
+
};
|
|
8837
|
+
}
|
|
8838
|
+
/**
|
|
8839
|
+
* Log a runtime-label failure at its TRANSITION, and at `warn`.
|
|
8840
|
+
*
|
|
8841
|
+
* This branch first shipped at `debug`, on the argument that the absent field
|
|
8842
|
+
* in the line was signal enough. It was not: within the hour `little-unraid`
|
|
8843
|
+
* was the one node still logging `pipelineRuntime=null` and there was no way
|
|
8844
|
+
* to tell "this node has no engine" from "the read threw", because the hub
|
|
8845
|
+
* writes `info` and the agent forwards to it. That is D438's blind
|
|
8846
|
+
* instrument for the third time. Logging the transition rather than every
|
|
8847
|
+
* refresh is what keeps it from becoming a storm.
|
|
8848
|
+
*/
|
|
8849
|
+
noteRuntimeInfoFailure(failure) {
|
|
8850
|
+
if (failure === this.lastRuntimeInfoFailure) return;
|
|
8851
|
+
if (failure === null) this.ctxIfReady?.logger.info("runtime labels: readable again");
|
|
8852
|
+
else this.ctxIfReady?.logger.warn("runtime labels unreadable — this node cannot name its engines", { meta: { error: failure } });
|
|
8853
|
+
this.lastRuntimeInfoFailure = failure;
|
|
8854
|
+
}
|
|
8855
|
+
/**
|
|
8737
8856
|
* The model format this node dispatches on when no `deviceKey` is given —
|
|
8738
8857
|
* its selected engine's format. `null` on any failure, which the caller
|
|
8739
8858
|
* turns into a refusal rather than a guess: a model pin that cannot be
|
|
@@ -8962,13 +9081,13 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
|
|
|
8962
9081
|
handleAgeMs: resolved.handleAgeMs,
|
|
8963
9082
|
...resolved.workerReason !== void 0 ? { workerReason: resolved.workerReason } : {}
|
|
8964
9083
|
};
|
|
8965
|
-
const encoded = await this.encodeNativeCropResult(resolved.pixels, "native", true);
|
|
8966
|
-
if (!encoded?.
|
|
9084
|
+
const encoded = await this.encodeNativeCropResult(resolved.pixels, "native", true, true);
|
|
9085
|
+
if (!encoded?.jpegBytes) return {
|
|
8967
9086
|
miss: "jpeg-encode-failed",
|
|
8968
9087
|
handleAgeMs: resolved.handleAgeMs
|
|
8969
9088
|
};
|
|
8970
9089
|
return {
|
|
8971
|
-
jpeg: Buffer.from(encoded.
|
|
9090
|
+
jpeg: Buffer.from(encoded.jpegBytes),
|
|
8972
9091
|
width: encoded.width,
|
|
8973
9092
|
height: encoded.height,
|
|
8974
9093
|
frameSpace: denormalizeRect(paddedBbox, handle.width, handle.height)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-emK7D4bc.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-DZsjVFey.js");
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
4
|
node_fs = require_chunk.__toESM(node_fs);
|
|
5
5
|
let node_child_process = require("node:child_process");
|