@fulmenhq/tsfulmen 0.2.0 → 0.2.3

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 (111) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +61 -7
  3. package/config/crucible-ts/agentic/roles/README.md +3 -3
  4. package/config/crucible-ts/library/fulencode/fixtures/README.md +18 -0
  5. package/config/crucible-ts/library/fulencode/fixtures/bom/bom.yaml +14 -0
  6. package/config/crucible-ts/library/fulencode/fixtures/detection/detection.yaml +12 -0
  7. package/config/crucible-ts/library/fulencode/fixtures/invalid-encodings/base64.yaml +10 -0
  8. package/config/crucible-ts/library/fulencode/fixtures/normalization/text-safe.yaml +10 -0
  9. package/config/crucible-ts/library/fulencode/fixtures/telemetry/telemetry-test-cases.yaml +24 -0
  10. package/config/crucible-ts/library/fulencode/fixtures/valid-encodings/base64.yaml +11 -0
  11. package/config/crucible-ts/taxonomy/library/platform-modules/v1.0.0/modules.yaml +2 -2
  12. package/config/crucible-ts/taxonomy/metrics.yaml +79 -1
  13. package/dist/appidentity/index.d.ts +31 -109
  14. package/dist/appidentity/index.js +369 -60
  15. package/dist/appidentity/index.js.map +1 -1
  16. package/dist/config/index.d.ts +46 -1
  17. package/dist/config/index.js +427 -62
  18. package/dist/config/index.js.map +1 -1
  19. package/dist/crucible/index.js +367 -59
  20. package/dist/crucible/index.js.map +1 -1
  21. package/dist/errors/index.d.ts +1 -1
  22. package/dist/errors/index.js +367 -59
  23. package/dist/errors/index.js.map +1 -1
  24. package/dist/foundry/index.d.ts +2 -1
  25. package/dist/foundry/index.js +368 -60
  26. package/dist/foundry/index.js.map +1 -1
  27. package/dist/fulencode/index.d.ts +102 -0
  28. package/dist/fulencode/index.js +806 -0
  29. package/dist/fulencode/index.js.map +1 -0
  30. package/dist/index.d.ts +4 -3
  31. package/dist/index.js +370 -61
  32. package/dist/index.js.map +1 -1
  33. package/dist/pathfinder/index.d.ts +1 -1
  34. package/dist/pathfinder/index.js +367 -59
  35. package/dist/pathfinder/index.js.map +1 -1
  36. package/dist/reports/license-inventory.csv +31 -24
  37. package/dist/schema/index.d.ts +16 -3
  38. package/dist/schema/index.js +368 -60
  39. package/dist/schema/index.js.map +1 -1
  40. package/dist/signals/index.d.ts +483 -395
  41. package/dist/signals/index.js +368 -60
  42. package/dist/signals/index.js.map +1 -1
  43. package/dist/telemetry/http/index.js +368 -59
  44. package/dist/telemetry/http/index.js.map +1 -1
  45. package/dist/telemetry/index.d.ts +1 -1
  46. package/dist/telemetry/index.js +367 -59
  47. package/dist/telemetry/index.js.map +1 -1
  48. package/dist/telemetry/prometheus/index.d.ts +1 -1
  49. package/dist/telemetry/prometheus/index.js +369 -59
  50. package/dist/telemetry/prometheus/index.js.map +1 -1
  51. package/dist/{types-BJswWpQC.d.ts → types-DdoeE7F5.d.ts} +1 -1
  52. package/dist/types-Dv5TERCM.d.ts +108 -0
  53. package/package.json +13 -8
  54. package/schemas/crucible-ts/library/fulencode/v1.0.0/README.md +37 -0
  55. package/schemas/crucible-ts/library/fulencode/v1.0.0/bom-result.schema.json +48 -0
  56. package/schemas/crucible-ts/library/fulencode/v1.0.0/decode-options.schema.json +60 -0
  57. package/schemas/crucible-ts/library/fulencode/v1.0.0/decoding-result.schema.json +70 -0
  58. package/schemas/crucible-ts/library/fulencode/v1.0.0/detect-options.schema.json +25 -0
  59. package/schemas/crucible-ts/library/fulencode/v1.0.0/detection-result.schema.json +57 -0
  60. package/schemas/crucible-ts/library/fulencode/v1.0.0/encode-options.schema.json +71 -0
  61. package/schemas/crucible-ts/library/fulencode/v1.0.0/encoding-result.schema.json +57 -0
  62. package/schemas/crucible-ts/library/fulencode/v1.0.0/fulencode-config.schema.json +8 -4
  63. package/schemas/crucible-ts/library/fulencode/v1.0.0/fulencode-error.schema.json +66 -0
  64. package/schemas/crucible-ts/library/fulencode/v1.0.0/normalization-result.schema.json +73 -0
  65. package/schemas/crucible-ts/library/fulencode/v1.0.0/normalize-options.schema.json +44 -0
  66. package/schemas/crucible-ts/meta/README.md +38 -2
  67. package/schemas/crucible-ts/meta/draft-04/schema.json +222 -0
  68. package/schemas/crucible-ts/meta/draft-06/schema.json +218 -0
  69. package/schemas/crucible-ts/meta/draft-2019-09/meta/applicator.json +93 -0
  70. package/schemas/crucible-ts/meta/draft-2019-09/meta/content.json +21 -0
  71. package/schemas/crucible-ts/meta/draft-2019-09/meta/core.json +58 -0
  72. package/schemas/crucible-ts/meta/draft-2019-09/meta/format.json +15 -0
  73. package/schemas/crucible-ts/meta/draft-2019-09/meta/meta-data.json +35 -0
  74. package/schemas/crucible-ts/meta/draft-2019-09/meta/validation.json +119 -0
  75. package/schemas/crucible-ts/meta/draft-2019-09/offline.schema.json +148 -0
  76. package/schemas/crucible-ts/meta/draft-2019-09/schema.json +62 -0
  77. package/schemas/crucible-ts/meta/fixtures/draft-04-sample.json +16 -0
  78. package/schemas/crucible-ts/meta/fixtures/draft-06-sample.json +16 -0
  79. package/schemas/crucible-ts/meta/fixtures/draft-07-sample.json +34 -0
  80. package/schemas/crucible-ts/meta/fixtures/draft-2019-09-sample.json +21 -0
  81. package/schemas/crucible-ts/meta/fixtures/draft-2020-12-sample.json +21 -0
  82. package/schemas/crucible-ts/taxonomy/library/fulencode/normalization-profiles/v1.0.0/profiles.yaml +16 -0
  83. package/schemas/crucible-ts/upstream/3leaps/crucible/PROVENANCE.md +64 -0
  84. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/access-tier.dimension.json +103 -0
  85. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/retention-lifecycle.dimension.json +103 -0
  86. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/schema-stability.dimension.json +100 -0
  87. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/sensitivity.dimension.json +130 -0
  88. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/velocity-mode.dimension.json +79 -0
  89. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/volatility.dimension.json +72 -0
  90. package/schemas/crucible-ts/upstream/3leaps/crucible/config/classifiers/dimensions/volume-tier.dimension.json +66 -0
  91. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/catalog/classifiers/README.md +29 -0
  92. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/access-tier-classification.md +163 -0
  93. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/classifiers-framework.md +157 -0
  94. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/data-sensitivity-classification.md +259 -0
  95. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/retention-lifecycle-classification.md +200 -0
  96. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/schema-stability-classification.md +205 -0
  97. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/velocity-mode-classification.md +222 -0
  98. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/volatility-classification.md +209 -0
  99. package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/volume-tier-classification.md +200 -0
  100. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/ailink/v0/README.md +48 -0
  101. package/schemas/crucible-ts/upstream/3leaps/{ailink → crucible/schemas/ailink}/v0/prompt.schema.json +4 -18
  102. package/schemas/crucible-ts/upstream/3leaps/{ailink → crucible/schemas/ailink}/v0/search-response.schema.json +7 -37
  103. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/classifiers/v0/dimension-definition.schema.json +247 -0
  104. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/classifiers/v0/sensitivity-level.schema.json +67 -0
  105. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/foundation/v0/error-response.schema.json +59 -0
  106. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/foundation/v0/lifecycle-phases.data.json +102 -0
  107. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/foundation/v0/lifecycle-phases.schema.json +101 -0
  108. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/foundation/v0/release-phase.schema.json +18 -0
  109. package/schemas/crucible-ts/upstream/3leaps/crucible/schemas/foundation/v0/types.schema.json +177 -0
  110. package/schemas/crucible-ts/upstream/3leaps/PROVENANCE.md +0 -43
  111. /package/schemas/crucible-ts/upstream/3leaps/{agentic → crucible/schemas/agentic}/v0/role-prompt.schema.json +0 -0
