@camstack/addon-post-analysis 1.2.108 → 1.2.110

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.
@@ -8291,6 +8291,15 @@ var LabelDefinitionSchema = object({
8291
8291
  description: string().optional(),
8292
8292
  icon: string().optional()
8293
8293
  });
8294
+ var ClassMapDefinitionSchema = object({
8295
+ mapping: record(string(), _enum([
8296
+ "person",
8297
+ "vehicle",
8298
+ "animal",
8299
+ "package"
8300
+ ])),
8301
+ preserveOriginal: boolean()
8302
+ });
8294
8303
  var MODEL_FORMATS = [
8295
8304
  "onnx",
8296
8305
  "coreml",
@@ -8469,7 +8478,13 @@ var ModelCatalogEntrySchema = object({
8469
8478
  * `id` stays the source of truth for resolution/download/persistence; grouping
8470
8479
  * is a presentation overlay resolved back to an `id`.
8471
8480
  */
8472
- group: ModelVariantGroupSchema.optional()
8481
+ group: ModelVariantGroupSchema.optional(),
8482
+ /**
8483
+ * Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
8484
+ * applies (Frigate / COCO public catalog). Set on a custom model whose raw
8485
+ * labels already ARE the CamStack macros (Scrypted identity map).
8486
+ */
8487
+ classMap: ClassMapDefinitionSchema.optional()
8473
8488
  });
8474
8489
  var ConvertTargetSchema = discriminatedUnion("format", [object({
8475
8490
  format: literal("openvino"),
@@ -8498,7 +8513,8 @@ var ModelConvertMetadataSchema = object({
8498
8513
  "ocr",
8499
8514
  "segmentation"
8500
8515
  ]),
8501
- faceAlignment: boolean().optional()
8516
+ faceAlignment: boolean().optional(),
8517
+ classMap: ClassMapDefinitionSchema.optional()
8502
8518
  });
8503
8519
  var ConvertResultSchema = object({
8504
8520
  entry: ModelCatalogEntrySchema,
@@ -18793,6 +18809,33 @@ var NativeCropRefSchema = object({
18793
18809
  h: number()
18794
18810
  })
18795
18811
  });
18812
+ object({
18813
+ crop: object({
18814
+ left: number(),
18815
+ top: number(),
18816
+ width: number().positive(),
18817
+ height: number().positive()
18818
+ }).optional(),
18819
+ content: object({
18820
+ width: number().int().positive(),
18821
+ height: number().int().positive()
18822
+ }),
18823
+ fit: _enum(["stretch", "contain"]),
18824
+ format: _enum([
18825
+ "rgb",
18826
+ "gray",
18827
+ "jpeg"
18828
+ ])
18829
+ });
18830
+ var FrameRefSchema = object({
18831
+ registryId: string().min(1),
18832
+ id: string().min(1),
18833
+ width: number().int().positive(),
18834
+ height: number().int().positive(),
18835
+ format: _enum(["rgb", "gray"]),
18836
+ timestamp: number(),
18837
+ capturedAt: number().optional()
18838
+ });
18796
18839
  var ModelFormatSchema$1 = _enum([
18797
18840
  "onnx",
18798
18841
  "coreml",
@@ -19037,6 +19080,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
19037
19080
  steps: array(PipelineStepInputSchema).min(1),
19038
19081
  frame: FrameInputSchema.optional(),
19039
19082
  /**
19083
+ * Process-local lazy frame. Valid only when caller and provider resolve
19084
+ * in the same execution-group process; split/cross-node callers use
19085
+ * `frame`/`image` inline compatibility instead.
19086
+ */
19087
+ frameRef: FrameRefSchema.optional(),
19088
+ /**
19040
19089
  * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
19041
19090
  * the decoded pixels live in. One more member of the one-of
19042
19091
  * frame/frameHandle/image/imageBase64/referenceImage group.
@@ -19332,7 +19381,10 @@ var NativeCropResultSchema = object({
19332
19381
  * Which source served this crop, so a quality-sensitive consumer (the native
19333
19382
  * `keyFrame`) can reject a degraded fallback:
19334
19383
  * - `native` — cut from the decode worker's retained NATIVE surface (the
19335
- * quality path).
19384
+ * quality path). A subject-tile serve is also native-resolution and stays
19385
+ * `native` here: the public enum cannot name `tile` without a breaking cap
19386
+ * change. Runner telemetry distinguishes lease vs tile via `source` on the
19387
+ * internal crop result (`nativeHits` vs `tileHits`).
19336
19388
  * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
19337
19389
  * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
19338
19390
  * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
@@ -19823,12 +19875,41 @@ var RunnerLocalLoadSchema = object({
19823
19875
  * legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
19824
19876
  * working unchanged when they switch to reading from the runner cap.
19825
19877
  */
19878
+ var FrameLazyCountersSchema = object({
19879
+ framesDecoded: number(),
19880
+ framesAdmitted: number(),
19881
+ framesDroppedPixelFree: number(),
19882
+ viewsMaterialized: number(),
19883
+ viewsSkipped: number(),
19884
+ workerToRunnerBytes: number(),
19885
+ runnerToPoolRawBytes: number(),
19886
+ runnerToPoolJpegBytes: number(),
19887
+ onDemandFullFrameRequests: number(),
19888
+ onDemandCropRequests: number(),
19889
+ nativeHits: number(),
19890
+ nativeMisses: number(),
19891
+ tileHits: number(),
19892
+ tileMisses: number(),
19893
+ fallbackHits: number(),
19894
+ fallbackMisses: number(),
19895
+ retainedWritesAvoided: number(),
19896
+ residentRefs: number(),
19897
+ residentBytes: number(),
19898
+ releases: number(),
19899
+ evictions: number(),
19900
+ staleMisses: number()
19901
+ });
19902
+ var FrameLazyMetricsSchema = object({
19903
+ node: FrameLazyCountersSchema,
19904
+ cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
19905
+ });
19826
19906
  var RunnerLocalMetricsSchema = object({
19827
19907
  nodeId: string(),
19828
19908
  activeCameras: number(),
19829
19909
  throttledCameras: number(),
19830
19910
  avgInferenceTimeMs: number(),
19831
- queueDepth: number()
19911
+ queueDepth: number(),
19912
+ frameLazy: FrameLazyMetricsSchema.optional()
19832
19913
  });
19833
19914
  method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
19834
19915
  handle: FrameHandleSchema,
@@ -21158,6 +21239,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
21158
21239
  location: StorageLocationSchema,
21159
21240
  relativePath: string()
21160
21241
  }), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number().nullable()), method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }), method(WriteChunkInputSchema, _void(), { kind: "mutation" }), method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }), method(AbortUploadInputSchema, _void(), { kind: "mutation" }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }), method(ReadChunkInputSchema, _instanceof(Uint8Array)), method(EndDownloadInputSchema, _void(), { kind: "mutation" });
