busroot-sdk 0.0.8-dev.2713878872 → 0.0.8-dev.2714578674

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/build/index.d.ts +1145 -548
  2. package/build/index.js +554 -60
  3. package/package.json +1 -1
package/build/index.js CHANGED
@@ -179,6 +179,188 @@ var require_task = __commonJS({
179
179
  }
180
180
  });
181
181
 
182
+ // ../../packages/common/build/feature-flags/types.js
183
+ var require_types = __commonJS({
184
+ "../../packages/common/build/feature-flags/types.js"(exports2) {
185
+ "use strict";
186
+ Object.defineProperty(exports2, "__esModule", { value: true });
187
+ exports2.FLAG_LEVELS = void 0;
188
+ exports2.FLAG_LEVELS = ["account", "plant", "stationGroup", "station"];
189
+ }
190
+ });
191
+
192
+ // ../../packages/common/build/feature-flags/registry.js
193
+ var require_registry = __commonJS({
194
+ "../../packages/common/build/feature-flags/registry.js"(exports2) {
195
+ "use strict";
196
+ Object.defineProperty(exports2, "__esModule", { value: true });
197
+ exports2.flagsForLevel = exports2.FLAG_KEYS = exports2.FEATURE_FLAGS = void 0;
198
+ exports2.FEATURE_FLAGS = {
199
+ enableTasks: {
200
+ key: "enableTasks",
201
+ label: "Enable Tasks",
202
+ description: "Enable the Tasks feature for this account (OI-managed paid feature).",
203
+ allowedLevels: ["account"],
204
+ oiManaged: true
205
+ },
206
+ enableCycleTimeAnalysis: {
207
+ key: "enableCycleTimeAnalysis",
208
+ label: "Enable Cycle Time Analysis",
209
+ description: "Show the Cycle Time Analysis menu item for this account (OI-managed).",
210
+ allowedLevels: ["account"],
211
+ oiManaged: true
212
+ },
213
+ enableLostProductionCost: {
214
+ key: "enableLostProductionCost",
215
+ label: "Enable Lost Production Cost",
216
+ description: "Enable tracking of lost production costs.",
217
+ allowedLevels: ["account"],
218
+ oiManaged: false
219
+ },
220
+ enableAutoEndOnQuantityComplete: {
221
+ key: "enableAutoEndOnQuantityComplete",
222
+ label: "Auto-End on Quantity Complete",
223
+ description: "Automatically end schedules when their target quantity is reached (account default).",
224
+ allowedLevels: ["account"],
225
+ oiManaged: false
226
+ },
227
+ enableDragBetweenStationsOnGantt: {
228
+ key: "enableDragBetweenStationsOnGantt",
229
+ label: "Allow Drag Between Stations on Gantt",
230
+ description: "Allow schedules to be reassigned to a different station by dragging on the Gantt chart.",
231
+ allowedLevels: ["account"],
232
+ oiManaged: false
233
+ },
234
+ enableProductionCorrection: {
235
+ key: "enableProductionCorrection",
236
+ label: "Allow Production Correction",
237
+ description: "Allow operators to enter negative values to correct previous production counting errors.",
238
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
239
+ oiManaged: false
240
+ },
241
+ enableManualProduction: {
242
+ key: "enableManualProduction",
243
+ label: "Allow Manual Production",
244
+ description: "Allow operators to manually enter production quantities.",
245
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
246
+ oiManaged: false
247
+ },
248
+ enableInterruptionMode: {
249
+ key: "enableInterruptionMode",
250
+ label: "Allow Interruption Mode",
251
+ description: "Allow operators to put a running schedule into interruption mode.",
252
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
253
+ oiManaged: false
254
+ },
255
+ enableQuickScheduleStart: {
256
+ key: "enableQuickScheduleStart",
257
+ label: "Allow Ad Hoc Schedules",
258
+ description: "Allow operators to start ad-hoc schedules from the tablet.",
259
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
260
+ oiManaged: false
261
+ },
262
+ enableScheduleCutShort: {
263
+ key: "enableScheduleCutShort",
264
+ label: "Allow Cutting Schedules Short",
265
+ description: "Allow operators to end a schedule before its planned end.",
266
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
267
+ oiManaged: false
268
+ },
269
+ enableRejectProduction: {
270
+ key: "enableRejectProduction",
271
+ label: "Allow Rejecting Production",
272
+ description: "Allow operators to reject (scrap) produced units.",
273
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
274
+ oiManaged: false
275
+ },
276
+ enableEditPartsPerCycle: {
277
+ key: "enableEditPartsPerCycle",
278
+ label: "Allow Editing Parts Per Cycle",
279
+ description: "Allow operators to override the parts-per-cycle value on a schedule.",
280
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
281
+ oiManaged: false
282
+ },
283
+ enableScheduleStationFlexibility: {
284
+ key: "enableScheduleStationFlexibility",
285
+ label: "Allow Schedule Station Flexibility",
286
+ description: "Allow schedules to run on any station within the group.",
287
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
288
+ oiManaged: false
289
+ },
290
+ enableSchedulingOnly: {
291
+ key: "enableSchedulingOnly",
292
+ label: "Scheduling Only",
293
+ description: "Restrict the station to scheduling only (no live production capture).",
294
+ allowedLevels: ["stationGroup", "station"],
295
+ oiManaged: false
296
+ },
297
+ enableManualDowntime: {
298
+ key: "enableManualDowntime",
299
+ label: "Allow Manual Downtime",
300
+ description: "Allow operators and API users to manually trigger downtime on a station, blocking production while active.",
301
+ allowedLevels: ["account", "plant", "stationGroup", "station"],
302
+ oiManaged: false
303
+ },
304
+ enableMeterOnly: {
305
+ key: "enableMeterOnly",
306
+ label: "Meter Only",
307
+ description: "Meter Only: When true, this station represents a physical energy meter (e.g. a plant incomer). It only publishes electrical signals and is hidden from production views, rollups, and station pickers by default.",
308
+ allowedLevels: ["stationGroup", "station"],
309
+ oiManaged: false
310
+ }
311
+ // `as const` so the literal `allowedLevels` tuples survive into `typeof FEATURE_FLAGS`, enabling the
312
+ // per-level type derivations (FlagsAtLevel / ResolvedFlags) below and precise schema-field typing.
313
+ };
314
+ exports2.FLAG_KEYS = Object.keys(exports2.FEATURE_FLAGS);
315
+ var flagsForLevel = (level) => exports2.FLAG_KEYS.map((k) => exports2.FEATURE_FLAGS[k]).filter((f) => f.allowedLevels.includes(level));
316
+ exports2.flagsForLevel = flagsForLevel;
317
+ }
318
+ });
319
+
320
+ // ../../packages/common/build/feature-flags/index.js
321
+ var require_feature_flags = __commonJS({
322
+ "../../packages/common/build/feature-flags/index.js"(exports2) {
323
+ "use strict";
324
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
325
+ if (k2 === void 0) k2 = k;
326
+ var desc = Object.getOwnPropertyDescriptor(m, k);
327
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
328
+ desc = { enumerable: true, get: function() {
329
+ return m[k];
330
+ } };
331
+ }
332
+ Object.defineProperty(o, k2, desc);
333
+ }) : (function(o, m, k, k2) {
334
+ if (k2 === void 0) k2 = k;
335
+ o[k2] = m[k];
336
+ }));
337
+ var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
338
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
339
+ };
340
+ Object.defineProperty(exports2, "__esModule", { value: true });
341
+ __exportStar(require_types(), exports2);
342
+ __exportStar(require_registry(), exports2);
343
+ }
344
+ });
345
+
346
+ // ../../packages/common/build/schemas/feature-flags.js
347
+ var require_feature_flags2 = __commonJS({
348
+ "../../packages/common/build/schemas/feature-flags.js"(exports2) {
349
+ "use strict";
350
+ Object.defineProperty(exports2, "__esModule", { value: true });
351
+ exports2.flagSchemaFieldsForLevel = exports2.AccountFlagValueSchema = exports2.FlagValueSchema = void 0;
352
+ var zod_1 = require("zod");
353
+ var feature_flags_1 = require_feature_flags();
354
+ exports2.FlagValueSchema = zod_1.z.enum(["on", "off", "inherit"]).nullish().describe("Feature flag value: 'on', 'off', or 'inherit' (defer to the nearest ancestor). Absent = inherit.");
355
+ exports2.AccountFlagValueSchema = zod_1.z.enum(["on", "off"]).nullish().describe("Feature flag value: 'on' or 'off'. The account is the top of the hierarchy, so 'inherit' is not allowed.");
356
+ var flagSchemaFieldsForLevel = (level) => Object.fromEntries((0, feature_flags_1.flagsForLevel)(level).map((f) => {
357
+ const base = level === "account" ? exports2.AccountFlagValueSchema : exports2.FlagValueSchema;
358
+ return [f.key, base.describe(`${f.label}: ${f.description}`)];
359
+ }));
360
+ exports2.flagSchemaFieldsForLevel = flagSchemaFieldsForLevel;
361
+ }
362
+ });
363
+
182
364
  // ../../packages/common/build/schemas/base-station-group.js
