@camstack/system 1.2.275 → 1.2.277
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/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/camera-grid/grid-last-frame-store.d.ts +39 -0
- package/dist/builtins/camera-grid/index.js +67 -12
- package/dist/builtins/camera-grid/index.mjs +67 -12
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-aggregation.d.ts +0 -6
- package/dist/builtins/device-manager/device-bindings-store.d.ts +22 -2
- package/dist/builtins/device-manager/device-manager.addon.d.ts +4 -0
- package/dist/builtins/device-manager/device-manager.addon.js +122 -58
- package/dist/builtins/device-manager/device-manager.addon.mjs +122 -59
- package/dist/builtins/device-manager/device-meta-types.d.ts +24 -3
- package/dist/builtins/device-manager/device-source-activation.d.ts +36 -0
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-CYXhwfDf.js → dist-Bu8HN-KS.js} +354 -11
- package/dist/{dist-DWSJVcZe.mjs → dist-DOF9wnat.mjs} +349 -12
- package/dist/index.d.ts +7 -0
- package/dist/index.js +61 -48
- package/dist/index.mjs +62 -50
- package/dist/kernel/capability-registry.d.ts +28 -8
- package/dist/{retired-settings-keys-DEFtUH8o.js → retired-settings-keys-BwE6S2eQ.js} +1 -1
- package/dist/{retired-settings-keys-BHGnCLW0.mjs → retired-settings-keys-DXMfts8c.mjs} +1 -1
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fn as EventCategory, P as addonPagesCapability, cn as BaseAddon, on as errMsg } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fn as EventCategory, I as addonWidgetsCapability, cn as BaseAddon, on as errMsg } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
//#region src/builtins/alerts/prune-policy.ts
|
|
8
8
|
/**
|
|
9
9
|
* Ids older than `cutoffMs`, oldest first.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fn as EventCategory, L as alertsCapability, cn as BaseAddon, n as AlertSchema, on as errMsg, xn as createEvent } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
//#region src/builtins/alerts/prune-policy.ts
|
|
3
3
|
/**
|
|
4
4
|
* Ids older than `cutoffMs`, oldest first.
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
//#region src/builtins/autotrack/autotrack-config.ts
|
|
8
8
|
var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
|
|
9
9
|
deadZoneFraction: .5,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as bareAddonId,
|
|
1
|
+
import { B as bareAddonId, Dn as isEvent, Fn as EventCategory, Lt as ptzAutotrackCapability, cn as BaseAddon, h as FIRST_LEVEL_MACRO_CLASSES, on as errMsg } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
//#region src/builtins/autotrack/autotrack-config.ts
|
|
3
3
|
var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
|
|
4
4
|
deadZoneFraction: .5,
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let buffer = require("buffer");
|
|
9
9
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Dt as mayWriteToLocation,
|
|
1
|
+
import { Dt as mayWriteToLocation, Fn as EventCategory, Qt as storageOccupancyCapability, cn as BaseAddon, z as backupCapability } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { Buffer as Buffer$1 } from "buffer";
|
|
4
4
|
import * as fsp from "node:fs/promises";
|
|
@@ -1,19 +1,58 @@
|
|
|
1
1
|
import { GridLastFrameStore } from './grid-snapshot.js';
|
|
2
|
+
import { GridStreamLogger } from './grid-stream-session.js';
|
|
3
|
+
/**
|
|
4
|
+
* Exactly the filesystem this store touches, so a caller can hand it a
|
|
5
|
+
* different one and a spec can hand it a broken one.
|
|
6
|
+
*
|
|
7
|
+
* The four calls are `node:fs/promises`' own shapes, narrowed to the single
|
|
8
|
+
* overload used here — that is what makes `promises` assignable to it with no
|
|
9
|
+
* cast.
|
|
10
|
+
*/
|
|
11
|
+
export interface GridFrameFs {
|
|
12
|
+
mkdir(dir: string, options: {
|
|
13
|
+
readonly recursive: true;
|
|
14
|
+
}): Promise<string | undefined>;
|
|
15
|
+
writeFile(path: string, data: Buffer): Promise<void>;
|
|
16
|
+
rename(from: string, to: string): Promise<void>;
|
|
17
|
+
readFile(path: string): Promise<Buffer>;
|
|
18
|
+
}
|
|
2
19
|
export interface GridLastFrameFileStoreOptions {
|
|
3
20
|
/** `ctx.dataDir` — the addon's exclusive directory. */
|
|
4
21
|
readonly dataDir: string;
|
|
22
|
+
/** Where a dropped frame is named. A silent drop reads as "never happened". */
|
|
23
|
+
readonly logger: GridStreamLogger;
|
|
5
24
|
readonly minIntervalMs?: number;
|
|
6
25
|
/** Injectable for tests; `Date.now` otherwise. */
|
|
7
26
|
readonly now?: () => number;
|
|
27
|
+
/** Injectable for tests; `node:fs/promises` otherwise. */
|
|
28
|
+
readonly fs?: GridFrameFs;
|
|
29
|
+
/** The bound on how long the caller waits on the disk. */
|
|
30
|
+
readonly deadlineMs?: number;
|
|
8
31
|
}
|
|
9
32
|
export declare class GridLastFrameFileStore implements GridLastFrameStore {
|
|
10
33
|
private readonly dir;
|
|
11
34
|
private readonly minIntervalMs;
|
|
12
35
|
private readonly now;
|
|
36
|
+
private readonly fs;
|
|
37
|
+
private readonly deadlineMs;
|
|
38
|
+
private readonly logger;
|
|
13
39
|
/** Last write per device, so the throttle survives without touching disk. */
|
|
14
40
|
private readonly lastWriteMs;
|
|
15
41
|
constructor(options: GridLastFrameFileStoreOptions);
|
|
16
42
|
put(deviceId: number, jpeg: Buffer): Promise<void>;
|
|
17
43
|
get(deviceId: number): Promise<Buffer | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Run one piece of disk work, and answer `fallback` whether it REFUSES or
|
|
46
|
+
* never answers at all.
|
|
47
|
+
*
|
|
48
|
+
* The `try/catch` this replaces covered only the first of those. Measured on
|
|
49
|
+
* the CI runner (Linux, root, in a container) 2026-09-20: `fs.mkdir` with
|
|
50
|
+
* `recursive: true` under a parent that cannot hold a directory never
|
|
51
|
+
* settles — procfs answers ENOENT and Node's recursive mkdir retries the
|
|
52
|
+
* child forever — and `put` is AWAITED on the snapshot answer path, so the
|
|
53
|
+
* grid's snapshot RPC never answered either. An exception is not the only
|
|
54
|
+
* way the disk says no.
|
|
55
|
+
*/
|
|
56
|
+
private bounded;
|
|
18
57
|
private pathFor;
|
|
19
58
|
}
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
let zod = require("zod");
|
|
8
8
|
let _camstack_types_node = require("@camstack/types/node");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -910,37 +910,89 @@ var GridFrameSampler = class {
|
|
|
910
910
|
var DEFAULT_MIN_INTERVAL_MS = 6e4;
|
|
911
911
|
/** Its own subdirectory, so the addon's other state is never mistaken for a frame. */
|
|
912
912
|
var SUBDIR = "last-frame";
|
|
913
|
+
/**
|
|
914
|
+
* How long the caller may be made to wait on the disk, for ANY of it.
|
|
915
|
+
*
|
|
916
|
+
* A bonus that costs a snapshot is not a bonus. One second is far more than a
|
|
917
|
+
* healthy local write needs and far less than a viewer will sit through.
|
|
918
|
+
*/
|
|
919
|
+
var DEFAULT_DEADLINE_MS = 1e3;
|
|
913
920
|
var GridLastFrameFileStore = class {
|
|
914
921
|
dir;
|
|
915
922
|
minIntervalMs;
|
|
916
923
|
now;
|
|
924
|
+
fs;
|
|
925
|
+
deadlineMs;
|
|
926
|
+
logger;
|
|
917
927
|
/** Last write per device, so the throttle survives without touching disk. */
|
|
918
928
|
lastWriteMs = /* @__PURE__ */ new Map();
|
|
919
929
|
constructor(options) {
|
|
920
930
|
this.dir = (0, node_path.join)(options.dataDir, SUBDIR);
|
|
921
931
|
this.minIntervalMs = options.minIntervalMs ?? DEFAULT_MIN_INTERVAL_MS;
|
|
922
932
|
this.now = options.now ?? (() => Date.now());
|
|
933
|
+
this.fs = options.fs ?? node_fs.promises;
|
|
934
|
+
this.deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
935
|
+
this.logger = options.logger;
|
|
923
936
|
}
|
|
924
937
|
async put(deviceId, jpeg) {
|
|
925
938
|
if (jpeg.length === 0) return;
|
|
926
939
|
const at = this.now();
|
|
927
940
|
const previous = this.lastWriteMs.get(deviceId);
|
|
928
941
|
if (previous !== void 0 && at - previous < this.minIntervalMs) return;
|
|
929
|
-
|
|
930
|
-
await
|
|
942
|
+
if (await this.bounded(deviceId, "keep the composed frame", false, async () => {
|
|
943
|
+
await this.fs.mkdir(this.dir, { recursive: true });
|
|
931
944
|
const target = this.pathFor(deviceId);
|
|
932
945
|
const staging = `${target}.writing`;
|
|
933
|
-
await
|
|
934
|
-
await
|
|
935
|
-
|
|
936
|
-
}
|
|
946
|
+
await this.fs.writeFile(staging, jpeg);
|
|
947
|
+
await this.fs.rename(staging, target);
|
|
948
|
+
return true;
|
|
949
|
+
})) this.lastWriteMs.set(deviceId, at);
|
|
937
950
|
}
|
|
938
951
|
async get(deviceId) {
|
|
939
|
-
|
|
940
|
-
const jpeg = await
|
|
952
|
+
return this.bounded(deviceId, "read the kept frame", null, async () => {
|
|
953
|
+
const jpeg = await this.fs.readFile(this.pathFor(deviceId));
|
|
941
954
|
return jpeg.length > 0 ? jpeg : null;
|
|
942
|
-
}
|
|
943
|
-
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Run one piece of disk work, and answer `fallback` whether it REFUSES or
|
|
959
|
+
* never answers at all.
|
|
960
|
+
*
|
|
961
|
+
* The `try/catch` this replaces covered only the first of those. Measured on
|
|
962
|
+
* the CI runner (Linux, root, in a container) 2026-09-20: `fs.mkdir` with
|
|
963
|
+
* `recursive: true` under a parent that cannot hold a directory never
|
|
964
|
+
* settles — procfs answers ENOENT and Node's recursive mkdir retries the
|
|
965
|
+
* child forever — and `put` is AWAITED on the snapshot answer path, so the
|
|
966
|
+
* grid's snapshot RPC never answered either. An exception is not the only
|
|
967
|
+
* way the disk says no.
|
|
968
|
+
*/
|
|
969
|
+
async bounded(deviceId, what, fallback, work) {
|
|
970
|
+
let timer = null;
|
|
971
|
+
const expiry = new Promise((resolve) => {
|
|
972
|
+
timer = setTimeout(() => resolve({ settled: false }), this.deadlineMs);
|
|
973
|
+
timer.unref();
|
|
974
|
+
});
|
|
975
|
+
try {
|
|
976
|
+
const outcome = await Promise.race([work().then((value) => ({
|
|
977
|
+
settled: true,
|
|
978
|
+
value
|
|
979
|
+
})), expiry]);
|
|
980
|
+
if (!outcome.settled) {
|
|
981
|
+
this.logger.warn(`camera grid: the disk did not ${what} within its deadline`, {
|
|
982
|
+
tags: { deviceId },
|
|
983
|
+
meta: { deadlineMs: this.deadlineMs }
|
|
984
|
+
});
|
|
985
|
+
return fallback;
|
|
986
|
+
}
|
|
987
|
+
return outcome.value;
|
|
988
|
+
} catch (error) {
|
|
989
|
+
this.logger.warn(`camera grid: the disk could not ${what}`, {
|
|
990
|
+
tags: { deviceId },
|
|
991
|
+
meta: { error: require_dist.errMsg(error) }
|
|
992
|
+
});
|
|
993
|
+
return fallback;
|
|
994
|
+
} finally {
|
|
995
|
+
if (timer !== null) clearTimeout(timer);
|
|
944
996
|
}
|
|
945
997
|
}
|
|
946
998
|
pathFor(deviceId) {
|
|
@@ -2954,7 +3006,10 @@ var CameraGridAddon = class extends require_dist.BaseAddon {
|
|
|
2954
3006
|
logger: this.ctx.logger.child("snapshot"),
|
|
2955
3007
|
sessionsFor: (deviceId) => this.sessionsForDevice(deviceId),
|
|
2956
3008
|
sampleFrame: async (session) => this.sampleFrame(session),
|
|
2957
|
-
store: new GridLastFrameFileStore({
|
|
3009
|
+
store: new GridLastFrameFileStore({
|
|
3010
|
+
dataDir: this.ctx.dataDir,
|
|
3011
|
+
logger: this.ctx.logger.child("snapshot")
|
|
3012
|
+
})
|
|
2958
3013
|
});
|
|
2959
3014
|
this.activity = new GridActivityForwarder({
|
|
2960
3015
|
eventBus: this.ctx.eventBus,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as motionCapability, Fn as EventCategory, Tn as hydrateSchema, U as cameraGridLayoutCapability, Y as declarationOwnerNodeId, Yt as snapshotCapability, b as MOTION_CLOSE_AFTER_MS, cn as BaseAddon, en as streamCatalogCapability, f as DERIVED_CAMERA_SILENCED_CAP_NAMES, fn as DeviceFeature, i as BaseDevice, mn as DeviceType, on as errMsg, p as DeclaredDevices, sn as buildFfmpegArgs, xn as createEvent } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { FfmpegProcess } from "@camstack/types/node";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -905,37 +905,89 @@ var GridFrameSampler = class {
|
|
|
905
905
|
var DEFAULT_MIN_INTERVAL_MS = 6e4;
|
|
906
906
|
/** Its own subdirectory, so the addon's other state is never mistaken for a frame. */
|
|
907
907
|
var SUBDIR = "last-frame";
|
|
908
|
+
/**
|
|
909
|
+
* How long the caller may be made to wait on the disk, for ANY of it.
|
|
910
|
+
*
|
|
911
|
+
* A bonus that costs a snapshot is not a bonus. One second is far more than a
|
|
912
|
+
* healthy local write needs and far less than a viewer will sit through.
|
|
913
|
+
*/
|
|
914
|
+
var DEFAULT_DEADLINE_MS = 1e3;
|
|
908
915
|
var GridLastFrameFileStore = class {
|
|
909
916
|
dir;
|
|
910
917
|
minIntervalMs;
|
|
911
918
|
now;
|
|
919
|
+
fs;
|
|
920
|
+
deadlineMs;
|
|
921
|
+
logger;
|
|
912
922
|
/** Last write per device, so the throttle survives without touching disk. */
|
|
913
923
|
lastWriteMs = /* @__PURE__ */ new Map();
|
|
914
924
|
constructor(options) {
|
|
915
925
|
this.dir = join(options.dataDir, SUBDIR);
|
|
916
926
|
this.minIntervalMs = options.minIntervalMs ?? DEFAULT_MIN_INTERVAL_MS;
|
|
917
927
|
this.now = options.now ?? (() => Date.now());
|
|
928
|
+
this.fs = options.fs ?? promises;
|
|
929
|
+
this.deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
930
|
+
this.logger = options.logger;
|
|
918
931
|
}
|
|
919
932
|
async put(deviceId, jpeg) {
|
|
920
933
|
if (jpeg.length === 0) return;
|
|
921
934
|
const at = this.now();
|
|
922
935
|
const previous = this.lastWriteMs.get(deviceId);
|
|
923
936
|
if (previous !== void 0 && at - previous < this.minIntervalMs) return;
|
|
924
|
-
|
|
925
|
-
await
|
|
937
|
+
if (await this.bounded(deviceId, "keep the composed frame", false, async () => {
|
|
938
|
+
await this.fs.mkdir(this.dir, { recursive: true });
|
|
926
939
|
const target = this.pathFor(deviceId);
|
|
927
940
|
const staging = `${target}.writing`;
|
|
928
|
-
await
|
|
929
|
-
await
|
|
930
|
-
|
|
931
|
-
}
|
|
941
|
+
await this.fs.writeFile(staging, jpeg);
|
|
942
|
+
await this.fs.rename(staging, target);
|
|
943
|
+
return true;
|
|
944
|
+
})) this.lastWriteMs.set(deviceId, at);
|
|
932
945
|
}
|
|
933
946
|
async get(deviceId) {
|
|
934
|
-
|
|
935
|
-
const jpeg = await
|
|
947
|
+
return this.bounded(deviceId, "read the kept frame", null, async () => {
|
|
948
|
+
const jpeg = await this.fs.readFile(this.pathFor(deviceId));
|
|
936
949
|
return jpeg.length > 0 ? jpeg : null;
|
|
937
|
-
}
|
|
938
|
-
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Run one piece of disk work, and answer `fallback` whether it REFUSES or
|
|
954
|
+
* never answers at all.
|
|
955
|
+
*
|
|
956
|
+
* The `try/catch` this replaces covered only the first of those. Measured on
|
|
957
|
+
* the CI runner (Linux, root, in a container) 2026-09-20: `fs.mkdir` with
|
|
958
|
+
* `recursive: true` under a parent that cannot hold a directory never
|
|
959
|
+
* settles — procfs answers ENOENT and Node's recursive mkdir retries the
|
|
960
|
+
* child forever — and `put` is AWAITED on the snapshot answer path, so the
|
|
961
|
+
* grid's snapshot RPC never answered either. An exception is not the only
|
|
962
|
+
* way the disk says no.
|
|
963
|
+
*/
|
|
964
|
+
async bounded(deviceId, what, fallback, work) {
|
|
965
|
+
let timer = null;
|
|
966
|
+
const expiry = new Promise((resolve) => {
|
|
967
|
+
timer = setTimeout(() => resolve({ settled: false }), this.deadlineMs);
|
|
968
|
+
timer.unref();
|
|
969
|
+
});
|
|
970
|
+
try {
|
|
971
|
+
const outcome = await Promise.race([work().then((value) => ({
|
|
972
|
+
settled: true,
|
|
973
|
+
value
|
|
974
|
+
})), expiry]);
|
|
975
|
+
if (!outcome.settled) {
|
|
976
|
+
this.logger.warn(`camera grid: the disk did not ${what} within its deadline`, {
|
|
977
|
+
tags: { deviceId },
|
|
978
|
+
meta: { deadlineMs: this.deadlineMs }
|
|
979
|
+
});
|
|
980
|
+
return fallback;
|
|
981
|
+
}
|
|
982
|
+
return outcome.value;
|
|
983
|
+
} catch (error) {
|
|
984
|
+
this.logger.warn(`camera grid: the disk could not ${what}`, {
|
|
985
|
+
tags: { deviceId },
|
|
986
|
+
meta: { error: errMsg(error) }
|
|
987
|
+
});
|
|
988
|
+
return fallback;
|
|
989
|
+
} finally {
|
|
990
|
+
if (timer !== null) clearTimeout(timer);
|
|
939
991
|
}
|
|
940
992
|
}
|
|
941
993
|
pathFor(deviceId) {
|
|
@@ -2949,7 +3001,10 @@ var CameraGridAddon = class extends BaseAddon {
|
|
|
2949
3001
|
logger: this.ctx.logger.child("snapshot"),
|
|
2950
3002
|
sessionsFor: (deviceId) => this.sessionsForDevice(deviceId),
|
|
2951
3003
|
sampleFrame: async (session) => this.sampleFrame(session),
|
|
2952
|
-
store: new GridLastFrameFileStore({
|
|
3004
|
+
store: new GridLastFrameFileStore({
|
|
3005
|
+
dataDir: this.ctx.dataDir,
|
|
3006
|
+
logger: this.ctx.logger.child("snapshot")
|
|
3007
|
+
})
|
|
2953
3008
|
});
|
|
2954
3009
|
this.activity = new GridActivityForwarder({
|
|
2955
3010
|
eventBus: this.ctx.eventBus,
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/console-logging/console-destination.ts
|
|
9
9
|
var LEVEL_RANK = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ct as logDestinationCapability,
|
|
1
|
+
import { Ct as logDestinationCapability, cn as BaseAddon } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/console-logging/console-destination.ts
|
|
4
4
|
var LEVEL_RANK = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-Bu8HN-KS.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
6
6
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as coreBlockAddonId, K as coreBlockIdFromAddonId,
|
|
1
|
+
import { G as coreBlockAddonId, K as coreBlockIdFromAddonId, c as CORE_BLOCKS_ADDON_ID, cn as BaseAddon, l as CORE_BLOCK_ADDON_PREFIX, on as errMsg, p as DeclaredDevices, q as coreBlocksCapability, u as CoreBlockSchema } from "../../dist-DOF9wnat.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -295,12 +295,6 @@ export declare function listBindableCapsForDeviceType(deps: AggregationDeps, inp
|
|
|
295
295
|
capName: string;
|
|
296
296
|
wrappers: string[];
|
|
297
297
|
}>>;
|
|
298
|
-
export declare function setWrapperActive(deps: AggregationDeps, input: {
|
|
299
|
-
deviceId: number;
|
|
300
|
-
capName: string;
|
|
301
|
-
wrapperAddonId: string;
|
|
302
|
-
active: boolean;
|
|
303
|
-
}): Promise<void>;
|
|
304
298
|
/**
|
|
305
299
|
* Per-device wireable-field catalog — the domain status fields a device's
|
|
306
300
|
* bound caps expose, for the operator's cross-device wiring UI. Binding-driven:
|
|
@@ -57,8 +57,18 @@ export interface BindingsDeps {
|
|
|
57
57
|
* `native-registered`) on its next boot.
|
|
58
58
|
*/
|
|
59
59
|
export declare function wireRemoteNativeCapSync(ctx: AddonContext, remoteNativeCaps: RemoteNativeCaps): void;
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
/**
|
|
61
|
+
* All a bindings-store read or write needs: the settings door. Named narrower
|
|
62
|
+
* than {@link BindingsDeps} so the activation verb (`device-source-activation.ts`)
|
|
63
|
+
* can reach the store without the device ledger and the remote-native cache it
|
|
64
|
+
* never touches — `BindingsDeps` satisfies it structurally, so every existing
|
|
65
|
+
* call site is unchanged.
|
|
66
|
+
*/
|
|
67
|
+
export interface BindingsStoreDeps {
|
|
68
|
+
readonly ctx: AddonContext;
|
|
69
|
+
}
|
|
70
|
+
export declare function readBindingsStore(deps: BindingsStoreDeps): Promise<DeviceBindingsStore>;
|
|
71
|
+
export declare function writeBindingsStore(deps: BindingsStoreDeps, next: DeviceBindingsStore): Promise<void>;
|
|
62
72
|
export declare function resolveWrapperNodeId(_wrapperAddonId: string): string;
|
|
63
73
|
/**
|
|
64
74
|
* Reduce a provider node id to the routable form `DeviceProxy` can pin.
|
|
@@ -151,6 +161,16 @@ export type LedgerNonEmpty = () => Promise<boolean>;
|
|
|
151
161
|
* changes nothing (D49: a read that fails must not destroy work).
|
|
152
162
|
*/
|
|
153
163
|
export declare function capAppliesToDeviceType(def: CapabilityDefinition | undefined, deviceType: string | undefined): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* An addon's identity, with the node it happens to run on stripped.
|
|
166
|
+
*
|
|
167
|
+
* A capability registered on an agent arrives under `<addonId>@<nodeId>` —
|
|
168
|
+
* `pipeline-analytics@little-unraid`. That is the same ADDON as the hub-local
|
|
169
|
+
* `pipeline-analytics`, and treating the two as different providers is what
|
|
170
|
+
* put `analytics` in device 592's clip picker three times (D554 amended).
|
|
171
|
+
* Where a provider RUNS is a routing fact; it is not part of who it is.
|
|
172
|
+
*/
|
|
173
|
+
export declare function addonIdentityOf(addonId: string): string;
|
|
154
174
|
export declare function getBindings(deps: BindingsDeps, input: {
|
|
155
175
|
deviceId: number;
|
|
156
176
|
}): Promise<{
|
|
@@ -166,8 +166,12 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
166
166
|
deviceId: number;
|
|
167
167
|
capName: string;
|
|
168
168
|
wrapperAddonId: string;
|
|
169
|
+
/** The source to toggle on a collection cap. Absent = all of the addon's. */
|
|
170
|
+
sourceId?: string;
|
|
169
171
|
active: boolean;
|
|
170
172
|
}): Promise<void>;
|
|
173
|
+
/** The narrow dependency bag the activation verb needs (D556). */
|
|
174
|
+
private get wrapperActivationDeps();
|
|
171
175
|
protected onInitialize(): Promise<ProviderRegistration[] | void>;
|
|
172
176
|
protected onShutdown(): Promise<void>;
|
|
173
177
|
}
|