@camstack/system 1.2.364 → 1.2.366

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 (72) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/addon-utils.d.ts +1 -1
  4. package/dist/addon-utils.js +3 -1
  5. package/dist/addon-utils.mjs +2 -2
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  7. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  10. package/dist/builtins/alerts/alerts.addon.js +1 -1
  11. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  12. package/dist/builtins/autotrack/index.js +1 -1
  13. package/dist/builtins/autotrack/index.mjs +1 -1
  14. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  15. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  16. package/dist/builtins/calendars/calendars.addon.js +1 -1
  17. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  18. package/dist/builtins/camera-grid/index.js +1 -1
  19. package/dist/builtins/camera-grid/index.mjs +1 -1
  20. package/dist/builtins/composer/composer.addon.js +2 -2
  21. package/dist/builtins/composer/composer.addon.mjs +2 -2
  22. package/dist/builtins/console-logging/index.js +1 -1
  23. package/dist/builtins/console-logging/index.mjs +1 -1
  24. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  25. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  26. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  27. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  28. package/dist/builtins/hub-forwarder/index.js +1 -1
  29. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  31. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  32. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  33. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  34. package/dist/builtins/local-network/local-network.addon.js +1 -1
  35. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  36. package/dist/builtins/loki-logging/index.js +1 -1
  37. package/dist/builtins/loki-logging/index.mjs +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  39. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  40. package/dist/builtins/platform-probe/index.js +1 -1
  41. package/dist/builtins/platform-probe/index.mjs +1 -1
  42. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  43. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/snapshot/index.js +1 -1
  45. package/dist/builtins/snapshot/index.mjs +1 -1
  46. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  47. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  49. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  51. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  52. package/dist/builtins/system-config/system-config.addon.js +1 -1
  53. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  54. package/dist/builtins/winston-logging/index.js +1 -1
  55. package/dist/builtins/winston-logging/index.mjs +1 -1
  56. package/dist/{child-cap-dispatch-B79ITdeA.js → child-cap-dispatch-D9uoEW2A.js} +1 -1
  57. package/dist/{child-cap-dispatch-CtBHqm2J.mjs → child-cap-dispatch-tHbRfy_7.mjs} +1 -1
  58. package/dist/{composition-sources-CNVBIV_9.js → composition-sources-AHfMboGi.js} +1 -1
  59. package/dist/{composition-sources-Dj84elbu.mjs → composition-sources-GjJ1QxMC.mjs} +1 -1
  60. package/dist/{dist-C-IdBSWx.mjs → dist-BUfYKaXx.mjs} +321 -14
  61. package/dist/{dist-DiScyAO6.js → dist-ub3muPSW.js} +338 -13
  62. package/dist/download/hosted-model-revisions.d.ts +10 -0
  63. package/dist/download/model-download-service.d.ts +8 -14
  64. package/dist/download/model-downloader.d.ts +44 -2
  65. package/dist/{event-loop-stall-monitor-NFG6boF5.mjs → event-loop-stall-monitor-CPI1xIcd.mjs} +300 -126
  66. package/dist/{event-loop-stall-monitor-CuTt1wq7.js → event-loop-stall-monitor-j0DRBkuI.js} +317 -125
  67. package/dist/index.d.ts +2 -2
  68. package/dist/index.js +6 -3
  69. package/dist/index.mjs +4 -4
  70. package/dist/{retired-settings-keys-CJpnYIp1.js → retired-settings-keys-DNCxYHe1.js} +1 -1
  71. package/dist/{retired-settings-keys-DIzyi9Jf.mjs → retired-settings-keys-Du41eTrH.mjs} +1 -1
  72. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { Lt as hfModelUrl, T as HF_REPO, j as MODEL_FORMATS } from "./dist-BUfYKaXx.mjs";
1
2
  import { createHash } from "node:crypto";
2
3
  import * as path$1 from "node:path";
3
4
  import path from "node:path";
@@ -7,6 +8,153 @@ import { promisify } from "node:util";
7
8
  import { createServer } from "node:http";
8
9
  import { brotliCompress, constants as constants$1, gzip } from "node:zlib";
9
10
  import { PerformanceObserver } from "node:perf_hooks";