@@ -1,6 +1,6 @@
1
1
  import { M as MetricName, a as MetricUnit, b as MetricsRegistry } from '../registry-_yEKhTTl.js';
2
2
  export { C as Counter, F as FlushOptions, G as Gauge, H as Histogram, c as HistogramBucket, d as HistogramOptions, e as HistogramSummary, g as MetricValue, f as MetricsEvent, i as isHistogramSummary, h as isValidMetricName, j as isValidMetricUnit } from '../registry-_yEKhTTl.js';
3
- import { A as AjvError } from '../types-BJswWpQC.js';
3
+ import { A as AjvError } from '../types-DdoeE7F5.js';
4
4
 
5
5
  /**
6
6
  * Taxonomy loader for metrics definitions
@@ -1,11 +1,14 @@
1
1
  import { readFile, access, mkdir, writeFile } from 'fs/promises';
2
2
  import { dirname, extname, join, relative } from 'path';
3
3
  import { parse, stringify } from 'yaml';
4
+ import addFormats from 'ajv-formats';
4
5
  import { spawn } from 'child_process';
5
6
  import { fileURLToPath } from 'url';
6
7
  import glob from 'fast-glob';
7
8
  import Ajv from 'ajv';
8
- import addFormats from 'ajv-formats';
9
+ import Ajv2019 from 'ajv/dist/2019';
10
+ import Ajv2020 from 'ajv/dist/2020';
11
+ import AjvDraft04 from 'ajv-draft-04';
9
12
  import { Readable } from 'stream';
10
13
  import picomatch from 'picomatch';
11
14
  import { suggest as suggest$1, substringSimilarity, score as score$1, normalize as normalize$1, jaro_winkler, damerau_levenshtein, osa_distance, levenshtein } from '@3leaps/string-metrics-wasm';
@@ -798,6 +801,27 @@ var init_types = __esm({
798
801
  "src/telemetry/types.ts"() {
799
802
  }
800
803
  });
804
+ function applyFulmenAjvFormats(ajv, options = {}) {
805
+ const mode = options.mode ?? "fast";
806
+ const formats = options.formats ?? DEFAULT_FORMATS;
807
+ addFormats(ajv, { mode, formats });
808
+ return ajv;
809
+ }
810
+ var DEFAULT_FORMATS;
811
+ var init_ajv_formats = __esm({
812
+ "src/schema/ajv-formats.ts"() {
813
+ DEFAULT_FORMATS = [
814
+ "date-time",
815
+ "email",
816
+ "hostname",
817
+ "ipv4",
818
+ "ipv6",
819
+ "uri",
820
+ "uri-reference",
821
+ "uuid"
822
+ ];
823
+ }
824
+ });
801
825
 
802
826
  // src/schema/errors.ts
803
827
  var errors_exports = {};
@@ -1522,20 +1546,14 @@ async function loadMetaSchema(draft) {
1522
1546
  const content = await readFile(metaSchemaPath, "utf-8");
1523
1547
  return JSON.parse(content);
1524
1548
  }
1525
- async function loadVocabularySchemas() {
1549
+ async function loadVocabularySchemas(draft) {
1550
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1551
+ return [];
1552
+ }
1526
1553
  const __filename3 = fileURLToPath(import.meta.url);
1527
1554
  const __dirname4 = dirname(__filename3);
1528
- const vocabDir = join(
1529
- __dirname4,
1530
- "..",
1531
- "..",
1532
- "schemas",
1533
- "crucible-ts",
1534
- "meta",
1535
- "draft-2020-12",
1536
- "meta"
1537
- );
1538
- const vocabFiles = [
1555
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1556
+ const vocabFiles = draft === "draft-2020-12" ? [
1539
1557
  "core.json",
1540
1558
  "applicator.json",
1541
1559
  "unevaluated.json",
@@ -1543,6 +1561,13 @@ async function loadVocabularySchemas() {
1543
1561
  "meta-data.json",
1544
1562
  "format-annotation.json",
1545
1563
  "content.json"
1564
+ ] : [
1565
+ "core.json",
1566
+ "applicator.json",
1567
+ "validation.json",
1568
+ "meta-data.json",
1569
+ "format.json",
1570
+ "content.json"
1546
1571
  ];
1547
1572
  const schemas = [];
1548
1573
  for (const file of vocabFiles) {
@@ -1597,47 +1622,65 @@ async function loadReferencedSchema(uri) {
1597
1622
  }
1598
1623
  return JSON.parse(content);
1599
1624
  }
1600
- function getAjv() {
1601
- if (!ajvInstance) {
1602
- ajvInstance = new Ajv({
1603
- strict: false,
1604
- allErrors: true,
1605
- verbose: true,
1606
- // Allow schemas with $id to be added without replacing existing ones
1607
- addUsedSchema: false,
1608
- // Enable async schema loading for YAML references
1609
- loadSchema: loadReferencedSchema
1610
- });
1611
- addFormats(ajvInstance, {
1612
- mode: "fast",
1613
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1614
- });
1615
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
1616
- if (ajvInstance) {
1617
- for (const vocabSchema of vocabSchemas) {
1618
- try {
1619
- ajvInstance.addMetaSchema(vocabSchema);
1620
- } catch {
1621
- }
1622
- }
1623
- ajvInstance.addMetaSchema(metaSchema);
1624
- }
1625
- }).catch((error) => {
1626
- throw new Error(`Failed to load metaschemas: ${error}`);
1627
- });
1625
+ function detectDialect(schema) {
1626
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1627
+ const maybeSchema = schema;
1628
+ const declared = maybeSchema.$schema;
1629
+ if (typeof declared === "string") {
1630
+ if (declared.includes("draft-04")) return "draft-04";
1631
+ if (declared.includes("draft-06")) return "draft-06";
1632
+ if (declared.includes("draft-07")) return "draft-07";
1633
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
1634
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
1635
+ }
1628
1636
  }
1629
- return ajvInstance;
1637
+ return "draft-2020-12";
1638
+ }
1639
+ function createAjv(dialect) {
1640
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1641
+ const ajv = new AjvCtor({
1642
+ strict: false,
1643
+ allErrors: true,
1644
+ verbose: true,
1645
+ // Allow schemas with $id to be added without replacing existing ones
1646
+ addUsedSchema: false,
1647
+ // draft-04 uses "id"; later drafts use "$id"
1648
+ schemaId: dialect === "draft-04" ? "id" : "$id",
1649
+ // Enable async schema loading for YAML references
1650
+ loadSchema: loadReferencedSchema
1651
+ });
1652
+ applyFulmenAjvFormats(ajv);
1653
+ return ajv;
1654
+ }
1655
+ async function getAjv(dialect) {
1656
+ const existing = ajvInstances.get(dialect);
1657
+ if (existing) {
1658
+ const ready = metaschemaReady.get(dialect);
1659
+ if (ready) await ready;
1660
+ return existing;
1661
+ }
1662
+ const ajv = createAjv(dialect);
1663
+ ajvInstances.set(dialect, ajv);
1664
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1665
+ for (const vocabSchema of vocabSchemas) {
1666
+ try {
1667
+ ajv.addMetaSchema(vocabSchema);
1668
+ } catch {
1669
+ }
1670
+ }
1671
+ try {
1672
+ ajv.addMetaSchema(metaSchema);
1673
+ } catch {
1674
+ }
1675
+ }).catch((error) => {
1676
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1677
+ });
1678
+ metaschemaReady.set(dialect, readyPromise);
1679
+ await readyPromise;
1680
+ return ajv;
1630
1681
  }
1631
1682
  async function compileSchema(schema, options = {}) {
1632
- const ajv = getAjv();
1633
- if (metaschemaReady) {
1634
- await metaschemaReady;
1635
- }
1636
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1637
- const cached = schemaCache.get(cacheKey);
1638
- if (cached !== void 0) {
1639
- return cached;
1640
- }
1683
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1641
1684
  let parsedSchema;
1642
1685
  if (typeof schema === "string") {
1643
1686
  try {
@@ -1655,18 +1698,27 @@ async function compileSchema(schema, options = {}) {
1655
1698
  } else {
1656
1699
  parsedSchema = schema;
1657
1700
  }
1701
+ const dialect = detectDialect(parsedSchema);
1702
+ const ajv = await getAjv(dialect);
1703
+ const cacheKey = `${dialect}:${baseKey}`;
1704
+ const cached = schemaCache.get(cacheKey);
1705
+ if (cached !== void 0) {
1706
+ return cached;
1707
+ }
1658
1708
  try {
1659
1709
  if (options.aliases && options.aliases.length > 0) {
1660
1710
  for (const alias of options.aliases) {
1661
1711
  if (alias && ajv.getSchema(alias) === void 0) {
1662
1712
  try {
1663
- ajv.addSchema(parsedSchema, alias);
1713
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
1714
+ ajv.addSchema(parsedSchema, alias);
1715
+ }
1664
1716
  } catch {
1665
1717
  }
1666
1718
  }
1667
1719
  }
1668
1720
  }
1669
- const validator = await ajv.compileAsync(parsedSchema);
1721
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1670
1722
  schemaCache.set(cacheKey, validator);
1671
1723
  return validator;
1672
1724
  } catch (error) {
@@ -1736,8 +1788,39 @@ async function validateFile(filePath, validator) {
1736
1788
  }
1737
1789
  async function validateSchema(schema) {
1738
1790
  try {
1739
- const validator = await compileSchema(schema);
1740
- validateData({}, validator);
1791
+ let parsedSchema;
1792
+ if (typeof schema === "string") {
1793
+ try {
1794
+ parsedSchema = JSON.parse(schema);
1795
+ } catch {
1796
+ parsedSchema = parse(schema);
1797
+ }
1798
+ } else if (Buffer.isBuffer(schema)) {
1799
+ const content = schema.toString("utf-8");
1800
+ try {
1801
+ parsedSchema = JSON.parse(content);
1802
+ } catch {
1803
+ parsedSchema = parse(content);
1804
+ }
1805
+ } else {
1806
+ parsedSchema = schema;
1807
+ }
1808
+ const dialect = detectDialect(parsedSchema);
1809
+ const ajv = await getAjv(dialect);
1810
+ const metaValid = ajv.validateSchema(parsedSchema);
1811
+ if (!metaValid && ajv.errors) {
1812
+ const diagnostics = ajv.errors.map(
1813
+ (error) => createDiagnostic(
1814
+ error.instancePath || "",
1815
+ error.message || "Schema meta-validation failed",
1816
+ error.keyword || "unknown",
1817
+ "ERROR",
1818
+ "ajv"
1819
+ )
1820
+ );
1821
+ return { valid: false, diagnostics, source: "ajv" };
1822
+ }
1823
+ await compileSchema(parsedSchema);
1741
1824
  return {
1742
1825
  valid: true,
1743
1826
  diagnostics: [],
@@ -1808,14 +1891,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
1808
1891
  throw error;
1809
1892
  }
1810
1893
  }
1811
- var ajvInstance, metaschemaReady, schemaCache;
1894
+ var ajvInstances, metaschemaReady, schemaCache;
1812
1895
  var init_validator = __esm({
1813
1896
  "src/schema/validator.ts"() {
1814
1897
  init_telemetry();
1898
+ init_ajv_formats();
1815
1899
  init_errors();
1816
1900
  init_registry2();
1817
1901
  init_utils();
1818
- metaschemaReady = null;
1902
+ ajvInstances = /* @__PURE__ */ new Map();
1903
+ metaschemaReady = /* @__PURE__ */ new Map();
1819
1904
  schemaCache = /* @__PURE__ */ new Map();
