@camstack/system 1.2.58 → 1.2.60

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 (54) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  15. package/dist/builtins/doorbell/binding-mirror.d.ts +65 -0
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +56 -11
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +248 -26
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +248 -26
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  22. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +2 -2
  36. package/dist/builtins/snapshot/index.mjs +2 -2
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/retired-settings-keys.d.ts +94 -0
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +142 -2
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +142 -2
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/system-config/system-config.addon.js +1 -1
  45. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  46. package/dist/builtins/winston-logging/index.js +1 -1
  47. package/dist/builtins/winston-logging/index.mjs +1 -1
  48. package/dist/{dist-UrXyJ9Zm.mjs → dist-C5XpOjbI.mjs} +841 -38
  49. package/dist/{dist-Chl2MvMd.js → dist-Cis_iV-u.js} +852 -37
  50. package/dist/index.js +187 -39
  51. package/dist/index.mjs +187 -39
  52. package/dist/kernel/addon-installer.d.ts +55 -7
  53. package/dist/kernel/addon-manifest.d.ts +36 -16
  54. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Chl2MvMd.js");
6
+ const require_dist = require("../../dist-Cis_iV-u.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs = require("node:fs");
9
9
  let node_module = require("node:module");
@@ -84,6 +84,141 @@ var SQLITE_MMAP_SIZE_BYTES = 268435456;
84
84
  */
85
85
  var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
86
86
  //#endregion
87
+ //#region src/builtins/sqlite-storage/retired-settings-keys.ts
88
+ /**
89
+ * Is THIS node the one whose settings store is the cluster's authority?
90
+ *
91
+ * Mirrors `resolveNodeRole` (server/backend `node-role.ts`) — unset or `'hub'`
92
+ * means hub, which is the launcher's own default. It is duplicated rather than
93
+ * imported because `@camstack/system` is a dependency OF the backend, not the
94
+ * other way round; the parse is four lines and the env var is the contract.
95
+ *
96
+ * Anything else answers `false`. Skipping where the purge should have run is a
97
+ * missed cleanup the next hub boot fixes; running where it should not have is a
98
+ * write to a store this node does not own. Only one of those is recoverable.
99
+ */
100
+ function settingsStoreIsAuthoritativeHere(env) {
101
+ const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
102
+ return raw === "" || raw === "hub";
103
+ }
104
+ /**
105
+ * The dead keys, per row.
106
+ *
107
+ * `detection-pipeline` / `addon-settings` / `root` — every key in this row is
108
+ * dead; the row itself is kept because the addon writes live keys into it
109
+ * (`pipelineTemplates`, `videoPipelineSteps`).
110
+ *
111
+ * - `pipelineSteps` / `pipelineEngine`: the persisted GLOBAL step + engine
112
+ * seed. Removed with `getGlobalSteps` becoming pure — dispatch builds steps
113
+ * from the orchestrator's `agentSettings` plus per-camera overrides, and
114
+ * never consulted the seed. See `addon-pipeline/src/detection-pipeline/provider.ts`.
115
+ * - `engineRuntime` / `engineBackend` / `engineDevice` / `probedBestEngine`:
116
+ * the per-node engine CASCADE, removed with the operator-facing engine
117
+ * election. Each node now runs a device-array of inference pools balanced
118
+ * per session, and the node-default pool derives live from the node's own
119
+ * hardware (`resolveAutoEngine`). See
120
+ * `addon-pipeline/src/detection-pipeline/engine-store-keys.ts`.
121
+ */
122
+ var RETIRED_SETTINGS_KEYS = [{
123
+ namespace: "detection-pipeline",
124
+ collection: "addon-settings",
125
+ row: "root",
126
+ keys: ["pipelineSteps", "pipelineEngine"],
127
+ perNodeKeys: [
128
+ "engineRuntime",
129
+ "engineBackend",
130
+ "engineDevice",
131
+ "probedBestEngine"
132
+ ],
133
+ reason: "global step/engine seed and the per-node engine cascade — both removed; the live per-camera dispatch path never read either"
134
+ }];
135
+ /**
136
+ * Adapt the settings backend to {@link RetiredKeyStore}.
137
+ *
138
+ * `get` answering a non-object (missing row) is a MISS, not a fault: it maps to
139
+ * `null` and the purge skips the row. A backend throw propagates, which is what
140
+ * routes it to the "read failed, row left untouched" branch.
141
+ */
142
+ function retiredKeyStoreOf(backend) {
143
+ return {
144
+ async read(spec) {
145
+ return require_dist.asJsonObject(await backend.get({
146
+ namespace: spec.namespace,
147
+ collection: spec.collection,
148
+ key: spec.row
149
+ }));
150
+ },
151
+ async write(spec, value) {
152
+ await backend.set({
153
+ namespace: spec.namespace,
154
+ collection: spec.collection,
155
+ key: spec.row,
156
+ value
157
+ });
158
+ }
159
+ };
160
+ }
161
+ /** Keys of `blob` that `spec` retires. Pure. */
162
+ function planRetiredKeyPurge(blob, spec) {
163
+ const exact = new Set(spec.keys);
164
+ const found = [];
165
+ for (const key of Object.keys(blob)) {
166
+ if (exact.has(key)) {
167
+ found.push(key);
168
+ continue;
169
+ }
170
+ for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
171
+ found.push(key);
172
+ break;
173
+ }
174
+ }
175
+ return found;
176
+ }
177
+ /**
178
+ * Delete every retired key present in the declared rows — on the hub.
179
+ *
180
+ * Off the authoritative node this is a no-op that touches nothing and says
181
+ * nothing (see the module header). Returns one entry per row that actually
182
+ * changed — empty when the store is already clean, which is the steady state
183
+ * after the first boot that ran it.
184
+ */
185
+ async function purgeRetiredSettingsKeys(store, logger, specs = RETIRED_SETTINGS_KEYS, env = process.env) {
186
+ if (!settingsStoreIsAuthoritativeHere(env)) return [];
187
+ const results = [];
188
+ for (const spec of specs) {
189
+ let blob;
190
+ try {
191
+ blob = await store.read(spec);
192
+ } catch (err) {
193
+ logger.warn("retired settings keys — read failed, row left untouched", { meta: {
194
+ addon: spec.namespace,
195
+ collection: spec.collection,
196
+ row: spec.row,
197
+ error: err instanceof Error ? err.message : String(err)
198
+ } });
199
+ continue;
200
+ }
201
+ if (blob === null) continue;
202
+ const doomed = planRetiredKeyPurge(blob, spec);
203
+ if (doomed.length === 0) continue;
204
+ const doomedSet = new Set(doomed);
205
+ const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)));
206
+ await store.write(spec, next);
207
+ const result = {
208
+ addon: spec.namespace,
209
+ collection: spec.collection,
210
+ row: spec.row,
211
+ keys: doomed
212
+ };
213
+ results.push(result);
214
+ logger.info(`purged ${doomed.length} retired settings keys`, { meta: {
215
+ ...result,
216
+ reason: spec.reason
217
+ } });
218
+ }
219
+ return results;
220
+ }
221
+ //#endregion
87
222
  //#region src/builtins/sqlite-storage/filter-compiler.ts