11
+ //#region src/download/hosted-model-revisions.ts
12
+ /**
13
+ * GENERATED by `scripts/check-hosted-model-revisions.ts --write` — DO NOT EDIT.
14
+ *
15
+ * The revision of every artefact on the CamStack model mirror, keyed by its
16
+ * path under `https://huggingface.co/camstack/camstack-models/resolve/main/`: the sha256 (`lfs.oid`)
17
+ * of its weight file (`revisionWeightUrl`). `resolveFormatRevision` reads
18
+ * it for every catalog format hosted there (D743, D744). Regenerate after
19
+ * any upload to the mirror; the script without `--write` says when it is due.
20
+ */
21
+ var HOSTED_MODEL_REVISIONS = {
22
+ "animalClassification/animal-classifier/coreml/animal-classifier.mlpackage": "2194acb46c1a957c2db766cce0c6f7c295aafc8ad1983843dcb8bc95eac42a34",
23
+ "animalClassification/animal-classifier/onnx/animal-classifier.onnx": "2ccd51865c1d2d600ff68235f5e72a434cb89555197a872489a2357f50771768",
24
+ "animalClassification/animal-classifier/openvino/animal-classifier.xml": "30edf890ff200e8b5169e7a28fcebe148b0d6a5dd3eb570d7a4de89acc9bd63c",
25
+ "animalClassification/bird-classifier/coreml/bird-classifier.mlpackage": "09d5acd27abc89a139399b70ff6711094c05ace5edd6d40b67bf816a41d39a22",
26
+ "animalClassification/bird-classifier/onnx/bird-classifier.onnx": "64dd1b966628145219f2424b42b18b76b8dc33a0a9d9ce90b8c057397fa76da2",
27
+ "animalClassification/bird-classifier/openvino/bird-classifier.xml": "bef863425e1774fba8bf8809a4d690466eeaf183e8d9278493a584a436304339",
28
+ "animalClassification/bird-classifier/tflite/bird-classifier_edgetpu.tflite": "f5b37245cfda13909a34264df58784981aef8dde3bc8604e0ee42caeba29229c",
29
+ "audioClassification/yamnet/onnx/camstack-yamnet.onnx": "d7459a0aefffef8c1ccc60c8e0a0f0758a724fc1b55b47194b418029e938ea26",
30
+ "audioClassification/yamnet/openvino/camstack-yamnet.xml": "11d9e8f47f8af09245bf6a519bc9967b1ead9ac8f8e89d985289ac0fb07926b7",
31
+ "clip/siglip2/coreml/camstack-siglip2-b16-224-vision.mlpackage": "6fdf1936f91d40a4b9b4fcaabe5b2dcadfc6d8c0e76ab1cc9e18c38e95d60787",
32
+ "clip/siglip2/onnx/camstack-siglip2-b16-224-text.onnx": "df6ffc0c44a49648f903fbf6c46722de416447d065e63db97bd3f8edd9bfc411",
33
+ "clip/siglip2/openvino/camstack-siglip2-b16-224-text.xml": "a85203c195eccea00c9d31af5f3594733353cc6b3a3c2802b4e32dc0136f8dae",
34
+ "clip/siglip2/openvino/camstack-siglip2-b16-224-vision.xml": "2e190c41cfa980c8a7a24cb13ecdd71ae0a08eb8665c22a27314aec64e78f325",
35
+ "faceDetection/yunet/coreml/camstack-yunet-2023mar.mlpackage": "d6296bc3ae3048bf3815ef7df8ee934fde48018276c0b2278f93ba359556103b",
36
+ "faceDetection/yunet/onnx/camstack-yunet-2023mar.onnx": "55df63fd5a5128903ee2b6dd4315aa82979be9a1d6cf83cc154b74cd4bd2c42f",
37
+ "faceDetection/yunet/openvino/camstack-yunet-2023mar.xml": "c246c87702b346a9d1de7a70ea61b256d06c7db59bd895acb1aadeaba1985dff",
38
+ "faceDetection/yunet/tflite/camstack-yunet-2023mar_edgetpu.tflite": "b7214dafaf18b8a5c55c7ea502c783e6a356043c2900ff9e02515a368285e982",
39
+ "faceRecognition/auraface/coreml/camstack-auraface-r100.mlpackage": "c189aaf7d6758dafb1603b4ea7f7c2161b69639434ddbce800e0cc632b26d7e0",
40
+ "faceRecognition/auraface/onnx/camstack-auraface-r100.onnx": "1c050a0fd76a5770fb94bd35d1ff99648cb31385280d7fba43284b9b6e63277f",
41
+ "faceRecognition/auraface/openvino/camstack-auraface-r100.xml": "e8f53b3a4bc7d13520d561d2610887bffe8438fac71a496ec1cfcce2616734bb",
42
+ "objectDetection/yolo26/coreml/camstack-yolo26l-256.mlpackage": "87968708ab7ee99aac97d1d6644f6b70149b7b2c38b3f2c3c64326c3cc2333e2",
43
+ "objectDetection/yolo26/coreml/camstack-yolo26l-320.mlpackage": "f0a136258aa69978f5647e07ac4019a4f8c8745dc6d6500c8e3a2876d041f9e5",
44
+ "objectDetection/yolo26/coreml/camstack-yolo26l.mlpackage": "b53d9ce65fc0f84dd13e460bf2666362ac6854f645c897dd0e0c6e8691995146",
45
+ "objectDetection/yolo26/coreml/camstack-yolo26m-256.mlpackage": "bf1ac4cb5f2a3749815a36dde6d6e57ce0662a005ccefd435283d4e38c21298d",
46
+ "objectDetection/yolo26/coreml/camstack-yolo26m-320.mlpackage": "4cf582bf1b14fb7abf87f61a1cf198ad2626fedec32873224bc6f2411e6680b7",
47
+ "objectDetection/yolo26/coreml/camstack-yolo26m.mlpackage": "e9a3751fd3c60676ce94fba7d3d9e8cccfcc8711cd55025eb8837ad8b0fe1424",
48
+ "objectDetection/yolo26/coreml/camstack-yolo26n-256.mlpackage": "1c0565e40b4e04194561df09de4c007ccedd4d5d784adf2341f3af7c97f42cb9",
49
+ "objectDetection/yolo26/coreml/camstack-yolo26n-320.mlpackage": "b30bb5ced9c127f0a813c7740c451a1766226dde43e277e01eb9d298d44f17cd",
50
+ "objectDetection/yolo26/coreml/camstack-yolo26n.mlpackage": "1e176157d427b358db9d87613b1a57040d20c7d0b4a7de70e4477e162dd3d4c5",
51
+ "objectDetection/yolo26/coreml/camstack-yolo26s-256.mlpackage": "e4f3ba3ba99d9a068c4e064fd74427b8a2b56c5e1f56f56979886a493068fbd0",
52
+ "objectDetection/yolo26/coreml/camstack-yolo26s-320.mlpackage": "156d8db8d1f2b9d6bc23ce2e123889d6ad79a5ce866dfccc35c0d869df79db64",
53
+ "objectDetection/yolo26/coreml/camstack-yolo26s.mlpackage": "6d760c59e32daea2812258bd6db417ddfc2eb1be6184daef54ba349bd8d0d125",
54
+ "objectDetection/yolo26/coreml/camstack-yolo26x.mlpackage": "62871ed80cd80cf2b742d2a1ac3ff83f534d9f51039febcb966ee173851fcd5d",
55
+ "objectDetection/yolo26/onnx/camstack-yolo26l-256.onnx": "1a5a963e9b76a324e2fa0e111d96b23176e47a5337e12576a755b512f843592b",
56
+ "objectDetection/yolo26/onnx/camstack-yolo26l-320.onnx": "15ee305cb925934dd73d15a398cc76adf8a51ffa430b11da1ba61f3686eb24b3",
57
+ "objectDetection/yolo26/onnx/camstack-yolo26l.onnx": "62e55ec70eb11f19e75c519a10c1632e08d38a9ddb3229450156a6b423780bbc",
58
+ "objectDetection/yolo26/onnx/camstack-yolo26m-256.onnx": "3d2df9b07b06bfafc6487200674ecb899957e56acbf2ea64383ad477cab30072",
59
+ "objectDetection/yolo26/onnx/camstack-yolo26m-320.onnx": "6a4fc7a9a0c61cf2a401407805bb850b49dbe1faf1cf0ae2edc794a8bc61cc51",
60
+ "objectDetection/yolo26/onnx/camstack-yolo26m.onnx": "907982ada658a53736edfcbdde7e9cf0489a54c058da0deea106bcb2794fe6bc",
61
+ "objectDetection/yolo26/onnx/camstack-yolo26n-256.onnx": "352b15664cea1af55f80f96e9b7a0f8338ca2e6b6053bc27152ad82fc689cb58",
62
+ "objectDetection/yolo26/onnx/camstack-yolo26n-320.onnx": "22d8f1c749394509161b9053361dec19be23c3479f0386a50caba418e7e897a7",
63
+ "objectDetection/yolo26/onnx/camstack-yolo26n.onnx": "8f2e6c82ccbc63153c2284546880b1310d832d2b75efea67cb9cda1fa35da968",
64
+ "objectDetection/yolo26/onnx/camstack-yolo26s-256.onnx": "354ff62da4ffd763f1e7b8761c5d070c1d3f3eafc5221a1a52a3f5b8e651471b",
65
+ "objectDetection/yolo26/onnx/camstack-yolo26s-320.onnx": "465069e0eb3d44e45c2de2c57a57c9ff6f1466cda0816d7ca05ce15e388b0ae3",
66
+ "objectDetection/yolo26/onnx/camstack-yolo26s.onnx": "d2e7ed3e5ef3b7fac18628c63d2bef0d5ae144cb2c239b195eb90ff96437fed5",
67
+ "objectDetection/yolo26/onnx/camstack-yolo26x.onnx": "6d10ddc274ea5e6597c69471a3e122eee7a4cb2a831c9449b30b0e69f4349133",
68
+ "objectDetection/yolo26/openvino/camstack-yolo26l-256-int8.xml": "d59a9920857a48d27bda14b1cbe3ef1390e1032c37a822cbc567f2a03d1f5e2f",
69
+ "objectDetection/yolo26/openvino/camstack-yolo26l-256.xml": "0c78828d4abe9858bb7ff543ee656adb874c11a76cbacbdcca7777d356be2ee3",
70
+ "objectDetection/yolo26/openvino/camstack-yolo26l-320-int8.xml": "9c441c07c7c2d47982b44a2f2d22af0d77ffd466f7279b843c9bbe21cd07a5d8",
71
+ "objectDetection/yolo26/openvino/camstack-yolo26l-320.xml": "0c78828d4abe9858bb7ff543ee656adb874c11a76cbacbdcca7777d356be2ee3",
72
+ "objectDetection/yolo26/openvino/camstack-yolo26l-fp16.xml": "653a3bf71753672f56a2c8dae65195c8c99029abb3d3d7a42e25860a2071df39",
73
+ "objectDetection/yolo26/openvino/camstack-yolo26l-int8.xml": "9e2c1de16b3533c0f8d2ed58e385515bbcb6ab40231bdace5b922795b88127be",
74
+ "objectDetection/yolo26/openvino/camstack-yolo26l.xml": "653a3bf71753672f56a2c8dae65195c8c99029abb3d3d7a42e25860a2071df39",
75
+ "objectDetection/yolo26/openvino/camstack-yolo26m-256-int8.xml": "01c747e08fbac6fedfc874924a267ba3d5597823b8d91c1c39f5db765047cfd8",
76
+ "objectDetection/yolo26/openvino/camstack-yolo26m-256.xml": "c5a4c729eceaec91f6af5077fa34b6f5cc17bc2130d07dc13ed883966e15902e",
77
+ "objectDetection/yolo26/openvino/camstack-yolo26m-320-int8.xml": "2b6be63ad9012c85976223ed46c63b80208479544f13221575f62c4b5a815314",
78
+ "objectDetection/yolo26/openvino/camstack-yolo26m-320.xml": "c5a4c729eceaec91f6af5077fa34b6f5cc17bc2130d07dc13ed883966e15902e",
79
+ "objectDetection/yolo26/openvino/camstack-yolo26m-fp16.xml": "4990e60c17e6e1158dca28750e7a50c3a6c72cac4fd1bbac73bba52ac3647c39",
80
+ "objectDetection/yolo26/openvino/camstack-yolo26m-int8.xml": "7cf3df0dd16d58468ad6ac185ec5f38ac6e20a49069f049007b6c0607586a6f7",
81
+ "objectDetection/yolo26/openvino/camstack-yolo26m.xml": "4990e60c17e6e1158dca28750e7a50c3a6c72cac4fd1bbac73bba52ac3647c39",
82
+ "objectDetection/yolo26/openvino/camstack-yolo26n-256-int8.xml": "acf0ed987bbd43bd819c9f061582c0ba17aa4a3f6bf55f0c8c7049486742f745",
83
+ "objectDetection/yolo26/openvino/camstack-yolo26n-256.xml": "caf498d56ba2288a54734b4d2c73737f77261d1410357f25998e00655c661736",
84
+ "objectDetection/yolo26/openvino/camstack-yolo26n-320-int8.xml": "639e4d54a0d5c6a17d02859a9b46c0ff4d404fbe0582e48ed3df4b8364867316",
85
+ "objectDetection/yolo26/openvino/camstack-yolo26n-320.xml": "caf498d56ba2288a54734b4d2c73737f77261d1410357f25998e00655c661736",
86
+ "objectDetection/yolo26/openvino/camstack-yolo26n-fp16.xml": "77f9da9a6330b130d21e7d9d2338b8f82f3541491c24566b80b4ff2852559b2d",
87
+ "objectDetection/yolo26/openvino/camstack-yolo26n-int8.xml": "92068cd26433694188cf383e3a7c3cc49a36de58387a8012de9c3768080781f3",
88
+ "objectDetection/yolo26/openvino/camstack-yolo26n.xml": "77f9da9a6330b130d21e7d9d2338b8f82f3541491c24566b80b4ff2852559b2d",
89
+ "objectDetection/yolo26/openvino/camstack-yolo26s-256-int8.xml": "483ca9622c3bb398c414cb3ae412bcab26aeb807c91f4d84dcd42356ce54c885",
90
+ "objectDetection/yolo26/openvino/camstack-yolo26s-256.xml": "8e4c513ffe7d571ed1e9545a990a43f6683a8bde04bdc580fdfd8c62e4e3193f",
91
+ "objectDetection/yolo26/openvino/camstack-yolo26s-320-int8.xml": "c02030199cda5083e41599c7aa9dd64d03597e033f03b61c62194de49719de65",
92
+ "objectDetection/yolo26/openvino/camstack-yolo26s-320.xml": "8e4c513ffe7d571ed1e9545a990a43f6683a8bde04bdc580fdfd8c62e4e3193f",
93
+ "objectDetection/yolo26/openvino/camstack-yolo26s-fp16.xml": "bb9355b5e559d646256ad80e4fc2f965c1c1f4905da3c014b7f15bb3d48d9d34",
94
+ "objectDetection/yolo26/openvino/camstack-yolo26s-int8.xml": "863bbfa6f45b617d5600ceb5488a2dc3c0d573b658e761a5c269b86f3f6cca77",
95
+ "objectDetection/yolo26/openvino/camstack-yolo26s.xml": "bb9355b5e559d646256ad80e4fc2f965c1c1f4905da3c014b7f15bb3d48d9d34",
96
+ "objectDetection/yolo26/openvino/camstack-yolo26x-fp16.xml": "053f64a4dc9c3b81d0b6c1a567c0d0b403eeb33a6771170712c4de1c09fc6f15",
97
+ "objectDetection/yolo26/openvino/camstack-yolo26x-int8.xml": "c1f374e058de562d3a848b93d6b39dd9ccb772e2f34f4c0dec4e0bb7787cfd2f",
98
+ "objectDetection/yolo26/openvino/camstack-yolo26x.xml": "053f64a4dc9c3b81d0b6c1a567c0d0b403eeb33a6771170712c4de1c09fc6f15",
99
+ "objectDetection/yolov9/coreml/camstack-yolov9c-320.mlpackage": "7999f25b805f92c732a450625021cda97b7185f8c9f98be28be82be1529fe171",
100
+ "objectDetection/yolov9/coreml/camstack-yolov9c-640.mlpackage": "9889ababc524db385a4305ebc1a42ada4dddc8028198d040fbba77c504228932",
101
+ "objectDetection/yolov9/coreml/camstack-yolov9m-320.mlpackage": "e0e0298051c71c79390f1a05f71a2bb882af52c096c70d69674afb84077f6de4",
102
+ "objectDetection/yolov9/coreml/camstack-yolov9m-640.mlpackage": "4c56eef689435b427c53e92e918be386781024c9b77492f9e486dcdd8ea3570c",
103
+ "objectDetection/yolov9/coreml/camstack-yolov9s-320.mlpackage": "6408badb44161268a337f87586ad82f40e4e315a58be460a648407cc38097675",
104
+ "objectDetection/yolov9/coreml/camstack-yolov9s-640.mlpackage": "9f68b8d418b0832e9788f115e2dfabc6ff54fbe5244815adafa0bd9f98204755",
105
+ "objectDetection/yolov9/coreml/camstack-yolov9t-320.mlpackage": "531aa2d14e7e321f2c90219b966cc3781781d0304c7bed7e6c3b75adaa2ada86",
106
+ "objectDetection/yolov9/coreml/camstack-yolov9t-640.mlpackage": "53dfec9857b3c9922fcbd5e9f88f34ed79de26d9bbcf0d705a1c7a4e5ac93398",
107
+ "objectDetection/yolov9/onnx/camstack-yolov9c-320.onnx": "41b48ef6b39f76340c9a2d40182eddf46ab311b68efbdf637148358f27f1f351",
108
+ "objectDetection/yolov9/onnx/camstack-yolov9c-640.onnx": "4b89e489c4f724ae6c7f271350c9bfd4c800c535e1699f2d6b2d7714293bb695",
109
+ "objectDetection/yolov9/onnx/camstack-yolov9m-320.onnx": "602acf6a2189ad713e5aa0393a3b47c04c8303d6907762f66424f168c3e1e55b",
110
+ "objectDetection/yolov9/onnx/camstack-yolov9m-640.onnx": "d0e69881f62ad156ccbe8699611ae2bd4e569431a9c020b3b6a681efdcd1cfea",
111
+ "objectDetection/yolov9/onnx/camstack-yolov9s-320.onnx": "e06ee6345026ab95f90f5f367b01164654376725fa8e263067ebb9f3417f89c3",
112
+ "objectDetection/yolov9/onnx/camstack-yolov9s-640.onnx": "9113734de682b6b9147b8ed331684d4dc65ddc58d33dd95a00d507c88e82b5c4",
113
+ "objectDetection/yolov9/onnx/camstack-yolov9t-320.onnx": "6c0b0a302a5aaabaf17749a3bda15588a0b196d6f16363198b678c68d2aa5d17",
114
+ "objectDetection/yolov9/onnx/camstack-yolov9t-640.onnx": "fd025ab7bf4d81d506a0561917813431dabe3d673aea4182ff61d7e5c803c2c8",
115
+ "objectDetection/yolov9/openvino/camstack-yolov9c-320-fp16.xml": "3bacbbd621df5570d25dc28b4371cd46847f252743a5246481efffbbc63b5c31",
116
+ "objectDetection/yolov9/openvino/camstack-yolov9c-320-int8.xml": "7614b514c12e03a35a697ed884abd26db1ed8b4ebb5288f8b9e7daf8335909ea",
117
+ "objectDetection/yolov9/openvino/camstack-yolov9c-640-fp16.xml": "702060a1904f8de7eb68b72ad5024976767ba546c3dc941f8b7c2e5d1a236d0d",
118
+ "objectDetection/yolov9/openvino/camstack-yolov9c-640-int8.xml": "3bda3338e0280108a32b6ae638637cd9a2b2a563b8c8ec328165372ee9d55bd9",
119
+ "objectDetection/yolov9/openvino/camstack-yolov9m-320-fp16.xml": "d381e5981e08bad4b8333bba3db48b4c0885883eb1bd524c71e69e2d8b745eaf",
120
+ "objectDetection/yolov9/openvino/camstack-yolov9m-320-int8.xml": "59e5274ed17b2acb57c9ba6999e2e92ea6f05e153fc11e6f8f19bd4973d36a74",
121
+ "objectDetection/yolov9/openvino/camstack-yolov9m-640-fp16.xml": "a64e8867a406c448e65fecea16932d759d85249427c6b43770387904d980d6aa",
122
+ "objectDetection/yolov9/openvino/camstack-yolov9m-640-int8.xml": "b229989cc83ad3c82a3c7f09a25c522eb25416a9d5ce9f1b58225ef4668694b5",
123
+ "objectDetection/yolov9/openvino/camstack-yolov9s-320-fp16.xml": "d07a908f4e44d3b482fad99fd280e1eff7f12546848b3acdfd478407a12fbaf6",
124
+ "objectDetection/yolov9/openvino/camstack-yolov9s-320-int8.xml": "c8c58877a7a3b969ecfced15244c027eb9b9295a82cae54b71197a1a0dbb31f8",
125
+ "objectDetection/yolov9/openvino/camstack-yolov9s-640-fp16.xml": "19448578cde92005a143b8de9655e767032e608cea496e154273ed05a97bed1f",
126
+ "objectDetection/yolov9/openvino/camstack-yolov9s-640-int8.xml": "7ea8b3d6bc98f15b2eada4e62cdcc35c211a181cbddf51251a11e97bee994fdd",
127
+ "objectDetection/yolov9/openvino/camstack-yolov9t-320-fp16.xml": "2f4dc7d06d49af8008fb3da0cdc04a35fdfbc5fafbb2f069822edd826fcc49de",
128
+ "objectDetection/yolov9/openvino/camstack-yolov9t-320-int8.xml": "4b3964c53d2617502aea7bc07fd5fe24da82b27896abe12fd87956b2765babf6",
129
+ "objectDetection/yolov9/openvino/camstack-yolov9t-640-fp16.xml": "d4dd2278941ccb210d0763d822353f6d951c6a4f4f5a1604d7e3536ce6ffb954",
130
+ "objectDetection/yolov9/openvino/camstack-yolov9t-640-int8.xml": "75e45bcf26fe37489d66ae306c3b7c08742b3f2af3c84cd5e41d0520aaef91bf",
131
+ "packageDetection/rfdetr-package/coreml/camstack-rfdetr-package.mlpackage": "73e42f03d66011a311bd83a0a3d1890e7eb1b9b2ba2b5688552a5d4fd60e0e72",
132
+ "packageDetection/rfdetr-package/onnx/camstack-rfdetr-package.onnx": "5af96c6b5936f179e317c9e3915b7c7f74ea3625355f97f6a071c3cd5af2475c",
133
+ "packageDetection/rfdetr-package/openvino/camstack-rfdetr-package.xml": "e2cb5bbccb7809d5c7e691a957bc5e008084119bdf29e2c95d6b6b0791e3061a",
134
+ "plateDetection/yolox-tiny-plate/coreml/camstack-yolox-tiny-plate.mlpackage": "a26ae8fe32e458f7396d56bed83b1243e6067f435b08cd6da134273b9151447d",
135
+ "plateDetection/yolox-tiny-plate/onnx/camstack-yolox-tiny-plate.onnx": "f57b5618eafe91bab19bbdcd9af9b0671dbdba30d76a6e0a3a86b0db24bb1ae6",
136
+ "plateDetection/yolox-tiny-plate/openvino/camstack-yolox-tiny-plate.xml": "83b808eb8fcda6f852477e2dbcdd5cad29ab6a8a58e90017714f167c6a345c27",
137
+ "plateDetection/yolox-tiny-plate/tflite/camstack-yolox-tiny-plate_edgetpu.tflite": "0e091263e9c93063f9da822387860a9cd21c0a554e714de276140921487b8e4d",
138
+ "plateRecognition/cct-s-v2-global/coreml/camstack-cct-s-v2-global.mlpackage": "31b9aba671d98ebd847f43a9e2555f7664c70ad11c3e42fdc0add528463fd3ee",
139
+ "plateRecognition/cct-s-v2-global/onnx/camstack-cct-s-v2-global.onnx": "79c3c00f906857e63453bc16cef6b4a0d34821dff685328eff565fd2da559d21",
140
+ "plateRecognition/cct-s-v2-global/openvino/camstack-cct-s-v2-global.xml": "7357db8b5f1c71a8af9376f2e35435bd0c0361f5193b7e02787f513514ecb8ab",
141
+ "segmentation/yolo26-seg/coreml/camstack-yolo26m-seg.mlpackage": "77ac5c932948df6c6d30f232b40302ccbdfc00f97f7fa78a90dbfb7cdb97608f",
142
+ "segmentation/yolo26-seg/coreml/camstack-yolo26n-seg.mlpackage": "81863a6503294d06063a3320de0a45d543d62b9a3d11009c8d54f5d626abd2a0",
143
+ "segmentation/yolo26-seg/coreml/camstack-yolo26s-seg.mlpackage": "47ee5d620a141bffd594afaf84c4362de2ccf74e036b71ec55ae53428ffbdde8",
144
+ "segmentation/yolo26-seg/onnx/camstack-yolo26m-seg.onnx": "c419fdc8cb8e65c8707586f461d59910487392a5b01711cac674559275df1f7b",
145
+ "segmentation/yolo26-seg/onnx/camstack-yolo26n-seg.onnx": "9f2480a359c8a44541b931d44c4a47898122a5b16ce78d8e5611351b1d4531b8",
146
+ "segmentation/yolo26-seg/onnx/camstack-yolo26s-seg.onnx": "88454cc2f5c2ebaf197536568ffb2ae146a48a85c5c053fd8c9f72f117262313",
147
+ "segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml": "08eea0ff1ff6b66a03eb255b9a9ce6952908c92c4684347672d666731148b28f",
148
+ "segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml": "d592be6c57fb6f0fc00277327b2421d7ec91fbc0da050bd97fab227df1158c5e",
149
+ "segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml": "c4508b20dbfe644220850cfc294e99c9fc396bd61443fcc8db8801a4388e9d99",
150
+ "segmentationRefiner/u2netp/coreml/camstack-u2netp.mlpackage": "67a1172b99e8b34493482b0095bcfe9960d423315a9bfe619b149ea7ba187e05",
151
+ "segmentationRefiner/u2netp/onnx/camstack-u2netp.onnx": "309c8469258dda742793dce0ebea8e6dd393174f89934733ecc8b14c76f4ddd8",
152
+ "segmentationRefiner/u2netp/openvino/camstack-u2netp.xml": "d060dec1fd64855daef223f635cb280f174415785318d4dc78af0d86de025397",
153
+ "vehicleClassification/vehicle-type-v1/coreml/vehicle-type-v1.mlpackage": "26cf66168010daa0a1d33a2fc12305e0e7465c977840861b4bf470c8cae8a7b8",
154
+ "vehicleClassification/vehicle-type-v1/onnx/vehicle-type-v1.onnx": "5153b77de54286bcfefddfdfec9e3c1101b10c30bc33e9181cf8c04e0156818f",
155
+ "vehicleClassification/vehicle-type-v1/openvino/vehicle-type-v1.xml": "bf352f4a602d5e6ef5b6df5140972ef68e79076be397a90c10f1efdda59f4a1e"
156
+ };
157
+ //#endregion
10
158
  //#region src/download/model-downloader.ts