183
365
  var require_base_station_group = __commonJS({
184
366
  "../../packages/common/build/schemas/base-station-group.js"(exports2) {
@@ -186,19 +368,21 @@ var require_base_station_group = __commonJS({
186
368
  Object.defineProperty(exports2, "__esModule", { value: true });
187
369
  exports2.BaseStationGroupSchema = void 0;
188
370
  var zod_1 = require("zod");
371
+ var feature_flags_1 = require_feature_flags2();
189
372
  exports2.BaseStationGroupSchema = zod_1.z.object({
190
373
  code: zod_1.z.string().describe("Unique group identifier within the plant (e.g., 'LINE-01', 'ASM-A', 'PACKAGING')"),
191
374
  name: zod_1.z.string().describe("Human-readable group name (e.g., 'Assembly Line 1', 'Packaging Line A')"),
192
- allowScheduleStationFlexibility: zod_1.z.boolean().describe("Allow flexible station assignment for schedules within this group"),
193
375
  shiftPatternCode: zod_1.z.string().nullish().describe("Shift Pattern: The ID of the shift pattern this group follows"),
194
- allowQuickScheduleStart: zod_1.z.boolean().nullish().describe("Allow operators to start ad-hoc schedules from tablets"),
376
+ // Feature flags (allowScheduleStationFlexibility, allowQuickScheduleStart) are now registry-managed
377
+ // tri-state `enable*` fields, added below via flagSchemaFieldsForLevel("stationGroup").
195
378
  includeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Reason Codes: If set, only these reason codes are available at this level. Applied before excludeReasonCodes."),
196
379
  excludeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Reason Codes: These reason codes are removed at this level. Applied after includeReasonCodes."),
197
380
  includeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Task Codes: If set, only these Task codes are available at this level. Applied before excludeTaskCodes."),
198
381
  excludeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Task Codes: These Task codes are removed at this level. Applied after includeTaskCodes."),
199
382
  totalEnergyStationCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Total Energy Station Codes: When set, this group's energy (kWh, cost, CO2) is the sum of these stations' energy instead of the additive sum over member stations."),
200
383
  createdAt: zod_1.z.string().nullish().describe("Group creation timestamp (ISO 8601, system managed)"),
201
- updatedAt: zod_1.z.string().nullish().describe("Last update timestamp (ISO 8601, system managed)")
384
+ updatedAt: zod_1.z.string().nullish().describe("Last update timestamp (ISO 8601, system managed)"),
385
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("stationGroup")
202
386
  }).strict();
203
387
  }
204
388
  });
@@ -531,6 +715,7 @@ var require_base_station = __commonJS({
531
715
  var enums_1 = require_enums();
532
716
  var time_1 = require_time();
533
717
  var shift_1 = require_shift();
718
+ var feature_flags_1 = require_feature_flags2();
534
719
  exports2.StationCodeSchema = zod_1.z.string().trim().min(1).max(32).regex(/^([a-zA-Z0-9-_]+)$/, {
535
720
  message: "Only allowed 0-9, A-Z, and -_"
536
721
  }).describe("Code: Assign a Code to the Station, this cannot be changed once set.");
@@ -557,15 +742,9 @@ var require_base_station = __commonJS({
557
742
  electricalUsageStoppedThresholdKw: zod_1.z.number().gte(0).nullish().describe("Electrical Usage Stopped Threshold (kW): Minimum power consumption in kilowatts that indicates the station is active. Below this threshold, Busroot will regard the station as stopped/downtime. Example: 2.0, 1.0, 5.0"),
558
743
  lineSpeedStoppedThreshold: zod_1.z.number().gte(0).nullish().describe("Line Speed Stopped Threshold: Minimum speed value that indicates the station is running. Below this threshold, Busroot will regard the station as stopped/downtime. Example: 10, 5, 20"),
559
744
  productiveHoldOnTime: zod_1.z.number().int().gte(0).lte(time_1.TimeIntervals["1d"].ms).nullish().describe("Productive Hold On Time: Time in minutes to consider station active after a productive signal. Each new signal restarts the hold-on timer."),
560
- // Flags
561
- allowInterruptionMode: zod_1.z.boolean().default(false).nullish().describe("Allow Interruption Mode: Enable the interruption tracking tool for capturing downtime from human-led manufacturing processes where digital data capture is impossible. Provides an intuitive way for operators to record interruptions. Default: false"),
562
- allowManualProduction: zod_1.z.boolean().default(false).nullish().describe("Allow Manual Production: Enable the manual production entry module on tablets, allowing operators to count production manually. Default: false"),
563
- allowQuickScheduleStart: zod_1.z.boolean().default(false).nullish().describe("Allow Ad Hoc Schedules: Allow operators to start ad-hoc schedules directly from tablets. Default: false"),
564
- allowScheduleCutShort: zod_1.z.boolean().default(false).nullish().describe("Allow Schedule Cut Short: Enable button in Schedule Management and on the Tablet Production page to end schedules early. Remaining quantity will be added to a new schedule with the same Work Order Reference. Default: false"),
565
- allowRejectProduction: zod_1.z.boolean().default(false).nullish().describe("Allow Reject Production: Enables reject (bad quality) production to be recorded by operators."),
566
- allowEditPartsPerCycle: zod_1.z.boolean().default(false).nullish().describe("Allow Edit Parts per Cycle: Allow operators to edit the number of parts in the next cycle using the tablet."),
567
- schedulingOnly: zod_1.z.boolean().default(false).nullish().describe("Scheduling Only: When true, this station is only visible on the Gantt Chart and Schedule List. It is hidden from dashboards, production views, reports, tablets, and other views. Default: false"),
568
- meterOnly: zod_1.z.boolean().default(false).nullish().describe("Meter Only: When true, this station represents a physical energy meter (e.g. a plant incomer). It only publishes electrical signals and is hidden from production views, rollups, and station pickers by default. Default: false"),
745
+ // Feature flags (allowInterruptionMode, allowManualProduction, allowQuickScheduleStart,
746
+ // allowScheduleCutShort, allowRejectProduction, allowEditPartsPerCycle, schedulingOnly) are now
747
+ // registry-managed tri-state `enable*` fields, added below via flagSchemaFieldsForLevel("station").
569
748
  // Current State
570
749
  currentOperatorInitials: zod_1.z.string().min(3).max(3).nullish().describe("Current Operator Initials: Current operator's initials (exactly 3 characters, system managed)"),
571
750
  statusCode: zod_1.z.string().nullish().describe("Status Code: Current status code of the station (system managed)"),
@@ -584,7 +763,8 @@ var require_base_station = __commonJS({
584
763
  // System info
585
764
  createdAt: zod_1.z.string().nullish().describe("Created At: Station creation timestamp (ISO 8601, system managed)"),
586
765
  updatedAt: zod_1.z.string().nullish().describe("Updated At: Last update timestamp (ISO 8601, system managed)"),
587
- archivedAt: zod_1.z.string().nullish().describe("Archived At: Archive timestamp (ISO 8601, system managed)")
766
+ archivedAt: zod_1.z.string().nullish().describe("Archived At: Archive timestamp (ISO 8601, system managed)"),
767
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("station")
588
768
  }).strict();
589
769
  }
590
770
  });
@@ -598,6 +778,7 @@ var require_plant = __commonJS({
598
778
  var zod_1 = require("zod");
599
779
  var base_station_group_1 = require_base_station_group();
600
780
  var base_station_1 = require_base_station();
781
+ var feature_flags_1 = require_feature_flags2();
601
782
  exports2.PlantCodeSchema = zod_1.z.string().trim().min(3).max(55).toLowerCase().regex(/^[a-z0-9\-]*$/g, "Can only contain letters, numbers and dashes.").describe("Plant Code: The unique code for this plant.");
602
783
  exports2.PLANT_MODEL = "plant";
603
784
  exports2.PlantSchema = zod_1.z.object({
@@ -616,7 +797,8 @@ var require_plant = __commonJS({
616
797
  }).describe("Station group configuration with nested stations")).describe("Array of station groups within this plant (minimum 1)"),
617
798
  createdAt: zod_1.z.string().nullish().describe("Plant creation timestamp (ISO 8601, system managed)"),
618
799
  updatedAt: zod_1.z.string().nullish().describe("Last update timestamp (ISO 8601, system managed)"),
619
- archivedAt: zod_1.z.string().nullish().describe("Archive timestamp (ISO 8601, system managed)")
800
+ archivedAt: zod_1.z.string().nullish().describe("Archive timestamp (ISO 8601, system managed)"),
801
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("plant")
620
802
  }).strict();
621
803
  }
622
804
  });
@@ -1643,6 +1825,7 @@ var require_base_account = __commonJS({
1643
1825
  exports2.BaseAccountSchema = void 0;
1644
1826
  var zod_1 = require("zod");
1645
1827
  var times_1 = require_times();
1828
+ var feature_flags_1 = require_feature_flags2();
1646
1829
  exports2.BaseAccountSchema = zod_1.z.object({
1647
1830
  id: zod_1.z.string().describe("Unique account identifier (e.g., 'acme-manufacturing', 'global-ops')"),
1648
1831
  domain: zod_1.z.string().describe("Busroot domain for the account (e.g., 'acme.busroot.io')"),
@@ -1650,17 +1833,13 @@ var require_base_account = __commonJS({
1650
1833
  electricalPricePerKwh: zod_1.z.number().gte(0).nullish().describe("Cost per kilowatt-hour for electrical consumption (e.g., 0.15)"),
1651
1834
  electricalCo2PerKwh: zod_1.z.number().gte(0).nullish().describe("CO2 emissions per kilowatt-hour (e.g., 0.233)"),
1652
1835
  currency: zod_1.z.string().nullish().describe("ISO 4217 currency code (e.g., 'USD', 'EUR', 'GBP')"),
1653
- enableLostProductionCost: zod_1.z.boolean().nullish().describe("Enable tracking of lost production costs"),
1654
- enableTasks: zod_1.z.boolean().nullish().describe("Enable the Tasks feature for this account (OI-managed paid feature)"),
1655
- allowCycleTimeAnalysis: zod_1.z.boolean().nullish().describe("Show the Cycle Time Analysis menu item for this account (OI-managed). Absent/false hides the menu item."),
1836
+ // Feature flags (enableLostProductionCost, enableTasks, enableCycleTimeAnalysis, enableManualDowntime,
1837
+ // enableAutoEndOnQuantityComplete, enableProductionCorrection, enableDragBetweenStationsOnGantt) are now
1838
+ // registry-managed tri-state `enable*` fields, added below via flagSchemaFieldsForLevel("account").
1656
1839
  dashboardConfig: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).nullish().describe("Custom dashboard configuration (key-value pairs)"),
1657
1840
  redshiftPassword: zod_1.z.string().nullish().describe("Redshift database password (system managed)"),
1658
1841
  uploadTransforms: zod_1.z.string().nullish().describe("Upload transformation configuration (system managed)"),
1659
- defaultAutoEndOnQuantityComplete: zod_1.z.boolean().nullish().describe("Automatically end schedules when target quantity is reached"),
1660
1842
  durationFormat: zod_1.z.enum(times_1.SUPPORTED_DURATION).nullish().describe("Duration display format (e.g., 'months', 'days', 'hours', 'minutes')"),
1661
- allowProductionCorrection: zod_1.z.boolean().nullish().describe("Allow operators to enter negative values to correct previous production counting errors"),
1662
- enableManualDowntime: zod_1.z.boolean().nullish().describe("Allow operators and API users to manually trigger downtime on a station, blocking production/productivity while active"),
1663
- allowDragBetweenStationsOnGantt: zod_1.z.boolean().nullish().describe("Allow schedules to be reassigned to a different station by dragging on the Gantt Chart"),
1664
1843
  appUrls: zod_1.z.string().nullish().describe("Custom application URLs (system managed)"),
1665
1844
  includeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Reason Codes: If set, only these reason codes are available at this level. Applied before excludeReasonCodes."),
1666
1845
  excludeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Reason Codes: These reason codes are removed at this level. Applied after includeReasonCodes."),
@@ -1668,7 +1847,8 @@ var require_base_account = __commonJS({
1668
1847
  excludeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Task Codes: These Task codes are removed at this level. Applied after includeTaskCodes."),
1669
1848
  createdAt: zod_1.z.string().nullish().describe("Account creation timestamp (ISO 8601, system managed)"),
1670
1849
  updatedAt: zod_1.z.string().nullish().describe("Last update timestamp (ISO 8601, system managed)"),
1671
- archivedAt: zod_1.z.string().nullish().describe("Archive timestamp (ISO 8601, system managed). When set, the account is archived and hidden from active lists.")
1850
+ archivedAt: zod_1.z.string().nullish().describe("Archive timestamp (ISO 8601, system managed). When set, the account is archived and hidden from active lists."),
1851
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("account")
1672
1852
  }).strict();
1673
1853
  }
1674
1854
  });
@@ -1989,6 +2169,10 @@ var require_manifest = __commonJS({
1989
2169
  var task_1 = require_task();
1990
2170
  var shift_1 = require_shift();
1991
2171
  var base_reason_1 = require_base_reason();
2172
+ var resolveGroupStationFlag = (groupValue, stationValue) => {
2173
+ const effective = [groupValue, stationValue].reduce((acc, value) => value && value !== "inherit" ? value : acc, "inherit");
2174
+ return effective === "on";
2175
+ };
1992
2176
  exports2.ManifestSchema = zod_1.z.object({
1993
2177
  version: zod_1.z.literal("v1").describe("Schema version. Currently 'v1'"),
1994
2178
  account: base_account_1.BaseAccountSchema.extend({
@@ -2083,11 +2267,11 @@ var require_manifest = __commonJS({
2083
2267
  const stationPath = [...groupPath, "stations", si];
2084
2268
  validateLevel(station, stationPath);
2085
2269
  validateShiftPatternCode(station.shiftPatternCode, [...stationPath, "shiftPatternCode"]);
2086
- if (station.meterOnly && station.schedulingOnly) {
2270
+ if (resolveGroupStationFlag(group.enableMeterOnly, station.enableMeterOnly) && resolveGroupStationFlag(group.enableSchedulingOnly, station.enableSchedulingOnly)) {
2087
2271
  ctx.addIssue({
2088
2272
  code: zod_1.z.ZodIssueCode.custom,
2089
2273
  message: `Station '${station.code}' cannot be both meterOnly and schedulingOnly`,
2090
- path: [...stationPath, "meterOnly"]
2274
+ path: [...stationPath, "enableMeterOnly"]
2091
2275
  });
2092
2276
  }
2093
2277
  if (!station.archivedAt) {
@@ -2516,6 +2700,7 @@ var require_schemas = __commonJS({
2516
2700
  __exportStar(require_dashboard(), exports2);
2517
2701
  __exportStar(require_device(), exports2);
2518
2702
  __exportStar(require_device_profile(), exports2);
2703
+ __exportStar(require_feature_flags2(), exports2);
2519
2704
  __exportStar(require_downtime(), exports2);
2520
2705
  __exportStar(require_manifest(), exports2);
2521
2706
  __exportStar(require_note(), exports2);
@@ -2546,6 +2731,7 @@ var require_plant2 = __commonJS({
2546
2731
  var zod_1 = require("zod");
2547
2732
  var moment_timezone_1 = __importDefault(require("moment-timezone"));
2548
2733
  var schemas_1 = require_schemas();
2734
+ var feature_flags_1 = require_feature_flags2();
2549
2735
  var AVAILABLE_TIMEZONES = moment_timezone_1.default.tz.names().filter((tz) => tz.includes("/") && !tz.includes("Etc"));
2550
2736
  exports2.PlantPostRequest = zod_1.z.object({
2551
2737
  code: schemas_1.PlantCodeSchema,
@@ -2553,6 +2739,10 @@ var require_plant2 = __commonJS({
2553
2739
  dayStartHour: zod_1.z.coerce.number().min(0).max(24).int("Day start hour must be whole hour.").describe("Start Hour for Day: When running 24 hours a day, this time will be considered the start and end when reporting by day.").optional(),
2554
2740
  weekStartDay: zod_1.z.coerce.number().int().min(0).max(6).describe("Week Start Day: The day the production week starts.").optional(),
2555
2741
  timezone: zod_1.z.enum(["UTC", ...AVAILABLE_TIMEZONES]).describe("Timezone: The timezone the plant operates in.").optional(),
2742
+ // Plant-level feature-flag config fields (tri-state on/off/inherit), generated from the registry.
2743
+ // Without these the flag values posted by the admin form are stripped by `.parse()` before
2744
+ // `PlantService.applyPlantFlags` ever sees them.
2745
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("plant"),
2556
2746
  includeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Task Codes: Only these Task codes are available at this level."),
2557
2747
  excludeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Task Codes: These Task codes are removed at this level."),
2558
2748
  includeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Reason Codes: If set, only these reason codes are available at this level. Applied before excludeReasonCodes."),
@@ -2736,6 +2926,7 @@ var require_station_group2 = __commonJS({
2736
2926
  var enums_1 = require_enums();
2737
2927
  var common_1 = require_common();
2738
2928
  var schemas_1 = require_schemas();
2929
+ var feature_flags_1 = require_feature_flags2();
2739
2930
  exports2.StationGroupCodeSchema = zod_1.z.string().trim().min(1).max(32).regex(/^([a-zA-Z0-9-_]+)$/, {
2740
2931
  message: "Only allowed 0-9, A-Z, and -_"
2741
2932
  });
@@ -2743,9 +2934,9 @@ var require_station_group2 = __commonJS({
2743
2934
  code: exports2.StationGroupCodeSchema.describe("Code: Assign a Code to the Station Group, this cannot be changed once set."),
2744
2935
  name: zod_1.z.string().trim().min(1).max(32).describe("Name: Assign a Name to the Station Group."),
2745
2936
  plantCode: schemas_1.PlantCodeSchema.describe("Plant: Select the plant you would like this group of stations associated with."),
2746
- allowScheduleStationFlexibility: zod_1.z.boolean().default(false).describe("Schedule Station Flexibility: Choose whether a schedule appears on the tablet for only the specified station or any station within its group."),
2747
2937
  shiftPatternCode: schemas_1.shiftPatternCodeSchema.describe("Shift Pattern: Select which Shift Pattern the group will follow."),
2748
- allowQuickScheduleStart: zod_1.z.boolean().optional().describe("Allow Ad Hoc Schedules: This allows operators with tablets to start new schedules directly from the tablet."),
2938
+ // Group-level feature-flag config fields (tri-state on/off/inherit), generated from the registry.
2939
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("stationGroup"),
2749
2940
  includeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Task Codes: Only these Task codes are available at this level."),
2750
2941
  excludeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Task Codes: These Task codes are removed at this level."),
2751
2942
  includeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Reason Codes: If set, only these reason codes are available at this level. Applied before excludeReasonCodes."),
@@ -5627,6 +5818,7 @@ var require_account2 = __commonJS({
5627
5818
  var js_yaml_1 = __importDefault(require_js_yaml());
5628
5819
  var schemas_1 = require_schemas();
5629
5820
  var times_1 = require_times();
5821
+ var feature_flags_1 = require_feature_flags2();
5630
5822
  exports2.NewAccountPostRequest = zod_1.z.object({
5631
5823
  domain: zod_1.z.string().trim().min(1).regex(/^[a-zA-Z0-9-.]+$/, {
5632
5824
  message: "Only letters, digits, dot, and hyphens are allowed"
@@ -5642,9 +5834,9 @@ var require_account2 = __commonJS({
5642
5834
  archived: zod_1.z.boolean().describe("Archived: true to archive the account, false to unarchive it")
5643
5835
  });
5644
5836
  exports2.AccountSettingPostRequest = zod_1.z.object({
5645
- enableLostProductionCost: zod_1.z.boolean().default(false).describe("Enable Lost Production Cost Analysis: Assign SKUs a value and have the cost of lost production displayed."),
5646
- enableTasks: zod_1.z.boolean().nullish().describe("Enable Tasks: Paid feature \u2014 toggled by Output Industries staff only."),
5647
- allowCycleTimeAnalysis: zod_1.z.boolean().nullish().describe("Show Cycle Time Analysis: Toggled by Output Industries staff only. Controls visibility of the Cycle Time Analysis menu item."),
5837
+ // Account-level feature-flag config fields (tri-state on/off), generated from the registry.
5838
+ // OI-managed flags are enforced server-side in the POST /api/account handler.
5839
+ ...(0, feature_flags_1.flagSchemaFieldsForLevel)("account"),
5648
5840
  currency: zod_1.z.enum(["GBP", "USD", "EUR"]).nullish(),
5649
5841
  electricalPricePerKwh: zod_1.z.number().gte(0).lt(1e3).nullish().describe("Price per kWh: Have the cost of electricity displayed by providing a kWh price in GBP."),
5650
5842
  electricalCo2PerKwh: zod_1.z.number().gte(0).lt(1e3).nullish().describe("CO2 per kWh: Have the amount of CO2 used displayed by providing an average CO2 per kWh."),
@@ -5682,11 +5874,7 @@ var require_account2 = __commonJS({
5682
5874
  return false;
5683
5875
  }
5684
5876
  }, "Invalid yaml").describe("Upload Transforms: This field accepts a JSONata expression (in YAML format) to define how table columns are mapped and transformed."),
5685
- defaultAutoEndOnQuantityComplete: zod_1.z.boolean().default(false).describe("Default Auto End On Quantity Complete: Control whether a schedule will automatically end when the planned quantity is reached."),
5686
5877
  durationFormat: zod_1.z.enum(times_1.SUPPORTED_DURATION).default("months").describe("Duration Format: Select the format to display durations, for example a downtime period or length of a schedule."),
5687
- allowProductionCorrection: zod_1.z.boolean().default(true).describe("Allow Production Correction: This enables operators to enter a negative value to correct a previous error during production counting."),
5688
- allowDragBetweenStationsOnGantt: zod_1.z.boolean().default(false).describe("Allow Drag Between Stations on Gantt: This allows a schedule to be reassigned from its specified station to another by dragging on the Gantt Chart."),
5689
- enableManualDowntime: zod_1.z.boolean().nullish().describe("Enable Manual Downtime: Paid/beta feature \u2014 toggled by Output Industries staff only. Allows operators and API users to manually trigger downtime on a station; while active, production and productivity signals are blocked."),
5690
5878
  includeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Reason Codes: If set, only these reason codes are available at this level. Applied before excludeReasonCodes."),
5691
5879
  excludeReasonCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Exclude Reason Codes: These reason codes are removed at this level. Applied after includeReasonCodes."),
5692
5880
  includeTaskCodes: zod_1.z.array(zod_1.z.string()).nullish().describe("Include Task Codes: If set, only these Task codes are available at this level. Applied before excludeTaskCodes."),
@@ -7017,6 +7205,58 @@ var require_station_allowed_list = __commonJS({
7017
7205
  }
7018
7206
  });
7019
7207
 
7208
+ // ../../packages/common/build/utils/feature-flags.js
7209
+ var require_feature_flags3 = __commonJS({
7210
+ "../../packages/common/build/utils/feature-flags.js"(exports2) {
7211
+ "use strict";
7212
+ Object.defineProperty(exports2, "__esModule", { value: true });
7213
+ exports2.resolveFlag = void 0;
7214
+ var feature_flags_1 = require_feature_flags();
7215
+ var levelsForTarget = (manifest, target) => {
7216
+ const account = manifest.account;
7217
+ if (!target) {
7218
+ return { account };
7219
+ }
7220
+ for (const plant of account.plants ?? []) {
7221
+ if ("plantCode" in target) {
7222
+ if (plant.code === target.plantCode) {
7223
+ return { account, plant };
7224
+ }
7225
+ continue;
7226
+ }
7227
+ for (const group of plant.stationGroups ?? []) {
7228
+ if ("stationGroupCode" in target) {
7229
+ if (group.code === target.stationGroupCode) {
7230
+ return { account, plant, stationGroup: group };
7231
+ }
7232
+ continue;
7233
+ }
7234
+ for (const station of group.stations ?? []) {
7235
+ if (station.code === target.stationCode) {
7236
+ return { account, plant, stationGroup: group, station };
7237
+ }
7238
+ }
7239
+ }
7240
+ }
7241
+ return { account };
7242
+ };
7243
+ var resolveFlag = (manifest, key, target) => {
7244
+ const def = feature_flags_1.FEATURE_FLAGS[key];
7245
+ const levels = levelsForTarget(manifest, target);
7246
+ let effective = "inherit";
7247
+ for (const level of def.allowedLevels) {
7248
+ const rawValue = levels[level]?.[key];
7249
+ const value = typeof rawValue === "boolean" ? rawValue ? "on" : "off" : rawValue;
7250
+ if (value && value !== "inherit") {
7251
+ effective = value;
7252
+ }
7253
+ }
7254
+ return effective === "on";
7255
+ };
7256
+ exports2.resolveFlag = resolveFlag;
7257
+ }
7258
+ });
7259
+
7020
7260
  // ../../packages/common/build/utils/station.js
7021
7261
  var require_station3 = __commonJS({
7022
7262
  "../../packages/common/build/utils/station.js"(exports2) {
@@ -7028,6 +7268,7 @@ var require_station3 = __commonJS({
7028
7268
  exports2.stationCodesFromManifest = stationCodesFromManifest;
7029
7269
  var shift_1 = require_shift3();
7030
7270
  var station_allowed_list_1 = require_station_allowed_list();
7271
+ var feature_flags_1 = require_feature_flags3();
7031
7272
  function stationsFromManifest(manifest, options) {
7032
7273
  if (!manifest) {
7033
7274
  return [];
@@ -7048,10 +7289,10 @@ var require_station3 = __commonJS({
7048
7289
  if (station.archivedAt) {
7049
7290
  continue;
7050
7291
  }
7051
- if (station.schedulingOnly && !options?.includeSchedulingOnly) {
7292
+ if (!options?.includeSchedulingOnly && (0, feature_flags_1.resolveFlag)(manifest, "enableSchedulingOnly", { stationCode: station.code })) {
7052
7293
  continue;
7053
7294
  }
7054
- if (station.meterOnly && !options?.includeMeterOnly && !options?.codes?.length) {
7295
+ if (!options?.includeMeterOnly && (0, feature_flags_1.resolveFlag)(manifest, "enableMeterOnly", { stationCode: station.code }) && !options?.codes?.length) {
7055
7296
  continue;
7056
7297
  }
7057
7298
  const resolvedShiftPatternCode = station.shiftPatternCode || group.shiftPatternCode;
@@ -7069,7 +7310,7 @@ var require_station3 = __commonJS({
7069
7310
  const allTaskCodes = manifest.account.tasks?.map((task) => task.code) ?? [];
7070
7311
  const resolvedTaskCodes = (0, station_allowed_list_1.resolveTaskCodes)(allTaskCodes, manifest.account, plant, group, station);
7071
7312
  const resolvedTaskCodeSet = new Set(resolvedTaskCodes);
7072
- const requiredTasks = manifest.account.enableTasks ? manifest.account.tasks?.filter((task) => resolvedTaskCodeSet.has(task.code)) ?? [] : [];
7313
+ const requiredTasks = (0, feature_flags_1.resolveFlag)(manifest, "enableTasks") ? manifest.account.tasks?.filter((task) => resolvedTaskCodeSet.has(task.code)) ?? [] : [];
7073
7314
  const stationResult = {
7074
7315
  accountId: manifest.account.id,
7075
7316
  code: station.code,
@@ -7091,14 +7332,6 @@ var require_station3 = __commonJS({
7091
7332
  electricalUsageStoppedThresholdKw: station.electricalUsageStoppedThresholdKw ?? null,
7092
7333
  lineSpeedStoppedThreshold: station.lineSpeedStoppedThreshold ?? null,
7093
7334
  productiveHoldOnTime: station.productiveHoldOnTime ?? null,
7094
- allowInterruptionMode: station.allowInterruptionMode,
7095
- allowManualProduction: station.allowManualProduction,
7096
- allowQuickScheduleStart: station.allowQuickScheduleStart,
7097
- allowScheduleCutShort: station.allowScheduleCutShort,
7098
- allowEditPartsPerCycle: station.allowEditPartsPerCycle,
7099
- schedulingOnly: station.schedulingOnly ?? false,
7100
- meterOnly: station.meterOnly ?? false,
7101
- allowRejectProduction: station.allowRejectProduction,
7102
7335
  currentOperatorInitials: station.currentOperatorInitials ?? null,
7103
7336
  statusCode: station.statusCode ?? null,
7104
7337
  statusCodeUpdatedAt: station.statusCodeUpdatedAt ?? null,
@@ -7112,7 +7345,6 @@ var require_station3 = __commonJS({
7112
7345
  timezone: plant.timezone,
7113
7346
  groupName: group.name,
7114
7347
  dayStartHour: plant.dayStartHour,
7115
- allowScheduleStationFlexibility: group.allowScheduleStationFlexibility,
7116
7348
  shifts,
7117
7349
  shiftPattern: shiftPattern ?? void 0,
7118
7350
  allowedReasonCodes: stationResolvedReasonCodes,
@@ -7154,10 +7386,10 @@ var require_station3 = __commonJS({
7154
7386
  if (station.archivedAt) {
7155
7387
  continue;
7156
7388
  }
7157
- if (station.schedulingOnly && !options?.includeSchedulingOnly) {
7389
+ if ((0, feature_flags_1.resolveFlag)(manifest, "enableSchedulingOnly", { stationCode: station.code }) && !options?.includeSchedulingOnly) {
7158
7390
  continue;
7159
7391
  }
7160
- if (station.meterOnly && !options?.includeMeterOnly) {
7392
+ if ((0, feature_flags_1.resolveFlag)(manifest, "enableMeterOnly", { stationCode: station.code }) && !options?.includeMeterOnly) {
7161
7393
  continue;
7162
7394
  }
7163
7395
  result.push({
@@ -8446,6 +8678,7 @@ var require_schedule3 = __commonJS({
8446
8678
  var station_1 = require_station3();
8447
8679
  var reason_1 = require_reason4();
8448
8680
  var schedules_1 = require_schedules();
8681
+ var feature_flags_1 = require_feature_flags3();
8449
8682
  exports2.ScheduleViewModel = schemas_1.ScheduleSchema.omit({
8450
8683
  calculatedQuantity: true,
8451
8684
  calculatedEndAt: true,
@@ -8489,7 +8722,6 @@ var require_schedule3 = __commonJS({
8489
8722
  quantityTotal: zod_1.z.number().nullish(),
8490
8723
  // From joined data
8491
8724
  downtimeDurationThresholdMultiplier: zod_1.z.number().nullish(),
8492
- allowScheduleCutShort: zod_1.z.boolean().nullish(),
8493
8725
  logs: zod_1.z.array(zod_1.z.string()).nullish(),
8494
8726
  // Computed state
8495
8727
  isStarted: zod_1.z.boolean(),
@@ -8575,13 +8807,12 @@ var require_schedule3 = __commonJS({
8575
8807
  isStarted,
8576
8808
  isEnded,
8577
8809
  status,
8578
- allowScheduleCutShort: manifestStation?.allowScheduleCutShort,
8579
8810
  effectivePartsPerCycle,
8580
8811
  partsPerCycle: r.partsPerCycle,
8581
8812
  logs: r.logs,
8582
8813
  effectiveMinimumCycleTime,
8583
8814
  priorityAt: r.priorityAt,
8584
- autoEndOnQuantityComplete: r.autoEndOnQuantityComplete ?? manifest.account.defaultAutoEndOnQuantityComplete,
8815
+ autoEndOnQuantityComplete: r.autoEndOnQuantityComplete ?? (0, feature_flags_1.resolveFlag)(manifest, "enableAutoEndOnQuantityComplete"),
8585
8816
  partsPerCycleOverride: r.partsPerCycleOverride,
8586
8817
  createdAt: (0, moment_timezone_1.default)(r.createdAt).toISOString(),
8587
8818
  updatedAt: (0, moment_timezone_1.default)(r.updatedAt).toISOString()
@@ -8672,13 +8903,24 @@ var require_station4 = __commonJS({
8672
8903
  excludeTaskCodes: true,
8673
8904
  createdAt: true,
8674
8905
  updatedAt: true,
8675
- archivedAt: true
8906
+ archivedAt: true,
8907
+ // Feature-flag config fields are not exposed on the VM — consumers resolve a flag's effective value
8908
+ // from the manifest via `resolveFlag(manifest, key, { stationCode })`.
8909
+ enableProductionCorrection: true,
8910
+ enableManualProduction: true,
8911
+ enableInterruptionMode: true,
8912
+ enableQuickScheduleStart: true,
8913
+ enableScheduleCutShort: true,
8914
+ enableRejectProduction: true,
8915
+ enableEditPartsPerCycle: true,
8916
+ enableScheduleStationFlexibility: true,
8917
+ enableSchedulingOnly: true,
8918
+ enableMeterOnly: true
8676
8919
  }).strip().extend({
8677
8920
  // Override fields where VM type differs from schema
8678
8921
  utilisationDowntimeThreshold: zod_1.z.number().nullish(),
8679
8922
  // Context from parent entities (plant, group)
8680
8923
  groupName: zod_1.z.string().nullish().describe("The name of the station group this station belongs to."),
8681
- allowScheduleStationFlexibility: zod_1.z.boolean().nullish().describe("Whether schedules in this group can be moved between stations."),
8682
8924
  plantCode: zod_1.z.string().describe("The code of the plant this station belongs to."),
8683
8925
  dayStartHour: zod_1.z.number().describe("The hour the production day starts for this station's plant (0-23)."),
8684
8926
  timezone: zod_1.z.string().describe("The IANA timezone of this station's plant (e.g. Europe/London)."),
@@ -8690,8 +8932,6 @@ var require_station4 = __commonJS({
8690
8932
  currentStopReasonCode: zod_1.z.string().nullish().describe("The reason code for the current downtime stop, if the station is stopped."),
8691
8933
  stoppedSince: zod_1.z.number().nullish().describe("Unix timestamp when the current downtime started, if the station is stopped."),
8692
8934
  cachedAt: zod_1.z.number().nullish().describe("Unix timestamp when this station data was last cached."),
8693
- // Group-level settings
8694
- allowQuickScheduleStartGroup: zod_1.z.boolean().nullish().describe("Whether the station group allows ad-hoc schedule starts from tablets."),
8695
8935
  // Related data
8696
8936
  lastStationWindow: station_window_1.StationWindowViewModel.nullish().describe("The most recent one-minute metrics window for this station."),
8697
8937
  notes: zod_1.z.array(note_1.NoteViewModel).optional().describe("Active notes attached to this station."),
@@ -8745,7 +8985,18 @@ var require_station_group3 = __commonJS({
8745
8985
  includeTaskCodes: true,
8746
8986
  excludeTaskCodes: true,
8747
8987
  createdAt: true,
8748
- updatedAt: true
8988
+ updatedAt: true,
8989
+ // Feature-flag config fields are not exposed on the VM — consumers resolve a flag's effective value
8990
+ // from the manifest via `resolveFlag(manifest, key, { stationGroupCode })`.
8991
+ enableProductionCorrection: true,
8992
+ enableManualProduction: true,
8993
+ enableInterruptionMode: true,
8994
+ enableQuickScheduleStart: true,
8995
+ enableScheduleCutShort: true,
8996
+ enableRejectProduction: true,
8997
+ enableEditPartsPerCycle: true,
8998
+ enableScheduleStationFlexibility: true,
8999
+ enableSchedulingOnly: true
8749
9000
  }).strip().extend({
8750
9001
  plantTimezone: zod_1.z.string(),
8751
9002
  stations: zod_1.z.array(schemas_1.StationSchema),
@@ -8819,6 +9070,249 @@ var require_plant3 = __commonJS({
8819
9070
  }
8820
9071
  });
8821
9072
 
9073
+ // ../../packages/common/build/configVars.js
9074
+ var require_configVars = __commonJS({
9075
+ "../../packages/common/build/configVars.js"(exports2) {
9076
+ "use strict";
9077
+ Object.defineProperty(exports2, "__esModule", { value: true });
9078
+ exports2.REDSHIFT_USERNAME = exports2.REDSHIFT_PORT = exports2.REDSHIFT_HOST_PUBLIC = exports2.REDSHIFT_HOST = exports2.SES_CONFIG = exports2.RATE_LIMIT_COUNT = exports2.RATE_LIMIT_WINDOW = exports2.MS_LOGIN_CALLBACK = exports2.MS_LOGIN_CLIENT_SECRET = exports2.MS_LOGIN_CLIENT_ID = exports2.ENABLE_DEMO_MODE = exports2.MQTT_PORT = exports2.MQTT_HOST = exports2.DEFAULT_ACCOUNT_ID = exports2.AUTH_BYPASS_DOMAIN = exports2.KNEX_SEEDS_DIR = exports2.QUEUE_LENGTH = exports2.LOGIN_COOKIE_SECRET = exports2.ENABLE_MS_TEAM_ALERT = exports2.ALERT_SLACK_TOKEN = exports2.ALERT_SLACK_CHANNEL = exports2.ENABLE_SLACK_ALERT = exports2.JWT_SECRET = exports2.AWS_BACKUP_S3_BUCKET = exports2.AWS_REGION = exports2.AWS_SECRET_ACCESS_KEY = exports2.AWS_ACCESS_KEY_ID = exports2.LOGIN_CALL_BACK_URL = exports2.TO_EMAIL_OVERRIDE = exports2.BCC_EMAIL = exports2.FROM_EMAIL = exports2.INFLUX_WRITE_PROTECTED = exports2.INFLUX_BUCKET = exports2.INFLUX_ORG = exports2.INFLUX_TOKEN = exports2.INFLUX_HOST_URL = exports2.PG_DB = exports2.PG_PASS = exports2.PG_USER = exports2.PG_HOST = exports2.BUSROOT_MQTT_FORWARDER_TOPIC = exports2.BUSROOT_MQTT_FORWARDER_PASS = exports2.BUSROOT_MQTT_FORWARDER_USER = exports2.BUSROOT_MQTT_FORWARDER_HOST = exports2.ADMIN_API_KEY = exports2.ADMIN_PASSWORD = exports2.ADMIN_USERNAME = exports2.DOMAIN_NAME = exports2.APP_ENV = exports2.NODE_ENV = void 0;
9079
+ exports2.CACHE_BUILDER_ERROR_BACKOFF_MS = exports2.CACHE_BUILDER_ACCOUNT_BUDGET_MS = exports2.CACHE_BUILDER_IDLE_SLEEP_MS = exports2.CACHE_WARMING_LOOKBACK_DAYS = exports2.CACHE_WARMING_THROTTLE_MS = exports2.CACHE_WARMING_STAGGER_MAX_MS = exports2.CACHE_WARMING_CRON = exports2.CACHE_WARMING_ENABLED = exports2.STATION_FANOUT_PAUSE_MS = exports2.STATION_FANOUT_BATCH_SIZE = exports2.DAU_BUFFER_MAX_MESSAGE_BYTES = exports2.DAU_BUFFER_MAX_AGE_MS = exports2.DAU_BUFFER_SIZE = exports2.ENABLE_MQTT_EVENTS = exports2.ANTHROPIC_API_KEY = exports2.REDSHIFT_DB = exports2.REDSHIFT_PASSWORD = void 0;
9080
+ function parsePositiveIntEnv(value, fallback) {
9081
+ const parsed = Number(value);
9082
+ return Number.isFinite(parsed) && parsed >= 1 ? Math.floor(parsed) : fallback;
9083
+ }
9084
+ function parseNonNegativeIntEnv(value, fallback) {
9085
+ const parsed = Number(value);
9086
+ return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : fallback;
9087
+ }
9088
+ exports2.NODE_ENV = process.env.NODE_ENV;
9089
+ exports2.APP_ENV = process.env.APP_ENV || exports2.NODE_ENV;
9090
+ exports2.DOMAIN_NAME = process.env.DOMAIN_NAME || "undefined";
9091
+ exports2.ADMIN_USERNAME = process.env.ADMIN_USERNAME || "admin";
9092
+ exports2.ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
9093
+ exports2.ADMIN_API_KEY = process.env.ADMIN_API_KEY;
9094
+ exports2.BUSROOT_MQTT_FORWARDER_HOST = process.env.BUSROOT_MQTT_FORWARDER_HOST;
9095
+ exports2.BUSROOT_MQTT_FORWARDER_USER = process.env.BUSROOT_MQTT_FORWARDER_USER;
9096
+ exports2.BUSROOT_MQTT_FORWARDER_PASS = process.env.BUSROOT_MQTT_FORWARDER_PASS;
9097
+ exports2.BUSROOT_MQTT_FORWARDER_TOPIC = process.env.BUSROOT_MQTT_FORWARDER_TOPIC || "#";
9098
+ exports2.PG_HOST = process.env.PG_HOST || exports2.DOMAIN_NAME;
9099
+ exports2.PG_USER = process.env.PG_USER || "postgres";
9100
+ exports2.PG_PASS = process.env.PG_PASS || "pHrJvh4ICt2Fh7Qu";
9101
+ exports2.PG_DB = process.env.PG_DB || "postgres";
9102
+ exports2.INFLUX_HOST_URL = process.env.INFLUX_HOST_URL || `http://${exports2.DOMAIN_NAME}:8086`;
9103
+ exports2.INFLUX_TOKEN = process.env.INFLUX_TOKEN;
9104
+ exports2.INFLUX_ORG = process.env.INFLUX_ORG || "";
9105
+ exports2.INFLUX_BUCKET = process.env.INFLUX_BUCKET || "default";
9106
+ exports2.INFLUX_WRITE_PROTECTED = process.env.INFLUX_WRITE_PROTECTED === "true";
9107
+ exports2.FROM_EMAIL = process.env.FROM_EMAIL || "busroot@output.industries";
9108
+ exports2.BCC_EMAIL = process.env.BCC_EMAIL;
9109
+ exports2.TO_EMAIL_OVERRIDE = process.env.TO_EMAIL_OVERRIDE;
9110
+ exports2.LOGIN_CALL_BACK_URL = process.env.LOGIN_CALL_BACK_URL || `https://${exports2.DOMAIN_NAME}`;
9111
+ exports2.AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID || "";
9112
+ exports2.AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY || "";
9113
+ exports2.AWS_REGION = process.env.AWS_REGION || "eu-west-2";
9114
+ exports2.AWS_BACKUP_S3_BUCKET = process.env.AWS_BACKUP_S3_BUCKET;
9115
+ exports2.JWT_SECRET = process.env.JWT_SECRET || "greentyre";
9116
+ exports2.ENABLE_SLACK_ALERT = (process.env.ENABLE_SLACK_ALERT || "").toLowerCase() === "true";
9117
+ exports2.ALERT_SLACK_CHANNEL = process.env.ALERT_SLACK_CHANNEL || "C03164SPJSH";
9118
+ exports2.ALERT_SLACK_TOKEN = process.env.ALERT_SLACK_TOKEN;
9119
+ exports2.ENABLE_MS_TEAM_ALERT = (process.env.ENABLE_MS_TEAM_ALERT || "").toLowerCase() === "true";
9120
+ exports2.LOGIN_COOKIE_SECRET = process.env.LOGIN_COOKIE_SECRET || "greentyre";
9121
+ exports2.QUEUE_LENGTH = parseInt(process.env["BUSROOT_QUEUE_LENGTH"] || "") || 200;
9122
+ exports2.KNEX_SEEDS_DIR = process.env.KNEX_SEEDS_DIR;
9123
+ exports2.AUTH_BYPASS_DOMAIN = process.env.AUTH_BYPASS_DOMAIN;
9124
+ exports2.DEFAULT_ACCOUNT_ID = process.env.DEFAULT_ACCOUNT_ID || "account_0000000000000000000";
9125
+ exports2.MQTT_HOST = process.env.MQTT_HOST || exports2.DOMAIN_NAME;
9126
+ exports2.MQTT_PORT = parseInt(process.env.MQTT_PORT || "") || 1883;
9127
+ exports2.ENABLE_DEMO_MODE = process.env.ENABLE_DEMO_MODE === "true";
9128
+ exports2.MS_LOGIN_CLIENT_ID = process.env.MS_LOGIN_CLIENT_ID;
9129
+ exports2.MS_LOGIN_CLIENT_SECRET = process.env.MS_LOGIN_CLIENT_SECRET;
9130
+ exports2.MS_LOGIN_CALLBACK = exports2.APP_ENV === "development" ? "http://localhost:3000" : `https://${exports2.DOMAIN_NAME}`;
9131
+ exports2.RATE_LIMIT_WINDOW = parseInt(process.env.RATE_LIMIT_WINDOW || "60000");
9132
+ exports2.RATE_LIMIT_COUNT = parseInt(process.env.RATE_LIMIT_COUNT || "10");
9133
+ exports2.SES_CONFIG = {
9134
+ accessKeyId: exports2.AWS_ACCESS_KEY_ID || "",
9135
+ secretAccessKey: exports2.AWS_SECRET_ACCESS_KEY || "",
9136
+ region: exports2.AWS_REGION,
9137
+ apiVersion: "2010-12-01"
9138
+ };
9139
+ exports2.REDSHIFT_HOST = process.env.REDSHIFT_HOST;
9140
+ exports2.REDSHIFT_HOST_PUBLIC = process.env.REDSHIFT_HOST_PUBLIC || "Not Set";
9141
+ exports2.REDSHIFT_PORT = Number(process.env.REDSHIFT_PORT || 5439);
9142
+ exports2.REDSHIFT_USERNAME = process.env.REDSHIFT_USERNAME || "admin";
9143
+ exports2.REDSHIFT_PASSWORD = process.env.REDSHIFT_PASSWORD;
9144
+ exports2.REDSHIFT_DB = process.env.REDSHIFT_DB || "dev";
9145
+ exports2.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
9146
+ exports2.ENABLE_MQTT_EVENTS = process.env.ENABLE_MQTT_EVENTS === "true" || false;
9147
+ exports2.DAU_BUFFER_SIZE = parseInt(process.env.DAU_BUFFER_SIZE || "") || 5;
9148
+ exports2.DAU_BUFFER_MAX_AGE_MS = parseInt(process.env.DAU_BUFFER_MAX_AGE_MS || "") || 30 * 24 * 60 * 60 * 1e3;
9149
+ exports2.DAU_BUFFER_MAX_MESSAGE_BYTES = parseInt(process.env.DAU_BUFFER_MAX_MESSAGE_BYTES || "") || 8192;
9150
+ exports2.STATION_FANOUT_BATCH_SIZE = parsePositiveIntEnv(process.env.STATION_WINDOW_BATCH_SIZE, 3);
9151
+ exports2.STATION_FANOUT_PAUSE_MS = parseNonNegativeIntEnv(process.env.STATION_WINDOW_BATCH_PAUSE_MS, 500);
9152
+ exports2.CACHE_WARMING_ENABLED = process.env.CACHE_WARMING_ENABLED === "true";
9153
+ exports2.CACHE_WARMING_CRON = process.env.CACHE_WARMING_CRON || "0 2 * * *";
9154
+ exports2.CACHE_WARMING_STAGGER_MAX_MS = parseNonNegativeIntEnv(process.env.CACHE_WARMING_STAGGER_MAX_MS, 30 * 60 * 1e3);
9155
+ exports2.CACHE_WARMING_THROTTLE_MS = parseNonNegativeIntEnv(process.env.CACHE_WARMING_THROTTLE_MS, 250);
9156
+ exports2.CACHE_WARMING_LOOKBACK_DAYS = parsePositiveIntEnv(process.env.CACHE_WARMING_LOOKBACK_DAYS, 730);
9157
+ exports2.CACHE_BUILDER_IDLE_SLEEP_MS = parseNonNegativeIntEnv(process.env.CACHE_BUILDER_IDLE_SLEEP_MS, 2e3);
9158
+ exports2.CACHE_BUILDER_ACCOUNT_BUDGET_MS = parsePositiveIntEnv(process.env.CACHE_BUILDER_ACCOUNT_BUDGET_MS, 6e4);
9159
+ exports2.CACHE_BUILDER_ERROR_BACKOFF_MS = parseNonNegativeIntEnv(process.env.CACHE_BUILDER_ERROR_BACKOFF_MS, 5e3);
9160
+ }
9161
+ });
9162
+
9163
+ // ../../packages/common/build/utils/manifest-helpers.js
9164
+ var require_manifest_helpers = __commonJS({
9165
+ "../../packages/common/build/utils/manifest-helpers.js"(exports2) {
9166
+ "use strict";
9167
+ Object.defineProperty(exports2, "__esModule", { value: true });
9168
+ exports2.EMPTY_MANIFEST = void 0;
9169
+ exports2.normalizeManifestFlags = normalizeManifestFlags;
9170
+ exports2.mergeManifest = mergeManifest;
9171
+ exports2.accountSettingDefaults = accountSettingDefaults;
9172
+ exports2.withAccountSettingDefaults = withAccountSettingDefaults;
9173
+ exports2.updateManifest = updateManifest;
9174
+ var zod_1 = require("zod");
9175
+ var configVars_1 = require_configVars();
9176
+ var base_account_1 = require_base_account();
9177
+ var manifest_1 = require_manifest();
9178
+ var feature_flags_1 = require_feature_flags();
9179
+ var LEGACY_FLAG_RENAMES = {
9180
+ allowCycleTimeAnalysis: "enableCycleTimeAnalysis",
9181
+ allowProductionCorrection: "enableProductionCorrection",
9182
+ allowDragBetweenStationsOnGantt: "enableDragBetweenStationsOnGantt",
9183
+ defaultAutoEndOnQuantityComplete: "enableAutoEndOnQuantityComplete",
9184
+ allowManualProduction: "enableManualProduction",
9185
+ allowInterruptionMode: "enableInterruptionMode",
9186
+ allowQuickScheduleStart: "enableQuickScheduleStart",
9187
+ allowScheduleCutShort: "enableScheduleCutShort",
9188
+ allowRejectProduction: "enableRejectProduction",
9189
+ allowEditPartsPerCycle: "enableEditPartsPerCycle",
9190
+ allowScheduleStationFlexibility: "enableScheduleStationFlexibility",
9191
+ schedulingOnly: "enableSchedulingOnly"
9192
+ };
9193
+ function renameLegacyFlagsInLevel(level) {
9194
+ if (!level || typeof level !== "object") {
9195
+ return;
9196
+ }
9197
+ for (const [oldKey, newKey] of Object.entries(LEGACY_FLAG_RENAMES)) {
9198
+ if (Object.prototype.hasOwnProperty.call(level, oldKey)) {
9199
+ if (!Object.prototype.hasOwnProperty.call(level, newKey)) {
9200
+ level[newKey] = level[oldKey];
9201
+ }
9202
+ delete level[oldKey];
9203
+ }
9204
+ }
9205
+ for (const key of feature_flags_1.FLAG_KEYS) {
9206
+ if (typeof level[key] === "boolean") {
9207
+ level[key] = level[key] ? "on" : "off";
9208
+ }
9209
+ }
9210
+ }
9211
+ function normalizeManifestFlags(raw) {
9212
+ const root = raw;
9213
+ const account = root?.account;
9214
+ if (!account) {
9215
+ return raw;
9216
+ }
9217
+ renameLegacyFlagsInLevel(account);
9218
+ if (account.enableProductionCorrection === void 0 || account.enableProductionCorrection === null) {
9219
+ account.enableProductionCorrection = "on";
9220
+ }
9221
+ const plants = account.plants;
9222
+ for (const plant of plants ?? []) {
9223
+ renameLegacyFlagsInLevel(plant);
9224
+ const groups = plant?.stationGroups;
9225
+ for (const group of groups ?? []) {
9226
+ renameLegacyFlagsInLevel(group);
9227
+ const stations = group?.stations;
9228
+ for (const station of stations ?? []) {
9229
+ renameLegacyFlagsInLevel(station);
9230
+ }
9231
+ }
9232
+ }
9233
+ return raw;
9234
+ }
9235
+ function deepMerge(base, override) {
9236
+ if (override === void 0) {
9237
+ return base;
9238
+ }
9239
+ if (base === null || base === void 0) {
9240
+ return override;
9241
+ }
9242
+ if (Array.isArray(override)) {
9243
+ return override;
9244
+ }
9245
+ if (typeof base === "object" && typeof override === "object" && !Array.isArray(base)) {
9246
+ const result = { ...base };
9247
+ for (const key of Object.keys(override)) {
9248
+ result[key] = deepMerge(base[key], override[key]);
9249
+ }
9250
+ return result;
9251
+ }
9252
+ return override;
9253
+ }
9254
+ function mergeManifest(override, base = exports2.EMPTY_MANIFEST) {
9255
+ return deepMerge(base, override);
9256
+ }
9257
+ exports2.EMPTY_MANIFEST = {
9258
+ version: "v1",
9259
+ account: {
9260
+ id: configVars_1.DEFAULT_ACCOUNT_ID,
9261
+ domain: "",
9262
+ plants: []
9263
+ }
9264
+ };
9265
+ var ACCOUNT_SYSTEM_FIELDS = ["id", "domain", "redshiftPassword", "createdAt", "updatedAt", "archivedAt"];
9266
+ function unwrapZodType(schema) {
9267
+ let current = schema;
9268
+ while (current instanceof zod_1.z.ZodOptional || current instanceof zod_1.z.ZodNullable || current instanceof zod_1.z.ZodDefault) {
9269
+ current = current instanceof zod_1.z.ZodDefault ? current._def.innerType : current.unwrap();
9270
+ }
9271
+ return current;
9272
+ }
9273
+ function accountSettingDefaults() {
9274
+ const defaults = {};
9275
+ const flagKeys = new Set(feature_flags_1.FLAG_KEYS);
9276
+ for (const [key, value] of Object.entries(base_account_1.BaseAccountSchema.shape)) {
9277
+ if (ACCOUNT_SYSTEM_FIELDS.includes(key)) {
9278
+ continue;
9279
+ }
9280
+ if (flagKeys.has(key)) {
9281
+ continue;
9282
+ }
9283
+ defaults[key] = unwrapZodType(value) instanceof zod_1.z.ZodBoolean ? false : null;
9284
+ }
9285
+ return defaults;
9286
+ }
9287
+ function withAccountSettingDefaults(manifest) {
9288
+ const defaults = accountSettingDefaults();
9289
+ const account = { ...manifest.account };
9290
+ for (const [key, defaultValue] of Object.entries(defaults)) {
9291
+ if (account[key] === void 0 || account[key] === null) {
9292
+ account[key] = defaultValue;
9293
+ }
9294
+ }
9295
+ return { ...manifest, account };
9296
+ }
9297
+ async function updateManifest(accountRepository, accountId, mutate) {
9298
+ const account = await accountRepository.raw(accountId);
9299
+ if (!account) {
9300
+ throw new Error(`Account not found: ${accountId}`);
9301
+ }
9302
+ if (!account.manifestJson) {
9303
+ const empty = { ...exports2.EMPTY_MANIFEST, account: { ...exports2.EMPTY_MANIFEST.account, id: accountId } };
9304
+ account.manifestJson = JSON.stringify(empty);
9305
+ await accountRepository.save([account]);
9306
+ }
9307
+ const manifest = manifest_1.ManifestSchema.parse(normalizeManifestFlags(JSON.parse(account.manifestJson)));
9308
+ mutate(manifest);
9309
+ manifest_1.ManifestSchema.parse(manifest);
9310
+ await accountRepository.save([{ ...account, manifestJson: JSON.stringify(manifest) }]);
9311
+ return manifest;
9312
+ }
9313
+ }
9314
+ });
9315
+
8822
9316
  // ../../packages/common/build/view-models/account.js
8823
9317
  var require_account3 = __commonJS({
8824
9318
  "../../packages/common/build/view-models/account.js"(exports2) {
@@ -8834,6 +9328,7 @@ var require_account3 = __commonJS({
8834
9328
  var zod_1 = require("zod");
8835
9329
  var manifest_1 = require_manifest();
8836
9330
  var error_key_1 = require_error_key();
9331
+ var manifest_helpers_1 = require_manifest_helpers();
8837
9332
  exports2.AccountViewModel = schemas_1.BaseAccountSchema.merge(zod_1.z.object({
8838
9333
  cursors: zod_1.z.array(zod_1.z.object({
8839
9334
  type: zod_1.z.string(),
@@ -8851,7 +9346,7 @@ var require_account3 = __commonJS({
8851
9346
  function getAccountViewModelFromQuery(queryResult) {
8852
9347
  return queryResult.map((queryRow) => {
8853
9348
  try {
8854
- const manifest = manifest_1.ManifestSchema.parse(JSON.parse(queryRow.manifestJson));
9349
+ const manifest = manifest_1.ManifestSchema.parse((0, manifest_helpers_1.normalizeManifestFlags)(JSON.parse(queryRow.manifestJson)));
8855
9350
  if (manifest.account.id !== queryRow.id) {
8856
9351
  throw new Error(error_key_1.ACCOUNT_MISMATCH);
8857
9352
  }
@@ -8920,16 +9415,15 @@ var require_user3 = __commonJS({
8920
9415
  exports2.UserAndAccount = auth_1.AuthContext.merge(zod_1.z.object({
8921
9416
  accounts: zod_1.z.array(exports2.UserAccountViewModel).optional(),
8922
9417
  apiKey: zod_1.z.string().optional(),
9418
+ // Feature flags are deliberately absent: this payload carries no manifest, so it cannot express
9419
+ // inheritance. Read flags from the full account on AuthContext via `resolveFlag`.
8923
9420
  account: account_1.AccountViewModel.pick({
8924
9421
  id: true,
8925
9422
  domain: true,
8926
- enableLostProductionCost: true,
8927
9423
  dashboardConfig: true,
8928
9424
  uploadTransforms: true,
8929
- defaultAutoEndOnQuantityComplete: true,
8930
9425
  appUrls: true,
8931
- durationFormat: true,
8932
- allowDragBetweenStationsOnGantt: true
9426
+ durationFormat: true
8933
9427
  }).optional()
8934
9428
  }));
8935
9429
  }