1820
1905
  }
1821
1906
  });
@@ -3770,6 +3855,224 @@ var init_capabilities2 = __esm({
3770
3855
  }
3771
3856
  });
3772
3857
 
3858
+ // src/foundry/signals/config-reload-endpoint.ts
3859
+ function createConfigReloadEndpoint(options) {
3860
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3861
+ return async (payload, req) => {
3862
+ const correlationId = payload.correlation_id ?? generateCorrelationId();
3863
+ const authResult = await auth(req);
3864
+ if (!authResult.authenticated) {
3865
+ if (logger) {
3866
+ logger.warn("Config reload endpoint: authentication failed", {
3867
+ correlation_id: correlationId,
3868
+ reason: authResult.reason
3869
+ });
3870
+ }
3871
+ if (telemetry) {
3872
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3873
+ correlation_id: correlationId
3874
+ });
3875
+ }
3876
+ return {
3877
+ status: "error",
3878
+ error: "authentication_failed",
3879
+ message: authResult.reason || "Authentication required",
3880
+ statusCode: 401
3881
+ };
3882
+ }
3883
+ const identity = authResult.identity || "unknown";
3884
+ if (rateLimit) {
3885
+ const rateLimitResult = await rateLimit(identity);
3886
+ if (!rateLimitResult.allowed) {
3887
+ if (logger) {
3888
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3889
+ correlation_id: correlationId,
3890
+ identity
3891
+ });
3892
+ }
3893
+ if (telemetry) {
3894
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3895
+ correlation_id: correlationId
3896
+ });
3897
+ }
3898
+ return {
3899
+ status: "error",
3900
+ error: "rate_limit_exceeded",
3901
+ message: "Rate limit exceeded. Please try again later.",
3902
+ statusCode: 429
3903
+ };
3904
+ }
3905
+ }
3906
+ if (telemetry) {
3907
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
3908
+ correlation_id: correlationId
3909
+ });
3910
+ }
3911
+ try {
3912
+ const config = await loader();
3913
+ if (validator) {
3914
+ const validation = await validator(config);
3915
+ if (!validation.valid) {
3916
+ if (logger) {
3917
+ logger.warn("Config reload endpoint: validation failed", {
3918
+ correlation_id: correlationId,
3919
+ error_count: validation.errors?.length ?? 0
3920
+ });
3921
+ }
3922
+ if (telemetry) {
3923
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
3924
+ correlation_id: correlationId,
3925
+ reason: "validation_failed"
3926
+ });
3927
+ }
3928
+ return {
3929
+ status: "error",
3930
+ error: "validation_failed",
3931
+ message: "Configuration validation failed",
3932
+ validation_errors: validation.errors,
3933
+ statusCode: 422
3934
+ };
3935
+ }
3936
+ }
3937
+ if (onReload2) {
3938
+ await onReload2(config);
3939
+ }
3940
+ if (telemetry) {
3941
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
3942
+ correlation_id: correlationId
3943
+ });
3944
+ }
3945
+ if (logger) {
3946
+ logger.info("Config reload endpoint: reload accepted", {
3947
+ correlation_id: correlationId,
3948
+ reason: payload.reason
3949
+ });
3950
+ }
3951
+ return {
3952
+ status: "reloaded",
3953
+ correlation_id: correlationId,
3954
+ message: "Configuration reloaded",
3955
+ statusCode: 200
3956
+ };
3957
+ } catch (error) {
3958
+ if (logger) {
3959
+ logger.warn("Config reload endpoint: reload failed", {
3960
+ correlation_id: correlationId,
3961
+ error: error instanceof Error ? error.message : String(error)
3962
+ });
3963
+ }
3964
+ if (telemetry) {
3965
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
3966
+ correlation_id: correlationId,
3967
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
3968
+ });
3969
+ }
3970
+ return {
3971
+ status: "error",
3972
+ error: "reload_failed",
3973
+ message: error instanceof Error ? error.message : String(error),
3974
+ statusCode: 500
3975
+ };
3976
+ }
3977
+ };
3978
+ }
3979
+ function generateCorrelationId() {
3980
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
3981
+ }
3982
+ var init_config_reload_endpoint = __esm({
3983
+ "src/foundry/signals/config-reload-endpoint.ts"() {
3984
+ }
3985
+ });
3986
+
3987
+ // src/appidentity/runtime.ts
3988
+ function detectRuntime() {
3989
+ const versions = process.versions;
3990
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
3991
+ return { name: "bun", version: versions.bun };
3992
+ }
3993
+ if (typeof versions.node === "string" && versions.node.length > 0) {
3994
+ return { name: "node", version: versions.node };
3995
+ }
3996
+ return { name: "unknown" };
3997
+ }
3998
+ function buildRuntimeInfo(options = {}) {
3999
+ const runtime = detectRuntime();
4000
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
4001
+ const vendor = options.vendor ?? options.identity?.app.vendor;
4002
+ return {
4003
+ service: {
4004
+ name: serviceName,
4005
+ vendor,
4006
+ version: options.version
4007
+ },
4008
+ runtime,
4009
+ platform: {
4010
+ os: process.platform,
4011
+ arch: process.arch
4012
+ }
4013
+ };
4014
+ }
4015
+ var init_runtime = __esm({
4016
+ "src/appidentity/runtime.ts"() {
4017
+ }
4018
+ });
4019
+
4020
+ // src/foundry/signals/control-discovery-endpoint.ts
4021
+ function createControlDiscoveryEndpoint(options) {
4022
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
4023
+ return async (req) => {
4024
+ if (auth) {
4025
+ const authResult = await auth(req);
4026
+ if (!authResult.authenticated) {
4027
+ if (logger) {
4028
+ logger.warn("Control discovery endpoint: authentication failed", {
4029
+ reason: authResult.reason
4030
+ });
4031
+ }
4032
+ if (telemetry) {
4033
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4034
+ service: identity.app.binary_name
4035
+ });
4036
+ }
4037
+ return {
4038
+ status: "error",
4039
+ error: "authentication_failed",
4040
+ message: authResult.reason || "Authentication required",
4041
+ statusCode: 401
4042
+ };
4043
+ }
4044
+ }
4045
+ if (telemetry) {
4046
+ telemetry.emit("fulmen.control.discovery.served", {
4047
+ service: identity.app.binary_name
4048
+ });
4049
+ }
4050
+ const runtime = buildRuntimeInfo({ identity, version });
4051
+ return {
4052
+ status: "ok",
4053
+ service: {
4054
+ name: identity.app.binary_name,
4055
+ vendor: identity.app.vendor,
4056
+ version
4057
+ },
4058
+ runtime: {
4059
+ name: runtime.runtime.name,
4060
+ version: runtime.runtime.version,
4061
+ platform: runtime.platform.os,
4062
+ arch: runtime.platform.arch
4063
+ },
4064
+ auth_summary: authSummary,
4065
+ endpoints,
4066
+ statusCode: 200
4067
+ };
4068
+ };
4069
+ }
4070
+ var init_control_discovery_endpoint = __esm({
4071
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4072
+ init_runtime();
4073
+ }
4074
+ });
4075
+
3773
4076
  // src/foundry/signals/convenience.ts