21242
+ /** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
21243
+ var ProfileSettingsSchemaBridge = unknown().nullable();
21244
+ var ProfileSettingsBagSchema = record(string(), unknown());
21161
21245
  /**
21162
21246
  * A live terminal session hosted by the provider addon. Output and input do
21163
21247
  * NOT flow through the capability — they use the addon data plane
@@ -21187,7 +21271,14 @@ var TerminalSessionInfoSchema = object({
21187
21271
  var TerminalProfileInfoSchema = object({
21188
21272
  profileId: string(),
21189
21273
  label: string(),
21190
- description: string().optional()
21274
+ description: string().optional(),
21275
+ /** Spawn defaults the instance form copies on create. */
21276
+ executable: string().optional(),
21277
+ args: array(string()).readonly().optional(),
21278
+ cwd: string().optional(),
21279
+ environment: array(string()).readonly().optional(),
21280
+ /** ConfigUISchema for instance knobs, or null when the profile has none. */
21281
+ settingsSchema: ProfileSettingsSchemaBridge.optional()
21191
21282
  });
21192
21283
  /**
21193
21284
  * A durable operator-created Terminal instance. Profiles are templates; only
@@ -21200,7 +21291,12 @@ var TerminalInstanceInfoSchema = object({
21200
21291
  profileId: string(),
21201
21292
  profileLabel: string(),
21202
21293
  name: string(),
21203
- enabled: boolean()
21294
+ enabled: boolean(),
21295
+ executable: string(),
21296
+ args: array(string()).readonly(),
21297
+ cwd: string(),
21298
+ environment: array(string()).readonly(),
21299
+ profileSettings: ProfileSettingsBagSchema
21204
21300
  });
21205
21301
  var TerminalLegacyCameraSchema = object({
21206
21302
  stableId: string(),
@@ -21230,7 +21326,23 @@ var TerminalOutputBatchSchema = object({
21230
21326
  method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
21231
21327
  targetNodeId: string().min(1),
21232
21328
  profileId: string().min(1),
21233
- name: string().trim().min(1).max(160).optional()
21329
+ name: string().trim().min(1).max(160).optional(),
21330
+ executable: string().max(1024).optional(),
21331
+ args: array(string().max(2048)).max(64).optional(),
21332
+ cwd: string().max(1024).optional(),
21333
+ environment: array(string().max(4096)).max(64).optional(),
21334
+ profileSettings: ProfileSettingsBagSchema.optional()
21335
+ }), TerminalInstanceInfoSchema, {
21336
+ kind: "mutation",
21337
+ auth: "admin"
21338
+ }), method(object({
21339
+ instanceId: string().min(1),
21340
+ name: string().trim().min(1).max(160).optional(),
21341
+ executable: string().max(1024).optional(),
21342
+ args: array(string().max(2048)).max(64).optional(),
21343
+ cwd: string().max(1024).optional(),
21344
+ environment: array(string().max(4096)).max(64).optional(),
21345
+ profileSettings: ProfileSettingsBagSchema.optional()
21234
21346
  }), TerminalInstanceInfoSchema, {
21235
21347
  kind: "mutation",
21236
21348
  auth: "admin"
@@ -21252,7 +21364,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
21252
21364
  }), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
21253
21365
  profileId: string(),
21254
21366
  cols: number().int().positive(),
21255
- rows: number().int().positive()
21367
+ rows: number().int().positive(),
21368
+ executable: string().max(1024).optional(),
21369
+ args: array(string().max(2048)).max(64).optional(),
21370
+ cwd: string().max(1024).optional(),
21371
+ environment: array(string().max(4096)).max(64).optional()
21256
21372
  }), TerminalSessionInfoSchema, {
21257
21373
  kind: "mutation",
21258
21374
  auth: "admin"
@@ -25167,10 +25283,10 @@ var lawnMowerControlCapability = {
25167
25283
  *
25168
25284
  * • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
25169
25285
  * to receive an ordered list of candidate base URLs it should race
25170
- * on connect — LAN IPv4 first (lowest latency when on same network),
25171
- * then public hostname (if a tunnel is up), then IPv6. The SDK can
25172
- * race them with short timeouts and stick with the winner for the
25173
- * session.
25286
+ * on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
25287
+ * when on the same network), then public hostname (if a tunnel is
25288
+ * up). The SDK can race them with short timeouts and stick with the
25289
+ * winner for the session.
25174
25290
  *
25175
25291
  * Why hub-only: agents are not directly addressable by the operator's
25176
25292
  * clients — they reverse-connect to the hub. Exposing their interfaces
@@ -25325,6 +25441,17 @@ var NotificationEndpointSchema = object({
25325
25441
  /** What the ranking currently resolves to (null when nothing is reachable). */
