@camstack/addon-ai 0.4.16 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +417 -43
- package/dist/addon.mjs +417 -43
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -5964,6 +5964,13 @@ var BaseAddon = class {
|
|
|
5964
5964
|
_readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
5965
5965
|
/** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
|
|
5966
5966
|
_registeredCapNames = [];
|
|
5967
|
+
/**
|
|
5968
|
+
* True only after `readAddonStore` actually answered. Constructor
|
|
5969
|
+
* defaults look like stored config when the store is down — a forked
|
|
5970
|
+
* addon that auto-starts from those defaults (cloudflare-tunnel quick
|
|
5971
|
+
* mode, 2026-08-25) is not "the operator chose this".
|
|
5972
|
+
*/
|
|
5973
|
+
settingsStoreReady = false;
|
|
5967
5974
|
/** Default config values. Provided via constructor. */
|
|
5968
5975
|
defaults;
|
|
5969
5976
|
constructor(defaults) {
|
|
@@ -6364,7 +6371,9 @@ var BaseAddon = class {
|
|
|
6364
6371
|
];
|
|
6365
6372
|
let lastErr;
|
|
6366
6373
|
for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
|
|
6367
|
-
|
|
6374
|
+
const stored = await settings.readAddonStore() ?? {};
|
|
6375
|
+
this.settingsStoreReady = true;
|
|
6376
|
+
return stored;
|
|
6368
6377
|
} catch (err) {
|
|
6369
6378
|
lastErr = err;
|
|
6370
6379
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6372,6 +6381,7 @@ var BaseAddon = class {
|
|
|
6372
6381
|
if (attempt === delaysMs.length) break;
|
|
6373
6382
|
await new Promise((r) => setTimeout(r, delaysMs[attempt]));
|
|
6374
6383
|
}
|
|
6384
|
+
this.settingsStoreReady = false;
|
|
6375
6385
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
6376
6386
|
return {};
|
|
6377
6387
|
}
|
|
@@ -8226,6 +8236,15 @@ var LabelDefinitionSchema = object({
|
|
|
8226
8236
|
description: string().optional(),
|
|
8227
8237
|
icon: string().optional()
|
|
8228
8238
|
});
|
|
8239
|
+
var ClassMapDefinitionSchema = object({
|
|
8240
|
+
mapping: record(string(), _enum([
|
|
8241
|
+
"person",
|
|
8242
|
+
"vehicle",
|
|
8243
|
+
"animal",
|
|
8244
|
+
"package"
|
|
8245
|
+
])),
|
|
8246
|
+
preserveOriginal: boolean()
|
|
8247
|
+
});
|
|
8229
8248
|
var MODEL_FORMATS = [
|
|
8230
8249
|
"onnx",
|
|
8231
8250
|
"coreml",
|
|
@@ -8309,6 +8328,12 @@ var ModelVariantGroupSchema = object({
|
|
|
8309
8328
|
*/
|
|
8310
8329
|
resolution: number$1().int().positive().optional()
|
|
8311
8330
|
});
|
|
8331
|
+
var ModelProviderIdSchema = _enum([
|
|
8332
|
+
"camstack",
|
|
8333
|
+
"frigate",
|
|
8334
|
+
"scrypted",
|
|
8335
|
+
"custom"
|
|
8336
|
+
]);
|
|
8312
8337
|
var ModelCatalogEntrySchema = object({
|
|
8313
8338
|
id: string(),
|
|
8314
8339
|
name: string(),
|
|
@@ -8404,7 +8429,19 @@ var ModelCatalogEntrySchema = object({
|
|
|
8404
8429
|
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
8405
8430
|
* is a presentation overlay resolved back to an `id`.
|
|
8406
8431
|
*/
|
|
8407
|
-
group: ModelVariantGroupSchema.optional()
|
|
8432
|
+
group: ModelVariantGroupSchema.optional(),
|
|
8433
|
+
/**
|
|
8434
|
+
* Catalog source for the pipeline stepper's provider-first picker. Absent on
|
|
8435
|
+
* built-in CamStack entries (treated as `camstack`) and on registry rows
|
|
8436
|
+
* persisted before this field existed (`inferModelProvider` fills those).
|
|
8437
|
+
*/
|
|
8438
|
+
provider: ModelProviderIdSchema.optional(),
|
|
8439
|
+
/**
|
|
8440
|
+
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8441
|
+
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8442
|
+
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
8443
|
+
*/
|
|
8444
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8408
8445
|
});
|
|
8409
8446
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
8410
8447
|
format: literal("openvino"),
|
|
@@ -8433,7 +8470,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
8433
8470
|
"ocr",
|
|
8434
8471
|
"segmentation"
|
|
8435
8472
|
]),
|
|
8436
|
-
faceAlignment: boolean().optional()
|
|
8473
|
+
faceAlignment: boolean().optional(),
|
|
8474
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8437
8475
|
});
|
|
8438
8476
|
var ConvertResultSchema = object({
|
|
8439
8477
|
entry: ModelCatalogEntrySchema,
|
|
@@ -14653,12 +14691,15 @@ var NcOccupancyConditionSchema = object({
|
|
|
14653
14691
|
* there is no second switch that can disagree with the first and every rule
|
|
14654
14692
|
* authored before the decision migrates for free (`audioModeOf`):
|
|
14655
14693
|
*
|
|
14656
|
-
* - **LABEL mode — `labels` present.** The rule fires
|
|
14657
|
-
*
|
|
14658
|
-
* `hitPercent` and `samplingSeconds` are ignored
|
|
14659
|
-
*
|
|
14660
|
-
*
|
|
14661
|
-
*
|
|
14694
|
+
* - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
|
|
14695
|
+
* labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
|
|
14696
|
+
* `hitPercent` and `samplingSeconds` are still ignored — a percentage of
|
|
14697
|
+
* frames is the wrong question for a classifier that labels 1–3 frames
|
|
14698
|
+
* per episode. The count window is the brake that drops a single-frame
|
|
14699
|
+
* false positive; the rule's own `throttle` cooldown is the other. The
|
|
14700
|
+
* per-label confidence floor is the analyzer's (`classificationMinScore`,
|
|
14701
|
+
* per device) — a label only reaches this condition if the classifier was
|
|
14702
|
+
* already confident enough. `confirmHits: 1` restores first-frame fire.
|
|
14662
14703
|
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14663
14704
|
* the condition: at least `hitPercent`% of the samples over
|
|
14664
14705
|
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
@@ -14685,14 +14726,22 @@ var NcOccupancyConditionSchema = object({
|
|
|
14685
14726
|
* an operator who typed `dog` mean the same thing.
|
|
14686
14727
|
*/
|
|
14687
14728
|
var NcAudioConditionSchema = object({
|
|
14688
|
-
/** LABEL MODE: audio macro labels. Present ⇒
|
|
14729
|
+
/** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
|
|
14689
14730
|
labels: array(string().min(1)).min(1).optional(),
|
|
14690
14731
|
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14691
14732
|
dbThreshold: number$1().min(-96).max(0).optional(),
|
|
14692
14733
|
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14693
14734
|
hitPercent: number$1().int().min(1).max(100).default(60),
|
|
14694
14735
|
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14695
|
-
samplingSeconds: number$1().int().min(1).max(300).default(10)
|
|
14736
|
+
samplingSeconds: number$1().int().min(1).max(300).default(10),
|
|
14737
|
+
/**
|
|
14738
|
+
* LABEL MODE: how many labelled frames must land inside
|
|
14739
|
+
* {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
|
|
14740
|
+
* Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
|
|
14741
|
+
*/
|
|
14742
|
+
confirmHits: number$1().int().min(1).max(20).optional(),
|
|
14743
|
+
/** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
|
|
14744
|
+
confirmWindowSec: number$1().int().min(1).max(60).optional()
|
|
14696
14745
|
});
|
|
14697
14746
|
/**
|
|
14698
14747
|
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
@@ -17066,6 +17115,46 @@ var RecentTracksPageSchema = object({
|
|
|
17066
17115
|
/** Cursor for the next page, or null when this page is the last. */
|
|
17067
17116
|
nextCursor: string().nullable()
|
|
17068
17117
|
});
|
|
17118
|
+
var LIST_GROUPS_DEFAULT_LIMIT = 40;
|
|
17119
|
+
var LIST_GROUPS_MAX_LIMIT = 100;
|
|
17120
|
+
var AnalyticsGroupRecordSchema = object({
|
|
17121
|
+
id: string(),
|
|
17122
|
+
deviceId: number$1().int(),
|
|
17123
|
+
openedAt: number$1().int(),
|
|
17124
|
+
closedAt: number$1().int(),
|
|
17125
|
+
timestamp: number$1().int(),
|
|
17126
|
+
memberCount: number$1().int(),
|
|
17127
|
+
memberTrackIds: array(string()).readonly(),
|
|
17128
|
+
className: string(),
|
|
17129
|
+
classes: array(string()).readonly(),
|
|
17130
|
+
/** Relative event-media path, or null when the group has no picture yet. */
|
|
17131
|
+
mediaUrl: string().nullable(),
|
|
17132
|
+
singleton: boolean()
|
|
17133
|
+
});
|
|
17134
|
+
var AnalyticsGroupMemberSchema = object({
|
|
17135
|
+
trackId: string(),
|
|
17136
|
+
deviceId: number$1().int(),
|
|
17137
|
+
className: string(),
|
|
17138
|
+
firstSeen: number$1().int(),
|
|
17139
|
+
lastSeen: number$1().int(),
|
|
17140
|
+
mediaUrl: string().nullable()
|
|
17141
|
+
});
|
|
17142
|
+
var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
|
|
17143
|
+
var ListGroupsQueryInput = object({
|
|
17144
|
+
/** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
|
|
17145
|
+
deviceIds: array(number$1()),
|
|
17146
|
+
/** Window lower bound on `closedAt` (inclusive). */
|
|
17147
|
+
since: number$1().optional(),
|
|
17148
|
+
/** Window upper bound on `openedAt` (inclusive). */
|
|
17149
|
+
until: number$1().optional(),
|
|
17150
|
+
limit: number$1().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
|
|
17151
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
17152
|
+
cursor: string().optional()
|
|
17153
|
+
});
|
|
17154
|
+
var ListGroupsPageSchema = object({
|
|
17155
|
+
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
17156
|
+
nextCursor: string().nullable()
|
|
17157
|
+
});
|
|
17069
17158
|
var KeyEventQueryInput = object({
|
|
17070
17159
|
deviceId: number$1(),
|
|
17071
17160
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -17141,7 +17230,9 @@ var TrackCascadeCountsSchema = object({
|
|
|
17141
17230
|
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
17142
17231
|
plates: number$1().int(),
|
|
17143
17232
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17144
|
-
embeddings: number$1().int()
|
|
17233
|
+
embeddings: number$1().int(),
|
|
17234
|
+
/** Group membership + group rows removed with their last member (best-effort). */
|
|
17235
|
+
groups: number$1().int()
|
|
17145
17236
|
});
|
|
17146
17237
|
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17147
17238
|
var DiskReconcileCountsSchema = object({
|
|
@@ -17287,7 +17378,10 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
|
|
|
17287
17378
|
* stationary registry). Default false: the timeline lists passages,
|
|
17288
17379
|
* not parking records (operator decision, 2026-08-15). */
|
|
17289
17380
|
includeStationary: boolean().optional()
|
|
17290
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(
|
|
17381
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
|
|
17382
|
+
deviceId: number$1(),
|
|
17383
|
+
groupId: string().min(1)
|
|
17384
|
+
}), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
|
|
17291
17385
|
kind: "mutation",
|
|
17292
17386
|
auth: "admin"
|
|
17293
17387
|
}), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number$1() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
|
|
@@ -17505,6 +17599,33 @@ var NativeCropRefSchema = object({
|
|
|
17505
17599
|
h: number$1()
|
|
17506
17600
|
})
|
|
17507
17601
|
});
|
|
17602
|
+
object({
|
|
17603
|
+
crop: object({
|
|
17604
|
+
left: number$1(),
|
|
17605
|
+
top: number$1(),
|
|
17606
|
+
width: number$1().positive(),
|
|
17607
|
+
height: number$1().positive()
|
|
17608
|
+
}).optional(),
|
|
17609
|
+
content: object({
|
|
17610
|
+
width: number$1().int().positive(),
|
|
17611
|
+
height: number$1().int().positive()
|
|
17612
|
+
}),
|
|
17613
|
+
fit: _enum(["stretch", "contain"]),
|
|
17614
|
+
format: _enum([
|
|
17615
|
+
"rgb",
|
|
17616
|
+
"gray",
|
|
17617
|
+
"jpeg"
|
|
17618
|
+
])
|
|
17619
|
+
});
|
|
17620
|
+
var FrameRefSchema = object({
|
|
17621
|
+
registryId: string().min(1),
|
|
17622
|
+
id: string().min(1),
|
|
17623
|
+
width: number$1().int().positive(),
|
|
17624
|
+
height: number$1().int().positive(),
|
|
17625
|
+
format: _enum(["rgb", "gray"]),
|
|
17626
|
+
timestamp: number$1(),
|
|
17627
|
+
capturedAt: number$1().optional()
|
|
17628
|
+
});
|
|
17508
17629
|
var ModelFormatSchema$1 = _enum([
|
|
17509
17630
|
"onnx",
|
|
17510
17631
|
"coreml",
|
|
@@ -17570,7 +17691,8 @@ var PipelineModelOptionSchema = object({
|
|
|
17570
17691
|
sizeMB: number$1()
|
|
17571
17692
|
})),
|
|
17572
17693
|
group: ModelVariantGroupSchema.optional(),
|
|
17573
|
-
legacy: boolean().optional()
|
|
17694
|
+
legacy: boolean().optional(),
|
|
17695
|
+
provider: ModelProviderIdSchema.optional()
|
|
17574
17696
|
});
|
|
17575
17697
|
var ConfigFieldBridge = custom();
|
|
17576
17698
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -17749,6 +17871,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
17749
17871
|
steps: array(PipelineStepInputSchema).min(1),
|
|
17750
17872
|
frame: FrameInputSchema.optional(),
|
|
17751
17873
|
/**
|
|
17874
|
+
* Process-local lazy frame. Valid only when caller and provider resolve
|
|
17875
|
+
* in the same execution-group process; split/cross-node callers use
|
|
17876
|
+
* `frame`/`image` inline compatibility instead.
|
|
17877
|
+
*/
|
|
17878
|
+
frameRef: FrameRefSchema.optional(),
|
|
17879
|
+
/**
|
|
17752
17880
|
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
17753
17881
|
* the decoded pixels live in. One more member of the one-of
|
|
17754
17882
|
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
@@ -18004,7 +18132,10 @@ var NativeCropResultSchema = object({
|
|
|
18004
18132
|
* Which source served this crop, so a quality-sensitive consumer (the native
|
|
18005
18133
|
* `keyFrame`) can reject a degraded fallback:
|
|
18006
18134
|
* - `native` — cut from the decode worker's retained NATIVE surface (the
|
|
18007
|
-
* quality path).
|
|
18135
|
+
* quality path). A subject-tile serve is also native-resolution and stays
|
|
18136
|
+
* `native` here: the public enum cannot name `tile` without a breaking cap
|
|
18137
|
+
* change. Runner telemetry distinguishes lease vs tile via `source` on the
|
|
18138
|
+
* internal crop result (`nativeHits` vs `tileHits`).
|
|
18008
18139
|
* - `ram-fullframe` — the native surface MISSED but the request was full-frame,
|
|
18009
18140
|
* so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
|
|
18010
18141
|
* the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
|
|
@@ -18495,12 +18626,41 @@ var RunnerLocalLoadSchema = object({
|
|
|
18495
18626
|
* legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
|
|
18496
18627
|
* working unchanged when they switch to reading from the runner cap.
|
|
18497
18628
|
*/
|
|
18629
|
+
var FrameLazyCountersSchema = object({
|
|
18630
|
+
framesDecoded: number$1(),
|
|
18631
|
+
framesAdmitted: number$1(),
|
|
18632
|
+
framesDroppedPixelFree: number$1(),
|
|
18633
|
+
viewsMaterialized: number$1(),
|
|
18634
|
+
viewsSkipped: number$1(),
|
|
18635
|
+
workerToRunnerBytes: number$1(),
|
|
18636
|
+
runnerToPoolRawBytes: number$1(),
|
|
18637
|
+
runnerToPoolJpegBytes: number$1(),
|
|
18638
|
+
onDemandFullFrameRequests: number$1(),
|
|
18639
|
+
onDemandCropRequests: number$1(),
|
|
18640
|
+
nativeHits: number$1(),
|
|
18641
|
+
nativeMisses: number$1(),
|
|
18642
|
+
tileHits: number$1(),
|
|
18643
|
+
tileMisses: number$1(),
|
|
18644
|
+
fallbackHits: number$1(),
|
|
18645
|
+
fallbackMisses: number$1(),
|
|
18646
|
+
retainedWritesAvoided: number$1(),
|
|
18647
|
+
residentRefs: number$1(),
|
|
18648
|
+
residentBytes: number$1(),
|
|
18649
|
+
releases: number$1(),
|
|
18650
|
+
evictions: number$1(),
|
|
18651
|
+
staleMisses: number$1()
|
|
18652
|
+
});
|
|
18653
|
+
var FrameLazyMetricsSchema = object({
|
|
18654
|
+
node: FrameLazyCountersSchema,
|
|
18655
|
+
cameras: array(FrameLazyCountersSchema.extend({ deviceId: number$1() }))
|
|
18656
|
+
});
|
|
18498
18657
|
var RunnerLocalMetricsSchema = object({
|
|
18499
18658
|
nodeId: string(),
|
|
18500
18659
|
activeCameras: number$1(),
|
|
18501
18660
|
throttledCameras: number$1(),
|
|
18502
18661
|
avgInferenceTimeMs: number$1(),
|
|
18503
|
-
queueDepth: number$1()
|
|
18662
|
+
queueDepth: number$1(),
|
|
18663
|
+
frameLazy: FrameLazyMetricsSchema.optional()
|
|
18504
18664
|
});
|
|
18505
18665
|
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number$1() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number$1() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number$1()).readonly()), method(object({
|
|
18506
18666
|
handle: FrameHandleSchema,
|
|
@@ -19800,6 +19960,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
|
|
|
19800
19960
|
location: StorageLocationSchema,
|
|
19801
19961
|
relativePath: string()
|
|
19802
19962
|
}), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number$1().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" });
|
|
19963
|
+
/** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
|
|
19964
|
+
var ProfileSettingsSchemaBridge = unknown().nullable();
|
|
19965
|
+
var ProfileSettingsBagSchema = record(string(), unknown());
|
|
19803
19966
|
/**
|
|
19804
19967
|
* A live terminal session hosted by the provider addon. Output and input do
|
|
19805
19968
|
* NOT flow through the capability — they use the addon data plane
|
|
@@ -19829,7 +19992,14 @@ var TerminalSessionInfoSchema = object({
|
|
|
19829
19992
|
var TerminalProfileInfoSchema = object({
|
|
19830
19993
|
profileId: string(),
|
|
19831
19994
|
label: string(),
|
|
19832
|
-
description: string().optional()
|
|
19995
|
+
description: string().optional(),
|
|
19996
|
+
/** Spawn defaults the instance form copies on create. */
|
|
19997
|
+
executable: string().optional(),
|
|
19998
|
+
args: array(string()).readonly().optional(),
|
|
19999
|
+
cwd: string().optional(),
|
|
20000
|
+
environment: array(string()).readonly().optional(),
|
|
20001
|
+
/** ConfigUISchema for instance knobs, or null when the profile has none. */
|
|
20002
|
+
settingsSchema: ProfileSettingsSchemaBridge.optional()
|
|
19833
20003
|
});
|
|
19834
20004
|
/**
|
|
19835
20005
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -19842,7 +20012,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
19842
20012
|
profileId: string(),
|
|
19843
20013
|
profileLabel: string(),
|
|
19844
20014
|
name: string(),
|
|
19845
|
-
enabled: boolean()
|
|
20015
|
+
enabled: boolean(),
|
|
20016
|
+
executable: string(),
|
|
20017
|
+
args: array(string()).readonly(),
|
|
20018
|
+
cwd: string(),
|
|
20019
|
+
environment: array(string()).readonly(),
|
|
20020
|
+
profileSettings: ProfileSettingsBagSchema
|
|
19846
20021
|
});
|
|
19847
20022
|
var TerminalLegacyCameraSchema = object({
|
|
19848
20023
|
stableId: string(),
|
|
@@ -19872,7 +20047,23 @@ var TerminalOutputBatchSchema = object({
|
|
|
19872
20047
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19873
20048
|
targetNodeId: string().min(1),
|
|
19874
20049
|
profileId: string().min(1),
|
|
19875
|
-
name: string().trim().min(1).max(160).optional()
|
|
20050
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20051
|
+
executable: string().max(1024).optional(),
|
|
20052
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20053
|
+
cwd: string().max(1024).optional(),
|
|
20054
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
20055
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
20056
|
+
}), TerminalInstanceInfoSchema, {
|
|
20057
|
+
kind: "mutation",
|
|
20058
|
+
auth: "admin"
|
|
20059
|
+
}), method(object({
|
|
20060
|
+
instanceId: string().min(1),
|
|
20061
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20062
|
+
executable: string().max(1024).optional(),
|
|
20063
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20064
|
+
cwd: string().max(1024).optional(),
|
|
20065
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
20066
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
19876
20067
|
}), TerminalInstanceInfoSchema, {
|
|
19877
20068
|
kind: "mutation",
|
|
19878
20069
|
auth: "admin"
|
|
@@ -19894,7 +20085,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
19894
20085
|
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19895
20086
|
profileId: string(),
|
|
19896
20087
|
cols: number$1().int().positive(),
|
|
19897
|
-
rows: number$1().int().positive()
|
|
20088
|
+
rows: number$1().int().positive(),
|
|
20089
|
+
executable: string().max(1024).optional(),
|
|
20090
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20091
|
+
cwd: string().max(1024).optional(),
|
|
20092
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
19898
20093
|
}), TerminalSessionInfoSchema, {
|
|
19899
20094
|
kind: "mutation",
|
|
19900
20095
|
auth: "admin"
|
|
@@ -22676,10 +22871,10 @@ DeviceType.LawnMower, method(object({ deviceId: number$1().int().nonnegative() }
|
|
|
22676
22871
|
*
|
|
22677
22872
|
* • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
|
|
22678
22873
|
* to receive an ordered list of candidate base URLs it should race
|
|
22679
|
-
* on connect — LAN IPv4 first (lowest latency
|
|
22680
|
-
* then public hostname (if a tunnel is
|
|
22681
|
-
* race them with short timeouts and stick with the
|
|
22682
|
-
* session.
|
|
22874
|
+
* on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
|
|
22875
|
+
* when on the same network), then public hostname (if a tunnel is
|
|
22876
|
+
* up). The SDK can race them with short timeouts and stick with the
|
|
22877
|
+
* winner for the session.
|
|
22683
22878
|
*
|
|
22684
22879
|
* Why hub-only: agents are not directly addressable by the operator's
|
|
22685
22880
|
* clients — they reverse-connect to the hub. Exposing their interfaces
|
|
@@ -22834,6 +23029,17 @@ var NotificationEndpointSchema = object({
|
|
|
22834
23029
|
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
22835
23030
|
resolved: string().nullable()
|
|
22836
23031
|
});
|
|
23032
|
+
/**
|
|
23033
|
+
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
23034
|
+
* AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
|
|
23035
|
+
* currently expands to, so the UI can show the effective set either way.
|
|
23036
|
+
*/
|
|
23037
|
+
var ViewerEndpointsSchema = object({
|
|
23038
|
+
/** The operator's explicit race set, or empty for AUTO. */
|
|
23039
|
+
baseUrls: array(string()).readonly(),
|
|
23040
|
+
/** What the ranking currently resolves to (may be empty if nothing is up). */
|
|
23041
|
+
resolved: array(string()).readonly()
|
|
23042
|
+
});
|
|
22837
23043
|
var AllowedAddressesSchema = object({
|
|
22838
23044
|
/**
|
|
22839
23045
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -22842,6 +23048,20 @@ var AllowedAddressesSchema = object({
|
|
|
22842
23048
|
* Network Addresses admin page and persisted by the addon.
|
|
22843
23049
|
*/
|
|
22844
23050
|
addresses: array(string()).readonly() });
|
|
23051
|
+
var TlsStatusSchema = object({
|
|
23052
|
+
mode: _enum([
|
|
23053
|
+
"generated",
|
|
23054
|
+
"uploaded",
|
|
23055
|
+
"disabled"
|
|
23056
|
+
]),
|
|
23057
|
+
leafFingerprintSha256: string().nullable(),
|
|
23058
|
+
caFingerprintSha256: string().nullable(),
|
|
23059
|
+
validTo: string().nullable(),
|
|
23060
|
+
sans: array(string()),
|
|
23061
|
+
caCertPem: string().nullable(),
|
|
23062
|
+
reissueError: string().nullable(),
|
|
23063
|
+
restartRequired: boolean()
|
|
23064
|
+
});
|
|
22845
23065
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
22846
23066
|
/**
|
|
22847
23067
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -22851,17 +23071,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22851
23071
|
*/
|
|
22852
23072
|
port: number$1().int().min(1).max(65535).optional(),
|
|
22853
23073
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
22854
|
-
* candidate. Default `
|
|
23074
|
+
* candidate. Default `false` — loopback is not a client route. */
|
|
22855
23075
|
includeLoopback: boolean().optional(),
|
|
22856
|
-
/** Skip IPv6 entries.
|
|
22857
|
-
*
|
|
23076
|
+
/** Skip IPv6 entries. Default `false` — the palette includes stable
|
|
23077
|
+
* LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
|
|
23078
|
+
* hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
|
|
22858
23079
|
ipv4Only: boolean().optional(),
|
|
22859
23080
|
/** Scheme to emit for LAN/loopback URLs. Default `'http'`.
|
|
22860
23081
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
22861
23082
|
* to avoid mixed-content blocks in the browser. The public
|
|
22862
23083
|
* tunnel always emits `https://` regardless. */
|
|
22863
23084
|
scheme: _enum(["http", "https"]).optional()
|
|
22864
|
-
}), 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" })
|
|
23085
|
+
}), 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, {
|
|
23086
|
+
kind: "mutation",
|
|
23087
|
+
auth: "admin"
|
|
23088
|
+
}), method(object({
|
|
23089
|
+
certPem: string().min(1),
|
|
23090
|
+
keyPem: string().min(1),
|
|
23091
|
+
caPem: string().optional()
|
|
23092
|
+
}), TlsStatusSchema, {
|
|
23093
|
+
kind: "mutation",
|
|
23094
|
+
auth: "admin"
|
|
23095
|
+
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
23096
|
+
kind: "mutation",
|
|
23097
|
+
auth: "admin"
|
|
23098
|
+
});
|
|
22865
23099
|
object({
|
|
22866
23100
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22867
23101
|
* failure to reach the target — operator intervention required. */
|
|
@@ -24042,7 +24276,12 @@ var PlateInfoSchema = object({
|
|
|
24042
24276
|
plateBbox: BoundingBoxSchema.optional(),
|
|
24043
24277
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
24044
24278
|
keyFrameMediaKey: string().optional(),
|
|
24045
|
-
base64: string().optional()
|
|
24279
|
+
base64: string().optional(),
|
|
24280
|
+
/**
|
|
24281
|
+
* Same crop as a data-plane URL. `getPlateByTrack` returns this and
|
|
24282
|
+
* never inlines JPEG; `listPlates` still inlines for the admin-ui.
|
|
24283
|
+
*/
|
|
24284
|
+
cropUrl: string().optional()
|
|
24046
24285
|
});
|
|
24047
24286
|
var MediaFileLiteSchema = object({
|
|
24048
24287
|
key: string(),
|
|
@@ -28624,6 +28863,12 @@ Object.freeze({
|
|
|
28624
28863
|
addonId: null,
|
|
28625
28864
|
access: "create"
|
|
28626
28865
|
},
|
|
28866
|
+
"localNetwork.downloadCa": {
|
|
28867
|
+
capName: "local-network",
|
|
28868
|
+
capScope: "system",
|
|
28869
|
+
addonId: null,
|
|
28870
|
+
access: "view"
|
|
28871
|
+
},
|
|
28627
28872
|
"localNetwork.getAllowedAddresses": {
|
|
28628
28873
|
capName: "local-network",
|
|
28629
28874
|
capScope: "system",
|
|
@@ -28648,18 +28893,42 @@ Object.freeze({
|
|
|
28648
28893
|
addonId: null,
|
|
28649
28894
|
access: "view"
|
|
28650
28895
|
},
|
|
28896
|
+
"localNetwork.getTlsStatus": {
|
|
28897
|
+
capName: "local-network",
|
|
28898
|
+
capScope: "system",
|
|
28899
|
+
addonId: null,
|
|
28900
|
+
access: "view"
|
|
28901
|
+
},
|
|
28902
|
+
"localNetwork.getViewerEndpoints": {
|
|
28903
|
+
capName: "local-network",
|
|
28904
|
+
capScope: "system",
|
|
28905
|
+
addonId: null,
|
|
28906
|
+
access: "view"
|
|
28907
|
+
},
|
|
28651
28908
|
"localNetwork.list": {
|
|
28652
28909
|
capName: "local-network",
|
|
28653
28910
|
capScope: "system",
|
|
28654
28911
|
addonId: null,
|
|
28655
28912
|
access: "view"
|
|
28656
28913
|
},
|
|
28914
|
+
"localNetwork.regenerateCertificate": {
|
|
28915
|
+
capName: "local-network",
|
|
28916
|
+
capScope: "system",
|
|
28917
|
+
addonId: null,
|
|
28918
|
+
access: "create"
|
|
28919
|
+
},
|
|
28657
28920
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
28658
28921
|
capName: "local-network",
|
|
28659
28922
|
capScope: "system",
|
|
28660
28923
|
addonId: null,
|
|
28661
28924
|
access: "delete"
|
|
28662
28925
|
},
|
|
28926
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
28927
|
+
capName: "local-network",
|
|
28928
|
+
capScope: "system",
|
|
28929
|
+
addonId: null,
|
|
28930
|
+
access: "create"
|
|
28931
|
+
},
|
|
28663
28932
|
"localNetwork.setAllowedAddresses": {
|
|
28664
28933
|
capName: "local-network",
|
|
28665
28934
|
capScope: "system",
|
|
@@ -28672,6 +28941,18 @@ Object.freeze({
|
|
|
28672
28941
|
addonId: null,
|
|
28673
28942
|
access: "create"
|
|
28674
28943
|
},
|
|
28944
|
+
"localNetwork.setViewerEndpoints": {
|
|
28945
|
+
capName: "local-network",
|
|
28946
|
+
capScope: "system",
|
|
28947
|
+
addonId: null,
|
|
28948
|
+
access: "create"
|
|
28949
|
+
},
|
|
28950
|
+
"localNetwork.uploadCertificate": {
|
|
28951
|
+
capName: "local-network",
|
|
28952
|
+
capScope: "system",
|
|
28953
|
+
addonId: null,
|
|
28954
|
+
access: "create"
|
|
28955
|
+
},
|
|
28675
28956
|
"lockControl.lock": {
|
|
28676
28957
|
capName: "lock-control",
|
|
28677
28958
|
capScope: "device",
|
|
@@ -29470,6 +29751,12 @@ Object.freeze({
|
|
|
29470
29751
|
addonId: null,
|
|
29471
29752
|
access: "view"
|
|
29472
29753
|
},
|
|
29754
|
+
"pipelineAnalytics.getGroup": {
|
|
29755
|
+
capName: "pipeline-analytics",
|
|
29756
|
+
capScope: "device",
|
|
29757
|
+
addonId: null,
|
|
29758
|
+
access: "view"
|
|
29759
|
+
},
|
|
29473
29760
|
"pipelineAnalytics.getKeyEvents": {
|
|
29474
29761
|
capName: "pipeline-analytics",
|
|
29475
29762
|
capScope: "device",
|
|
@@ -29554,6 +29841,12 @@ Object.freeze({
|
|
|
29554
29841
|
addonId: null,
|
|
29555
29842
|
access: "view"
|
|
29556
29843
|
},
|
|
29844
|
+
"pipelineAnalytics.listGroups": {
|
|
29845
|
+
capName: "pipeline-analytics",
|
|
29846
|
+
capScope: "device",
|
|
29847
|
+
addonId: null,
|
|
29848
|
+
access: "view"
|
|
29849
|
+
},
|
|
29557
29850
|
"pipelineAnalytics.listOpsLog": {
|
|
29558
29851
|
capName: "pipeline-analytics",
|
|
29559
29852
|
capScope: "device",
|
|
@@ -31552,6 +31845,12 @@ Object.freeze({
|
|
|
31552
31845
|
addonId: null,
|
|
31553
31846
|
access: "create"
|
|
31554
31847
|
},
|
|
31848
|
+
"terminalSession.updateInstance": {
|
|
31849
|
+
capName: "terminal-session",
|
|
31850
|
+
capScope: "system",
|
|
31851
|
+
addonId: null,
|
|
31852
|
+
access: "create"
|
|
31853
|
+
},
|
|
31555
31854
|
"terminalSession.writeInput": {
|
|
31556
31855
|
capName: "terminal-session",
|
|
31557
31856
|
capScope: "system",
|
|
@@ -32967,6 +33266,11 @@ Object.freeze({
|
|
|
32967
33266
|
form: "single",
|
|
32968
33267
|
optional: false
|
|
32969
33268
|
}],
|
|
33269
|
+
"pipelineAnalytics.getGroup": [{
|
|
33270
|
+
name: "deviceId",
|
|
33271
|
+
form: "single",
|
|
33272
|
+
optional: false
|
|
33273
|
+
}],
|
|
32970
33274
|
"pipelineAnalytics.getKeyEvents": [{
|
|
32971
33275
|
name: "deviceId",
|
|
32972
33276
|
form: "single",
|
|
@@ -33022,6 +33326,11 @@ Object.freeze({
|
|
|
33022
33326
|
form: "array",
|
|
33023
33327
|
optional: false
|
|
33024
33328
|
}],
|
|
33329
|
+
"pipelineAnalytics.listGroups": [{
|
|
33330
|
+
name: "deviceIds",
|
|
33331
|
+
form: "array",
|
|
33332
|
+
optional: false
|
|
33333
|
+
}],
|
|
33025
33334
|
"pipelineAnalytics.listOpsLog": [{
|
|
33026
33335
|
name: "deviceId",
|
|
33027
33336
|
form: "single",
|
|
@@ -34039,6 +34348,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
34039
34348
|
}]
|
|
34040
34349
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
34041
34350
|
string().min(1);
|
|
34351
|
+
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
34352
|
+
stepId: "face-embedding",
|
|
34353
|
+
key: "minLandmarkFaceSize",
|
|
34354
|
+
label: "Min face size for recognition (detection px)",
|
|
34355
|
+
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.",
|
|
34356
|
+
type: "slider",
|
|
34357
|
+
min: 0,
|
|
34358
|
+
max: 64,
|
|
34359
|
+
step: 2,
|
|
34360
|
+
default: 24
|
|
34361
|
+
}];
|
|
34362
|
+
function clusterStepSettingKey(stepId, fieldKey) {
|
|
34363
|
+
return `clusterStepSetting:${stepId}:${fieldKey}`;
|
|
34364
|
+
}
|
|
34365
|
+
var ClusterSettingNumberSchema = number$1().finite();
|
|
34366
|
+
function readClusterStepSettings(config) {
|
|
34367
|
+
const out = {};
|
|
34368
|
+
for (const field of CLUSTER_STEP_SETTING_FIELDS) {
|
|
34369
|
+
const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
|
|
34370
|
+
const value = parsed.success ? parsed.data : field.default;
|
|
34371
|
+
const existing = out[field.stepId] ?? {};
|
|
34372
|
+
out[field.stepId] = {
|
|
34373
|
+
...existing,
|
|
34374
|
+
[field.key]: value
|
|
34375
|
+
};
|
|
34376
|
+
}
|
|
34377
|
+
return out;
|
|
34378
|
+
}
|
|
34379
|
+
readClusterStepSettings({});
|
|
34042
34380
|
object({
|
|
34043
34381
|
/**
|
|
34044
34382
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -71715,7 +72053,7 @@ function entryForRef(ref) {
|
|
|
71715
72053
|
};
|
|
71716
72054
|
}
|
|
71717
72055
|
//#endregion
|
|
71718
|
-
//#region ../system/dist/file-data-plane-
|
|
72056
|
+
//#region ../system/dist/file-data-plane-BhKdxJgf.mjs
|
|
71719
72057
|
function isNonEmptyFile(filePath) {
|
|
71720
72058
|
return node_fs.existsSync(filePath) && node_fs.statSync(filePath).size > 0;
|
|
71721
72059
|
}
|
|
@@ -71737,21 +72075,56 @@ function buildHeaders(url) {
|
|
|
71737
72075
|
if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
|
|
71738
72076
|
return headers;
|
|
71739
72077
|
}
|
|
71740
|
-
|
|
71741
|
-
|
|
71742
|
-
|
|
71743
|
-
|
|
71744
|
-
|
|
71745
|
-
|
|
71746
|
-
|
|
72078
|
+
var DEFAULT_MAX_REDIRECTS = 5;
|
|
72079
|
+
function normalizeDownloadOptions(third) {
|
|
72080
|
+
if (typeof third === "function") return { onProgress: third };
|
|
72081
|
+
return third ?? {};
|
|
72082
|
+
}
|
|
72083
|
+
function isRedirectStatus(status) {
|
|
72084
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
72085
|
+
}
|
|
72086
|
+
function resolveRedirectUrl(current, location) {
|
|
72087
|
+
return new URL(location, current);
|
|
72088
|
+
}
|
|
72089
|
+
async function downloadFile(url, destPath, onProgressOrOptions) {
|
|
71747
72090
|
if (node_fs.existsSync(destPath)) return destPath;
|
|
72091
|
+
const opts = normalizeDownloadOptions(onProgressOrOptions);
|
|
72092
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
72093
|
+
const maxRedirects = opts.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
|
|
71748
72094
|
node_fs.mkdirSync(node_path$1.dirname(destPath), { recursive: true });
|
|
71749
72095
|
const tmpPath = destPath + ".downloading";
|
|
71750
72096
|
try {
|
|
71751
|
-
|
|
71752
|
-
|
|
71753
|
-
|
|
71754
|
-
|
|
72097
|
+
let current = url;
|
|
72098
|
+
const seen = /* @__PURE__ */ new Set();
|
|
72099
|
+
let response;
|
|
72100
|
+
const manual = opts.redirectPolicy !== void 0;
|
|
72101
|
+
for (let hop = 0; hop <= maxRedirects; hop++) {
|
|
72102
|
+
const parsed = new URL(current);
|
|
72103
|
+
if (opts.redirectPolicy) opts.redirectPolicy(parsed, hop);
|
|
72104
|
+
if (seen.has(parsed.href)) throw new Error(`Redirect loop detected at ${parsed.href}`);
|
|
72105
|
+
seen.add(parsed.href);
|
|
72106
|
+
const controller = opts.timeoutMs !== void 0 ? new AbortController() : void 0;
|
|
72107
|
+
const timer = controller && opts.timeoutMs !== void 0 ? setTimeout(() => controller.abort(), opts.timeoutMs) : void 0;
|
|
72108
|
+
try {
|
|
72109
|
+
response = await fetchImpl(current, {
|
|
72110
|
+
redirect: manual ? "manual" : "follow",
|
|
72111
|
+
headers: buildHeaders(current),
|
|
72112
|
+
...controller ? { signal: controller.signal } : {}
|
|
72113
|
+
});
|
|
72114
|
+
} finally {
|
|
72115
|
+
if (timer) clearTimeout(timer);
|
|
72116
|
+
}
|
|
72117
|
+
if (manual && isRedirectStatus(response.status)) {
|
|
72118
|
+
const location = response.headers.get("location");
|
|
72119
|
+
if (!location) throw new Error(`Redirect ${response.status} missing Location header`);
|
|
72120
|
+
if (hop >= maxRedirects) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
|
|
72121
|
+
current = resolveRedirectUrl(current, location).href;
|
|
72122
|
+
continue;
|
|
72123
|
+
}
|
|
72124
|
+
break;
|
|
72125
|
+
}
|
|
72126
|
+
if (!response) throw new Error(`No response downloading ${url}`);
|
|
72127
|
+
if (manual && isRedirectStatus(response.status)) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
|
|
71755
72128
|
if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
|
|
71756
72129
|
if (!response.body) throw new Error(`No response body from ${url}`);
|
|
71757
72130
|
const total = parseInt(response.headers.get("content-length") ?? "0", 10);
|
|
@@ -71762,9 +72135,10 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
71762
72135
|
for (;;) {
|
|
71763
72136
|
const { done, value } = await reader.read();
|
|
71764
72137
|
if (done || !value) break;
|
|
71765
|
-
fileStream.write(value);
|
|
71766
72138
|
downloaded += value.length;
|
|
71767
|
-
|
|
72139
|
+
if (opts.maxBytes !== void 0 && downloaded > opts.maxBytes) throw new Error(`Downloaded ${downloaded} bytes exceeds the ${opts.maxBytes}-byte limit`);
|
|
72140
|
+
fileStream.write(value);
|
|
72141
|
+
opts.onProgress?.(downloaded, total);
|
|
71768
72142
|
}
|
|
71769
72143
|
} finally {
|
|
71770
72144
|
fileStream.end();
|