@camstack/system 1.1.49 → 1.1.51

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.
Files changed (53) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  15. package/dist/builtins/hub-forwarder/index.js +1 -1
  16. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  17. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  18. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  19. package/dist/builtins/local-network/local-network.addon.js +1 -1
  20. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  21. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  22. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  23. package/dist/builtins/platform-probe/coral-accelerators.d.ts +22 -0
  24. package/dist/builtins/platform-probe/index.js +67 -2
  25. package/dist/builtins/platform-probe/index.mjs +67 -2
  26. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  27. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  28. package/dist/builtins/snapshot/index.js +162 -12
  29. package/dist/builtins/snapshot/index.mjs +162 -12
  30. package/dist/builtins/snapshot/snapshot-media-handler.d.ts +38 -0
  31. package/dist/builtins/snapshot/snapshot.addon.d.ts +26 -0
  32. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  33. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  34. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  35. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  36. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  37. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  38. package/dist/builtins/system-config/system-config.addon.js +1 -1
  39. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  40. package/dist/builtins/winston-logging/index.js +1 -1
  41. package/dist/builtins/winston-logging/index.mjs +1 -1
  42. package/dist/{dist-8phgmJ6b.mjs → dist-BWQX9yUj.mjs} +162 -8
  43. package/dist/{dist-BDV1WKRg.js → dist-C3uHEBtP.js} +173 -7
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +37 -23
  46. package/dist/index.mjs +37 -24
  47. package/dist/kernel/addon-installer.d.ts +8 -0
  48. package/dist/kernel/deps/manifest-native-deps.d.ts +1 -1
  49. package/dist/kernel/deps/npm-command.d.ts +38 -0
  50. package/dist/kernel/hwaccel/hwaccel-resolver.d.ts +2 -0
  51. package/dist/{manifest-python-deps-B09I9ems.mjs → manifest-python-deps-BA6KA9If.mjs} +298 -127
  52. package/dist/{manifest-python-deps-DAA0ULwT.js → manifest-python-deps-CVcJ9hDX.js} +309 -126
  53. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-BDV1WKRg.js");
3
- const require_manifest_python_deps = require("./manifest-python-deps-DAA0ULwT.js");
2
+ const require_dist = require("./dist-C3uHEBtP.js");
3
+ const require_manifest_python_deps = require("./manifest-python-deps-CVcJ9hDX.js");
4
4
  const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
5
5
  let node_fs = require("node:fs");
6
6
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,5 +1,5 @@
1
- import { k as isCollectionArrayMethod, t as ALL_CAPABILITY_DEFINITIONS } from "./dist-8phgmJ6b.mjs";
2
- import { F as createUdsLoggerWithControl, I as LocalChildClient, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps, yt as resolveAddonClass } from "./manifest-python-deps-B09I9ems.mjs";
1
+ import { A as isCollectionArrayMethod, t as ALL_CAPABILITY_DEFINITIONS } from "./dist-BWQX9yUj.mjs";
2
+ import { F as createUdsLoggerWithControl, I as LocalChildClient, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps } from "./manifest-python-deps-BA6KA9If.mjs";
3
3
  import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
4
4
  import { register } from "node:module";