25326
25442
  resolved: string().nullable()
25327
25443
  });
25444
+ /**
25445
+ * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
25446
+ * AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
25447
+ * currently expands to, so the UI can show the effective set either way.
25448
+ */
25449
+ var ViewerEndpointsSchema = object({
25450
+ /** The operator's explicit race set, or empty for AUTO. */
25451
+ baseUrls: array(string()).readonly(),
25452
+ /** What the ranking currently resolves to (may be empty if nothing is up). */
25453
+ resolved: array(string()).readonly()
25454
+ });
25328
25455
  var AllowedAddressesSchema = object({
25329
25456
  /**
25330
25457
  * Allowlist of interface addresses operators have explicitly opted
@@ -25333,6 +25460,20 @@ var AllowedAddressesSchema = object({
25333
25460
  * Network Addresses admin page and persisted by the addon.
25334
25461
  */
25335
25462
  addresses: array(string()).readonly() });
25463
+ var TlsStatusSchema = object({
25464
+ mode: _enum([
25465
+ "generated",
25466
+ "uploaded",
25467
+ "disabled"
25468
+ ]),
25469
+ leafFingerprintSha256: string().nullable(),
25470
+ caFingerprintSha256: string().nullable(),
25471
+ validTo: string().nullable(),
25472
+ sans: array(string()),
25473
+ caCertPem: string().nullable(),
25474
+ reissueError: string().nullable(),
25475
+ restartRequired: boolean()
25476
+ });
25336
25477
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
25337
25478
  /**
25338
25479
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -25342,17 +25483,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
25342
25483
  */
