@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.mjs
CHANGED
|
@@ -5990,6 +5990,13 @@ var BaseAddon = class {
|
|
|
5990
5990
|
_readinessGeneration = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2, 14);
|
|
5991
5991
|
/** Capability names this addon registered at init — used to emit matching `down` events on shutdown. */
|
|
5992
5992
|
_registeredCapNames = [];
|
|
5993
|
+
/**
|
|
5994
|
+
* True only after `readAddonStore` actually answered. Constructor
|
|
5995
|
+
* defaults look like stored config when the store is down — a forked
|
|
5996
|
+
* addon that auto-starts from those defaults (cloudflare-tunnel quick
|
|
5997
|
+
* mode, 2026-08-25) is not "the operator chose this".
|
|
5998
|
+
*/
|
|
5999
|
+
settingsStoreReady = false;
|
|
5993
6000
|
/** Default config values. Provided via constructor. */
|
|
5994
6001
|
defaults;
|
|
5995
6002
|
constructor(defaults) {
|
|
@@ -6390,7 +6397,9 @@ var BaseAddon = class {
|
|
|
6390
6397
|
];
|
|
6391
6398
|
let lastErr;
|
|
6392
6399
|
for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
|
|
6393
|
-
|
|
6400
|
+
const stored = await settings.readAddonStore() ?? {};
|
|
6401
|
+
this.settingsStoreReady = true;
|
|
6402
|
+
return stored;
|
|
6394
6403
|
} catch (err) {
|
|
6395
6404
|
lastErr = err;
|
|
6396
6405
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6398,6 +6407,7 @@ var BaseAddon = class {
|
|
|
6398
6407
|
if (attempt === delaysMs.length) break;
|
|
6399
6408
|
await new Promise((r) => setTimeout(r, delaysMs[attempt]));
|
|
6400
6409
|
}
|
|
6410
|
+
this.settingsStoreReady = false;
|
|
6401
6411
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
6402
6412
|
return {};
|
|
6403
6413
|
}
|
|
@@ -8252,6 +8262,15 @@ var LabelDefinitionSchema = object({
|
|
|
8252
8262
|
description: string().optional(),
|
|
8253
8263
|
icon: string().optional()
|
|
8254
8264
|
});
|
|
8265
|
+
var ClassMapDefinitionSchema = object({
|
|
8266
|
+
mapping: record(string(), _enum([
|
|
8267
|
+
"person",
|
|
8268
|
+
"vehicle",
|
|
8269
|
+
"animal",
|
|
8270
|
+
"package"
|
|
8271
|
+
])),
|
|
8272
|
+
preserveOriginal: boolean()
|
|
8273
|
+
});
|
|
8255
8274
|
var MODEL_FORMATS = [
|
|
8256
8275
|
"onnx",
|
|
8257
8276
|
"coreml",
|
|
@@ -8335,6 +8354,12 @@ var ModelVariantGroupSchema = object({
|
|
|
8335
8354
|
*/
|
|
8336
8355
|
resolution: number$1().int().positive().optional()
|
|
8337
8356
|
});
|
|
8357
|
+
var ModelProviderIdSchema = _enum([
|
|
8358
|
+
"camstack",
|
|
8359
|
+
"frigate",
|
|
8360
|
+
"scrypted",
|
|
8361
|
+
"custom"
|
|
8362
|
+
]);
|
|
8338
8363
|
var ModelCatalogEntrySchema = object({
|
|
8339
8364
|
id: string(),
|
|
8340
8365
|
name: string(),
|
|
@@ -8430,7 +8455,19 @@ var ModelCatalogEntrySchema = object({
|
|
|
8430
8455
|
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
8431
8456
|
* is a presentation overlay resolved back to an `id`.
|
|
8432
8457
|
*/
|
|
8433
|
-
group: ModelVariantGroupSchema.optional()
|
|
8458
|
+
group: ModelVariantGroupSchema.optional(),
|
|
8459
|
+
/**
|
|
8460
|
+
* Catalog source for the pipeline stepper's provider-first picker. Absent on
|
|
8461
|
+
* built-in CamStack entries (treated as `camstack`) and on registry rows
|
|
8462
|
+
* persisted before this field existed (`inferModelProvider` fills those).
|
|
8463
|
+
*/
|
|
8464
|
+
provider: ModelProviderIdSchema.optional(),
|
|
8465
|
+
/**
|
|
8466
|
+
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8467
|
+
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8468
|
+
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
8469
|
+
*/
|
|
8470
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8434
8471
|
});
|
|
8435
8472
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
8436
8473
|
format: literal("openvino"),
|
|
@@ -8459,7 +8496,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
8459
8496
|
"ocr",
|
|
8460
8497
|
"segmentation"
|
|
8461
8498
|
]),
|
|
8462
|
-
faceAlignment: boolean().optional()
|
|
8499
|
+
faceAlignment: boolean().optional(),
|
|
8500
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8463
8501
|
});
|
|
8464
8502
|
var ConvertResultSchema = object({
|
|
8465
8503
|
entry: ModelCatalogEntrySchema,
|
|
@@ -14679,12 +14717,15 @@ var NcOccupancyConditionSchema = object({
|
|
|
14679
14717
|
* there is no second switch that can disagree with the first and every rule
|
|
14680
14718
|
* authored before the decision migrates for free (`audioModeOf`):
|
|
14681
14719
|
*
|
|
14682
|
-
* - **LABEL mode — `labels` present.** The rule fires
|
|
14683
|
-
*
|
|
14684
|
-
* `hitPercent` and `samplingSeconds` are ignored
|
|
14685
|
-
*
|
|
14686
|
-
*
|
|
14687
|
-
*
|
|
14720
|
+
* - **LABEL mode — `labels` present.** The rule fires when `confirmHits`
|
|
14721
|
+
* labelled frames land inside `confirmWindowSec` (default 2 in 5 s).
|
|
14722
|
+
* `hitPercent` and `samplingSeconds` are still ignored — a percentage of
|
|
14723
|
+
* frames is the wrong question for a classifier that labels 1–3 frames
|
|
14724
|
+
* per episode. The count window is the brake that drops a single-frame
|
|
14725
|
+
* false positive; the rule's own `throttle` cooldown is the other. The
|
|
14726
|
+
* per-label confidence floor is the analyzer's (`classificationMinScore`,
|
|
14727
|
+
* per device) — a label only reaches this condition if the classifier was
|
|
14728
|
+
* already confident enough. `confirmHits: 1` restores first-frame fire.
|
|
14688
14729
|
* - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
|
|
14689
14730
|
* the condition: at least `hitPercent`% of the samples over
|
|
14690
14731
|
* `samplingSeconds` must be at or above `dbThreshold` dBFS (see
|
|
@@ -14711,14 +14752,22 @@ var NcOccupancyConditionSchema = object({
|
|
|
14711
14752
|
* an operator who typed `dog` mean the same thing.
|
|
14712
14753
|
*/
|
|
14713
14754
|
var NcAudioConditionSchema = object({
|
|
14714
|
-
/** LABEL MODE: audio macro labels. Present ⇒
|
|
14755
|
+
/** LABEL MODE: audio macro labels. Present ⇒ count-in-window confirm. */
|
|
14715
14756
|
labels: array(string().min(1)).min(1).optional(),
|
|
14716
14757
|
/** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
|
|
14717
14758
|
dbThreshold: number$1().min(-96).max(0).optional(),
|
|
14718
14759
|
/** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
|
|
14719
14760
|
hitPercent: number$1().int().min(1).max(100).default(60),
|
|
14720
14761
|
/** LEVEL MODE ONLY: length of the sampling window in seconds. */
|
|
14721
|
-
samplingSeconds: number$1().int().min(1).max(300).default(10)
|
|
14762
|
+
samplingSeconds: number$1().int().min(1).max(300).default(10),
|
|
14763
|
+
/**
|
|
14764
|
+
* LABEL MODE: how many labelled frames must land inside
|
|
14765
|
+
* {@link NcAudioConditionSchema.shape.confirmWindowSec} before dispatch.
|
|
14766
|
+
* Absent ⇒ 2 (the matcher default). `1` is first-frame fire.
|
|
14767
|
+
*/
|
|
14768
|
+
confirmHits: number$1().int().min(1).max(20).optional(),
|
|
14769
|
+
/** LABEL MODE: the window those frames must share, in seconds. Absent ⇒ 5. */
|
|
14770
|
+
confirmWindowSec: number$1().int().min(1).max(60).optional()
|
|
14722
14771
|
});
|
|
14723
14772
|
/**
|
|
14724
14773
|
* Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
|
|
@@ -17092,6 +17141,46 @@ var RecentTracksPageSchema = object({
|
|
|
17092
17141
|
/** Cursor for the next page, or null when this page is the last. */
|
|
17093
17142
|
nextCursor: string().nullable()
|
|
17094
17143
|
});
|
|
17144
|
+
var LIST_GROUPS_DEFAULT_LIMIT = 40;
|
|
17145
|
+
var LIST_GROUPS_MAX_LIMIT = 100;
|
|
17146
|
+
var AnalyticsGroupRecordSchema = object({
|
|
17147
|
+
id: string(),
|
|
17148
|
+
deviceId: number$1().int(),
|
|
17149
|
+
openedAt: number$1().int(),
|
|
17150
|
+
closedAt: number$1().int(),
|
|
17151
|
+
timestamp: number$1().int(),
|
|
17152
|
+
memberCount: number$1().int(),
|
|
17153
|
+
memberTrackIds: array(string()).readonly(),
|
|
17154
|
+
className: string(),
|
|
17155
|
+
classes: array(string()).readonly(),
|
|
17156
|
+
/** Relative event-media path, or null when the group has no picture yet. */
|
|
17157
|
+
mediaUrl: string().nullable(),
|
|
17158
|
+
singleton: boolean()
|
|
17159
|
+
});
|
|
17160
|
+
var AnalyticsGroupMemberSchema = object({
|
|
17161
|
+
trackId: string(),
|
|
17162
|
+
deviceId: number$1().int(),
|
|
17163
|
+
className: string(),
|
|
17164
|
+
firstSeen: number$1().int(),
|
|
17165
|
+
lastSeen: number$1().int(),
|
|
17166
|
+
mediaUrl: string().nullable()
|
|
17167
|
+
});
|
|
17168
|
+
var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
|
|
17169
|
+
var ListGroupsQueryInput = object({
|
|
17170
|
+
/** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
|
|
17171
|
+
deviceIds: array(number$1()),
|
|
17172
|
+
/** Window lower bound on `closedAt` (inclusive). */
|
|
17173
|
+
since: number$1().optional(),
|
|
17174
|
+
/** Window upper bound on `openedAt` (inclusive). */
|
|
17175
|
+
until: number$1().optional(),
|
|
17176
|
+
limit: number$1().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
|
|
17177
|
+
/** Opaque continuation cursor from a previous page's `nextCursor`. */
|
|
17178
|
+
cursor: string().optional()
|
|
17179
|
+
});
|
|
17180
|
+
var ListGroupsPageSchema = object({
|
|
17181
|
+
groups: array(AnalyticsGroupRecordSchema).readonly(),
|
|
17182
|
+
nextCursor: string().nullable()
|
|
17183
|
+
});
|
|
17095
17184
|
var KeyEventQueryInput = object({
|
|
17096
17185
|
deviceId: number$1(),
|
|
17097
17186
|
/** Window lower bound (track firstSeen ≥ since). */
|
|
@@ -17167,7 +17256,9 @@ var TrackCascadeCountsSchema = object({
|
|
|
17167
17256
|
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
17168
17257
|
plates: number$1().int(),
|
|
17169
17258
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
17170
|
-
embeddings: number$1().int()
|
|
17259
|
+
embeddings: number$1().int(),
|
|
17260
|
+
/** Group membership + group rows removed with their last member (best-effort). */
|
|
17261
|
+
groups: number$1().int()
|
|
17171
17262
|
});
|
|
17172
17263
|
/** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
|
|
17173
17264
|
var DiskReconcileCountsSchema = object({
|
|
@@ -17313,7 +17404,10 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
|
|
|
17313
17404
|
* stationary registry). Default false: the timeline lists passages,
|
|
17314
17405
|
* not parking records (operator decision, 2026-08-15). */
|
|
17315
17406
|
includeStationary: boolean().optional()
|
|
17316
|
-
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(
|
|
17407
|
+
}), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
|
|
17408
|
+
deviceId: number$1(),
|
|
17409
|
+
groupId: string().min(1)
|
|
17410
|
+
}), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
|
|
17317
17411
|
kind: "mutation",
|
|
17318
17412
|
auth: "admin"
|
|
17319
17413
|
}), 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({
|
|
@@ -17531,6 +17625,33 @@ var NativeCropRefSchema = object({
|
|
|
17531
17625
|
h: number$1()
|
|
17532
17626
|
})
|
|
17533
17627
|
});
|
|
17628
|
+
object({
|
|
17629
|
+
crop: object({
|
|
17630
|
+
left: number$1(),
|
|
17631
|
+
top: number$1(),
|
|
17632
|
+
width: number$1().positive(),
|
|
17633
|
+
height: number$1().positive()
|
|
17634
|
+
}).optional(),
|
|
17635
|
+
content: object({
|
|
17636
|
+
width: number$1().int().positive(),
|
|
17637
|
+
height: number$1().int().positive()
|
|
17638
|
+
}),
|
|
17639
|
+
fit: _enum(["stretch", "contain"]),
|
|
17640
|
+
format: _enum([
|
|
17641
|
+
"rgb",
|
|
17642
|
+
"gray",
|
|
17643
|
+
"jpeg"
|
|
17644
|
+
])
|
|
17645
|
+
});
|
|
17646
|
+
var FrameRefSchema = object({
|
|
17647
|
+
registryId: string().min(1),
|
|
17648
|
+
id: string().min(1),
|
|
17649
|
+
width: number$1().int().positive(),
|
|
17650
|
+
height: number$1().int().positive(),
|
|
17651
|
+
format: _enum(["rgb", "gray"]),
|
|
17652
|
+
timestamp: number$1(),
|
|
17653
|
+
capturedAt: number$1().optional()
|
|
17654
|
+
});
|
|
17534
17655
|
var ModelFormatSchema$1 = _enum([
|
|
17535
17656
|
"onnx",
|
|
17536
17657
|
"coreml",
|
|
@@ -17596,7 +17717,8 @@ var PipelineModelOptionSchema = object({
|
|
|
17596
17717
|
sizeMB: number$1()
|
|
17597
17718
|
})),
|
|
17598
17719
|
group: ModelVariantGroupSchema.optional(),
|
|
17599
|
-
legacy: boolean().optional()
|
|
17720
|
+
legacy: boolean().optional(),
|
|
17721
|
+
provider: ModelProviderIdSchema.optional()
|
|
17600
17722
|
});
|
|
17601
17723
|
var ConfigFieldBridge = custom();
|
|
17602
17724
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -17775,6 +17897,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
17775
17897
|
steps: array(PipelineStepInputSchema).min(1),
|
|
17776
17898
|
frame: FrameInputSchema.optional(),
|
|
17777
17899
|
/**
|
|
17900
|
+
* Process-local lazy frame. Valid only when caller and provider resolve
|
|
17901
|
+
* in the same execution-group process; split/cross-node callers use
|
|
17902
|
+
* `frame`/`image` inline compatibility instead.
|
|
17903
|
+
*/
|
|
17904
|
+
frameRef: FrameRefSchema.optional(),
|
|
17905
|
+
/**
|
|
17778
17906
|
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
17779
17907
|
* the decoded pixels live in. One more member of the one-of
|
|
17780
17908
|
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
@@ -18030,7 +18158,10 @@ var NativeCropResultSchema = object({
|
|
|
18030
18158
|
* Which source served this crop, so a quality-sensitive consumer (the native
|
|
18031
18159
|
* `keyFrame`) can reject a degraded fallback:
|
|
18032
18160
|
* - `native` — cut from the decode worker's retained NATIVE surface (the
|
|
18033
|
-
* quality path).
|
|
18161
|
+
* quality path). A subject-tile serve is also native-resolution and stays
|
|
18162
|
+
* `native` here: the public enum cannot name `tile` without a breaking cap
|
|
18163
|
+
* change. Runner telemetry distinguishes lease vs tile via `source` on the
|
|
18164
|
+
* internal crop result (`nativeHits` vs `tileHits`).
|
|
18034
18165
|
* - `ram-fullframe` — the native surface MISSED but the request was full-frame,
|
|
18035
18166
|
* so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
|
|
18036
18167
|
* the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
|
|
@@ -18521,12 +18652,41 @@ var RunnerLocalLoadSchema = object({
|
|
|
18521
18652
|
* legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
|
|
18522
18653
|
* working unchanged when they switch to reading from the runner cap.
|
|
18523
18654
|
*/
|
|
18655
|
+
var FrameLazyCountersSchema = object({
|
|
18656
|
+
framesDecoded: number$1(),
|
|
18657
|
+
framesAdmitted: number$1(),
|
|
18658
|
+
framesDroppedPixelFree: number$1(),
|
|
18659
|
+
viewsMaterialized: number$1(),
|
|
18660
|
+
viewsSkipped: number$1(),
|
|
18661
|
+
workerToRunnerBytes: number$1(),
|
|
18662
|
+
runnerToPoolRawBytes: number$1(),
|
|
18663
|
+
runnerToPoolJpegBytes: number$1(),
|
|
18664
|
+
onDemandFullFrameRequests: number$1(),
|
|
18665
|
+
onDemandCropRequests: number$1(),
|
|
18666
|
+
nativeHits: number$1(),
|
|
18667
|
+
nativeMisses: number$1(),
|
|
18668
|
+
tileHits: number$1(),
|
|
18669
|
+
tileMisses: number$1(),
|
|
18670
|
+
fallbackHits: number$1(),
|
|
18671
|
+
fallbackMisses: number$1(),
|
|
18672
|
+
retainedWritesAvoided: number$1(),
|
|
18673
|
+
residentRefs: number$1(),
|
|
18674
|
+
residentBytes: number$1(),
|
|
18675
|
+
releases: number$1(),
|
|
18676
|
+
evictions: number$1(),
|
|
18677
|
+
staleMisses: number$1()
|
|
18678
|
+
});
|
|
18679
|
+
var FrameLazyMetricsSchema = object({
|
|
18680
|
+
node: FrameLazyCountersSchema,
|
|
18681
|
+
cameras: array(FrameLazyCountersSchema.extend({ deviceId: number$1() }))
|
|
18682
|
+
});
|
|
18524
18683
|
var RunnerLocalMetricsSchema = object({
|
|
18525
18684
|
nodeId: string(),
|
|
18526
18685
|
activeCameras: number$1(),
|
|
18527
18686
|
throttledCameras: number$1(),
|
|
18528
18687
|
avgInferenceTimeMs: number$1(),
|
|
18529
|
-
queueDepth: number$1()
|
|
18688
|
+
queueDepth: number$1(),
|
|
18689
|
+
frameLazy: FrameLazyMetricsSchema.optional()
|
|
18530
18690
|
});
|
|
18531
18691
|
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({
|
|
18532
18692
|
handle: FrameHandleSchema,
|
|
@@ -19826,6 +19986,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
|
|
|
19826
19986
|
location: StorageLocationSchema,
|
|
19827
19987
|
relativePath: string()
|
|
19828
19988
|
}), _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" });
|
|
19989
|
+
/** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
|
|
19990
|
+
var ProfileSettingsSchemaBridge = unknown().nullable();
|
|
19991
|
+
var ProfileSettingsBagSchema = record(string(), unknown());
|
|
19829
19992
|
/**
|
|
19830
19993
|
* A live terminal session hosted by the provider addon. Output and input do
|
|
19831
19994
|
* NOT flow through the capability — they use the addon data plane
|
|
@@ -19855,7 +20018,14 @@ var TerminalSessionInfoSchema = object({
|
|
|
19855
20018
|
var TerminalProfileInfoSchema = object({
|
|
19856
20019
|
profileId: string(),
|
|
19857
20020
|
label: string(),
|
|
19858
|
-
description: string().optional()
|
|
20021
|
+
description: string().optional(),
|
|
20022
|
+
/** Spawn defaults the instance form copies on create. */
|
|
20023
|
+
executable: string().optional(),
|
|
20024
|
+
args: array(string()).readonly().optional(),
|
|
20025
|
+
cwd: string().optional(),
|
|
20026
|
+
environment: array(string()).readonly().optional(),
|
|
20027
|
+
/** ConfigUISchema for instance knobs, or null when the profile has none. */
|
|
20028
|
+
settingsSchema: ProfileSettingsSchemaBridge.optional()
|
|
19859
20029
|
});
|
|
19860
20030
|
/**
|
|
19861
20031
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -19868,7 +20038,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
19868
20038
|
profileId: string(),
|
|
19869
20039
|
profileLabel: string(),
|
|
19870
20040
|
name: string(),
|
|
19871
|
-
enabled: boolean()
|
|
20041
|
+
enabled: boolean(),
|
|
20042
|
+
executable: string(),
|
|
20043
|
+
args: array(string()).readonly(),
|
|
20044
|
+
cwd: string(),
|
|
20045
|
+
environment: array(string()).readonly(),
|
|
20046
|
+
profileSettings: ProfileSettingsBagSchema
|
|
19872
20047
|
});
|
|
19873
20048
|
var TerminalLegacyCameraSchema = object({
|
|
19874
20049
|
stableId: string(),
|
|
@@ -19898,7 +20073,23 @@ var TerminalOutputBatchSchema = object({
|
|
|
19898
20073
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19899
20074
|
targetNodeId: string().min(1),
|
|
19900
20075
|
profileId: string().min(1),
|
|
19901
|
-
name: string().trim().min(1).max(160).optional()
|
|
20076
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20077
|
+
executable: string().max(1024).optional(),
|
|
20078
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20079
|
+
cwd: string().max(1024).optional(),
|
|
20080
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
20081
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
20082
|
+
}), TerminalInstanceInfoSchema, {
|
|
20083
|
+
kind: "mutation",
|
|
20084
|
+
auth: "admin"
|
|
20085
|
+
}), method(object({
|
|
20086
|
+
instanceId: string().min(1),
|
|
20087
|
+
name: string().trim().min(1).max(160).optional(),
|
|
20088
|
+
executable: string().max(1024).optional(),
|
|
20089
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20090
|
+
cwd: string().max(1024).optional(),
|
|
20091
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
20092
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
19902
20093
|
}), TerminalInstanceInfoSchema, {
|
|
19903
20094
|
kind: "mutation",
|
|
19904
20095
|
auth: "admin"
|
|
@@ -19920,7 +20111,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
19920
20111
|
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19921
20112
|
profileId: string(),
|
|
19922
20113
|
cols: number$1().int().positive(),
|
|
19923
|
-
rows: number$1().int().positive()
|
|
20114
|
+
rows: number$1().int().positive(),
|
|
20115
|
+
executable: string().max(1024).optional(),
|
|
20116
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
20117
|
+
cwd: string().max(1024).optional(),
|
|
20118
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
19924
20119
|
}), TerminalSessionInfoSchema, {
|
|
19925
20120
|
kind: "mutation",
|
|
19926
20121
|
auth: "admin"
|
|
@@ -22702,10 +22897,10 @@ DeviceType.LawnMower, method(object({ deviceId: number$1().int().nonnegative() }
|
|
|
22702
22897
|
*
|
|
22703
22898
|
* • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
|
|
22704
22899
|
* to receive an ordered list of candidate base URLs it should race
|
|
22705
|
-
* on connect — LAN IPv4 first (lowest latency
|
|
22706
|
-
* then public hostname (if a tunnel is
|
|
22707
|
-
* race them with short timeouts and stick with the
|
|
22708
|
-
* session.
|
|
22900
|
+
* on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
|
|
22901
|
+
* when on the same network), then public hostname (if a tunnel is
|
|
22902
|
+
* up). The SDK can race them with short timeouts and stick with the
|
|
22903
|
+
* winner for the session.
|
|
22709
22904
|
*
|
|
22710
22905
|
* Why hub-only: agents are not directly addressable by the operator's
|
|
22711
22906
|
* clients — they reverse-connect to the hub. Exposing their interfaces
|
|
@@ -22860,6 +23055,17 @@ var NotificationEndpointSchema = object({
|
|
|
22860
23055
|
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
22861
23056
|
resolved: string().nullable()
|
|
22862
23057
|
});
|
|
23058
|
+
/**
|
|
23059
|
+
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
23060
|
+
* AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
|
|
23061
|
+
* currently expands to, so the UI can show the effective set either way.
|
|
23062
|
+
*/
|
|
23063
|
+
var ViewerEndpointsSchema = object({
|
|
23064
|
+
/** The operator's explicit race set, or empty for AUTO. */
|
|
23065
|
+
baseUrls: array(string()).readonly(),
|
|
23066
|
+
/** What the ranking currently resolves to (may be empty if nothing is up). */
|
|
23067
|
+
resolved: array(string()).readonly()
|
|
23068
|
+
});
|
|
22863
23069
|
var AllowedAddressesSchema = object({
|
|
22864
23070
|
/**
|
|
22865
23071
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -22868,6 +23074,20 @@ var AllowedAddressesSchema = object({
|
|
|
22868
23074
|
* Network Addresses admin page and persisted by the addon.
|
|
22869
23075
|
*/
|
|
22870
23076
|
addresses: array(string()).readonly() });
|
|
23077
|
+
var TlsStatusSchema = object({
|
|
23078
|
+
mode: _enum([
|
|
23079
|
+
"generated",
|
|
23080
|
+
"uploaded",
|
|
23081
|
+
"disabled"
|
|
23082
|
+
]),
|
|
23083
|
+
leafFingerprintSha256: string().nullable(),
|
|
23084
|
+
caFingerprintSha256: string().nullable(),
|
|
23085
|
+
validTo: string().nullable(),
|
|
23086
|
+
sans: array(string()),
|
|
23087
|
+
caCertPem: string().nullable(),
|
|
23088
|
+
reissueError: string().nullable(),
|
|
23089
|
+
restartRequired: boolean()
|
|
23090
|
+
});
|
|
22871
23091
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
22872
23092
|
/**
|
|
22873
23093
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -22877,17 +23097,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22877
23097
|
*/
|
|
22878
23098
|
port: number$1().int().min(1).max(65535).optional(),
|
|
22879
23099
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
22880
|
-
* candidate. Default `
|
|
23100
|
+
* candidate. Default `false` — loopback is not a client route. */
|
|
22881
23101
|
includeLoopback: boolean().optional(),
|
|
22882
|
-
/** Skip IPv6 entries.
|
|
22883
|
-
*
|
|
23102
|
+
/** Skip IPv6 entries. Default `false` — the palette includes stable
|
|
23103
|
+
* LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
|
|
23104
|
+
* hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
|
|
22884
23105
|
ipv4Only: boolean().optional(),
|
|
22885
23106
|
/** Scheme to emit for LAN/loopback URLs. Default `'http'`.
|
|
22886
23107
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
22887
23108
|
* to avoid mixed-content blocks in the browser. The public
|
|
22888
23109
|
* tunnel always emits `https://` regardless. */
|
|
22889
23110
|
scheme: _enum(["http", "https"]).optional()
|
|
22890
|
-
}), 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" })
|
|
23111
|
+
}), 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, {
|
|
23112
|
+
kind: "mutation",
|
|
23113
|
+
auth: "admin"
|
|
23114
|
+
}), method(object({
|
|
23115
|
+
certPem: string().min(1),
|
|
23116
|
+
keyPem: string().min(1),
|
|
23117
|
+
caPem: string().optional()
|
|
23118
|
+
}), TlsStatusSchema, {
|
|
23119
|
+
kind: "mutation",
|
|
23120
|
+
auth: "admin"
|
|
23121
|
+
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
23122
|
+
kind: "mutation",
|
|
23123
|
+
auth: "admin"
|
|
23124
|
+
});
|
|
22891
23125
|
object({
|
|
22892
23126
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22893
23127
|
* failure to reach the target — operator intervention required. */
|
|
@@ -24068,7 +24302,12 @@ var PlateInfoSchema = object({
|
|
|
24068
24302
|
plateBbox: BoundingBoxSchema.optional(),
|
|
24069
24303
|
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
24070
24304
|
keyFrameMediaKey: string().optional(),
|
|
24071
|
-
base64: string().optional()
|
|
24305
|
+
base64: string().optional(),
|
|
24306
|
+
/**
|
|
24307
|
+
* Same crop as a data-plane URL. `getPlateByTrack` returns this and
|
|
24308
|
+
* never inlines JPEG; `listPlates` still inlines for the admin-ui.
|
|
24309
|
+
*/
|
|
24310
|
+
cropUrl: string().optional()
|
|
24072
24311
|
});
|
|
24073
24312
|
var MediaFileLiteSchema = object({
|
|
24074
24313
|
key: string(),
|
|
@@ -28650,6 +28889,12 @@ Object.freeze({
|
|
|
28650
28889
|
addonId: null,
|
|
28651
28890
|
access: "create"
|
|
28652
28891
|
},
|
|
28892
|
+
"localNetwork.downloadCa": {
|
|
28893
|
+
capName: "local-network",
|
|
28894
|
+
capScope: "system",
|
|
28895
|
+
addonId: null,
|
|
28896
|
+
access: "view"
|
|
28897
|
+
},
|
|
28653
28898
|
"localNetwork.getAllowedAddresses": {
|
|
28654
28899
|
capName: "local-network",
|
|
28655
28900
|
capScope: "system",
|
|
@@ -28674,18 +28919,42 @@ Object.freeze({
|
|
|
28674
28919
|
addonId: null,
|
|
28675
28920
|
access: "view"
|
|
28676
28921
|
},
|
|
28922
|
+
"localNetwork.getTlsStatus": {
|
|
28923
|
+
capName: "local-network",
|
|
28924
|
+
capScope: "system",
|
|
28925
|
+
addonId: null,
|
|
28926
|
+
access: "view"
|
|
28927
|
+
},
|
|
28928
|
+
"localNetwork.getViewerEndpoints": {
|
|
28929
|
+
capName: "local-network",
|
|
28930
|
+
capScope: "system",
|
|
28931
|
+
addonId: null,
|
|
28932
|
+
access: "view"
|
|
28933
|
+
},
|
|
28677
28934
|
"localNetwork.list": {
|
|
28678
28935
|
capName: "local-network",
|
|
28679
28936
|
capScope: "system",
|
|
28680
28937
|
addonId: null,
|
|
28681
28938
|
access: "view"
|
|
28682
28939
|
},
|
|
28940
|
+
"localNetwork.regenerateCertificate": {
|
|
28941
|
+
capName: "local-network",
|
|
28942
|
+
capScope: "system",
|
|
28943
|
+
addonId: null,
|
|
28944
|
+
access: "create"
|
|
28945
|
+
},
|
|
28683
28946
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
28684
28947
|
capName: "local-network",
|
|
28685
28948
|
capScope: "system",
|
|
28686
28949
|
addonId: null,
|
|
28687
28950
|
access: "delete"
|
|
28688
28951
|
},
|
|
28952
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
28953
|
+
capName: "local-network",
|
|
28954
|
+
capScope: "system",
|
|
28955
|
+
addonId: null,
|
|
28956
|
+
access: "create"
|
|
28957
|
+
},
|
|
28689
28958
|
"localNetwork.setAllowedAddresses": {
|
|
28690
28959
|
capName: "local-network",
|
|
28691
28960
|
capScope: "system",
|
|
@@ -28698,6 +28967,18 @@ Object.freeze({
|
|
|
28698
28967
|
addonId: null,
|
|
28699
28968
|
access: "create"
|
|
28700
28969
|
},
|
|
28970
|
+
"localNetwork.setViewerEndpoints": {
|
|
28971
|
+
capName: "local-network",
|
|
28972
|
+
capScope: "system",
|
|
28973
|
+
addonId: null,
|
|
28974
|
+
access: "create"
|
|
28975
|
+
},
|
|
28976
|
+
"localNetwork.uploadCertificate": {
|
|
28977
|
+
capName: "local-network",
|
|
28978
|
+
capScope: "system",
|
|
28979
|
+
addonId: null,
|
|
28980
|
+
access: "create"
|
|
28981
|
+
},
|
|
28701
28982
|
"lockControl.lock": {
|
|
28702
28983
|
capName: "lock-control",
|
|
28703
28984
|
capScope: "device",
|
|
@@ -29496,6 +29777,12 @@ Object.freeze({
|
|
|
29496
29777
|
addonId: null,
|
|
29497
29778
|
access: "view"
|
|
29498
29779
|
},
|
|
29780
|
+
"pipelineAnalytics.getGroup": {
|
|
29781
|
+
capName: "pipeline-analytics",
|
|
29782
|
+
capScope: "device",
|
|
29783
|
+
addonId: null,
|
|
29784
|
+
access: "view"
|
|
29785
|
+
},
|
|
29499
29786
|
"pipelineAnalytics.getKeyEvents": {
|
|
29500
29787
|
capName: "pipeline-analytics",
|
|
29501
29788
|
capScope: "device",
|
|
@@ -29580,6 +29867,12 @@ Object.freeze({
|
|
|
29580
29867
|
addonId: null,
|
|
29581
29868
|
access: "view"
|
|
29582
29869
|
},
|
|
29870
|
+
"pipelineAnalytics.listGroups": {
|
|
29871
|
+
capName: "pipeline-analytics",
|
|
29872
|
+
capScope: "device",
|
|
29873
|
+
addonId: null,
|
|
29874
|
+
access: "view"
|
|
29875
|
+
},
|
|
29583
29876
|
"pipelineAnalytics.listOpsLog": {
|
|
29584
29877
|
capName: "pipeline-analytics",
|
|
29585
29878
|
capScope: "device",
|
|
@@ -31578,6 +31871,12 @@ Object.freeze({
|
|
|
31578
31871
|
addonId: null,
|
|
31579
31872
|
access: "create"
|
|
31580
31873
|
},
|
|
31874
|
+
"terminalSession.updateInstance": {
|
|
31875
|
+
capName: "terminal-session",
|
|
31876
|
+
capScope: "system",
|
|
31877
|
+
addonId: null,
|
|
31878
|
+
access: "create"
|
|
31879
|
+
},
|
|
31581
31880
|
"terminalSession.writeInput": {
|
|
31582
31881
|
capName: "terminal-session",
|
|
31583
31882
|
capScope: "system",
|
|
@@ -32993,6 +33292,11 @@ Object.freeze({
|
|
|
32993
33292
|
form: "single",
|
|
32994
33293
|
optional: false
|
|
32995
33294
|
}],
|
|
33295
|
+
"pipelineAnalytics.getGroup": [{
|
|
33296
|
+
name: "deviceId",
|
|
33297
|
+
form: "single",
|
|
33298
|
+
optional: false
|
|
33299
|
+
}],
|
|
32996
33300
|
"pipelineAnalytics.getKeyEvents": [{
|
|
32997
33301
|
name: "deviceId",
|
|
32998
33302
|
form: "single",
|
|
@@ -33048,6 +33352,11 @@ Object.freeze({
|
|
|
33048
33352
|
form: "array",
|
|
33049
33353
|
optional: false
|
|
33050
33354
|
}],
|
|
33355
|
+
"pipelineAnalytics.listGroups": [{
|
|
33356
|
+
name: "deviceIds",
|
|
33357
|
+
form: "array",
|
|
33358
|
+
optional: false
|
|
33359
|
+
}],
|
|
33051
33360
|
"pipelineAnalytics.listOpsLog": [{
|
|
33052
33361
|
name: "deviceId",
|
|
33053
33362
|
form: "single",
|
|
@@ -34065,6 +34374,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
34065
34374
|
}]
|
|
34066
34375
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
34067
34376
|
string().min(1);
|
|
34377
|
+
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
34378
|
+
stepId: "face-embedding",
|
|
34379
|
+
key: "minLandmarkFaceSize",
|
|
34380
|
+
label: "Min face size for recognition (detection px)",
|
|
34381
|
+
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.",
|
|
34382
|
+
type: "slider",
|
|
34383
|
+
min: 0,
|
|
34384
|
+
max: 64,
|
|
34385
|
+
step: 2,
|
|
34386
|
+
default: 24
|
|
34387
|
+
}];
|
|
34388
|
+
function clusterStepSettingKey(stepId, fieldKey) {
|
|
34389
|
+
return `clusterStepSetting:${stepId}:${fieldKey}`;
|
|
34390
|
+
}
|
|
34391
|
+
var ClusterSettingNumberSchema = number$1().finite();
|
|
34392
|
+
function readClusterStepSettings(config) {
|
|
34393
|
+
const out = {};
|
|
34394
|
+
for (const field of CLUSTER_STEP_SETTING_FIELDS) {
|
|
34395
|
+
const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
|
|
34396
|
+
const value = parsed.success ? parsed.data : field.default;
|
|
34397
|
+
const existing = out[field.stepId] ?? {};
|
|
34398
|
+
out[field.stepId] = {
|
|
34399
|
+
...existing,
|
|
34400
|
+
[field.key]: value
|
|
34401
|
+
};
|
|
34402
|
+
}
|
|
34403
|
+
return out;
|
|
34404
|
+
}
|
|
34405
|
+
readClusterStepSettings({});
|
|
34068
34406
|
object({
|
|
34069
34407
|
/**
|
|
34070
34408
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -71741,7 +72079,7 @@ function entryForRef(ref) {
|
|
|
71741
72079
|
};
|
|
71742
72080
|
}
|
|
71743
72081
|
//#endregion
|
|
71744
|
-
//#region ../system/dist/file-data-plane-
|
|
72082
|
+
//#region ../system/dist/file-data-plane-BhKdxJgf.mjs
|
|
71745
72083
|
function isNonEmptyFile(filePath) {
|
|
71746
72084
|
return fs.existsSync(filePath) && fs.statSync(filePath).size > 0;
|
|
71747
72085
|
}
|
|
@@ -71763,21 +72101,56 @@ function buildHeaders(url) {
|
|
|
71763
72101
|
if (hfToken && url.includes("huggingface.co")) headers["Authorization"] = `Bearer ${hfToken}`;
|
|
71764
72102
|
return headers;
|
|
71765
72103
|
}
|
|
71766
|
-
|
|
71767
|
-
|
|
71768
|
-
|
|
71769
|
-
|
|
71770
|
-
|
|
71771
|
-
|
|
71772
|
-
|
|
72104
|
+
var DEFAULT_MAX_REDIRECTS = 5;
|
|
72105
|
+
function normalizeDownloadOptions(third) {
|
|
72106
|
+
if (typeof third === "function") return { onProgress: third };
|
|
72107
|
+
return third ?? {};
|
|
72108
|
+
}
|
|
72109
|
+
function isRedirectStatus(status) {
|
|
72110
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
72111
|
+
}
|
|
72112
|
+
function resolveRedirectUrl(current, location) {
|
|
72113
|
+
return new URL(location, current);
|
|
72114
|
+
}
|
|
72115
|
+
async function downloadFile(url, destPath, onProgressOrOptions) {
|
|
71773
72116
|
if (fs.existsSync(destPath)) return destPath;
|
|
72117
|
+
const opts = normalizeDownloadOptions(onProgressOrOptions);
|
|
72118
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
72119
|
+
const maxRedirects = opts.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
|
|
71774
72120
|
fs.mkdirSync(path$1.dirname(destPath), { recursive: true });
|
|
71775
72121
|
const tmpPath = destPath + ".downloading";
|
|
71776
72122
|
try {
|
|
71777
|
-
|
|
71778
|
-
|
|
71779
|
-
|
|
71780
|
-
|
|
72123
|
+
let current = url;
|
|
72124
|
+
const seen = /* @__PURE__ */ new Set();
|
|
72125
|
+
let response;
|
|
72126
|
+
const manual = opts.redirectPolicy !== void 0;
|
|
72127
|
+
for (let hop = 0; hop <= maxRedirects; hop++) {
|
|
72128
|
+
const parsed = new URL(current);
|
|
72129
|
+
if (opts.redirectPolicy) opts.redirectPolicy(parsed, hop);
|
|
72130
|
+
if (seen.has(parsed.href)) throw new Error(`Redirect loop detected at ${parsed.href}`);
|
|
72131
|
+
seen.add(parsed.href);
|
|
72132
|
+
const controller = opts.timeoutMs !== void 0 ? new AbortController() : void 0;
|
|
72133
|
+
const timer = controller && opts.timeoutMs !== void 0 ? setTimeout(() => controller.abort(), opts.timeoutMs) : void 0;
|
|
72134
|
+
try {
|
|
72135
|
+
response = await fetchImpl(current, {
|
|
72136
|
+
redirect: manual ? "manual" : "follow",
|
|
72137
|
+
headers: buildHeaders(current),
|
|
72138
|
+
...controller ? { signal: controller.signal } : {}
|
|
72139
|
+
});
|
|
72140
|
+
} finally {
|
|
72141
|
+
if (timer) clearTimeout(timer);
|
|
72142
|
+
}
|
|
72143
|
+
if (manual && isRedirectStatus(response.status)) {
|
|
72144
|
+
const location = response.headers.get("location");
|
|
72145
|
+
if (!location) throw new Error(`Redirect ${response.status} missing Location header`);
|
|
72146
|
+
if (hop >= maxRedirects) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
|
|
72147
|
+
current = resolveRedirectUrl(current, location).href;
|
|
72148
|
+
continue;
|
|
72149
|
+
}
|
|
72150
|
+
break;
|
|
72151
|
+
}
|
|
72152
|
+
if (!response) throw new Error(`No response downloading ${url}`);
|
|
72153
|
+
if (manual && isRedirectStatus(response.status)) throw new Error(`Too many redirects (max ${maxRedirects}) downloading ${url}`);
|
|
71781
72154
|
if (!response.ok) throw new Error(`HTTP ${response.status} downloading ${url}`);
|
|
71782
72155
|
if (!response.body) throw new Error(`No response body from ${url}`);
|
|
71783
72156
|
const total = parseInt(response.headers.get("content-length") ?? "0", 10);
|
|
@@ -71788,9 +72161,10 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
71788
72161
|
for (;;) {
|
|
71789
72162
|
const { done, value } = await reader.read();
|
|
71790
72163
|
if (done || !value) break;
|
|
71791
|
-
fileStream.write(value);
|
|
71792
72164
|
downloaded += value.length;
|
|
71793
|
-
|
|
72165
|
+
if (opts.maxBytes !== void 0 && downloaded > opts.maxBytes) throw new Error(`Downloaded ${downloaded} bytes exceeds the ${opts.maxBytes}-byte limit`);
|
|
72166
|
+
fileStream.write(value);
|
|
72167
|
+
opts.onProgress?.(downloaded, total);
|
|
71794
72168
|
}
|
|
71795
72169
|
} finally {
|
|
71796
72170
|
fileStream.end();
|