@norskvideo/norsk-auto-manager 0.1.0

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.
@@ -0,0 +1,14 @@
1
+ export interface CancellableTimer {
2
+ cancel(): void;
3
+ }
4
+ export interface Clock {
5
+ now(): Date;
6
+ setTimeout(fn: () => void, ms: number): CancellableTimer;
7
+ setInterval(fn: () => void, ms: number): CancellableTimer;
8
+ }
9
+ export declare class RealClock implements Clock {
10
+ now(): Date;
11
+ setTimeout(fn: () => void, ms: number): CancellableTimer;
12
+ setInterval(fn: () => void, ms: number): CancellableTimer;
13
+ }
14
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // Clock abstraction so AutoManager can be tested deterministically.
3
+ // Production code uses RealClock; tests inject a MockClock that exposes
4
+ // advanceTime() to drive timers manually.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RealClock = void 0;
7
+ class RealClock {
8
+ now() {
9
+ return new Date();
10
+ }
11
+ setTimeout(fn, ms) {
12
+ const handle = setTimeout(fn, ms);
13
+ return { cancel: () => clearTimeout(handle) };
14
+ }
15
+ setInterval(fn, ms) {
16
+ const handle = setInterval(fn, ms);
17
+ return { cancel: () => clearInterval(handle) };
18
+ }
19
+ }
20
+ exports.RealClock = RealClock;
21
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1,59 @@
1
+ import * as ManagerPB from "@norskvideo/norsk-api/lib/manager_pb";
2
+ import * as CommonPB from "@norskvideo/norsk-api/lib/shared/common_pb";
3
+ import { Bundle, BundleJobSpec, BundleState, Capability, CapabilityRequirement, GpuResource, IntraReplicaRules, JobRequirements, LaunchSpec, NodeAssignedJob, NodeInventory, PriorityBand, ResiliencePolicy } from "./types";
4
+ export declare function fromPbPriorityBand(pb: ManagerPB.PriorityBand): PriorityBand;
5
+ export declare function fromPbPlacementRule(pb: ManagerPB.PlacementRule): "off" | "soft" | "hard";
6
+ export declare function fromPbCapability(pb: CommonPB.Capability): Capability;
7
+ export declare function fromPbCapabilityRequirement(pb: CommonPB.CapabilityRequirement): CapabilityRequirement;
8
+ export declare function fromPbGpuResource(pb: CommonPB.GpuResource): GpuResource;
9
+ export declare function fromPbJobRequirements(pb: ManagerPB.JobRequirements): JobRequirements;
10
+ export declare function fromPbBundle(pb: ManagerPB.Bundle): Bundle;
11
+ export declare function fromPbBundleState(pb: ManagerPB.BundleState): BundleState;
12
+ export declare function toPbBundleState(s: BundleState): ManagerPB.BundleState;
13
+ export declare function fromPbBundleJobSpec(pb: ManagerPB.BundleJobSpec): BundleJobSpec;
14
+ /**
15
+ * Reconstruct the typed `LaunchSpec` from the wire-form `Job` carried
16
+ * on a `BundleJobSpec`. If the carried Job has `productTemplate` set, this
17
+ * is a product-template launch; otherwise it's the legacy services/volumes
18
+ * path. The legacy variant passes the Job through as-is (cast to the
19
+ * SDK's typed shape — same trick the v0 code used).
20
+ */
21
+ /** Tags starting with this prefix are SDK-managed (well-known reserved
22
+ * names) — `__stopDateTime` carries the auto-stop instant for example.
23
+ * Callers can't set them via `LaunchSpec.tags` (silently stripped on
24
+ * outbound) and they're surfaced as typed fields rather than raw tags
25
+ * on inbound. */
26
+ export declare const RESERVED_TAG_PREFIX = "__";
27
+ /** Well-known reserved tag carrying the operator's submitted auto-stop
28
+ * instant. The daemon has no first-class concept of stop time, so we
29
+ * ride along on its tag-preservation behaviour. */
30
+ export declare const STOP_DATE_TIME_TAG = "__stopDateTime";
31
+ export declare function fromPbLaunchSpec(pbJob: ManagerPB.Job): LaunchSpec;
32
+ export declare function fromPbIntraReplicaRules(pb: ManagerPB.IntraReplicaRules): IntraReplicaRules;
33
+ export declare function fromPbResiliencePolicy(pb: ManagerPB.ResiliencePolicy): ResiliencePolicy;
34
+ export declare function fromPbNodeInventory(pb: CommonPB.NodeInventory): NodeInventory;
35
+ export declare function fromPbNodeAssignedJob(pb: CommonPB.NodeAssignedJob): NodeAssignedJob;
36
+ import { PlacementRule, PriorityBand as PbPriorityBand } from "@norskvideo/norsk-api/lib/manager_pb";
37
+ export declare function toPbPriorityBand(b: PriorityBand): PbPriorityBand;
38
+ export declare function toPbPlacementRule(r: "off" | "soft" | "hard"): PlacementRule;
39
+ export declare function toPbCapability(c: Capability): CommonPB.Capability;
40
+ export declare function toPbCapabilityRequirement(r: CapabilityRequirement): CommonPB.CapabilityRequirement;
41
+ export declare function toPbJobRequirements(r: JobRequirements): ManagerPB.JobRequirements;
42
+ export declare function toPbBundleJobSpec(j: BundleJobSpec): ManagerPB.BundleJobSpec;
43
+ /**
44
+ * Project a typed `LaunchSpec` into the wire-form `Job` slot on
45
+ * `BundleJobSpec`. Manager stamps this template per replica during
46
+ * bundle expansion (setting jobId, tags, state, currentHash). For the
47
+ * product-template variant only `productTemplate`, `productTemplateParameters`, and
48
+ * `startDateTime` are meaningful — the rest stays at proto defaults.
49
+ * The worker reads `productTemplate` + `productTemplateParameters` and ignores
50
+ * services / volumes / writeFiles.
51
+ *
52
+ * `startDateTime` defaults to `new Date()` (immediate) when callers
53
+ * omit it; Manager-daemon's `apiToJobTemplate` rejects unset values.
54
+ */
55
+ export declare function toPbLaunchSpec(launch: LaunchSpec): ManagerPB.Job;
56
+ export declare function toPbIntraReplicaRules(r: IntraReplicaRules): ManagerPB.IntraReplicaRules;
57
+ export declare function toPbResiliencePolicy(r: ResiliencePolicy): ManagerPB.ResiliencePolicy;
58
+ export declare function toPbBundle(b: Bundle): ManagerPB.Bundle;
59
+ //# sourceMappingURL=conversions.d.ts.map
@@ -0,0 +1,416 @@
1
+ "use strict";
2
+ // PB ↔ AutoManager-typed conversions. The SDK passes raw protobuf
3
+ // payloads through for the new AutoManager events; AutoManager runs
4
+ // these converters at the boundary so the rest of the codebase works
5
+ // against the cleaner typed shapes from `./types`.
6
+ //
7
+ // Converters are deliberately permissive about defaults: an empty map
8
+ // from PB becomes `undefined` to match the optional-field convention
9
+ // used in the typed model, an absent ID wrapper stays the empty string.
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.STOP_DATE_TIME_TAG = exports.RESERVED_TAG_PREFIX = void 0;
45
+ exports.fromPbPriorityBand = fromPbPriorityBand;
46
+ exports.fromPbPlacementRule = fromPbPlacementRule;
47
+ exports.fromPbCapability = fromPbCapability;
48
+ exports.fromPbCapabilityRequirement = fromPbCapabilityRequirement;
49
+ exports.fromPbGpuResource = fromPbGpuResource;
50
+ exports.fromPbJobRequirements = fromPbJobRequirements;
51
+ exports.fromPbBundle = fromPbBundle;
52
+ exports.fromPbBundleState = fromPbBundleState;
53
+ exports.toPbBundleState = toPbBundleState;
54
+ exports.fromPbBundleJobSpec = fromPbBundleJobSpec;
55
+ exports.fromPbLaunchSpec = fromPbLaunchSpec;
56
+ exports.fromPbIntraReplicaRules = fromPbIntraReplicaRules;
57
+ exports.fromPbResiliencePolicy = fromPbResiliencePolicy;
58
+ exports.fromPbNodeInventory = fromPbNodeInventory;
59
+ exports.fromPbNodeAssignedJob = fromPbNodeAssignedJob;
60
+ exports.toPbPriorityBand = toPbPriorityBand;
61
+ exports.toPbPlacementRule = toPbPlacementRule;
62
+ exports.toPbCapability = toPbCapability;
63
+ exports.toPbCapabilityRequirement = toPbCapabilityRequirement;
64
+ exports.toPbJobRequirements = toPbJobRequirements;
65
+ exports.toPbBundleJobSpec = toPbBundleJobSpec;
66
+ exports.toPbLaunchSpec = toPbLaunchSpec;
67
+ exports.toPbIntraReplicaRules = toPbIntraReplicaRules;
68
+ exports.toPbResiliencePolicy = toPbResiliencePolicy;
69
+ exports.toPbBundle = toPbBundle;
70
+ const protobuf_1 = require("@bufbuild/protobuf");
71
+ const wkt_1 = require("@bufbuild/protobuf/wkt");
72
+ const ManagerPB = __importStar(require("@norskvideo/norsk-api/lib/manager_pb"));
73
+ const common_pb_1 = require("@norskvideo/norsk-api/lib/shared/common_pb");
74
+ const norsk_manager_sdk_1 = require("@norskvideo/norsk-manager-sdk");
75
+ const manager_pb_1 = require("@norskvideo/norsk-api/lib/manager_pb");
76
+ // ----- enums -----------------------------------------------------------
77
+ function fromPbPriorityBand(pb) {
78
+ switch (pb) {
79
+ case ManagerPB.PriorityBand.GOLD:
80
+ return "gold";
81
+ case ManagerPB.PriorityBand.SILVER:
82
+ return "silver";
83
+ case ManagerPB.PriorityBand.BRONZE:
84
+ return "bronze";
85
+ }
86
+ }
87
+ function fromPbPlacementRule(pb) {
88
+ switch (pb) {
89
+ case ManagerPB.PlacementRule.OFF:
90
+ return "off";
91
+ case ManagerPB.PlacementRule.SOFT:
92
+ return "soft";
93
+ case ManagerPB.PlacementRule.HARD:
94
+ return "hard";
95
+ }
96
+ }
97
+ // ----- capabilities ----------------------------------------------------
98
+ function fromPbCapability(pb) {
99
+ return {
100
+ name: pb.name,
101
+ count: pb.count,
102
+ attributes: emptyMapToUndefined(pb.attributes),
103
+ };
104
+ }
105
+ function fromPbCapabilityRequirement(pb) {
106
+ return {
107
+ name: pb.name,
108
+ count: pb.count,
109
+ attributeMatches: emptyMapToUndefined(pb.attributeMatches),
110
+ };
111
+ }
112
+ // ----- GPU -------------------------------------------------------------
113
+ function fromPbGpuResource(pb) {
114
+ return {
115
+ index: pb.index,
116
+ model: pb.model?.value,
117
+ totalCapacity: pb.totalCapacity,
118
+ reservedCapacity: pb.reservedCapacity,
119
+ exclusivelyReserved: pb.exclusivelyReserved,
120
+ };
121
+ }
122
+ // ----- requirements ----------------------------------------------------
123
+ function fromPbJobRequirements(pb) {
124
+ return {
125
+ requiredCapacity: pb.requiredCapacity,
126
+ requiredCores: pb.requiredCores?.value,
127
+ requiredGpuCapacity: pb.requiredGpuCapacity?.value,
128
+ requiredGpuModel: pb.requiredGpuModel?.value,
129
+ requiredCapabilities: pb.requiredCapabilities.map(fromPbCapabilityRequirement),
130
+ priorityBand: fromPbPriorityBand(pb.priorityBand),
131
+ };
132
+ }
133
+ // ----- bundle ----------------------------------------------------------
134
+ function fromPbBundle(pb) {
135
+ return {
136
+ bundleId: bundleIdString(pb.bundleId),
137
+ pool: pb.pool,
138
+ jobs: pb.jobs.map(fromPbBundleJobSpec),
139
+ intraReplicaPlacement: pb.intraReplicaPlacement
140
+ ? fromPbIntraReplicaRules(pb.intraReplicaPlacement)
141
+ : undefined,
142
+ resiliencePolicy: pb.resiliencePolicy ? fromPbResiliencePolicy(pb.resiliencePolicy) : undefined,
143
+ state: fromPbBundleState(pb.state),
144
+ };
145
+ }
146
+ function fromPbBundleState(pb) {
147
+ switch (pb) {
148
+ case ManagerPB.BundleState.DELETING:
149
+ return "deleting";
150
+ default:
151
+ return "live";
152
+ }
153
+ }
154
+ function toPbBundleState(s) {
155
+ switch (s) {
156
+ case "deleting":
157
+ return ManagerPB.BundleState.DELETING;
158
+ case "live":
159
+ return ManagerPB.BundleState.LIVE;
160
+ }
161
+ }
162
+ function fromPbBundleJobSpec(pb) {
163
+ return {
164
+ jobName: pb.jobName,
165
+ requirements: pb.requirements
166
+ ? fromPbJobRequirements(pb.requirements)
167
+ : {},
168
+ coLocateWith: emptyArrayToUndefined(pb.coLocateWith),
169
+ separateFrom: emptyArrayToUndefined(pb.separateFrom),
170
+ launch: pb.job ? fromPbLaunchSpec(pb.job) : undefined,
171
+ };
172
+ }
173
+ /**
174
+ * Reconstruct the typed `LaunchSpec` from the wire-form `Job` carried
175
+ * on a `BundleJobSpec`. If the carried Job has `productTemplate` set, this
176
+ * is a product-template launch; otherwise it's the legacy services/volumes
177
+ * path. The legacy variant passes the Job through as-is (cast to the
178
+ * SDK's typed shape — same trick the v0 code used).
179
+ */
180
+ /** Tags starting with this prefix are SDK-managed (well-known reserved
181
+ * names) — `__stopDateTime` carries the auto-stop instant for example.
182
+ * Callers can't set them via `LaunchSpec.tags` (silently stripped on
183
+ * outbound) and they're surfaced as typed fields rather than raw tags
184
+ * on inbound. */
185
+ exports.RESERVED_TAG_PREFIX = "__";
186
+ /** Well-known reserved tag carrying the operator's submitted auto-stop
187
+ * instant. The daemon has no first-class concept of stop time, so we
188
+ * ride along on its tag-preservation behaviour. */
189
+ exports.STOP_DATE_TIME_TAG = "__stopDateTime";
190
+ function buildJobTags(stopDateTime, userTags) {
191
+ const out = {};
192
+ if (userTags) {
193
+ for (const [k, v] of Object.entries(userTags)) {
194
+ if (k.startsWith(exports.RESERVED_TAG_PREFIX))
195
+ continue; // silently strip
196
+ out[k] = v;
197
+ }
198
+ }
199
+ if (stopDateTime)
200
+ out[exports.STOP_DATE_TIME_TAG] = stopDateTime.toISOString();
201
+ return out;
202
+ }
203
+ function splitJobTags(tags) {
204
+ const userTags = {};
205
+ let stopDateTime;
206
+ for (const [k, v] of Object.entries(tags)) {
207
+ if (k === exports.STOP_DATE_TIME_TAG) {
208
+ const d = new Date(v);
209
+ if (!Number.isNaN(d.getTime()))
210
+ stopDateTime = d;
211
+ continue;
212
+ }
213
+ if (k.startsWith(exports.RESERVED_TAG_PREFIX))
214
+ continue; // unknown reserved — hide
215
+ userTags[k] = v;
216
+ }
217
+ return {
218
+ ...(stopDateTime ? { stopDateTime } : {}),
219
+ ...(Object.keys(userTags).length > 0 ? { userTags } : {}),
220
+ };
221
+ }
222
+ function fromPbLaunchSpec(pbJob) {
223
+ if (pbJob.productTemplate) {
224
+ const { stopDateTime, userTags } = splitJobTags(pbJob.tags ?? {});
225
+ return {
226
+ kind: "productTemplate",
227
+ ref: (0, norsk_manager_sdk_1.fromProductTemplateRef)(pbJob.productTemplate),
228
+ parameters: Object.keys(pbJob.productTemplateParameters).length > 0
229
+ ? pbJob.productTemplateParameters
230
+ : undefined,
231
+ // Preserve the operator's submitted schedule so consumers can
232
+ // surface it even before a JobPending event has been streamed
233
+ // (e.g. bundles in mgr's bundleMap whose materialised jobs
234
+ // haven't arrived yet — calendar view depends on this).
235
+ ...(pbJob.startDateTime ? { startDateTime: (0, wkt_1.timestampDate)(pbJob.startDateTime) } : {}),
236
+ ...(stopDateTime ? { stopDateTime } : {}),
237
+ ...(userTags ? { tags: userTags } : {}),
238
+ };
239
+ }
240
+ return { kind: "legacy", job: pbJob };
241
+ }
242
+ function fromPbIntraReplicaRules(pb) {
243
+ return {
244
+ sameNodeDefault: pb.sameNodeDefault,
245
+ sameAz: fromPbPlacementRule(pb.sameAz),
246
+ };
247
+ }
248
+ function fromPbResiliencePolicy(pb) {
249
+ return {
250
+ ...(pb.pool.length > 0 ? { pool: pb.pool } : {}),
251
+ sameNode: fromPbAntiAffinity(pb.sameNode),
252
+ sameAz: fromPbAntiAffinity(pb.sameAz),
253
+ sameCloud: fromPbAntiAffinity(pb.sameCloud),
254
+ };
255
+ }
256
+ function fromPbAntiAffinity(pb) {
257
+ return pb === ManagerPB.AntiAffinity.FORBID ? "forbid" : "allow";
258
+ }
259
+ function toPbAntiAffinity(a) {
260
+ return a === "forbid" ? ManagerPB.AntiAffinity.FORBID : ManagerPB.AntiAffinity.ALLOW;
261
+ }
262
+ // ----- inventory -------------------------------------------------------
263
+ function fromPbNodeInventory(pb) {
264
+ return {
265
+ totalCapacity: pb.totalCapacity,
266
+ utilizationCap: pb.utilizationCap,
267
+ totalCores: pb.totalCores,
268
+ gpus: pb.gpus.map(fromPbGpuResource),
269
+ capabilities: pb.capabilities.map(fromPbCapability),
270
+ reservedCapacity: pb.reservedCapacity,
271
+ reservedCores: pb.reservedCores,
272
+ reservedCapabilities: pb.reservedCapabilities.map(fromPbCapability),
273
+ reachable: pb.reachable,
274
+ lastSeen: pb.lastSeen
275
+ ? new Date(Number(pb.lastSeen.seconds) * 1000 + (pb.lastSeen.nanos ?? 0) / 1000000)
276
+ : new Date(0),
277
+ cordoned: pb.cordoned,
278
+ assignedJobs: pb.assignedJobs.map(fromPbNodeAssignedJob),
279
+ };
280
+ }
281
+ function fromPbNodeAssignedJob(pb) {
282
+ return {
283
+ jobId: jobIdString(pb.jobId),
284
+ coreSet: pb.coreSet.length > 0 ? [...pb.coreSet] : undefined,
285
+ gpuIndex: pb.gpuIndex?.value,
286
+ };
287
+ }
288
+ // ----- helpers ---------------------------------------------------------
289
+ function bundleIdString(pb) {
290
+ return pb?.bundleId ?? "";
291
+ }
292
+ function jobIdString(pb) {
293
+ return pb?.jobId ?? "";
294
+ }
295
+ function emptyMapToUndefined(m) {
296
+ return Object.keys(m).length > 0 ? { ...m } : undefined;
297
+ }
298
+ function emptyArrayToUndefined(arr) {
299
+ return arr.length > 0 ? [...arr] : undefined;
300
+ }
301
+ // =============================================================================
302
+ // AutoManager-typed → PB conversions. Used when AutoManager submits a Bundle
303
+ // (or future request types) over the wire.
304
+ // =============================================================================
305
+ const manager_pb_2 = require("@norskvideo/norsk-api/lib/manager_pb");
306
+ const common_pb_2 = require("@norskvideo/norsk-api/lib/shared/common_pb");
307
+ function toPbPriorityBand(b) {
308
+ switch (b) {
309
+ case "gold":
310
+ return manager_pb_2.PriorityBand.GOLD;
311
+ case "silver":
312
+ return manager_pb_2.PriorityBand.SILVER;
313
+ case "bronze":
314
+ return manager_pb_2.PriorityBand.BRONZE;
315
+ }
316
+ }
317
+ function toPbPlacementRule(r) {
318
+ switch (r) {
319
+ case "off":
320
+ return manager_pb_2.PlacementRule.OFF;
321
+ case "soft":
322
+ return manager_pb_2.PlacementRule.SOFT;
323
+ case "hard":
324
+ return manager_pb_2.PlacementRule.HARD;
325
+ }
326
+ }
327
+ function toPbCapability(c) {
328
+ return (0, protobuf_1.create)(common_pb_1.CapabilitySchema, {
329
+ name: c.name,
330
+ count: c.count,
331
+ attributes: c.attributes ?? {},
332
+ });
333
+ }
334
+ function toPbCapabilityRequirement(r) {
335
+ return (0, protobuf_1.create)(common_pb_1.CapabilityRequirementSchema, {
336
+ name: r.name,
337
+ count: r.count,
338
+ attributeMatches: r.attributeMatches ?? {},
339
+ });
340
+ }
341
+ function toPbJobRequirements(r) {
342
+ return (0, protobuf_1.create)(manager_pb_2.JobRequirementsSchema, {
343
+ requiredCapacity: r.requiredCapacity,
344
+ requiredCores: r.requiredCores !== undefined
345
+ ? (0, protobuf_1.create)(common_pb_1.OptionalIntSchema, { value: r.requiredCores })
346
+ : undefined,
347
+ requiredGpuCapacity: r.requiredGpuCapacity !== undefined
348
+ ? (0, protobuf_1.create)(common_pb_2.OptionalFloatSchema, { value: r.requiredGpuCapacity })
349
+ : undefined,
350
+ requiredGpuModel: r.requiredGpuModel !== undefined
351
+ ? (0, protobuf_1.create)(common_pb_1.OptionalStringSchema, { value: r.requiredGpuModel })
352
+ : undefined,
353
+ requiredCapabilities: r.requiredCapabilities.map(toPbCapabilityRequirement),
354
+ priorityBand: toPbPriorityBand(r.priorityBand),
355
+ });
356
+ }
357
+ function toPbBundleJobSpec(j) {
358
+ return (0, protobuf_1.create)(manager_pb_2.BundleJobSpecSchema, {
359
+ jobName: j.jobName,
360
+ requirements: toPbJobRequirements(j.requirements),
361
+ coLocateWith: j.coLocateWith ?? [],
362
+ separateFrom: j.separateFrom ?? [],
363
+ job: j.launch ? toPbLaunchSpec(j.launch) : undefined,
364
+ });
365
+ }
366
+ /**
367
+ * Project a typed `LaunchSpec` into the wire-form `Job` slot on
368
+ * `BundleJobSpec`. Manager stamps this template per replica during
369
+ * bundle expansion (setting jobId, tags, state, currentHash). For the
370
+ * product-template variant only `productTemplate`, `productTemplateParameters`, and
371
+ * `startDateTime` are meaningful — the rest stays at proto defaults.
372
+ * The worker reads `productTemplate` + `productTemplateParameters` and ignores
373
+ * services / volumes / writeFiles.
374
+ *
375
+ * `startDateTime` defaults to `new Date()` (immediate) when callers
376
+ * omit it; Manager-daemon's `apiToJobTemplate` rejects unset values.
377
+ */
378
+ function toPbLaunchSpec(launch) {
379
+ if (launch.kind === "productTemplate") {
380
+ const tags = buildJobTags(launch.stopDateTime, launch.tags);
381
+ return (0, protobuf_1.create)(manager_pb_1.JobSchema, {
382
+ startDateTime: (0, wkt_1.timestampFromDate)(launch.startDateTime ?? new Date()),
383
+ productTemplate: (0, norsk_manager_sdk_1.toProductTemplateRef)(launch.ref),
384
+ productTemplateParameters: launch.parameters ?? {},
385
+ ...(Object.keys(tags).length > 0 ? { tags } : {}),
386
+ });
387
+ }
388
+ return launch.job;
389
+ }
390
+ function toPbIntraReplicaRules(r) {
391
+ return (0, protobuf_1.create)(manager_pb_2.IntraReplicaRulesSchema, {
392
+ sameNodeDefault: r.sameNodeDefault,
393
+ sameAz: toPbPlacementRule(r.sameAz),
394
+ });
395
+ }
396
+ function toPbResiliencePolicy(r) {
397
+ return (0, protobuf_1.create)(manager_pb_2.ResiliencePolicySchema, {
398
+ pool: r.pool ?? "",
399
+ sameNode: toPbAntiAffinity(r.sameNode),
400
+ sameAz: toPbAntiAffinity(r.sameAz),
401
+ sameCloud: toPbAntiAffinity(r.sameCloud),
402
+ });
403
+ }
404
+ function toPbBundle(b) {
405
+ return (0, protobuf_1.create)(manager_pb_2.BundleSchema, {
406
+ bundleId: (0, protobuf_1.create)(common_pb_1.BundleIdSchema, { bundleId: b.bundleId }),
407
+ pool: b.pool,
408
+ jobs: b.jobs.map(toPbBundleJobSpec),
409
+ intraReplicaPlacement: b.intraReplicaPlacement
410
+ ? toPbIntraReplicaRules(b.intraReplicaPlacement)
411
+ : undefined,
412
+ resiliencePolicy: b.resiliencePolicy ? toPbResiliencePolicy(b.resiliencePolicy) : undefined,
413
+ state: toPbBundleState(b.state ?? "live"),
414
+ });
415
+ }
416
+ //# sourceMappingURL=conversions.js.map
@@ -0,0 +1,9 @@
1
+ export * from "./automanager";
2
+ export * from "./types";
3
+ export { Clock, RealClock, CancellableTimer } from "./clock";
4
+ export { KnownCapabilities, KnownCapabilityName } from "./knownCapabilities";
5
+ export { validateBundle, ValidationError, ValidationErrorCode, ValidationResult, PoolDescriptor, NodeProfile, } from "./validation";
6
+ export * from "./placement";
7
+ export { freeCapability, freeCapacity, freeCores, findCapability, usedCapacityFraction, } from "./inventory";
8
+ export { AutoSettingsValidationError, SettingsValidationError, SettingsValidationErrorCode, validateAutoSettings, } from "./settingsValidation";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.validateAutoSettings = exports.AutoSettingsValidationError = exports.usedCapacityFraction = exports.findCapability = exports.freeCores = exports.freeCapacity = exports.freeCapability = exports.validateBundle = exports.KnownCapabilities = exports.RealClock = void 0;
18
+ // Public exports for @norskvideo/norsk-auto-manager.
19
+ __exportStar(require("./automanager"), exports);
20
+ __exportStar(require("./types"), exports);
21
+ var clock_1 = require("./clock");
22
+ Object.defineProperty(exports, "RealClock", { enumerable: true, get: function () { return clock_1.RealClock; } });
23
+ var knownCapabilities_1 = require("./knownCapabilities");
24
+ Object.defineProperty(exports, "KnownCapabilities", { enumerable: true, get: function () { return knownCapabilities_1.KnownCapabilities; } });
25
+ var validation_1 = require("./validation");
26
+ Object.defineProperty(exports, "validateBundle", { enumerable: true, get: function () { return validation_1.validateBundle; } });
27
+ __exportStar(require("./placement"), exports);
28
+ var inventory_1 = require("./inventory");
29
+ Object.defineProperty(exports, "freeCapability", { enumerable: true, get: function () { return inventory_1.freeCapability; } });
30
+ Object.defineProperty(exports, "freeCapacity", { enumerable: true, get: function () { return inventory_1.freeCapacity; } });
31
+ Object.defineProperty(exports, "freeCores", { enumerable: true, get: function () { return inventory_1.freeCores; } });
32
+ Object.defineProperty(exports, "findCapability", { enumerable: true, get: function () { return inventory_1.findCapability; } });
33
+ Object.defineProperty(exports, "usedCapacityFraction", { enumerable: true, get: function () { return inventory_1.usedCapacityFraction; } });
34
+ var settingsValidation_1 = require("./settingsValidation");
35
+ Object.defineProperty(exports, "AutoSettingsValidationError", { enumerable: true, get: function () { return settingsValidation_1.AutoSettingsValidationError; } });
36
+ Object.defineProperty(exports, "validateAutoSettings", { enumerable: true, get: function () { return settingsValidation_1.validateAutoSettings; } });
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,34 @@
1
+ import { Capability, NodeInventory } from "./types";
2
+ /**
3
+ * Free capacity available for placement. `(totalCapacity * utilizationCap)
4
+ * - reservedCapacity`. Can be negative if the node is over-allocated;
5
+ * placement should treat negative values as "no room".
6
+ *
7
+ * @public
8
+ */
9
+ export declare function freeCapacity(inv: NodeInventory): number;
10
+ /** @public */
11
+ export declare function freeCores(inv: NodeInventory): number;
12
+ /**
13
+ * Fraction (0-1) of the node's total capacity currently reserved.
14
+ * Used by the bin-pack scoring strategy.
15
+ *
16
+ * @public
17
+ */
18
+ export declare function usedCapacityFraction(inv: NodeInventory): number;
19
+ /**
20
+ * Free count of a specific capability on this node. Looks up the total
21
+ * by name and subtracts whatever's currently reserved. Returns 0 if the
22
+ * capability isn't advertised.
23
+ *
24
+ * @public
25
+ */
26
+ export declare function freeCapability(inv: NodeInventory, name: string): number;
27
+ /**
28
+ * Find the advertised capability with the given name. Returns undefined
29
+ * if not present. Useful when callers also need attribute info.
30
+ *
31
+ * @public
32
+ */
33
+ export declare function findCapability(inv: NodeInventory, name: string): Capability | undefined;
34
+ //# sourceMappingURL=inventory.d.ts.map
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ // Derivation helpers over NodeInventory snapshots. These are pure
3
+ // projections — no state of their own — so the placement algorithm can
4
+ // remain a pure function over what the worker advertised.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.freeCapacity = freeCapacity;
7
+ exports.freeCores = freeCores;
8
+ exports.usedCapacityFraction = usedCapacityFraction;
9
+ exports.freeCapability = freeCapability;
10
+ exports.findCapability = findCapability;
11
+ /**
12
+ * Free capacity available for placement. `(totalCapacity * utilizationCap)
13
+ * - reservedCapacity`. Can be negative if the node is over-allocated;
14
+ * placement should treat negative values as "no room".
15
+ *
16
+ * @public
17
+ */
18
+ function freeCapacity(inv) {
19
+ return inv.totalCapacity * inv.utilizationCap - inv.reservedCapacity;
20
+ }
21
+ /** @public */
22
+ function freeCores(inv) {
23
+ return inv.totalCores - inv.reservedCores;
24
+ }
25
+ /**
26
+ * Fraction (0-1) of the node's total capacity currently reserved.
27
+ * Used by the bin-pack scoring strategy.
28
+ *
29
+ * @public
30
+ */
31
+ function usedCapacityFraction(inv) {
32
+ if (inv.totalCapacity <= 0)
33
+ return 0;
34
+ return inv.reservedCapacity / inv.totalCapacity;
35
+ }
36
+ /**
37
+ * Free count of a specific capability on this node. Looks up the total
38
+ * by name and subtracts whatever's currently reserved. Returns 0 if the
39
+ * capability isn't advertised.
40
+ *
41
+ * @public
42
+ */
43
+ function freeCapability(inv, name) {
44
+ const total = capabilityCount(inv.capabilities, name);
45
+ const used = capabilityCount(inv.reservedCapabilities, name);
46
+ return Math.max(0, total - used);
47
+ }
48
+ /**
49
+ * Find the advertised capability with the given name. Returns undefined
50
+ * if not present. Useful when callers also need attribute info.
51
+ *
52
+ * @public
53
+ */
54
+ function findCapability(inv, name) {
55
+ return inv.capabilities.find((c) => c.name === name);
56
+ }
57
+ function capabilityCount(caps, name) {
58
+ let total = 0;
59
+ for (const c of caps)
60
+ if (c.name === name)
61
+ total += c.count;
62
+ return total;
63
+ }
64
+ //# sourceMappingURL=inventory.js.map
@@ -0,0 +1,18 @@
1
+ /** @public */
2
+ export declare const KnownCapabilities: {
3
+ /** Whole NVIDIA GPU, exclusive. For fractional, use requiredGpuCapacity. */
4
+ readonly NvidiaGpu: "norsk.io/nvidia-gpu";
5
+ /** Intel Quick Sync Video — fixed-function media accelerator. */
6
+ readonly IntelQsv: "norsk.io/intel-qsv";
7
+ /** Netint Quadra T1U accelerator. */
8
+ readonly Quadra: "norsk.io/quadra";
9
+ /** Blackmagic DeckLink capture/playout card. */
10
+ readonly Decklink: "norsk.io/decklink";
11
+ /** SMPTE ST 2110 capable NIC. */
12
+ readonly St2110Nic: "norsk.io/st2110-nic";
13
+ /** Media eXchange Layer slot, provided by an MXL bridge NodeService. */
14
+ readonly Mxl: "norsk.io/mxl";
15
+ };
16
+ /** @public */
17
+ export type KnownCapabilityName = (typeof KnownCapabilities)[keyof typeof KnownCapabilities];
18
+ //# sourceMappingURL=knownCapabilities.d.ts.map