88
223
  /** Thrown by `mutate` mode. Distinct type so a caller can map it to a 400. */
89
224
  var UnsafeFilterError = class extends Error {
@@ -1264,7 +1399,7 @@ var SqliteSettingsAddon = class extends require_dist.BaseAddon {
1264
1399
  super({});
1265
1400
  }
1266
1401
  async onInitialize() {
1267
- const addonId = this.ctx.id.replace("addon:", "");
1402
+ const addonId = require_dist.bareAddonId(this.ctx.id);
1268
1403
  const path = await import("node:path");
1269
1404
  const dbPath = this.ctx.dataDir ? path.join(this.ctx.dataDir, "camstack.db") : path.join("camstack-data", "addons-data", addonId, "camstack.db");
1270
1405
  const pathSource = this.ctx.dataDir ? "dataDir" : "fallback (hardcoded)";
@@ -1283,6 +1418,11 @@ var SqliteSettingsAddon = class extends require_dist.BaseAddon {
1283
1418
  this.ctx.logger.info("DB file status", { meta: { dbExists } });
1284
1419
  this.backend = new SqliteSettingsBackend(dbPath, { ...require_dist.RUNTIME_DEFAULTS });
1285
1420
  await this.backend.initialize();
1421
+ try {
1422
+ await purgeRetiredSettingsKeys(retiredKeyStoreOf(this.backend), this.ctx.logger.child("RetiredKeys"));
1423
+ } catch (err) {
1424
+ this.ctx.logger.warn("Retired-key purge failed", { meta: { error: require_dist.errMsg(err) } });
1425
+ }
1286
1426
  const vectorIndex = this.createVectorIndex(this.backend);
1287
1427
  await vectorIndex.loadIndexRegistry();
1288
1428
  this.vectorIndex = vectorIndex;
@@ -1,4 +1,4 @@
1
- import { C as dataStoreProviderCapability, Mt as parseJsonUnknown, ct as vectorDimFromBase64, dt as BaseAddon, l as RUNTIME_DEFAULTS, lt as vectorStoreCapability, ut as errMsg, w as decodeVectorBase64, xt as asJsonObject } from "../../dist-UrXyJ9Zm.mjs";
1
+ import { Ct as asJsonObject, Pt as parseJsonUnknown, T as decodeVectorBase64, dt as vectorStoreCapability, ft as errMsg, l as RUNTIME_DEFAULTS, pt as BaseAddon, ut as vectorDimFromBase64, w as dataStoreProviderCapability, x as bareAddonId } from "../../dist-C5XpOjbI.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { statSync } from "node:fs";
@@ -78,6 +78,141 @@ var SQLITE_MMAP_SIZE_BYTES = 268435456;
78
78
  */
79
79
  var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
80
80
  //#endregion
81
+ //#region src/builtins/sqlite-storage/retired-settings-keys.ts
82
+ /**
83
+ * Is THIS node the one whose settings store is the cluster's authority?
84
+ *
85
+ * Mirrors `resolveNodeRole` (server/backend `node-role.ts`) — unset or `'hub'`
86
+ * means hub, which is the launcher's own default. It is duplicated rather than
87
+ * imported because `@camstack/system` is a dependency OF the backend, not the
88
+ * other way round; the parse is four lines and the env var is the contract.
89
+ *
90
+ * Anything else answers `false`. Skipping where the purge should have run is a
91
+ * missed cleanup the next hub boot fixes; running where it should not have is a
92
+ * write to a store this node does not own. Only one of those is recoverable.
93
+ */
94
+ function settingsStoreIsAuthoritativeHere(env) {
95
+ const raw = (env["CAMSTACK_ROLE"] ?? "").trim().toLowerCase();
96
+ return raw === "" || raw === "hub";
97
+ }
98
+ /**
99
+ * The dead keys, per row.
100
+ *
101
+ * `detection-pipeline` / `addon-settings` / `root` — every key in this row is
102
+ * dead; the row itself is kept because the addon writes live keys into it
103
+ * (`pipelineTemplates`, `videoPipelineSteps`).
104
+ *
105
+ * - `pipelineSteps` / `pipelineEngine`: the persisted GLOBAL step + engine
106
+ * seed. Removed with `getGlobalSteps` becoming pure — dispatch builds steps
107
+ * from the orchestrator's `agentSettings` plus per-camera overrides, and
108
+ * never consulted the seed. See `addon-pipeline/src/detection-pipeline/provider.ts`.
109
+ * - `engineRuntime` / `engineBackend` / `engineDevice` / `probedBestEngine`:
110
+ * the per-node engine CASCADE, removed with the operator-facing engine
111
+ * election. Each node now runs a device-array of inference pools balanced
112
+ * per session, and the node-default pool derives live from the node's own
113
+ * hardware (`resolveAutoEngine`). See
114
+ * `addon-pipeline/src/detection-pipeline/engine-store-keys.ts`.
115
+ */
116
+ var RETIRED_SETTINGS_KEYS = [{
117
+ namespace: "detection-pipeline",
118
+ collection: "addon-settings",
119
+ row: "root",
120
+ keys: ["pipelineSteps", "pipelineEngine"],
121
+ perNodeKeys: [
122
+ "engineRuntime",
123
+ "engineBackend",
124
+ "engineDevice",
125
+ "probedBestEngine"
126
+ ],
127
+ reason: "global step/engine seed and the per-node engine cascade — both removed; the live per-camera dispatch path never read either"
128
+ }];
129
+ /**
130
+ * Adapt the settings backend to {@link RetiredKeyStore}.
131
+ *
132
+ * `get` answering a non-object (missing row) is a MISS, not a fault: it maps to
133
+ * `null` and the purge skips the row. A backend throw propagates, which is what
134
+ * routes it to the "read failed, row left untouched" branch.
135
+ */
136
+ function retiredKeyStoreOf(backend) {
137
+ return {
138
+ async read(spec) {
139
+ return asJsonObject(await backend.get({
140
+ namespace: spec.namespace,
141
+ collection: spec.collection,
142
+ key: spec.row
143
+ }));
144
+ },
145
+ async write(spec, value) {
146
+ await backend.set({
147
+ namespace: spec.namespace,
148
+ collection: spec.collection,
149
+ key: spec.row,
150
+ value
151
+ });
152
+ }
153
+ };
154
+ }
155
+ /** Keys of `blob` that `spec` retires. Pure. */
156
+ function planRetiredKeyPurge(blob, spec) {
157
+ const exact = new Set(spec.keys);
158
+ const found = [];
159
+ for (const key of Object.keys(blob)) {
160
+ if (exact.has(key)) {
161
+ found.push(key);
162
+ continue;
163
+ }
164
+ for (const perNode of spec.perNodeKeys) if (key === perNode || key.startsWith(`${perNode}@`)) {
165
+ found.push(key);
166
+ break;
167
+ }
168
+ }
169
+ return found;
170
+ }
171
+ /**
172
+ * Delete every retired key present in the declared rows — on the hub.
173
+ *
174
+ * Off the authoritative node this is a no-op that touches nothing and says
175
+ * nothing (see the module header). Returns one entry per row that actually
176
+ * changed — empty when the store is already clean, which is the steady state
177
+ * after the first boot that ran it.
178
+ */
179
+ async function purgeRetiredSettingsKeys(store, logger, specs = RETIRED_SETTINGS_KEYS, env = process.env) {
180
+ if (!settingsStoreIsAuthoritativeHere(env)) return [];
181
+ const results = [];
182
+ for (const spec of specs) {
183
+ let blob;
184
+ try {
185
+ blob = await store.read(spec);
186
+ } catch (err) {
187
+ logger.warn("retired settings keys — read failed, row left untouched", { meta: {
188
+ addon: spec.namespace,
189
+ collection: spec.collection,
190
+ row: spec.row,
191
+ error: err instanceof Error ? err.message : String(err)
192
+ } });
193
+ continue;
194
+ }
195
+ if (blob === null) continue;
196
+ const doomed = planRetiredKeyPurge(blob, spec);
197
+ if (doomed.length === 0) continue;
198
+ const doomedSet = new Set(doomed);
199
+ const next = Object.fromEntries(Object.entries(blob).filter(([key]) => !doomedSet.has(key)));
200
+ await store.write(spec, next);
201
+ const result = {
202
+ addon: spec.namespace,
203
+ collection: spec.collection,
204
+ row: spec.row,
205
+ keys: doomed
206
+ };
207
+ results.push(result);
208
+ logger.info(`purged ${doomed.length} retired settings keys`, { meta: {
209
+ ...result,
210
+ reason: spec.reason
211
+ } });
212
+ }
213
+ return results;
214
+ }
215
+ //#endregion
81
216
  //#region src/builtins/sqlite-storage/filter-compiler.ts
82
217
  /** Thrown by `mutate` mode. Distinct type so a caller can map it to a 400. */
83
218
  var UnsafeFilterError = class extends Error {
@@ -1258,7 +1393,7 @@ var SqliteSettingsAddon = class extends BaseAddon {
1258
1393
  super({});
1259
1394
  }
1260
1395
  async onInitialize() {
1261
- const addonId = this.ctx.id.replace("addon:", "");
1396
+ const addonId = bareAddonId(this.ctx.id);
1262
1397
  const path = await import("node:path");
1263
1398
  const dbPath = this.ctx.dataDir ? path.join(this.ctx.dataDir, "camstack.db") : path.join("camstack-data", "addons-data", addonId, "camstack.db");
1264
1399
  const pathSource = this.ctx.dataDir ? "dataDir" : "fallback (hardcoded)";
@@ -1277,6 +1412,11 @@ var SqliteSettingsAddon = class extends BaseAddon {
1277
1412
  this.ctx.logger.info("DB file status", { meta: { dbExists } });
1278
1413
  this.backend = new SqliteSettingsBackend(dbPath, { ...RUNTIME_DEFAULTS });
1279
1414
  await this.backend.initialize();
1415
+ try {
1416
+ await purgeRetiredSettingsKeys(retiredKeyStoreOf(this.backend), this.ctx.logger.child("RetiredKeys"));
1417
+ } catch (err) {
1418
+ this.ctx.logger.warn("Retired-key purge failed", { meta: { error: errMsg(err) } });
1419
+ }
1280
1420
  const vectorIndex = this.createVectorIndex(this.backend);
1281
1421
  await vectorIndex.loadIndexRegistry();
1282
1422
  this.vectorIndex = vectorIndex;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Chl2MvMd.js");
6
+ const require_dist = require("../../dist-Cis_iV-u.js");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { dt as BaseAddon, et as settingsStoreCapability, f as StorageLocationTypeSchema, jt as parseJsonObject, nt as storageCapability } from "../../dist-UrXyJ9Zm.mjs";
1
+ import { Nt as parseJsonObject, f as StorageLocationTypeSchema, it as storageCapability, nt as settingsStoreCapability, pt as BaseAddon } from "../../dist-C5XpOjbI.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  import * as fs from "node:fs/promises";
4
4
  import { buildStorageLocationRegistry } from "@camstack/system";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Chl2MvMd.js");
6
+ const require_dist = require("../../dist-Cis_iV-u.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { Ot as hydrateSchema, dt as BaseAddon, ut as errMsg } from "../../dist-UrXyJ9Zm.mjs";
1
+ import { At as hydrateSchema, ft as errMsg, pt as BaseAddon } from "../../dist-C5XpOjbI.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Chl2MvMd.js");
6
+ const require_dist = require("../../dist-Cis_iV-u.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { U as logDestinationCapability, dt as BaseAddon } from "../../dist-UrXyJ9Zm.mjs";
1
+ import { G as logDestinationCapability, pt as BaseAddon } from "../../dist-C5XpOjbI.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";