11
159
  function isNonEmptyFile(filePath) {
12
160
  return fs.existsSync(filePath) && fs.statSync(filePath).size > 0;
@@ -211,22 +359,23 @@ async function computeSha256(filePath) {
211
359
  });
212
360
  }
213
361
  /**
214
- * Download every file in a HuggingFace directory bundle (e.g.,
215
- * `.mlpackage` / OpenVINO IR pair) atomically. `knownFiles` lists the
216
- * relative paths inside the directory; the function fetches each from
217
- * `${url}/${file}` and renames the staging directory only on full
218
- * success. Mirrors `ModelDownloadService.downloadDirectory` but
219
- * exposed as a standalone for catalog-less callers.
362
+ * Download every file in a HuggingFace directory bundle (e.g. `.mlpackage`)
363
+ * atomically. `knownFiles` lists the relative paths inside the directory; a
364
+ * bundle that declares none is listed through the Hugging Face tree API. The
365
+ * staging directory replaces `destDir` only when every file arrived, so a
366
+ * bundle already on disk keeps serving until then.
220
367
  */
221
368
  async function downloadDirectory(url, destDir, knownFiles, onProgress) {
222
369
  const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/main\/(.+)/);
223
370
  if (!match) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
224
371
  const [, repo, dirPath] = match;