25343
25484
  port: number().int().min(1).max(65535).optional(),
25344
25485
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
25345
- * candidate. Default `true`. */
25486
+ * candidate. Default `false` — loopback is not a client route. */
25346
25487
  includeLoopback: boolean().optional(),
25347
- /** Skip IPv6 entries. Some legacy clients can't parse them.
25348
- * Default `false`. */
25488
+ /** Skip IPv6 entries. Default `false` the palette includes stable
25489
+ * LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
25490
+ * hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
25349
25491
  ipv4Only: boolean().optional(),
25350
25492
  /** Scheme to emit for LAN/loopback URLs. Default `'http'`.
25351
25493
  * Pass `'https'` when the caller is itself loaded over HTTPS
25352
25494
  * to avoid mixed-content blocks in the browser. The public
25353
25495
  * tunnel always emits `https://` regardless. */
25354
25496
  scheme: _enum(["http", "https"]).optional()
25355
- }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
25497
+ }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
25498
+ kind: "mutation",
25499
+ auth: "admin"
25500
+ }), method(object({
25501
+ certPem: string().min(1),
25502
+ keyPem: string().min(1),
25503
+ caPem: string().optional()
25504
+ }), TlsStatusSchema, {
25505
+ kind: "mutation",
25506
+ auth: "admin"
25507
+ }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
25508
+ kind: "mutation",
25509
+ auth: "admin"
25510
+ });
25356
25511
  var LockControlStatusSchema = object({
25357
25512
  /** Lifecycle state of the lock. `jammed` means the motor reported
25358
25513
  * failure to reach the target — operator intervention required. */
@@ -33490,6 +33645,12 @@ Object.freeze({
33490
33645
  addonId: null,
33491
33646
  access: "create"
33492
33647
  },
33648
+ "localNetwork.downloadCa": {
33649
+ capName: "local-network",
33650
+ capScope: "system",
33651
+ addonId: null,
33652
+ access: "view"
33653
+ },
33493
33654
  "localNetwork.getAllowedAddresses": {
33494
33655
  capName: "local-network",
33495
33656
  capScope: "system",
@@ -33514,18 +33675,42 @@ Object.freeze({
33514
33675
  addonId: null,
33515
33676
  access: "view"
33516
33677
  },
33678
+ "localNetwork.getTlsStatus": {
33679
+ capName: "local-network",
33680
+ capScope: "system",
33681
+ addonId: null,
33682
+ access: "view"
33683
+ },
33684
+ "localNetwork.getViewerEndpoints": {
33685
+ capName: "local-network",
33686
+ capScope: "system",
33687
+ addonId: null,
33688
+ access: "view"
33689
+ },
33517
33690
  "localNetwork.list": {
33518
33691
  capName: "local-network",
33519
33692
  capScope: "system",
33520
33693
  addonId: null,
33521
33694
  access: "view"
33522
33695
  },
33696
+ "localNetwork.regenerateCertificate": {
33697
+ capName: "local-network",
33698
+ capScope: "system",
33699
+ addonId: null,
33700
+ access: "create"
33701
+ },
33523
33702
  "localNetwork.resetAllowlistToBestMatch": {
33524
33703
  capName: "local-network",
33525
33704
  capScope: "system",
33526
33705
  addonId: null,
33527
33706
  access: "delete"
33528
33707
  },
33708
+ "localNetwork.revertToGeneratedCertificate": {
33709
+ capName: "local-network",
33710
+ capScope: "system",
33711
+ addonId: null,
33712
+ access: "create"
33713
+ },
33529
33714
  "localNetwork.setAllowedAddresses": {
33530
33715
  capName: "local-network",
33531
33716
  capScope: "system",
@@ -33538,6 +33723,18 @@ Object.freeze({
33538
33723
  addonId: null,
33539
33724
  access: "create"
33540
33725
  },
33726
+ "localNetwork.setViewerEndpoints": {
33727
+ capName: "local-network",
33728
+ capScope: "system",
33729
+ addonId: null,
33730
+ access: "create"
33731
+ },
33732
+ "localNetwork.uploadCertificate": {
33733
+ capName: "local-network",
33734
+ capScope: "system",
33735
+ addonId: null,
33736
+ access: "create"
33737
+ },
33541
33738
  "lockControl.lock": {
33542
33739
  capName: "lock-control",
33543
33740
  capScope: "device",
@@ -36418,6 +36615,12 @@ Object.freeze({
36418
36615
  addonId: null,
36419
36616
  access: "create"
36420
36617
  },
36618
+ "terminalSession.updateInstance": {
36619
+ capName: "terminal-session",
36620
+ capScope: "system",
36621
+ addonId: null,
36622
+ access: "create"
36623
+ },
36421
36624
  "terminalSession.writeInput": {
36422
36625
  capName: "terminal-session",
36423
36626
  capScope: "system",
@@ -39169,6 +39372,35 @@ function pickClusterStepModels(view) {
39169
39372
  }
39170
39373
  return readClusterStepModels(flat);
39171
39374
  }
39375
+ var CLUSTER_STEP_SETTING_FIELDS = [{
39376
+ stepId: "face-embedding",
39377
+ key: "minLandmarkFaceSize",
39378
+ label: "Min face size for recognition (detection px)",
39379
+ description: "Refuse to embed a face smaller than this IN THE DETECTION FRAME. Applies to every node — the enrolled gallery is one index, and two admission floors would fill it from two policies. 0 disables the gate.",
39380
+ type: "slider",
39381
+ min: 0,
39382
+ max: 64,
39383
+ step: 2,
39384
+ default: 24
39385
+ }];
39386
+ function clusterStepSettingKey(stepId, fieldKey) {
39387
+ return `clusterStepSetting:${stepId}:${fieldKey}`;
39388
+ }
39389
+ var ClusterSettingNumberSchema = number().finite();
39390
+ function readClusterStepSettings(config) {
39391
+ const out = {};
39392
+ for (const field of CLUSTER_STEP_SETTING_FIELDS) {
39393
+ const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
39394
+ const value = parsed.success ? parsed.data : field.default;
39395
+ const existing = out[field.stepId] ?? {};
39396
+ out[field.stepId] = {
39397
+ ...existing,
39398
+ [field.key]: value
39399
+ };
39400
+ }
39401
+ return out;
39402
+ }
39403
+ readClusterStepSettings({});
39172
39404
  object({
39173
39405
  /**
39174
39406
  * Fraction of the box's own size added on EACH side before cutting.
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BV5xFo8f.js");
5
+ const require_dist = require("../dist-RzJseCJP.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_fs$1 = require_dist.__toESM(node_fs, 1);
8
8
  node_fs = require_dist.__toESM(node_fs);
@@ -60,7 +60,7 @@ function readViaPs(pid) {
60
60
  });
61
61
  }
62
62
  //#endregion
63
- //#region ../system/dist/file-data-plane-CuE_hBli.mjs
63
+ //#region ../system/dist/file-data-plane-BhKdxJgf.mjs
64
64
  function isNonEmptyFile(filePath) {
65
65
  return node_fs$1.existsSync(filePath) && node_fs$1.statSync(filePath).size > 0;
66
66
  }
@@ -86,21 +86,56 @@ function buildHeaders(url) {
86
86
  if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
87
87
  return headers;
88
88
  }
89
- /**
90
- * Download a single file from a URL to a destination path.
91
- * Uses native fetch() (Node 22+) which handles redirects natively.
92
- * Streams to disk with optional progress callback.
93
- * Returns the destination path. Skips download if file already exists.
94
- */
95
- async function downloadFile(url, destPath, onProgress) {
89
+ var DEFAULT_MAX_REDIRECTS = 5;
90
+ function normalizeDownloadOptions(third) {
91
+ if (typeof third === "function") return { onProgress: third };
92
+ return third ?? {};
93
+ }
94
+ function isRedirectStatus(status) {
95
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
96
+ }
97
+ function resolveRedirectUrl(current, location) {
98
+ return new URL(location, current);
99
+ }
100
+ async function downloadFile(url, destPath, onProgressOrOptions) {
96
101
  if (node_fs$1.existsSync(destPath)) return destPath;
102
+ const opts = normalizeDownloadOptions(onProgressOrOptions);
103
+ const fetchImpl = opts.fetchImpl ?? fetch;
104
+ const maxRedirects = opts.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
97
105
  node_fs$1.mkdirSync(node_path$1.dirname(destPath), { recursive: true });
98
106
  const tmpPath = destPath + ".downloading";
99
107
  try {
100
- const response = await fetch(url, {
101
- redirect: "follow",
102
- headers: buildHeaders(url)
103
- });
108
+ let current = url;
109
+ const seen = /* @__PURE__ */ new Set();
110
+ let response;
111
+ const manual = opts.redirectPolicy !== void 0;
112
+ for (let hop = 0; hop <= maxRedirects; hop++) {
113
+ const parsed = new URL(current);
114
+ if (opts.redirectPolicy) opts.redirectPolicy(parsed, hop);
115
+ if (seen.has(parsed.href)) throw new Error(`Redirect loop detected at ${parsed.href}`);
116
+ seen.add(parsed.href);
117
+ const controller = opts.timeoutMs !== void 0 ? new AbortController() : void 0;
118
+ const timer = controller && opts.timeoutMs !== void 0 ? setTimeout(() => controller.abort(), opts.timeoutMs) : void 0;
119
+ try {
120
+ response = await fetchImpl(current, {
121
+ redirect: manual ? "manual" : "follow",
122
+ headers: buildHeaders(current),
123
+ ...controller ? { signal: controller.signal } : {}
124
+ });
125
+ } finally {
126
+ if (timer) clearTimeout(timer);
127
+ }
128
+ if (manual && isRedirectStatus(response.status)) {
129
+ const location = response.headers.get("location");
130
+ if (!location) throw new Error(`Redirect ${response.status} missing Location header`);
131
+ if (hop >= maxRedirects) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
132
+ current = resolveRedirectUrl(current, location).href;
133
+ continue;
134
+ }
135
+ break;
136
+ }
137
+ if (!response) throw new Error(`No response downloading ${url}`);
138
+ if (manual && isRedirectStatus(response.status)) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
104
139
  if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
105
140
  if (!response.body) throw new Error(`No response body from ${url}`);
106
141
  const total = parseInt(response.headers.get("content-length") ?? "0", 10);
@@ -111,9 +146,10 @@ async function downloadFile(url, destPath, onProgress) {
111
146
  for (;;) {
112
147
  const { done, value } = await reader.read();
113
148
  if (done || !value) break;
114
- fileStream.write(value);
115
149
  downloaded += value.length;
116
- onProgress?.(downloaded, total);
150
+ if (opts.maxBytes !== void 0 && downloaded > opts.maxBytes) throw new Error(`Downloaded ${downloaded} bytes exceeds the ${opts.maxBytes}-byte limit`);
151
+ fileStream.write(value);
152
+ opts.onProgress?.(downloaded, total);
117
153
  }
118
154
  } finally {
119
155
  fileStream.end();
@@ -1,4 +1,4 @@
1
- import { A as PoolMemoryWatchdog, St as BaseAddon, ct as parseProcStatus, et as embeddingEncoderCapability, mt as resolvePoolMemoryPolicy, rt as hfModelUrl } from "../dist-DFNQ5VB_.mjs";
1
+ import { A as PoolMemoryWatchdog, St as BaseAddon, ct as parseProcStatus, et as embeddingEncoderCapability, mt as resolvePoolMemoryPolicy, rt as hfModelUrl } from "../dist-BtpxOtGv.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import "node:fs";
@@ -68,7 +68,7 @@ function readViaPs(pid) {
68
68
  });
69
69
  }
70
70
  //#endregion
71
- //#region ../system/dist/file-data-plane-CuE_hBli.mjs
71
+ //#region ../system/dist/file-data-plane-BhKdxJgf.mjs
72
72
  function isNonEmptyFile(filePath) {
73
73
  return fs.existsSync(filePath) && fs.statSync(filePath).size > 0;
74
74
  }
@@ -94,21 +94,56 @@ function buildHeaders(url) {
94
94
  if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
95
95
  return headers;
96
96
  }
97
- /**
98
- * Download a single file from a URL to a destination path.
99
- * Uses native fetch() (Node 22+) which handles redirects natively.
100
- * Streams to disk with optional progress callback.
101
- * Returns the destination path. Skips download if file already exists.
102
- */
103
- async function downloadFile(url, destPath, onProgress) {
97
+ var DEFAULT_MAX_REDIRECTS = 5;
98
+ function normalizeDownloadOptions(third) {
99
+ if (typeof third === "function") return { onProgress: third };
100
+ return third ?? {};
101
+ }
102
+ function isRedirectStatus(status) {
103
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
104
+ }
105
+ function resolveRedirectUrl(current, location) {
106
+ return new URL(location, current);
107
+ }
108
+ async function downloadFile(url, destPath, onProgressOrOptions) {
104
109
  if (fs.existsSync(destPath)) return destPath;
110
+ const opts = normalizeDownloadOptions(onProgressOrOptions);
111
+ const fetchImpl = opts.fetchImpl ?? fetch;
112
+ const maxRedirects = opts.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
105
113
  fs.mkdirSync(path$1.dirname(destPath), { recursive: true });
106
114
  const tmpPath = destPath + ".downloading";
107
115
  try {
108
- const response = await fetch(url, {
109
- redirect: "follow",
110
- headers: buildHeaders(url)
111
- });
116
+ let current = url;
117
+ const seen = /* @__PURE__ */ new Set();
118
+ let response;
119
+ const manual = opts.redirectPolicy !== void 0;
120
+ for (let hop = 0; hop <= maxRedirects; hop++) {
121
+ const parsed = new URL(current);
122
+ if (opts.redirectPolicy) opts.redirectPolicy(parsed, hop);
123
+ if (seen.has(parsed.href)) throw new Error(`Redirect loop detected at ${parsed.href}`);
124
+ seen.add(parsed.href);
125
+ const controller = opts.timeoutMs !== void 0 ? new AbortController() : void 0;
126
+ const timer = controller && opts.timeoutMs !== void 0 ? setTimeout(() => controller.abort(), opts.timeoutMs) : void 0;
127
+ try {
128
+ response = await fetchImpl(current, {
129
+ redirect: manual ? "manual" : "follow",
130
+ headers: buildHeaders(current),
131
+ ...controller ? { signal: controller.signal } : {}
132
+ });
133
+ } finally {
134
+ if (timer) clearTimeout(timer);
135
+ }
136
+ if (manual && isRedirectStatus(response.status)) {
137
+ const location = response.headers.get("location");
138
+ if (!location) throw new Error(`Redirect ${response.status} missing Location header`);
139
+ if (hop >= maxRedirects) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
140
+ current = resolveRedirectUrl(current, location).href;
141
+ continue;
142
+ }
143
+ break;
144
+ }
145
+ if (!response) throw new Error(`No response downloading ${url}`);
146
+ if (manual && isRedirectStatus(response.status)) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
112
147
  if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
113
148
  if (!response.body) throw new Error(`No response body from ${url}`);
114
149
  const total = parseInt(response.headers.get("content-length") ?? "0", 10);
@@ -119,9 +154,10 @@ async function downloadFile(url, destPath, onProgress) {
119
154
  for (;;) {
120
155
  const { done, value } = await reader.read();
121
156
  if (done || !value) break;
122
- fileStream.write(value);
123
157
  downloaded += value.length;
124
- onProgress?.(downloaded, total);
158
+ if (opts.maxBytes !== void 0 && downloaded > opts.maxBytes) throw new Error(`Downloaded ${downloaded} bytes exceeds the ${opts.maxBytes}-byte limit`);
159
+ fileStream.write(value);
160
+ opts.onProgress?.(downloaded, total);
125
161
  }
126
162
  } finally {
127
163
  fileStream.end();
@@ -1,6 +1,6 @@
1
1
  import { a as e, i as t, n, o as r, r as i, t as a } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.js-C0AuF9av.mjs";
2
2
  import { t as o } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-B3Wx5J80.mjs";
3
- import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-bze3q2fV.mjs";
3
+ import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-m0Jqwk3C.mjs";
4
4
  import { n as m, r as h, t as g } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-Bm-iyjmq.mjs";
5
5
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
6
6
  var _ = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), v = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), y = (e) => {
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.2.28",
6
+ version: "1.2.30",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_pipeline_analytics_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.96",
21
+ version: "1.2.99",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_analytics_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.66",
36
+ version: "1.2.68",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_pipeline_analytics_widgets",