@stelis/agent-q-core 0.0.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.
- package/README.md +144 -0
- package/dist/adapter-internal.d.ts +5 -0
- package/dist/adapter-internal.js +6 -0
- package/dist/adapter-internal.js.map +1 -0
- package/dist/config.d.ts +74 -0
- package/dist/config.js +489 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +320 -0
- package/dist/core.js +840 -0
- package/dist/core.js.map +1 -0
- package/dist/device.d.ts +55 -0
- package/dist/device.js +23 -0
- package/dist/device.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/host-output-schema.d.ts +2437 -0
- package/dist/host-output-schema.js +655 -0
- package/dist/host-output-schema.js.map +1 -0
- package/dist/protocol-error.d.ts +4 -0
- package/dist/protocol-error.js +9 -0
- package/dist/protocol-error.js.map +1 -0
- package/dist/protocol-management-primitives.d.ts +27 -0
- package/dist/protocol-management-primitives.js +51 -0
- package/dist/protocol-management-primitives.js.map +1 -0
- package/dist/protocol-primitives.d.ts +53 -0
- package/dist/protocol-primitives.js +331 -0
- package/dist/protocol-primitives.js.map +1 -0
- package/dist/protocol.d.ts +207 -0
- package/dist/protocol.js +897 -0
- package/dist/protocol.js.map +1 -0
- package/dist/provider-protocol.d.ts +262 -0
- package/dist/provider-protocol.js +637 -0
- package/dist/provider-protocol.js.map +1 -0
- package/dist/public-error.d.ts +9 -0
- package/dist/public-error.js +79 -0
- package/dist/public-error.js.map +1 -0
- package/dist/safe-text.d.ts +31 -0
- package/dist/safe-text.js +143 -0
- package/dist/safe-text.js.map +1 -0
- package/dist/transport-invariants.d.ts +18 -0
- package/dist/transport-invariants.js +24 -0
- package/dist/transport-invariants.js.map +1 -0
- package/dist/usb.d.ts +76 -0
- package/dist/usb.js +454 -0
- package/dist/usb.js.map +1 -0
- package/package.json +58 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { sanitizeDeviceStatusSnapshot, } from "./protocol.js";
|
|
5
|
+
import { MAX_LABEL_LENGTH, PURPOSE_PATTERN, RESERVED_PURPOSES, isIsoTimestamp, isSafeDeviceId, isValidLabel, isValidPurpose, sanitizePortHint, } from "./safe-text.js";
|
|
6
|
+
export const CONFIG_SCHEMA_VERSION = 0;
|
|
7
|
+
// The label/purpose policy is defined once in safe-text.ts (the single source of
|
|
8
|
+
// truth) and re-exported here so existing importers and tests continue to
|
|
9
|
+
// resolve these symbols through config.ts.
|
|
10
|
+
export { MAX_LABEL_LENGTH, PURPOSE_PATTERN, RESERVED_PURPOSES, isValidLabel, isValidPurpose };
|
|
11
|
+
// Unknown-timestamp sentinel. A stored lastSeenAt that is missing or malformed is
|
|
12
|
+
// coerced to the Unix epoch so the field always reaches MCP output as a valid ISO
|
|
13
|
+
// instant. The epoch is a deliberate "unknown / not recently observed" marker:
|
|
14
|
+
// Agent-Q does not invent a plausible current time (a false observation), and it
|
|
15
|
+
// does not drop the device for a bad display-only timestamp.
|
|
16
|
+
const EPOCH_ISO = "1970-01-01T00:00:00.000Z";
|
|
17
|
+
export class ConfigError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
constructor(code, message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "ConfigError";
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function defaultAgentQConfig() {
|
|
26
|
+
return {
|
|
27
|
+
schemaVersion: CONFIG_SCHEMA_VERSION,
|
|
28
|
+
activeDeviceId: null,
|
|
29
|
+
activeDeviceIdsByPurpose: {},
|
|
30
|
+
devices: [],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function getConfigPath(options = {}) {
|
|
34
|
+
const env = options.env ?? process.env;
|
|
35
|
+
const base = env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.length > 0
|
|
36
|
+
? env.XDG_CONFIG_HOME
|
|
37
|
+
: join(options.homeDir ?? homedir(), ".config");
|
|
38
|
+
return join(base, "agent-q", "config.json");
|
|
39
|
+
}
|
|
40
|
+
export class ConfigStore {
|
|
41
|
+
path;
|
|
42
|
+
constructor(path = getConfigPath()) {
|
|
43
|
+
this.path = path;
|
|
44
|
+
}
|
|
45
|
+
// Raw content last warned about, so repeated loads of the same unchanged
|
|
46
|
+
// (hand-edited) config do not re-emit the normalization warning.
|
|
47
|
+
lastWarnedRaw = null;
|
|
48
|
+
async load() {
|
|
49
|
+
try {
|
|
50
|
+
const raw = await readFile(this.path, "utf8");
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
const normalized = normalizeAgentQConfig(parsed);
|
|
53
|
+
if (normalized !== undefined) {
|
|
54
|
+
// One pipeline produces the config AND the report; every transformation it
|
|
55
|
+
// performs increments the report (guarded by the per-field tests and the
|
|
56
|
+
// fixpoint test), so the warning reflects what was actually changed.
|
|
57
|
+
this.maybeWarnNormalized(raw, normalized.report);
|
|
58
|
+
return normalized.config;
|
|
59
|
+
}
|
|
60
|
+
// Structurally invalid but valid JSON: do not silently overwrite without
|
|
61
|
+
// surfacing it. stderr is safe here; stdout is the MCP JSON-RPC channel.
|
|
62
|
+
// The interpolated path is operator-controlled local config (from
|
|
63
|
+
// getConfigPath or the embedding caller), not untrusted request/device
|
|
64
|
+
// text, so unlike a device/Firmware string it is logged as-is to stay
|
|
65
|
+
// useful for debugging.
|
|
66
|
+
console.warn(`agent-q: config at ${this.path} is not a recognized schema; using defaults.`);
|
|
67
|
+
return defaultAgentQConfig();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
71
|
+
return defaultAgentQConfig();
|
|
72
|
+
}
|
|
73
|
+
if (error instanceof SyntaxError) {
|
|
74
|
+
console.warn(`agent-q: config at ${this.path} is not valid JSON; using defaults.`);
|
|
75
|
+
return defaultAgentQConfig();
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// The single disk-write boundary. It re-runs the same normalization pipeline
|
|
81
|
+
// used on load, so the file on disk can never hold an unsafe record or a
|
|
82
|
+
// dangling route regardless of how the caller built the in-memory config — the
|
|
83
|
+
// boundary enforces the SoT itself rather than trusting callers. Private:
|
|
84
|
+
// callers mutate config only through the methods above.
|
|
85
|
+
async writeConfig(config) {
|
|
86
|
+
const normalized = normalizeAgentQConfig(config);
|
|
87
|
+
if (normalized === undefined) {
|
|
88
|
+
// Unreachable: writeConfig only ever receives a current-schema config built by
|
|
89
|
+
// this store. Fail loudly rather than silently writing defaults (which
|
|
90
|
+
// would drop data) if that assumption ever breaks.
|
|
91
|
+
throw new ConfigError("internal_output_error", "Refusing to write an unrecognized config shape.");
|
|
92
|
+
}
|
|
93
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
94
|
+
await writeFile(this.path, `${JSON.stringify(normalized.config, null, 2)}\n`, "utf8");
|
|
95
|
+
}
|
|
96
|
+
// One aggregated stderr warning when stored config was normalized, deduped by
|
|
97
|
+
// raw content so a persistent hand-edited file does not warn on every load.
|
|
98
|
+
// Only counts are logged, never a raw (possibly unsafe) value.
|
|
99
|
+
maybeWarnNormalized(rawContent, report) {
|
|
100
|
+
if (reportTotal(report) === 0 || rawContent === this.lastWarnedRaw) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.lastWarnedRaw = rawContent;
|
|
104
|
+
console.warn(`agent-q: normalized stored config (droppedRecords=${report.droppedRecords}, ` +
|
|
105
|
+
`droppedRoutes=${report.droppedRoutes}, clearedActiveDeviceId=${report.clearedActiveDeviceId}, ` +
|
|
106
|
+
`coercedLabels=${report.coercedLabels}, coercedTimestamps=${report.coercedTimestamps}, ` +
|
|
107
|
+
`sanitizedDeviceDisplayText=${report.sanitizedDeviceDisplayText}, ` +
|
|
108
|
+
`sanitizedPortHints=${report.sanitizedPortHints}).`);
|
|
109
|
+
}
|
|
110
|
+
async listDevices() {
|
|
111
|
+
const config = await this.load();
|
|
112
|
+
return config.devices.map((record) => toDeviceListing(record, config));
|
|
113
|
+
}
|
|
114
|
+
async rememberUsbStatus(status, portPath, options = {}) {
|
|
115
|
+
const config = await this.load();
|
|
116
|
+
const lastSeenAt = (options.observedAt ?? new Date()).toISOString();
|
|
117
|
+
// portPath is an OS-supplied diagnostic string; sanitize it before it is
|
|
118
|
+
// stored and later surfaced as lastPortHint in MCP output.
|
|
119
|
+
const lastPortHint = sanitizePortHint(portPath);
|
|
120
|
+
// Defensive: callers pass a wire-parsed (already sanitized) status, but the
|
|
121
|
+
// write boundary re-applies the same policy so the stored registry can never
|
|
122
|
+
// hold an unsafe identity, invalid provisioning state, or unsanitized display
|
|
123
|
+
// string.
|
|
124
|
+
const safeStatus = sanitizeDeviceStatusSnapshot(status);
|
|
125
|
+
if (safeStatus === null) {
|
|
126
|
+
throw new ConfigError("invalid_device", "Refusing to store a device with an unsafe identity.");
|
|
127
|
+
}
|
|
128
|
+
const safeDevice = safeStatus.device;
|
|
129
|
+
const index = config.devices.findIndex((candidate) => candidate.deviceId === safeDevice.deviceId);
|
|
130
|
+
if (index >= 0) {
|
|
131
|
+
// Status refresh must not wipe local metadata such as label.
|
|
132
|
+
const existing = config.devices[index];
|
|
133
|
+
config.devices[index] = {
|
|
134
|
+
...existing,
|
|
135
|
+
transport: "usb",
|
|
136
|
+
lastPortHint,
|
|
137
|
+
lastSeenAt,
|
|
138
|
+
lastStatus: safeStatus,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
config.devices.push({
|
|
143
|
+
deviceId: safeDevice.deviceId,
|
|
144
|
+
transport: "usb",
|
|
145
|
+
lastPortHint,
|
|
146
|
+
lastSeenAt,
|
|
147
|
+
label: null,
|
|
148
|
+
lastStatus: safeStatus,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (options.setActive === true) {
|
|
152
|
+
config.activeDeviceId = safeDevice.deviceId;
|
|
153
|
+
}
|
|
154
|
+
await this.writeConfig(config);
|
|
155
|
+
return config;
|
|
156
|
+
}
|
|
157
|
+
async setDeviceMetadata(input) {
|
|
158
|
+
if (!isSafeDeviceId(input.deviceId)) {
|
|
159
|
+
throw new ConfigError("invalid_device_id", "deviceId is not a valid device identifier.");
|
|
160
|
+
}
|
|
161
|
+
if (!Object.prototype.hasOwnProperty.call(input, "label")) {
|
|
162
|
+
throw new ConfigError("invalid_metadata", "set_device_metadata requires at least one metadata field.");
|
|
163
|
+
}
|
|
164
|
+
if (!isValidLabel(input.label)) {
|
|
165
|
+
throw new ConfigError("invalid_label", `label must be null or a 1-${MAX_LABEL_LENGTH} character string.`);
|
|
166
|
+
}
|
|
167
|
+
const config = await this.load();
|
|
168
|
+
const index = config.devices.findIndex((candidate) => candidate.deviceId === input.deviceId);
|
|
169
|
+
if (index < 0) {
|
|
170
|
+
throw new ConfigError("device_not_found", "Device is not known to Agent-Q.");
|
|
171
|
+
}
|
|
172
|
+
const record = {
|
|
173
|
+
...config.devices[index],
|
|
174
|
+
label: input.label ?? null,
|
|
175
|
+
};
|
|
176
|
+
config.devices[index] = record;
|
|
177
|
+
await this.writeConfig(config);
|
|
178
|
+
return record;
|
|
179
|
+
}
|
|
180
|
+
async setActiveDevice(deviceId, purpose) {
|
|
181
|
+
if (!isSafeDeviceId(deviceId)) {
|
|
182
|
+
throw new ConfigError("invalid_device_id", "deviceId is not a valid device identifier.");
|
|
183
|
+
}
|
|
184
|
+
if (purpose !== undefined) {
|
|
185
|
+
if (RESERVED_PURPOSES.has(purpose)) {
|
|
186
|
+
throw new ConfigError("reserved_purpose", `purpose '${purpose}' is reserved. Omit purpose to set the default device.`);
|
|
187
|
+
}
|
|
188
|
+
if (!isValidPurpose(purpose)) {
|
|
189
|
+
throw new ConfigError("invalid_purpose", "purpose must be 1-32 characters of [A-Za-z0-9_.-].");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const config = await this.load();
|
|
193
|
+
const record = config.devices.find((candidate) => candidate.deviceId === deviceId);
|
|
194
|
+
if (record === undefined) {
|
|
195
|
+
throw new ConfigError("device_not_found", "Device is not known to Agent-Q.");
|
|
196
|
+
}
|
|
197
|
+
if (purpose === undefined) {
|
|
198
|
+
config.activeDeviceId = deviceId;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
config.activeDeviceIdsByPurpose[purpose] = deviceId;
|
|
202
|
+
}
|
|
203
|
+
await this.writeConfig(config);
|
|
204
|
+
return record;
|
|
205
|
+
}
|
|
206
|
+
async getActiveDevice(purpose) {
|
|
207
|
+
if (purpose !== undefined && RESERVED_PURPOSES.has(purpose)) {
|
|
208
|
+
throw new ConfigError("reserved_purpose", `purpose '${purpose}' is reserved. Omit purpose to read the default device.`);
|
|
209
|
+
}
|
|
210
|
+
if (purpose !== undefined && !isValidPurpose(purpose)) {
|
|
211
|
+
throw new ConfigError("invalid_purpose", "purpose must be 1-32 characters of [A-Za-z0-9_.-].");
|
|
212
|
+
}
|
|
213
|
+
const config = await this.load();
|
|
214
|
+
// Read the purpose map with an own-property lookup so an inherited name (for
|
|
215
|
+
// example "toString" or "hasOwnProperty") can never resolve to a prototype
|
|
216
|
+
// value instead of an actual routing entry.
|
|
217
|
+
const deviceId = purpose === undefined
|
|
218
|
+
? config.activeDeviceId
|
|
219
|
+
: Object.prototype.hasOwnProperty.call(config.activeDeviceIdsByPurpose, purpose)
|
|
220
|
+
? config.activeDeviceIdsByPurpose[purpose]
|
|
221
|
+
: undefined;
|
|
222
|
+
if (deviceId === null || deviceId === undefined || deviceId.length === 0) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
return config.devices.find((candidate) => candidate.deviceId === deviceId);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function toDeviceListing(record, config) {
|
|
229
|
+
const assignedPurposes = Object.entries(config.activeDeviceIdsByPurpose)
|
|
230
|
+
.filter(([, deviceId]) => deviceId === record.deviceId)
|
|
231
|
+
.map(([purpose]) => purpose)
|
|
232
|
+
.sort();
|
|
233
|
+
return {
|
|
234
|
+
deviceId: record.deviceId,
|
|
235
|
+
transport: record.transport,
|
|
236
|
+
lastPortHint: record.lastPortHint,
|
|
237
|
+
lastSeenAt: record.lastSeenAt,
|
|
238
|
+
label: record.label,
|
|
239
|
+
lastStatus: record.lastStatus,
|
|
240
|
+
assignedPurposes,
|
|
241
|
+
isDefaultActive: config.activeDeviceId === record.deviceId,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Drop routing selections that point at devices not present in `devices`.
|
|
246
|
+
* Writes always validate references, so dangling entries only arise from a
|
|
247
|
+
* hand-edited config. Pruning keeps `list_devices` and purpose routing
|
|
248
|
+
* self-consistent instead of failing later with `device_not_found`.
|
|
249
|
+
*
|
|
250
|
+
* Stable exported shape. Internally this is the report-free view of the
|
|
251
|
+
* pipeline's reference-pruning stage ({@link pruneReferences}); load and write
|
|
252
|
+
* thread a NormalizationReport through that stage instead.
|
|
253
|
+
*/
|
|
254
|
+
export function normalizeReferences(config) {
|
|
255
|
+
return pruneReferences(config, emptyReport());
|
|
256
|
+
}
|
|
257
|
+
function emptyReport() {
|
|
258
|
+
return {
|
|
259
|
+
droppedRecords: 0,
|
|
260
|
+
droppedRoutes: 0,
|
|
261
|
+
clearedActiveDeviceId: 0,
|
|
262
|
+
coercedLabels: 0,
|
|
263
|
+
coercedTimestamps: 0,
|
|
264
|
+
sanitizedDeviceDisplayText: 0,
|
|
265
|
+
sanitizedPortHints: 0,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function reportTotal(report) {
|
|
269
|
+
return (report.droppedRecords +
|
|
270
|
+
report.droppedRoutes +
|
|
271
|
+
report.clearedActiveDeviceId +
|
|
272
|
+
report.coercedLabels +
|
|
273
|
+
report.coercedTimestamps +
|
|
274
|
+
report.sanitizedDeviceDisplayText +
|
|
275
|
+
report.sanitizedPortHints);
|
|
276
|
+
}
|
|
277
|
+
// THE single config-normalization pipeline. Both load() (untrusted disk input)
|
|
278
|
+
// and writeConfig() (defensive re-normalization of in-memory state) go through
|
|
279
|
+
// here, and every drop/reset/sanitize/prune is recorded in one report. An empty
|
|
280
|
+
// report therefore means the input was already fully normalized: there is no path
|
|
281
|
+
// that changes the config without recording it. Returns undefined only when the
|
|
282
|
+
// envelope is not a recognized schema — a wholesale fallback to defaults, not a
|
|
283
|
+
// field-level normalization, which the caller surfaces separately.
|
|
284
|
+
function normalizeAgentQConfig(input) {
|
|
285
|
+
const recognized = recognizeConfigShape(input);
|
|
286
|
+
if (recognized === undefined) {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
const report = emptyReport();
|
|
290
|
+
const assembled = assembleConfig(recognized, report);
|
|
291
|
+
const config = pruneReferences(assembled, report);
|
|
292
|
+
return { config, report };
|
|
293
|
+
}
|
|
294
|
+
// Top-level envelope gate. A config whose envelope is wrong or whose schema
|
|
295
|
+
// version is not current is treated as foreign (the caller uses defaults);
|
|
296
|
+
// individual current-schema device records and routing entries are normalized
|
|
297
|
+
// later rather than rejected wholesale, so one bad record cannot discard the
|
|
298
|
+
// whole registry.
|
|
299
|
+
function recognizeConfigShape(value) {
|
|
300
|
+
if (!isRecord(value)) {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
if (!(typeof value.activeDeviceId === "string" || value.activeDeviceId === null)) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
if (value.schemaVersion === CONFIG_SCHEMA_VERSION) {
|
|
307
|
+
if (!isRecord(value.activeDeviceIdsByPurpose) || !Array.isArray(value.devices)) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
activeDeviceId: value.activeDeviceId,
|
|
312
|
+
activeDeviceIdsByPurpose: value.activeDeviceIdsByPurpose,
|
|
313
|
+
devices: value.devices,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
// Build a clean registry from loosely-typed input. The disk-ingress trust
|
|
319
|
+
// boundary, mirroring protocol.ts at the wire: records that cannot be made safe
|
|
320
|
+
// are dropped, duplicates removed, salvageable records sanitized, malformed
|
|
321
|
+
// routes discarded. Every change is recorded in `report`.
|
|
322
|
+
function assembleConfig(recognized, report) {
|
|
323
|
+
const normalizedDevices = [];
|
|
324
|
+
const seenDeviceIds = new Set();
|
|
325
|
+
for (const device of recognized.devices) {
|
|
326
|
+
const record = normalizeDeviceRecord(device, report);
|
|
327
|
+
if (record === null) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (seenDeviceIds.has(record.deviceId)) {
|
|
331
|
+
// Duplicate deviceId from a hand-edited config: keep the first, drop the rest.
|
|
332
|
+
report.droppedRecords += 1;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
seenDeviceIds.add(record.deviceId);
|
|
336
|
+
normalizedDevices.push(record);
|
|
337
|
+
}
|
|
338
|
+
// Purpose names are validated before being used as map keys (isValidPurpose
|
|
339
|
+
// rejects prototype-sensitive names); reads elsewhere use own-property lookups.
|
|
340
|
+
const normalizedPurposes = {};
|
|
341
|
+
for (const [purpose, deviceId] of Object.entries(recognized.activeDeviceIdsByPurpose)) {
|
|
342
|
+
if (isValidPurpose(purpose) && isSafeDeviceId(deviceId)) {
|
|
343
|
+
normalizedPurposes[purpose] = deviceId;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
report.droppedRoutes += 1;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// A malformed (non-safe) activeDeviceId is reset to null and counted; a
|
|
350
|
+
// well-formed but dangling one is handled by pruneReferences.
|
|
351
|
+
let activeDeviceId = null;
|
|
352
|
+
if (isSafeDeviceId(recognized.activeDeviceId)) {
|
|
353
|
+
activeDeviceId = recognized.activeDeviceId;
|
|
354
|
+
}
|
|
355
|
+
else if (recognized.activeDeviceId !== null) {
|
|
356
|
+
report.clearedActiveDeviceId += 1;
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
schemaVersion: CONFIG_SCHEMA_VERSION,
|
|
360
|
+
activeDeviceId,
|
|
361
|
+
activeDeviceIdsByPurpose: normalizedPurposes,
|
|
362
|
+
devices: normalizedDevices,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// Drop routing selections (and a dangling default active id) that point at
|
|
366
|
+
// devices not present in `devices`, recording each removal in `report`.
|
|
367
|
+
function pruneReferences(config, report) {
|
|
368
|
+
const knownDeviceIds = new Set(config.devices.map((device) => device.deviceId));
|
|
369
|
+
let activeDeviceId = config.activeDeviceId;
|
|
370
|
+
if (activeDeviceId !== null && !knownDeviceIds.has(activeDeviceId)) {
|
|
371
|
+
// Well-formed id that names no known device: a dangling active selection.
|
|
372
|
+
activeDeviceId = null;
|
|
373
|
+
report.clearedActiveDeviceId += 1;
|
|
374
|
+
}
|
|
375
|
+
const activeDeviceIdsByPurpose = {};
|
|
376
|
+
for (const [purpose, deviceId] of Object.entries(config.activeDeviceIdsByPurpose)) {
|
|
377
|
+
if (knownDeviceIds.has(deviceId)) {
|
|
378
|
+
activeDeviceIdsByPurpose[purpose] = deviceId;
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
report.droppedRoutes += 1;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (activeDeviceId === config.activeDeviceId &&
|
|
385
|
+
Object.keys(activeDeviceIdsByPurpose).length === Object.keys(config.activeDeviceIdsByPurpose).length) {
|
|
386
|
+
return config;
|
|
387
|
+
}
|
|
388
|
+
return { ...config, activeDeviceId, activeDeviceIdsByPurpose };
|
|
389
|
+
}
|
|
390
|
+
// Normalize one stored device record to a safe DeviceRecord, or null when its
|
|
391
|
+
// identity cannot be trusted. deviceId, transport, and the cached device's
|
|
392
|
+
// identity are REJECTED when malformed (the record is dropped); display text,
|
|
393
|
+
// label, port hint, and timestamp are sanitized or reset to a safe value. Every
|
|
394
|
+
// such change is recorded in `report`, so a silently-altered field cannot escape
|
|
395
|
+
// the operator warning.
|
|
396
|
+
function normalizeDeviceRecord(value, report) {
|
|
397
|
+
if (!isRecord(value) || !isSafeDeviceId(value.deviceId) || value.transport !== "usb") {
|
|
398
|
+
report.droppedRecords += 1;
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
if (!isRecord(value.lastStatus)) {
|
|
402
|
+
report.droppedRecords += 1;
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
const rawStatus = value.lastStatus;
|
|
406
|
+
const status = sanitizeDeviceStatusSnapshot(rawStatus);
|
|
407
|
+
if (status === null) {
|
|
408
|
+
report.droppedRecords += 1;
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
const rawDevice = rawStatus.device;
|
|
412
|
+
const device = status.device;
|
|
413
|
+
// Identity invariant: the record id and the cached device id must name the same
|
|
414
|
+
// device. A hand-edited config that splits them would route/select one id while
|
|
415
|
+
// exposing cached status for another, so the record is dropped.
|
|
416
|
+
if (device.deviceId !== value.deviceId) {
|
|
417
|
+
report.droppedRecords += 1;
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
// Stripping control bytes from (or truncating) firmwareName, hardware, or
|
|
421
|
+
// firmwareVersion is a normalization too; count each present-but-rewritten
|
|
422
|
+
// field so the warning reports it (a missing field defaulting to "" is not
|
|
423
|
+
// counted, matching the label/timestamp rule).
|
|
424
|
+
report.sanitizedDeviceDisplayText += countSanitizedDisplayFields(rawDevice, device);
|
|
425
|
+
const rawLabel = value.label;
|
|
426
|
+
let label;
|
|
427
|
+
if (isValidLabel(rawLabel)) {
|
|
428
|
+
label = rawLabel;
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
label = null;
|
|
432
|
+
// A present-but-invalid label was reset; a missing label (null/undefined) is
|
|
433
|
+
// normal and not counted.
|
|
434
|
+
if (rawLabel !== undefined && rawLabel !== null) {
|
|
435
|
+
report.coercedLabels += 1;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
let lastSeenAt;
|
|
439
|
+
if (isIsoTimestamp(value.lastSeenAt)) {
|
|
440
|
+
lastSeenAt = value.lastSeenAt;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
lastSeenAt = EPOCH_ISO;
|
|
444
|
+
if (value.lastSeenAt !== undefined) {
|
|
445
|
+
report.coercedTimestamps += 1;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const lastPortHint = sanitizePortHint(value.lastPortHint);
|
|
449
|
+
// A present-but-rewritten port hint is a sanitization; a missing one is not.
|
|
450
|
+
if (value.lastPortHint !== undefined && lastPortHint !== value.lastPortHint) {
|
|
451
|
+
report.sanitizedPortHints += 1;
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
deviceId: value.deviceId,
|
|
455
|
+
transport: "usb",
|
|
456
|
+
lastPortHint,
|
|
457
|
+
lastSeenAt,
|
|
458
|
+
label,
|
|
459
|
+
lastStatus: status,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
// Number of device display-text fields that sanitization changed. Only a field
|
|
463
|
+
// that was PRESENT on disk and differs from its sanitized form counts, so a clean
|
|
464
|
+
// value adds nothing and a missing field (defaulted to "") is not counted —
|
|
465
|
+
// consistent with how labels and timestamps treat a missing value.
|
|
466
|
+
function countSanitizedDisplayFields(rawDevice, sanitized) {
|
|
467
|
+
if (!isRecord(rawDevice)) {
|
|
468
|
+
return 0;
|
|
469
|
+
}
|
|
470
|
+
const fields = [
|
|
471
|
+
"firmwareName",
|
|
472
|
+
"hardware",
|
|
473
|
+
"firmwareVersion",
|
|
474
|
+
];
|
|
475
|
+
let count = 0;
|
|
476
|
+
for (const field of fields) {
|
|
477
|
+
if (rawDevice[field] !== undefined && rawDevice[field] !== sanitized[field]) {
|
|
478
|
+
count += 1;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return count;
|
|
482
|
+
}
|
|
483
|
+
function isRecord(value) {
|
|
484
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
485
|
+
}
|
|
486
|
+
function isNodeError(error) {
|
|
487
|
+
return error instanceof Error && "code" in error;
|
|
488
|
+
}
|
|
489
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EACL,4BAA4B,GAG7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC,iFAAiF;AACjF,0EAA0E;AAC1E,2CAA2C;AAC3C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC;AAE9F,kFAAkF;AAClF,kFAAkF;AAClF,+EAA+E;AAC/E,iFAAiF;AACjF,6DAA6D;AAC7D,MAAM,SAAS,GAAG,0BAA0B,CAAC;AAiD7C,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO;QACL,aAAa,EAAE,qBAAqB;QACpC,cAAc,EAAE,IAAI;QACpB,wBAAwB,EAAE,EAAE;QAC5B,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAA6B,EAAE;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC;QAChE,CAAC,CAAC,GAAG,CAAC,eAAe;QACrB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,OAAO,WAAW;IACb,IAAI,CAAS;IAEtB,YAAY,IAAI,GAAG,aAAa,EAAE;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,yEAAyE;IACzE,iEAAiE;IACzD,aAAa,GAAkB,IAAI,CAAC;IAE5C,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,UAAU,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;YACjD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,2EAA2E;gBAC3E,yEAAyE;gBACzE,qEAAqE;gBACrE,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;gBACjD,OAAO,UAAU,CAAC,MAAM,CAAC;YAC3B,CAAC;YACD,yEAAyE;YACzE,yEAAyE;YACzE,kEAAkE;YAClE,uEAAuE;YACvE,sEAAsE;YACtE,wBAAwB;YACxB,OAAO,CAAC,IAAI,CACV,sBAAsB,IAAI,CAAC,IAAI,8CAA8C,CAC9E,CAAC;YACF,OAAO,mBAAmB,EAAE,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAClD,OAAO,mBAAmB,EAAE,CAAC;YAC/B,CAAC;YACD,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CACV,sBAAsB,IAAI,CAAC,IAAI,qCAAqC,CACrE,CAAC;gBACF,OAAO,mBAAmB,EAAE,CAAC;YAC/B,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,yEAAyE;IACzE,+EAA+E;IAC/E,0EAA0E;IAC1E,wDAAwD;IAChD,KAAK,CAAC,WAAW,CAAC,MAAoB;QAC5C,MAAM,UAAU,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACjD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,+EAA+E;YAC/E,uEAAuE;YACvE,mDAAmD;YACnD,MAAM,IAAI,WAAW,CAAC,uBAAuB,EAAE,iDAAiD,CAAC,CAAC;QACpG,CAAC;QACD,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxF,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,+DAA+D;IACvD,mBAAmB,CAAC,UAAkB,EAAE,MAA2B;QACzE,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,UAAU,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;YACnE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC;QAChC,OAAO,CAAC,IAAI,CACV,qDAAqD,MAAM,CAAC,cAAc,IAAI;YAC5E,iBAAiB,MAAM,CAAC,aAAa,2BAA2B,MAAM,CAAC,qBAAqB,IAAI;YAChG,iBAAiB,MAAM,CAAC,aAAa,uBAAuB,MAAM,CAAC,iBAAiB,IAAI;YACxF,8BAA8B,MAAM,CAAC,0BAA0B,IAAI;YACnE,sBAAsB,MAAM,CAAC,kBAAkB,IAAI,CACtD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,MAA4B,EAC5B,QAAgB,EAChB,UAAoC,EAAE;QAEtC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACpE,yEAAyE;QACzE,2DAA2D;QAC3D,MAAM,YAAY,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAChD,4EAA4E;QAC5E,6EAA6E;QAC7E,8EAA8E;QAC9E,UAAU;QACV,MAAM,UAAU,GAAG,4BAA4B,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,qDAAqD,CAAC,CAAC;QACjG,CAAC;QACD,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;QACrC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,KAAK,UAAU,CAAC,QAAQ,CAAC,CAAC;QAElG,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,6DAA6D;YAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG;gBACtB,GAAG,QAAQ;gBACX,SAAS,EAAE,KAAK;gBAChB,YAAY;gBACZ,UAAU;gBACV,UAAU,EAAE,UAAU;aACvB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBAClB,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,SAAS,EAAE,KAAK;gBAChB,YAAY;gBACZ,UAAU;gBACV,KAAK,EAAE,IAAI;gBACX,UAAU,EAAE,UAAU;aACvB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,CAAC,cAAc,GAAG,UAAU,CAAC,QAAQ,CAAC;QAC9C,CAAC;QAED,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC/B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,KAA6B;QACnD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,WAAW,CAAC,mBAAmB,EAAE,4CAA4C,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,WAAW,CAAC,kBAAkB,EAAE,2DAA2D,CAAC,CAAC;QACzG,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,WAAW,CACnB,eAAe,EACf,6BAA6B,gBAAgB,oBAAoB,CAClE,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7F,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,IAAI,WAAW,CAAC,kBAAkB,EAAE,iCAAiC,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,MAAM,GAAiB;YAC3B,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;SAC3B,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAC/B,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC/B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,QAAgB,EAAE,OAAgB;QACtD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,WAAW,CAAC,mBAAmB,EAAE,4CAA4C,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,WAAW,CACnB,kBAAkB,EAClB,YAAY,OAAO,wDAAwD,CAC5E,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,WAAW,CACnB,iBAAiB,EACjB,oDAAoD,CACrD,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QACnF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,WAAW,CAAC,kBAAkB,EAAE,iCAAiC,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,cAAc,GAAG,QAAQ,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,wBAAwB,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;QACtD,CAAC;QACD,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC/B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAAgB;QACpC,IAAI,OAAO,KAAK,SAAS,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,WAAW,CACnB,kBAAkB,EAClB,YAAY,OAAO,yDAAyD,CAC7E,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,WAAW,CACnB,iBAAiB,EACjB,oDAAoD,CACrD,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,6EAA6E;QAC7E,2EAA2E;QAC3E,4CAA4C;QAC5C,MAAM,QAAQ,GACZ,OAAO,KAAK,SAAS;YACnB,CAAC,CAAC,MAAM,CAAC,cAAc;YACvB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,wBAAwB,EAAE,OAAO,CAAC;gBAC9E,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,OAAO,CAAC;gBAC1C,CAAC,CAAC,SAAS,CAAC;QAClB,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IAC7E,CAAC;CACF;AAED,SAAS,eAAe,CAAC,MAAoB,EAAE,MAAoB;IACjE,MAAM,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC;SACrE,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,CAAC;SACtD,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC;SAC3B,IAAI,EAAE,CAAC;IACV,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,gBAAgB;QAChB,eAAe,EAAE,MAAM,CAAC,cAAc,KAAK,MAAM,CAAC,QAAQ;KAC3D,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAoB;IACtD,OAAO,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;AAChD,CAAC;AAgBD,SAAS,WAAW;IAClB,OAAO;QACL,cAAc,EAAE,CAAC;QACjB,aAAa,EAAE,CAAC;QAChB,qBAAqB,EAAE,CAAC;QACxB,aAAa,EAAE,CAAC;QAChB,iBAAiB,EAAE,CAAC;QACpB,0BAA0B,EAAE,CAAC;QAC7B,kBAAkB,EAAE,CAAC;KACtB,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAA2B;IAC9C,OAAO,CACL,MAAM,CAAC,cAAc;QACrB,MAAM,CAAC,aAAa;QACpB,MAAM,CAAC,qBAAqB;QAC5B,MAAM,CAAC,aAAa;QACpB,MAAM,CAAC,iBAAiB;QACxB,MAAM,CAAC,0BAA0B;QACjC,MAAM,CAAC,kBAAkB,CAC1B,CAAC;AACJ,CAAC;AAQD,+EAA+E;AAC/E,+EAA+E;AAC/E,gFAAgF;AAChF,kFAAkF;AAClF,gFAAgF;AAChF,gFAAgF;AAChF,mEAAmE;AACnE,SAAS,qBAAqB,CAC5B,KAAc;IAEd,MAAM,UAAU,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAG,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,4EAA4E;AAC5E,2EAA2E;AAC3E,8EAA8E;AAC9E,6EAA6E;AAC7E,kBAAkB;AAClB,SAAS,oBAAoB,CAAC,KAAc;IAC1C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,cAAc,KAAK,QAAQ,IAAI,KAAK,CAAC,cAAc,KAAK,IAAI,CAAC,EAAE,CAAC;QACjF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,KAAK,qBAAqB,EAAE,CAAC;QAClD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/E,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO;YACL,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,wBAAwB,EAAE,KAAK,CAAC,wBAAwB;YACxD,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,0EAA0E;AAC1E,gFAAgF;AAChF,4EAA4E;AAC5E,0DAA0D;AAC1D,SAAS,cAAc,CAAC,UAA4B,EAAE,MAA2B;IAC/E,MAAM,iBAAiB,GAAmB,EAAE,CAAC;IAC7C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,SAAS;QACX,CAAC;QACD,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,+EAA+E;YAC/E,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,4EAA4E;IAC5E,gFAAgF;IAChF,MAAM,kBAAkB,GAA2B,EAAE,CAAC;IACtD,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,wBAAwB,CAAC,EAAE,CAAC;QACtF,IAAI,cAAc,CAAC,OAAO,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxD,kBAAkB,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,8DAA8D;IAC9D,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,cAAc,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC9C,cAAc,GAAG,UAAU,CAAC,cAAc,CAAC;IAC7C,CAAC;SAAM,IAAI,UAAU,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QAC9C,MAAM,CAAC,qBAAqB,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,OAAO;QACL,aAAa,EAAE,qBAAqB;QACpC,cAAc;QACd,wBAAwB,EAAE,kBAAkB;QAC5C,OAAO,EAAE,iBAAiB;KAC3B,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,wEAAwE;AACxE,SAAS,eAAe,CAAC,MAAoB,EAAE,MAA2B;IACxE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEhF,IAAI,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;IAC3C,IAAI,cAAc,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;QACnE,0EAA0E;QAC1E,cAAc,GAAG,IAAI,CAAC;QACtB,MAAM,CAAC,qBAAqB,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,wBAAwB,GAA2B,EAAE,CAAC;IAC5D,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,EAAE,CAAC;QAClF,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,wBAAwB,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,IACE,cAAc,KAAK,MAAM,CAAC,cAAc;QACxC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,MAAM,EACpG,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,GAAG,MAAM,EAAE,cAAc,EAAE,wBAAwB,EAAE,CAAC;AACjE,CAAC;AAED,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAC9E,gFAAgF;AAChF,iFAAiF;AACjF,wBAAwB;AACxB,SAAS,qBAAqB,CAAC,KAAc,EAAE,MAA2B;IACxE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QACrF,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;IACnC,MAAM,MAAM,GAAG,4BAA4B,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,gFAAgF;IAChF,gFAAgF;IAChF,gEAAgE;IAChE,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,+CAA+C;IAC/C,MAAM,CAAC,0BAA0B,IAAI,2BAA2B,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAEpF,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC;IAC7B,IAAI,KAAoB,CAAC;IACzB,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,KAAK,GAAG,QAAQ,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,IAAI,CAAC;QACb,6EAA6E;QAC7E,0BAA0B;QAC1B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAChD,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,IAAI,UAAkB,CAAC;IACvB,IAAI,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,SAAS,CAAC;QACvB,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC1D,6EAA6E;IAC7E,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,KAAK,CAAC,YAAY,EAAE,CAAC;QAC5E,MAAM,CAAC,kBAAkB,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK;QAChB,YAAY;QACZ,UAAU;QACV,KAAK;QACL,UAAU,EAAE,MAAM;KACnB,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,kFAAkF;AAClF,4EAA4E;AAC5E,mEAAmE;AACnE,SAAS,2BAA2B,CAAC,SAAkB,EAAE,SAAuB;IAC9E,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAA2D;QACrE,cAAc;QACd,UAAU;QACV,iBAAiB;KAClB,CAAC;IACF,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5E,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC;AACnD,CAAC"}
|