225
- const files = (knownFiles ?? []).map((f) => ({
372
+ if (repo === void 0 || dirPath === void 0) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
373
+ const relativePaths = knownFiles !== void 0 && knownFiles.length > 0 ? knownFiles : (await listHfFiles(repo, dirPath)).map((f) => f.substring(dirPath.length + 1));
374
+ if (relativePaths.length === 0) throw new Error(`No files found in HuggingFace directory: ${dirPath}`);
375
+ const files = relativePaths.map((f) => ({
226
376
  relativePath: f,
227
377
  fileUrl: `https://huggingface.co/${repo}/resolve/main/${dirPath}/${f}`
228
378
  }));
229
- if (files.length === 0) throw new Error(`Directory bundle requires explicit \`files\` list (got none for ${url})`);
230
379
  const tmpDir = destDir + ".downloading";
231
380
  fs.rmSync(tmpDir, {
232
381
  recursive: true,
@@ -256,6 +405,21 @@ async function downloadDirectory(url, destDir, knownFiles, onProgress) {
256
405
  throw err;
257
406
  }
258
407
  }
408
+ function isHfTreeEntry(value) {
409
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && "path" in value && typeof value.path === "string";
410
+ }
411
+ /** Every file path (repo-relative) under `dirPath`, recursively, via the HF tree API. */
412
+ async function listHfFiles(repo, dirPath) {
413
+ const listing = await fetchJson(`https://huggingface.co/api/models/${repo}/tree/main/${dirPath}`);
414
+ if (!Array.isArray(listing)) throw new Error(`Unexpected HuggingFace tree for ${dirPath}`);
415
+ const files = [];
416
+ for (const entry of listing) {
417
+ if (!isHfTreeEntry(entry)) continue;
418
+ if (entry.type === "file") files.push(entry.path);
419
+ else if (entry.type === "directory") files.push(...await listHfFiles(repo, entry.path));
420
+ }
421
+ return files;
422
+ }
259
423
  /**
260
424
  * Where a node records the catalog `revision` of the format it downloaded:
261
425
  * a sibling of the file or bundle, `<name>.revision` (D743). Outside the
@@ -271,23 +435,92 @@ function readRevisionRecord(modelPath) {
271
435
  return null;
272
436
  }
273
437
  }
438
+ /** The CamStack model mirror. Every artefact under it has a generated revision. */
439
+ var HOSTED_MODEL_MIRROR_PREFIX = hfModelUrl(HF_REPO, "");
274
440
  /**
275
- * True when the copy at `modelPath` is the one the catalog names. A format
276
- * without a `revision` is current whenever it is present (the behaviour
277
- * before revisions existed); with one, the node's record must match it — no
278
- * record means the copy predates the revision and may hold replaced bytes.
441
+ * The revision a format must be at — the ONE rule every model download path
442
+ * reads (D743, D744). An inline `revision` wins; otherwise a format hosted on
443
+ * the CamStack mirror takes the revision generated for its path from the
444
+ * mirror's LFS metadata (`hosted-model-revisions.ts`); anything else has none.
445
+ */
446
+ function resolveFormatRevision(formatEntry) {
447
+ if (formatEntry.revision !== void 0) return formatEntry.revision;
448
+ if (!formatEntry.url.startsWith(HOSTED_MODEL_MIRROR_PREFIX)) return void 0;
449
+ return HOSTED_MODEL_REVISIONS[formatEntry.url.slice(HOSTED_MODEL_MIRROR_PREFIX.length)];
450
+ }
451
+ /** The member of a Core ML package that holds its weights. */
452
+ var COREML_WEIGHT_MEMBER = "Data/com.apple.CoreML/weights/weight.bin";
453
+ function revisionWeightFile(formatEntry) {
454
+ if (formatEntry.isDirectory) return formatEntry.url.endsWith(".mlpackage") ? {
455
+ in: "bundle",
456
+ member: COREML_WEIGHT_MEMBER
457
+ } : null;
458
+ const weights = siblingFilesFor(formatEntry).find((f) => f.endsWith(".bin"));
459
+ return weights !== void 0 ? {
460
+ in: "sibling",
461
+ name: weights
462
+ } : { in: "main" };
463
+ }
464
+ /** Where the revision's weight file is hosted — what the mirror's `lfs.oid` describes. */
465
+ function revisionWeightUrl(formatEntry) {
466
+ const weights = revisionWeightFile(formatEntry);
467
+ if (weights === null) return null;
468
+ switch (weights.in) {
469
+ case "bundle": return `${formatEntry.url}/${weights.member}`;
470
+ case "sibling": return siblingUrl(formatEntry.url, weights.name);
471
+ case "main": return formatEntry.url;
472
+ }
473
+ }
474
+ function revisionWeightPath(modelsDir, modelPath, formatEntry) {
475
+ const weights = revisionWeightFile(formatEntry);
476
+ if (weights === null) return null;
477
+ switch (weights.in) {
478
+ case "bundle": return path$1.join(modelPath, weights.member);
479
+ case "sibling": return path$1.join(modelsDir, weights.name);
480
+ case "main": return modelPath;
481
+ }
482
+ }
483
+ /**
484
+ * True when the copy at `modelPath` is RECORDED at the revision the format
485
+ * must be at. A format without a revision is current whenever it is present
486
+ * (the behaviour before revisions existed); with one, the node's record must
487
+ * match it — no record means the copy has not been proven current yet.
279
488
  */
280
489
  function isCurrentRevision(modelPath, formatEntry) {
281
- return formatEntry.revision === void 0 || readRevisionRecord(modelPath) === formatEntry.revision;
490
+ const revision = resolveFormatRevision(formatEntry);
491
+ return revision === void 0 || readRevisionRecord(modelPath) === revision;
282
492
  }
283
- function writeRevisionRecord(modelPath, formatEntry) {
284
- if (formatEntry.revision === void 0) return;
493
+ function writeRevisionRecord(modelPath, revision) {
494
+ if (revision === void 0) return;
285
495
  const record = revisionRecordPath(modelPath);
286
496
  const tmp = `${record}.tmp`;
287
- fs.writeFileSync(tmp, `${formatEntry.revision}\n`);
497
+ fs.writeFileSync(tmp, `${revision}\n`);
288
498
  fs.renameSync(tmp, record);
289
499
  }
290
500
  /**
501
+ * Prove an unrecorded (or differently recorded) copy current by hashing its
502
+ * weight file: when it IS the revision, record it and skip the download. This
503
+ * is what keeps a new revision from re-downloading every copy that already
504
+ * holds the right bytes — a copy fetched before revisions existed, one that a
505
+ * hub distributed, one fetched before the catalog caught up with the mirror.
506
+ */
507
+ async function adoptByHash(modelsDir, modelPath, formatEntry) {
508
+ const revision = resolveFormatRevision(formatEntry);
509
+ if (revision === void 0) return false;
510
+ const weights = revisionWeightPath(modelsDir, modelPath, formatEntry);
511
+ if (weights === null || !isNonEmptyFile(weights)) return false;
512
+ if (await computeSha256(weights) !== revision) return false;
513
+ writeRevisionRecord(modelPath, revision);
514
+ return true;
515
+ }
516
+ /** True when every file of the format is on disk, whatever revision it holds. */
517
+ function isCompleteCopy(modelsDir, modelPath, formatEntry) {
518
+ if (!fs.existsSync(modelPath)) return false;
519
+ if (formatEntry.isDirectory) return fs.existsSync(path$1.join(modelPath, "Manifest.json"));
520
+ if (fs.statSync(modelPath).size <= 0) return false;
521
+ return siblingFilesFor(formatEntry).every((f) => isNonEmptyFile(path$1.join(modelsDir, f)));
522
+ }
523
+ /**
291
524
  * Replace a single-file format and its siblings that are already on disk.
292
525
  * `downloadFile` keeps an existing file, so every file is fetched to a
293
526
  * `.updating` name first and moved over the old one only when ALL of them
@@ -332,8 +565,8 @@ async function ensureModel(modelsDir, entry, format, onProgress) {
332
565
  recursive: true,
333
566
  force: true
334
567
  });
335
- else if (siblings.some((f) => !isNonEmptyFile(path$1.join(modelsDir, f)))) outdated = !isCurrentRevision(modelPath, formatEntry);
336
- else if (isCurrentRevision(modelPath, formatEntry)) return modelPath;
568
+ else if (siblings.some((f) => !isNonEmptyFile(path$1.join(modelsDir, f)))) outdated = !isCurrentRevision(modelPath, formatEntry) && !await adoptByHash(modelsDir, modelPath, formatEntry);
569
+ else if (isCurrentRevision(modelPath, formatEntry) || await adoptByHash(modelsDir, modelPath, formatEntry)) return modelPath;
337
570
  else outdated = true;
338
571
  fs.mkdirSync(modelsDir, { recursive: true });
339
572
  if (formatEntry.isDirectory) await downloadDirectory(formatEntry.url, modelPath, formatEntry.files, onProgress);
@@ -342,9 +575,39 @@ async function ensureModel(modelsDir, entry, format, onProgress) {
342
575
  await downloadFile(formatEntry.url, modelPath, (downloaded, total) => onProgress?.(downloaded, total === 0 ? void 0 : total));
343
576
  for (const sibling of siblings) await downloadFile(siblingUrl(formatEntry.url, sibling), path$1.join(modelsDir, sibling));
344
577
  }
345
- writeRevisionRecord(modelPath, formatEntry);
578
+ writeRevisionRecord(modelPath, resolveFormatRevision(formatEntry));
346
579
  return modelPath;
347
580
  }
581
+ /**
582
+ * Walk `entries` and record the revision of every complete copy on disk that
583
+ * hashes to it, so a node's listing reports present models as downloaded
584
+ * without waiting for something to `ensureModel` them. Offline: it reads
585
+ * files and never fetches. A copy already recorded current is not re-hashed.
586
+ */
587
+ async function adoptModelRevisions(modelsDir, entries) {
588
+ const adopted = [];
589
+ const stale = [];
590
+ const seen = /* @__PURE__ */ new Set();
591
+ for (const entry of entries) for (const format of Object.keys(entry.formats).filter(isModelFormat)) {
592
+ const formatEntry = entry.formats[format];
593
+ if (formatEntry === void 0 || resolveFormatRevision(formatEntry) === void 0) continue;
594
+ const modelPath = getModelFilePath(modelsDir, entry, format);
595
+ if (modelPath === null || seen.has(modelPath)) continue;
596
+ seen.add(modelPath);
597
+ if (!isCompleteCopy(modelsDir, modelPath, formatEntry)) continue;
598
+ if (isCurrentRevision(modelPath, formatEntry)) continue;
599
+ const key = `${entry.id}/${format}`;
600
+ if (await adoptByHash(modelsDir, modelPath, formatEntry)) adopted.push(key);
601
+ else stale.push(key);
602
+ }
603
+ return {
604
+ adopted,
605
+ stale
606
+ };
607
+ }
608
+ function isModelFormat(value) {
609
+ return MODEL_FORMATS.some((f) => f === value);
610
+ }
348
611
  /** Compute the on-disk path for a given model + format, even when not yet downloaded. */
349
612
  function getModelFilePath(modelsDir, entry, format) {
350
613
  const formatEntry = entry.formats[format];
@@ -354,8 +617,10 @@ function getModelFilePath(modelsDir, entry, format) {
354
617
  }
355
618
  /**
356
619
  * True iff the model file (or `Manifest.json` for directory bundles) exists and
357
- * is non-empty, and — when the catalog names a `revision` — the node recorded
358
- * that same revision for it (D743).
620
+ * is non-empty, and — when the format has a revision ({@link
621
+ * resolveFormatRevision}) — the node recorded that same revision for it
622
+ * (D743). Synchronous, so it never hashes: a copy not yet proven current
623
+ * answers false until `ensureModel` or {@link adoptModelRevisions} records it.
359
624
  */
360
625
  function isModelDownloaded(modelsDir, entry, format) {
361
626
  const formatEntry = entry.formats[format];
@@ -363,9 +628,7 @@ function isModelDownloaded(modelsDir, entry, format) {
363
628
  const modelPath = getModelFilePath(modelsDir, entry, format);
364
629
  if (!modelPath || !fs.existsSync(modelPath)) return false;
365
630
  if (!isCurrentRevision(modelPath, formatEntry)) return false;
366
- if (formatEntry.isDirectory) return fs.existsSync(path$1.join(modelPath, "Manifest.json"));
367
- if (fs.statSync(modelPath).size <= 0) return false;
368
- return siblingFilesFor(formatEntry).every((f) => isNonEmptyFile(path$1.join(modelsDir, f)));
631
+ return isCompleteCopy(modelsDir, modelPath, formatEntry);
369
632
  }
370
633
  /** Remove the on-disk model file/directory. Returns true if something was deleted. */
371
634
  function deleteModelFromDisk(modelsDir, entry, format) {
@@ -389,11 +652,13 @@ function deleteModelFromDisk(modelsDir, entry, format) {
389
652
  //#endregion
390
653
  //#region src/download/model-download-service.ts
391
654
  /**
392
- * Unified model download service.
655
+ * A catalog-bound face over the shared downloader.
393
656
  *
394
- * Handles downloading model files and extra files (labels, dicts) from a
395
- * catalog of ModelCatalogEntry items. Supports single-file models and
396
- * directory bundles (e.g., .mlpackage for CoreML).
657
+ * Downloading, completeness and the revision contract (D743) are
658
+ * `ensureModel` / `isModelDownloaded`'s — this class only resolves a model id
659
+ * (and a default format) against its catalog and keys progress by model id.
660
+ * It used to carry its own copy of the download loop, which never learnt the
661
+ * revision; one rule now lives in one place.
397
662
  *
398
663
  * Addons use this via `context.models.ensure(modelId, format)`.
399
664
  */
@@ -409,33 +674,16 @@ var ModelDownloadService = class {
409
674
  this.catalog = map;
410
675
  }
411
676
  /**
412
- * Ensure a model (and its extra files) is downloaded.
677
+ * Ensure a model (and its extra files) is downloaded and current.
413
678
  * Returns the local filesystem path to the model file/directory.
414
679
  */
415
680
  async ensure(modelId, format) {
416
681
  const entry = this.catalog.get(modelId);
417
682
  if (!entry) throw new Error(`ModelDownloadService: unknown model "${modelId}"`);
418
683
  const selectedFormat = format ?? this.pickDefaultFormat(entry);
419
- const formatEntry = entry.formats[selectedFormat];
420
- if (!formatEntry) throw new Error(`ModelDownloadService: model "${modelId}" has no ${selectedFormat} format`);
421
- await this.ensureExtraFiles(modelId);
422
- const modelPath = this.modelFilePath(entry, selectedFormat);
423
- const siblings = siblingFilesFor(formatEntry);
424
- if (fs.existsSync(modelPath)) {
425
- if (formatEntry.isDirectory) if (!fs.existsSync(path$1.join(modelPath, "Manifest.json"))) fs.rmSync(modelPath, {
426
- recursive: true,
427
- force: true
428
- });
429
- else return modelPath;
430
- else if (siblings.every((f) => isNonEmptyFile(path$1.join(this.modelsDir, f)))) return modelPath;
431
- }
432
- fs.mkdirSync(this.modelsDir, { recursive: true });
433
- if (formatEntry.isDirectory) await this.downloadDirectory(formatEntry.url, modelPath, formatEntry.files, modelId);
434
- else {
435
- await downloadFile(formatEntry.url, modelPath, this.onProgress ? (downloaded, total) => this.onProgress(modelId, downloaded, total) : void 0);
436
- for (const sibling of siblings) await downloadFile(siblingUrl(formatEntry.url, sibling), path$1.join(this.modelsDir, sibling));
437
- }
438
- return modelPath;
684
+ if (!entry.formats[selectedFormat]) throw new Error(`ModelDownloadService: model "${modelId}" has no ${selectedFormat} format`);
685
+ const onProgress = this.onProgress;
686
+ return ensureModel(this.modelsDir, entry, selectedFormat, onProgress ? (downloaded, total) => onProgress(modelId, downloaded, total ?? 0) : void 0);
439
687
  }
440
688
  /**
441
689
  * Ensure extra files for a model are downloaded.
@@ -458,18 +706,11 @@ var ModelDownloadService = class {
458
706
  getModelsDir() {
459
707
  return this.modelsDir;
460
708
  }
461
- /** Check if a model file is already present on disk. */
709
+ /** True when the model is on disk, complete, and at its revision (D743). */
462
710
  isDownloaded(modelId, format) {
463
711
  const entry = this.catalog.get(modelId);
464
712
  if (!entry) return false;
465
- const selectedFormat = format ?? this.pickDefaultFormat(entry);
466
- const formatEntry = entry.formats[selectedFormat];
467
- if (!formatEntry) return false;
468
- const modelPath = this.modelFilePath(entry, selectedFormat);
469
- if (!fs.existsSync(modelPath)) return false;
470
- if (formatEntry.isDirectory) return fs.existsSync(path$1.join(modelPath, "Manifest.json"));
471
- if (fs.statSync(modelPath).size <= 0) return false;
472
- return siblingFilesFor(formatEntry).every((f) => isNonEmptyFile(path$1.join(this.modelsDir, f)));
713
+ return isModelDownloaded(this.modelsDir, entry, format ?? this.pickDefaultFormat(entry));
473
714
  }
474
715
  /** Get the catalog entry for a model by ID. */
475
716
  getEntry(modelId) {
@@ -487,73 +728,6 @@ var ModelDownloadService = class {
487
728
  if (first) return first;
488
729
  throw new Error(`ModelDownloadService: model "${entry.id}" has no formats`);
489
730
  }
490
- modelFilePath(entry, format) {
491
- const formatEntry = entry.formats[format];
492
- if (!formatEntry) throw new Error(`Model ${entry.id} has no ${format} format`);
493
- const urlParts = formatEntry.url.split("/");
494
- const filename = urlParts[urlParts.length - 1] ?? `${entry.id}.${format}`;
495
- return path$1.join(this.modelsDir, filename);
496
- }
497
- /**
498
- * Download a directory bundle (e.g., .mlpackage) from HuggingFace.
499
- * ATOMIC: downloads to temp dir, renames only on complete success.
500
- */
501
- async downloadDirectory(url, destDir, knownFiles, _modelId) {
502
- const match = url.match(/huggingface\.co\/([^/]+\/[^/]+)\/resolve\/main\/(.+)/);
503
- if (!match) throw new Error(`Cannot parse HuggingFace URL: ${url}`);
504
- const [, repo, dirPath] = match;
505
- let filesToDownload;
506
- if (knownFiles && knownFiles.length > 0) filesToDownload = knownFiles.map((f) => ({
507
- relativePath: f,
508
- fileUrl: `https://huggingface.co/${repo}/resolve/main/${dirPath}/${f}`
509
- }));
510
- else {
511
- const hfFiles = await this.listHfFiles(repo, dirPath);
512
- if (hfFiles.length === 0) throw new Error(`No files found in HuggingFace directory: ${dirPath}`);
513
- filesToDownload = hfFiles.map((f) => ({
514
- relativePath: f.path.substring(dirPath.length + 1),
515
- fileUrl: `https://huggingface.co/${repo}/resolve/main/${f.path}`
516
- }));
517
- }
518
- const tmpDir = destDir + ".downloading";
519
- fs.rmSync(tmpDir, {
520
- recursive: true,
521
- force: true
522
- });
523
- fs.mkdirSync(tmpDir, { recursive: true });
524
- try {
525
- for (const file of filesToDownload) {
526
- const destPath = path$1.join(tmpDir, file.relativePath);
527
- fs.mkdirSync(path$1.dirname(destPath), { recursive: true });
528
- await downloadFile(file.fileUrl, destPath);
529
- }
530
- fs.rmSync(destDir, {
531
- recursive: true,
532
- force: true
533
- });
534
- fs.renameSync(tmpDir, destDir);
535
- } catch (err) {
536
- fs.rmSync(tmpDir, {
537
- recursive: true,
538
- force: true
539
- });
540
- throw err;
541
- }
542
- }
543
- /** Recursively list all files in a HuggingFace directory via API. */
544
- async listHfFiles(repo, dirPath) {
545
- const entries = await fetchJson(`https://huggingface.co/api/models/${repo}/tree/main/${dirPath}`);
546
- const files = [];
547
- for (const entry of entries) if (entry.type === "file") files.push({
548
- path: entry.path,
549
- size: entry.size ?? entry.lfs?.size ?? 0
550
- });
551
- else if (entry.type === "directory") {
552
- const subFiles = await this.listHfFiles(repo, entry.path);
553
- files.push(...subFiles);
554
- }
555
- return files;
556
- }
557
731
  };
558
732
  //#endregion
559
733
  //#region src/http/authenticated-file-server.ts
@@ -1203,4 +1377,4 @@ function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS)
1203
1377
  };
1204
1378
  }
1205
1379
  //#endregion
1206
- export { ensureModel as _, createFileDataPlaneHandler as a, isModelDownloadHttpError as b, parseRangeHeader as c, ModelDownloadService as d, ModelDownloadHttpError as f, downloadModel as g, downloadFile as h, startEventLoopStallMonitor as i, parseTokenizedUrl as l, deleteModelFromDisk as m, STALL_TICK_MS as n, contentTypeFor as o, collectModelFiles as p, attributeStall as r, createAuthenticatedFileServer as s, STALL_REPORT_MS as t, resolveFilePath as u, fetchJson as v, isModelDownloaded as x, getModelFilePath as y };
1380
+ export { resolveFormatRevision as C, isModelDownloaded as S, downloadModel as _, createFileDataPlaneHandler as a, getModelFilePath as b, parseRangeHeader as c, ModelDownloadService as d, ModelDownloadHttpError as f, downloadFile as g, deleteModelFromDisk as h, startEventLoopStallMonitor as i, parseTokenizedUrl as l, collectModelFiles as m, STALL_TICK_MS as n, contentTypeFor as o, adoptModelRevisions as p, attributeStall as r, createAuthenticatedFileServer as s, STALL_REPORT_MS as t, resolveFilePath as u, ensureModel as v, revisionWeightUrl as w, isModelDownloadHttpError as x, fetchJson as y };