5
5
  import * as fs from "node:fs";
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { ct as EventCategory, et as errMsg, f as addonPagesCapability, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { Ot as EventCategory, f as addonPagesCapability, nt as errMsg, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import * as fs from "node:fs";
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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { ct as EventCategory, et as errMsg, m as addonWidgetsCapability, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { Ot as EventCategory, m as addonWidgetsCapability, nt as errMsg, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import * as fs from "node:fs";
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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  //#region src/builtins/alerts/alerts.addon.ts
8
8
  var CATEGORY_MAPPINGS = {
9
9
  "addon.crashed": {
@@ -1,4 +1,4 @@
1
- import { ct as EventCategory, et as errMsg, h as alertsCapability, ht as createEvent, n as AlertSchema, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { Ot as EventCategory, gt as createEvent, h as alertsCapability, n as AlertSchema, nt as errMsg, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  //#region src/builtins/alerts/alerts.addon.ts
3
3
  var CATEGORY_MAPPINGS = {
4
4
  "addon.crashed": {
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_fs$1 = require_chunk.__toESM(node_fs, 1);
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { ct as EventCategory, tt as BaseAddon, v as backupCapability } from "../../dist-8phgmJ6b.mjs";
1
+ import { Ot as EventCategory, rt as BaseAddon, v as backupCapability } from "../../dist-BWQX9yUj.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import Kt from "node:fs";
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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.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 { F as logDestinationCapability, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { I as logDestinationCapability, rt as BaseAddon } from "../../dist-BWQX9yUj.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 = {
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  let _camstack_types_node = require("@camstack/types/node");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { $ as validateExpressionSource, B as parseStreamParamsFormPatch, C as enumerateItemArrayFields, D as getByPath, Et as sleep, G as setByPath, S as deviceStatusCapability, T as evaluateLinkExpression, Z as toExpressionValue, a as DeviceStatusSchema, at as DeviceFeature, b as deviceManagerCapability, bt as isDeviceConfigCap, c as STREAM_PROFILE_META, ct as EventCategory, dt as WELL_KNOWN_TAB_MAP, et as errMsg, g as applyTransform, i as CAP_NAMES_WITH_STATUS, ot as DeviceRole, st as DeviceType, t as ALL_CAPABILITY_DEFINITIONS, tt as BaseAddon, w as enumerateSchemaFields, x as deviceStateCapability, y as buildStreamParamsConfigSchema, z as normalizeUnit } from "../../dist-8phgmJ6b.mjs";
1
+ import { $ as toExpressionValue, B as normalizeUnit, C as enumerateItemArrayFields, Dt as sleep, H as parseStreamParamsFormPatch, O as getByPath, Ot as EventCategory, S as deviceStatusCapability, T as evaluateLinkExpression, a as DeviceStatusSchema, b as deviceManagerCapability, c as STREAM_PROFILE_META, ct as DeviceRole, ft as WELL_KNOWN_TAB_MAP, g as applyTransform, i as CAP_NAMES_WITH_STATUS, lt as DeviceType, nt as errMsg, q as setByPath, rt as BaseAddon, st as DeviceFeature, t as ALL_CAPABILITY_DEFINITIONS, tt as validateExpressionSource, w as enumerateSchemaFields, x as deviceStateCapability, xt as isDeviceConfigCap, y as buildStreamParamsConfigSchema } from "../../dist-BWQX9yUj.mjs";
2
2
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { F as logDestinationCapability, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { I as logDestinationCapability, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,4 +1,4 @@
1
- import { Q as userManagementCapability, _ as authProviderCapability, d as UserRecordSchema, l as ScopedTokenSchema, r as ApiKeyRecordSchema, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { _ as authProviderCapability, d as UserRecordSchema, et as userManagementCapability, l as ScopedTokenSchema, r as ApiKeyRecordSchema, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as crypto$1 from "node:crypto";
4
4
  import { randomUUID } from "node:crypto";
@@ -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-BDV1WKRg.js");
3
+ const require_dist = require("../../dist-C3uHEBtP.js");
4
4
  let node_os = require("node:os");
5
5
  node_os = require_chunk.__toESM(node_os);
6
6
  //#region src/builtins/local-network/local-network.addon.ts
@@ -1,4 +1,4 @@
1
- import { P as localNetworkCapability, ct as EventCategory, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { F as localNetworkCapability, Ot as EventCategory, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  import * as os from "node:os";
3
3
  //#region src/builtins/local-network/local-network.addon.ts
4
4
  /**
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { L as metricsProviderCapability, ct as EventCategory, ht as createEvent, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { Ot as EventCategory, R as metricsProviderCapability, gt as createEvent, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile, execFileSync } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -0,0 +1,22 @@
1
+ import { CoralInfo } from '@camstack/types';
2
+ /**
3
+ * Filesystem seam for Coral USB Edge TPU detection — mirrors the
4
+ * `AcceleratorFs` pattern in `intel-accelerators.ts` so the classifier stays a
5
+ * pure, unit-testable function over injected reads.
6
+ */
7
+ export interface CoralFs {
8
+ /** USB device directory names under `/sys/bus/usb/devices` (e.g. `2-4`). */
9
+ listUsbDevices(): readonly string[];
10
+ /** Read a `<sysfsNode>/<attr>` value (idVendor / idProduct), trimmed. */
11
+ readUsbAttr(device: string, attr: string): string;
12
+ /** Whether a filesystem path exists (the libedgetpu delegate). */
13
+ exists(path: string): boolean;
14
+ }
15
+ /**
16
+ * Detect a Coral USB Edge TPU. Present when EITHER a matching USB device is
17
+ * enumerated OR the libedgetpu delegate is installed (the delegate is the
18
+ * hub-provisioned runtime prerequisite; on a container whose `/sys` USB view is
19
+ * limited, its presence is a reliable operator-intent signal that the Coral is
20
+ * wired through). Returns `null` when neither signal is found.
21
+ */
22
+ export declare function classifyCoralAccelerator(fsx: CoralFs): CoralInfo | null;
@@ -3,13 +3,59 @@ 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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
10
10
  let node_util = require("node:util");
11
11
  let node_os = require("node:os");
12
12
  node_os = require_chunk.__toESM(node_os);
13
+ //#region src/builtins/platform-probe/coral-accelerators.ts
14
+ /**
15
+ * Coral USB Edge TPU USB identifiers.
16
+ *
17
+ * The device enumerates as `1a6e:089a` (Global Unichip) BEFORE the libedgetpu
18
+ * firmware loads, then re-enumerates as `18d1:9302` (Google) AFTER. Either id
19
+ * means a Coral is physically attached, so both count as a positive signal.
20
+ */
21
+ var CORAL_USB_IDS = [{
22
+ vendor: "1a6e",
23
+ product: "089a"
24
+ }, {
25
+ vendor: "18d1",
26
+ product: "9302"
27
+ }];
28
+ /**
29
+ * Default install path of the libedgetpu delegate on the hub, provisioned
30
+ * out-of-band by `/data/deps/edgetpu-install.sh` (it is a system shared
31
+ * library, not a pip package). Overridable via `CAMSTACK_EDGETPU_LIB` — kept in
32
+ * sync with the Python pool's own default in `inference_pool.py`.
33
+ */
34
+ var DEFAULT_EDGETPU_LIB = "/data/deps/edgetpu/lib/libedgetpu.so.1";
35
+ /**
36
+ * Detect a Coral USB Edge TPU. Present when EITHER a matching USB device is
37
+ * enumerated OR the libedgetpu delegate is installed (the delegate is the
38
+ * hub-provisioned runtime prerequisite; on a container whose `/sys` USB view is
39
+ * limited, its presence is a reliable operator-intent signal that the Coral is
40
+ * wired through). Returns `null` when neither signal is found.
41
+ */
42
+ function classifyCoralAccelerator(fsx) {
43
+ for (const device of fsx.listUsbDevices()) {
44
+ const vendor = fsx.readUsbAttr(device, "idVendor").toLowerCase();
45
+ const product = fsx.readUsbAttr(device, "idProduct").toLowerCase();
46
+ if (CORAL_USB_IDS.some((id) => id.vendor === vendor && id.product === product)) return {
47
+ type: "coral-edgetpu",
48
+ bus: "usb"
49
+ };
50
+ }
51
+ const libPath = process.env["CAMSTACK_EDGETPU_LIB"] ?? DEFAULT_EDGETPU_LIB;
52
+ if (fsx.exists(libPath)) return {
53
+ type: "coral-edgetpu",
54
+ bus: "usb"
55
+ };
56
+ return null;
57
+ }
58
+ //#endregion
13
59
  //#region src/builtins/platform-probe/platform-scorer.ts
14
60
  /**
15
61
  * Promisified `execFile`. Used across the scorer so every subprocess
@@ -162,6 +208,24 @@ var PlatformScorer = class {
162
208
  } });
163
209
  let gpu = null;
164
210
  let npu = null;
211
+ const coral = classifyCoralAccelerator({
212
+ listUsbDevices: () => {
213
+ try {
214
+ return node_fs.readdirSync("/sys/bus/usb/devices");
215
+ } catch {
216
+ return [];
217
+ }
218
+ },
219
+ readUsbAttr: (device, attr) => {
220
+ try {
221
+ return node_fs.readFileSync(`/sys/bus/usb/devices/${device}/${attr}`, "utf8").trim();
222
+ } catch {
223
+ return "";
224
+ }
225
+ },
226
+ exists: (p) => node_fs.existsSync(p)
227
+ });
228
+ if (coral) this.logger.info("Coral USB Edge TPU detected", { meta: { bus: coral.bus } });
165
229
  if (platform === "darwin" && arch === "arm64") {
166
230
  gpu = {
167
231
  type: "apple",
@@ -220,7 +284,8 @@ var PlatformScorer = class {
220
284
  totalRAM_MB,
221
285
  availableRAM_MB,
222
286
  gpu,
223
- npu
287
+ npu,
288
+ coral
224
289
  };
225
290
  }
226
291
  /**
@@ -1,8 +1,54 @@
1
- import { V as platformProbeCapability, W as scoreRuntimes, _t as emitReadiness, ct as EventCategory, et as errMsg, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { K as scoreRuntimes, Ot as EventCategory, U as platformProbeCapability, nt as errMsg, rt as BaseAddon, vt as emitReadiness } from "../../dist-BWQX9yUj.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { promisify } from "node:util";
5
5
  import * as os from "node:os";
6
+ //#region src/builtins/platform-probe/coral-accelerators.ts
7
+ /**
8
+ * Coral USB Edge TPU USB identifiers.
9
+ *
10
+ * The device enumerates as `1a6e:089a` (Global Unichip) BEFORE the libedgetpu
11
+ * firmware loads, then re-enumerates as `18d1:9302` (Google) AFTER. Either id
12
+ * means a Coral is physically attached, so both count as a positive signal.
13
+ */
14
+ var CORAL_USB_IDS = [{
15
+ vendor: "1a6e",
16
+ product: "089a"
17
+ }, {
18
+ vendor: "18d1",
19
+ product: "9302"
20
+ }];
21
+ /**
22
+ * Default install path of the libedgetpu delegate on the hub, provisioned
23
+ * out-of-band by `/data/deps/edgetpu-install.sh` (it is a system shared
24
+ * library, not a pip package). Overridable via `CAMSTACK_EDGETPU_LIB` — kept in
25
+ * sync with the Python pool's own default in `inference_pool.py`.
26
+ */
27
+ var DEFAULT_EDGETPU_LIB = "/data/deps/edgetpu/lib/libedgetpu.so.1";
28
+ /**
29
+ * Detect a Coral USB Edge TPU. Present when EITHER a matching USB device is
30
+ * enumerated OR the libedgetpu delegate is installed (the delegate is the
31
+ * hub-provisioned runtime prerequisite; on a container whose `/sys` USB view is
32
+ * limited, its presence is a reliable operator-intent signal that the Coral is
33
+ * wired through). Returns `null` when neither signal is found.
34
+ */
35
+ function classifyCoralAccelerator(fsx) {
36
+ for (const device of fsx.listUsbDevices()) {
37
+ const vendor = fsx.readUsbAttr(device, "idVendor").toLowerCase();
38
+ const product = fsx.readUsbAttr(device, "idProduct").toLowerCase();
39
+ if (CORAL_USB_IDS.some((id) => id.vendor === vendor && id.product === product)) return {
40
+ type: "coral-edgetpu",
41
+ bus: "usb"
42
+ };
43
+ }
44
+ const libPath = process.env["CAMSTACK_EDGETPU_LIB"] ?? DEFAULT_EDGETPU_LIB;
45
+ if (fsx.exists(libPath)) return {
46
+ type: "coral-edgetpu",
47
+ bus: "usb"
48
+ };
49
+ return null;
50
+ }
51
+ //#endregion
6
52
  //#region src/builtins/platform-probe/platform-scorer.ts
7
53
  /**
8
54
  * Promisified `execFile`. Used across the scorer so every subprocess
@@ -155,6 +201,24 @@ var PlatformScorer = class {
155
201
  } });
156
202
  let gpu = null;
157
203
  let npu = null;
204
+ const coral = classifyCoralAccelerator({
205
+ listUsbDevices: () => {
206
+ try {
207
+ return fs.readdirSync("/sys/bus/usb/devices");
208
+ } catch {
209
+ return [];
210
+ }
211
+ },
212
+ readUsbAttr: (device, attr) => {
213
+ try {
214
+ return fs.readFileSync(`/sys/bus/usb/devices/${device}/${attr}`, "utf8").trim();
215
+ } catch {
216
+ return "";
217
+ }
218
+ },
219
+ exists: (p) => fs.existsSync(p)
220
+ });
221
+ if (coral) this.logger.info("Coral USB Edge TPU detected", { meta: { bus: coral.bus } });
158
222
  if (platform === "darwin" && arch === "arm64") {
159
223
  gpu = {
160
224
  type: "apple",
@@ -213,7 +277,8 @@ var PlatformScorer = class {
213
277
  totalRAM_MB,
214
278
  availableRAM_MB,
215
279
  gpu,
216
- npu
280
+ npu,
281
+ coral
217
282
  };
218
283
  }
219
284
  /**
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { ct as EventCategory, tt as BaseAddon } from "../../dist-8phgmJ6b.mjs";
1
+ import { Ot as EventCategory, rt as BaseAddon } from "../../dist-BWQX9yUj.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -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-BDV1WKRg.js");
6
+ const require_dist = require("../../dist-C3uHEBtP.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/builtins/snapshot/snapshot-coalescing.ts
9
9
  /**
@@ -200,12 +200,102 @@ function raceForResult(promise, timeoutMs) {
200
200
  });
201
201
  }
202
202
  //#endregion
203
+ //#region src/builtins/snapshot/snapshot-media-handler.ts
204
+ /**
205
+ * Parse the handler-relative path (`/<deviceId>.jpg?query`) into a request.
206
+ * Returns null for a malformed / nested / non-numeric id so the handler answers
207
+ * 404 without ever reaching `getMedia`.
208
+ */
209
+ function parseSnapshotMediaRequest(url) {
210
+ const qIdx = url.indexOf("?");
211
+ const rawPath = qIdx === -1 ? url : url.slice(0, qIdx);
212
+ const query = qIdx === -1 ? "" : url.slice(qIdx + 1);
213
+ const segment = rawPath.replace(/^\/+/, "");
214
+ if (segment.length === 0 || segment.includes("/")) return null;
215
+ const idPart = segment.replace(/\.jpe?g$/i, "");
216
+ if (!/^\d+$/.test(idPart)) return null;
217
+ const deviceId = Number.parseInt(idPart, 10);
218
+ if (!Number.isSafeInteger(deviceId)) return null;
219
+ const params = new URLSearchParams(query);
220
+ const rawStream = params.get("streamId");
221
+ const streamId = rawStream !== null && rawStream.length > 0 ? rawStream : void 0;
222
+ const rawForce = params.get("force");
223
+ return {
224
+ deviceId,
225
+ streamId,
226
+ force: rawForce === "1" || rawForce === "true"
227
+ };
228
+ }
229
+ /**
230
+ * Create a data-plane handler that serves per-device snapshots as JPEG images.
231
+ * `deps.getMedia` is called once per request; null → 404, throw → 500. A
232
+ * conditional GET with a matching `If-None-Match` produces a 304.
233
+ */
234
+ function createSnapshotMediaHandler(deps) {
235
+ return async (req, res) => {
236
+ if (req.method !== "GET" && req.method !== "HEAD") {
237
+ res.writeHead(405, { allow: "GET, HEAD" }).end();
238
+ return;
239
+ }
240
+ const parsed = parseSnapshotMediaRequest(req.url ?? "/");
241
+ if (parsed === null) {
242
+ res.writeHead(404).end();
243
+ return;
244
+ }
245
+ let media;
246
+ try {
247
+ media = await deps.getMedia(parsed.deviceId, parsed.streamId, parsed.force);
248
+ } catch {
249
+ const body = "Internal server error";
250
+ res.writeHead(500, {
251
+ "content-type": "text/plain",
252
+ "content-length": String(Buffer.byteLength(body))
253
+ });
254
+ if (req.method === "HEAD") res.end();
255
+ else res.end(body);
256
+ return;
257
+ }
258
+ if (media === null) {
259
+ res.writeHead(404).end();
260
+ return;
261
+ }
262
+ const etag = `"${parsed.deviceId}-${media.capturedAt}"`;
263
+ const cacheControl = `private, max-age=${Math.max(0, Math.floor(media.maxAgeS))}`;
264
+ if (req.headers["if-none-match"] === etag) {
265
+ res.writeHead(304, {
266
+ etag,
267
+ "cache-control": cacheControl
268
+ }).end();
269
+ return;
270
+ }
271
+ res.writeHead(200, {
272
+ "content-type": media.contentType,
273
+ "cache-control": cacheControl,
274
+ etag,
275
+ "content-length": String(media.bytes.byteLength)
276
+ });
277
+ if (req.method === "HEAD") res.end();
278
+ else res.end(Buffer.from(media.bytes));
279
+ };
280
+ }
281
+ //#endregion
203
282
  //#region src/builtins/snapshot/snapshot.addon.ts
204
283
  /** Default cache window for non-battery cams (seconds). 10s feels live. */
205
284
  var NON_BATTERY_DEFAULT_MAX_AGE_S = 10;
206
285
  /** Default cache window for battery cams (seconds). 1h ≈ "don't wake the cam unless asked". */
207
286
  var BATTERY_DEFAULT_MAX_AGE_S = 3600;
208
287
  /**
288
+ * Effective per-device max cache age (seconds): the operator override
289
+ * (`snapshotMaxAgeS`) when a finite non-negative number, else the battery-aware
290
+ * default. Shared by the freshness gate (`getSnapshot`) and the HTTP endpoint's
291
+ * `Cache-Control: max-age` (so the browser/`expo-image` layer caches for exactly
292
+ * as long as the wrapper considers the frame fresh).
293
+ */
294
+ function effectiveMaxAgeS(prefs, isBattery) {
295
+ const def = isBattery ? BATTERY_DEFAULT_MAX_AGE_S : NON_BATTERY_DEFAULT_MAX_AGE_S;
296
+ return typeof prefs.snapshotMaxAgeS === "number" && prefs.snapshotMaxAgeS >= 0 ? prefs.snapshotMaxAgeS : def;
297
+ }
298
+ /**
209
299
  * SnapshotAddon — wrapper over the `snapshot` capability.
210
300
  *
211
301
  * Activated per-device (toggleable by user; default active). When active,
@@ -245,25 +335,86 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
245
335
  */
246
336
  ownerCache = null;
247
337
  static OWNER_CACHE_TTL_MS = 3e4;
338
+ /**
339
+ * Handle for the `/addon/snapshot/media/<deviceId>.jpg` HTTP data-plane
340
+ * endpoint (§3.1). Null when the host provides no `ctx.dataPlane` facility
341
+ * (the tRPC `getSnapshot` base64 surface still works — the endpoint is a
342
+ * strictly-additive delivery path). Disposed on shutdown.
343
+ */
344
+ mediaDataPlane = null;
248
345
  constructor() {
249
346
  super({ staleTtlMs: 6e4 });
250
347
  }
251
348
  async onInitialize() {
252
349
  this.ctx.logger.info("Snapshot wrapper initialized");
350
+ const provider = {
351
+ getSnapshot: (input) => this.getSnapshot(input),
352
+ invalidateCache: (input) => this.invalidateCache(input),
353
+ getDeviceSettingsContribution: (input) => this.buildDeviceSettingsContribution(input.deviceId),
354
+ getDeviceLiveContribution: async () => null,
355
+ applyDeviceSettingsPatch: (input) => this.saveDeviceSettingsPatch(input.deviceId, input.patch),
356
+ getStatus: async (input) => this.getStatus(input.deviceId),
357
+ getSnapshotOverview: (input) => this.getSnapshotOverview(input)
358
+ };
359
+ await this.serveMediaDataPlane();
253
360
  return [{
254
361
  capability: require_dist.snapshotCapability,
255
- provider: {
256
- getSnapshot: (input) => this.getSnapshot(input),
257
- invalidateCache: (input) => this.invalidateCache(input),
258
- getDeviceSettingsContribution: (input) => this.buildDeviceSettingsContribution(input.deviceId),
259
- getDeviceLiveContribution: async () => null,
260
- applyDeviceSettingsPatch: (input) => this.saveDeviceSettingsPatch(input.deviceId, input.patch),
261
- getStatus: async (input) => this.getStatus(input.deviceId),
262
- getSnapshotOverview: (input) => this.getSnapshotOverview(input)
263
- }
362
+ provider
264
363
  }];
265
364
  }
365
+ /**
366
+ * Serve the per-device snapshot image endpoint on the addon data-plane
367
+ * (§3.1). Delivery: `GET /addon/snapshot/media/<deviceId>.jpg` → raw JPEG with
368
+ * `ETag` + `Cache-Control: private, max-age=<device maxAgeS>`, so a grid is N
369
+ * independent `<Image>` tiles served from the coalesced server cache (no
370
+ * base64 tax, no batch head-of-line blocking, browser/`expo-image` caching for
371
+ * free). Best-effort: a host without a `dataPlane` facility keeps the tRPC
372
+ * base64 path.
373
+ */
374
+ async serveMediaDataPlane() {
375
+ const handler = createSnapshotMediaHandler({ getMedia: (deviceId, streamId, force) => this.resolveSnapshotMedia(deviceId, streamId, force) });
376
+ try {
377
+ this.mediaDataPlane = await this.ctx.dataPlane?.serve({
378
+ prefix: "media",
379
+ access: "authenticated",
380
+ handler
381
+ }) ?? null;
382
+ this.ctx.logger.info("snapshot media data-plane served", { meta: { baseUrl: this.mediaDataPlane ? `/addon/${this.ctx.id}/media` : "(no dataPlane facility)" } });
383
+ } catch (err) {
384
+ this.ctx.logger.warn("snapshot media data-plane failed to serve", { meta: { error: require_dist.errMsg(err) } });
385
+ this.mediaDataPlane = null;
386
+ }
387
+ }
388
+ /**
389
+ * Resolve a device (+ optional stream, + force) to renderable JPEG bytes for
390
+ * the HTTP endpoint. Runs the same coalesced capture ladder as the tRPC
391
+ * `getSnapshot` (so every consumer shares one upstream pull), then decodes the
392
+ * base64 cache into raw bytes and stamps the frame identity: `capturedAt` from
393
+ * the cache entry (matching the ETag `getSnapshotOverview` advertises) and the
394
+ * effective per-device `maxAgeS` for `Cache-Control`. Null → 404.
395
+ */
396
+ async resolveSnapshotMedia(deviceId, streamId, force) {
397
+ const image = await this.getSnapshot({
398
+ deviceId,
399
+ ...streamId !== void 0 ? { streamId } : {},
400
+ force
401
+ });
402
+ if (!image) return null;
403
+ const capturedAt = this.cache.get(deviceId)?.ts ?? Date.now();
404
+ const prefs = await this.readDeviceSettings(deviceId).catch(() => ({}));
405
+ const isBattery = (await this.lookupDeviceMeta(deviceId))?.isBattery ?? false;
406
+ return {
407
+ bytes: Buffer.from(image.base64, "base64"),
408
+ contentType: image.contentType,
409
+ capturedAt,
410
+ maxAgeS: effectiveMaxAgeS(prefs, isBattery)
411
+ };
412
+ }
266
413
  async onShutdown() {
414
+ if (this.mediaDataPlane) {
415
+ await this.mediaDataPlane.dispose().catch(() => void 0);
416
+ this.mediaDataPlane = null;
417
+ }
267
418
  this.cache.clear();
268
419
  this.captureFlight.clear();
269
420
  this.ownerCache = null;
@@ -316,8 +467,7 @@ var SnapshotAddon = class SnapshotAddon extends require_dist.BaseAddon {
316
467
  tags: { deviceId },
317
468
  meta: { stream: effectiveStreamId ?? "auto" }
318
469
  });
319
- const defaultMaxAgeS = isBatteryDevice ? BATTERY_DEFAULT_MAX_AGE_S : NON_BATTERY_DEFAULT_MAX_AGE_S;
320
- const effectiveMaxAgeMs = (typeof prefs.snapshotMaxAgeS === "number" && prefs.snapshotMaxAgeS >= 0 ? prefs.snapshotMaxAgeS : defaultMaxAgeS) * 1e3;
470
+ const effectiveMaxAgeMs = effectiveMaxAgeS(prefs, isBatteryDevice) * 1e3;
321
471
  const decision = decideSnapshotServe({
322
472
  now,
323
473
  cachedAt: hit?.ts ?? null,