3774
4077
  async function onShutdown(manager, handler, options = {}) {
3775
4078
  await manager.register("SIGTERM", handler, options);
@@ -4029,7 +4332,7 @@ var init_guards = __esm({
4029
4332
  function createSignalEndpoint(options) {
4030
4333
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4031
4334
  return async (payload, req) => {
4032
- const correlationId = payload.correlation_id ?? generateCorrelationId();
4335
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
4033
4336
  const authResult = await auth(req);
4034
4337
  if (!authResult.authenticated) {
4035
4338
  if (logger) {
@@ -4152,7 +4455,7 @@ function normalizeSignalName(signal) {
4152
4455
  }
4153
4456
  return `SIG${upper}`;
4154
4457
  }
4155
- function generateCorrelationId() {
4458
+ function generateCorrelationId2() {
4156
4459
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4157
4460
  }
4158
4461
  function createBearerTokenAuth(expectedToken) {
@@ -4619,6 +4922,8 @@ var init_signals = __esm({
4619
4922
  "src/foundry/signals/index.ts"() {
4620
4923
  init_capabilities2();
4621
4924
  init_catalog();
4925
+ init_config_reload_endpoint();
4926
+ init_control_discovery_endpoint();
4622
4927
  init_convenience();
4623
4928
  init_double_tap();
4624
4929
  init_guards();
@@ -4764,7 +5069,9 @@ __export(foundry_exports, {
4764
5069
  clearMimeTypeCache: () => clearMimeTypeCache,
4765
5070
  clearPatternCache: () => clearPatternCache,
4766
5071
  createBearerTokenAuth: () => createBearerTokenAuth,
5072
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4767
5073
  createConfigReloadHandler: () => createConfigReloadHandler,
5074
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4768
5075
  createDoubleTapTracker: () => createDoubleTapTracker,
4769
5076
  createSignalEndpoint: () => createSignalEndpoint,
4770
5077
  createSignalManager: () => createSignalManager,
@@ -5820,6 +6127,7 @@ var init_cli = __esm({
5820
6127
  // src/schema/index.ts
5821
6128
  var init_schema = __esm({
5822
6129
  "src/schema/index.ts"() {
6130
+ init_ajv_formats();
5823
6131
  init_cli();
5824
6132
  init_errors();
5825
6133
  init_export();