@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,5 +1,5 @@
1
1
  export { F as FulmenError, a as FulmenErrorData, b as FulmenErrorOptions, L as LEVEL_TO_SEVERITY, S as SEVERITY_LEVELS, k as Severity, m as SeverityLevel, n as SeverityName, f as compareSeverity, e as extractErrorMessage, d as extractStackTrace, g as getDefaultSeverity, i as isFulmenError, c as isFulmenErrorData, h as isSeverityLevel, j as isSeverityName, l as levelToSeverity, s as serializeError, o as severityToLevel } from '../fulmen-error-B_kX8jSC.js';
2
- import { A as AjvError } from '../types-BJswWpQC.js';
2
+ import { A as AjvError } from '../types-DdoeE7F5.js';
3
3
 
4
4
  /**
5
5
  * Correlation ID generation and validation for error tracking
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
+ import addFormats from 'ajv-formats';
2
3
  import { spawn } from 'child_process';
3
4
  import { readFile, access, mkdir, writeFile } from 'fs/promises';
4
5
  import { parse, stringify } from 'yaml';
@@ -6,7 +7,9 @@ import { dirname, extname, join, relative } from 'path';
6
7
  import { fileURLToPath } from 'url';
7
8
  import glob from 'fast-glob';
8
9
  import Ajv from 'ajv';
9
- import addFormats from 'ajv-formats';
10
+ import Ajv2019 from 'ajv/dist/2019';
11
+ import Ajv2020 from 'ajv/dist/2020';
12
+ import AjvDraft04 from 'ajv-draft-04';
10
13
  import { Readable } from 'stream';
11
14
  import picomatch from 'picomatch';
12
15
  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';
@@ -170,6 +173,27 @@ var init_serialization = __esm({
170
173
  init_severity();
171
174
  }
172
175
  });
176
+ function applyFulmenAjvFormats(ajv, options = {}) {
177
+ const mode = options.mode ?? "fast";
178
+ const formats = options.formats ?? DEFAULT_FORMATS;
179
+ addFormats(ajv, { mode, formats });
180
+ return ajv;
181
+ }
182
+ var DEFAULT_FORMATS;
183
+ var init_ajv_formats = __esm({
184
+ "src/schema/ajv-formats.ts"() {
185
+ DEFAULT_FORMATS = [
186
+ "date-time",
187
+ "email",
188
+ "hostname",
189
+ "ipv4",
190
+ "ipv6",
191
+ "uri",
192
+ "uri-reference",
193
+ "uuid"
194
+ ];
195
+ }
196
+ });
173
197
 
174
198
  // src/schema/errors.ts
175
199
  var errors_exports = {};
@@ -1622,20 +1646,14 @@ async function loadMetaSchema(draft) {
1622
1646
  const content = await readFile(metaSchemaPath, "utf-8");
1623
1647
  return JSON.parse(content);
1624
1648
  }
1625
- async function loadVocabularySchemas() {
1649
+ async function loadVocabularySchemas(draft) {
1650
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1651
+ return [];
1652
+ }
1626
1653
  const __filename3 = fileURLToPath(import.meta.url);
1627
1654
  const __dirname4 = dirname(__filename3);
1628
- const vocabDir = join(
1629
- __dirname4,
1630
- "..",
1631
- "..",
1632
- "schemas",
1633
- "crucible-ts",
1634
- "meta",
1635
- "draft-2020-12",
1636
- "meta"
1637
- );
1638
- const vocabFiles = [
1655
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1656
+ const vocabFiles = draft === "draft-2020-12" ? [
1639
1657
  "core.json",
1640
1658
  "applicator.json",
1641
1659
  "unevaluated.json",
@@ -1643,6 +1661,13 @@ async function loadVocabularySchemas() {
1643
1661
  "meta-data.json",
1644
1662
  "format-annotation.json",
1645
1663
  "content.json"
1664
+ ] : [
1665
+ "core.json",
1666
+ "applicator.json",
1667
+ "validation.json",
1668
+ "meta-data.json",
1669
+ "format.json",
1670
+ "content.json"
1646
1671
  ];
1647
1672
  const schemas = [];
1648
1673
  for (const file of vocabFiles) {
@@ -1697,47 +1722,65 @@ async function loadReferencedSchema(uri) {
1697
1722
  }
1698
1723
  return JSON.parse(content);
1699
1724
  }
1700
- function getAjv() {
1701
- if (!ajvInstance) {
1702
- ajvInstance = new Ajv({
1703
- strict: false,
1704
- allErrors: true,
1705
- verbose: true,
1706
- // Allow schemas with $id to be added without replacing existing ones
1707
- addUsedSchema: false,
1708
- // Enable async schema loading for YAML references
1709
- loadSchema: loadReferencedSchema
1710
- });
1711
- addFormats(ajvInstance, {
1712
- mode: "fast",
1713
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1714
- });
1715
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
1716
- if (ajvInstance) {
1717
- for (const vocabSchema of vocabSchemas) {
1718
- try {
1719
- ajvInstance.addMetaSchema(vocabSchema);
1720
- } catch {
1721
- }
1722
- }
1723
- ajvInstance.addMetaSchema(metaSchema);
1724
- }
1725
- }).catch((error) => {
1726
- throw new Error(`Failed to load metaschemas: ${error}`);
1727
- });
1725
+ function detectDialect(schema) {
1726
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1727
+ const maybeSchema = schema;
1728
+ const declared = maybeSchema.$schema;
1729
+ if (typeof declared === "string") {
1730
+ if (declared.includes("draft-04")) return "draft-04";
1731
+ if (declared.includes("draft-06")) return "draft-06";
1732
+ if (declared.includes("draft-07")) return "draft-07";
1733
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
1734
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
1735
+ }
1728
1736
  }
1729
- return ajvInstance;
1737
+ return "draft-2020-12";
1738
+ }
1739
+ function createAjv(dialect) {
1740
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1741
+ const ajv = new AjvCtor({
1742
+ strict: false,
1743
+ allErrors: true,
1744
+ verbose: true,
1745
+ // Allow schemas with $id to be added without replacing existing ones
1746
+ addUsedSchema: false,
1747
+ // draft-04 uses "id"; later drafts use "$id"
1748
+ schemaId: dialect === "draft-04" ? "id" : "$id",
1749
+ // Enable async schema loading for YAML references
1750
+ loadSchema: loadReferencedSchema
1751
+ });
1752
+ applyFulmenAjvFormats(ajv);
1753
+ return ajv;
1754
+ }
1755
+ async function getAjv(dialect) {
1756
+ const existing = ajvInstances.get(dialect);
1757
+ if (existing) {
1758
+ const ready = metaschemaReady.get(dialect);
1759
+ if (ready) await ready;
1760
+ return existing;
1761
+ }
1762
+ const ajv = createAjv(dialect);
1763
+ ajvInstances.set(dialect, ajv);
1764
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1765
+ for (const vocabSchema of vocabSchemas) {
1766
+ try {
1767
+ ajv.addMetaSchema(vocabSchema);
1768
+ } catch {
1769
+ }
1770
+ }
1771
+ try {
1772
+ ajv.addMetaSchema(metaSchema);
1773
+ } catch {
1774
+ }
1775
+ }).catch((error) => {
1776
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1777
+ });
1778
+ metaschemaReady.set(dialect, readyPromise);
1779
+ await readyPromise;
1780
+ return ajv;
1730
1781
  }
1731
1782
  async function compileSchema(schema, options = {}) {
1732
- const ajv = getAjv();
1733
- if (metaschemaReady) {
1734
- await metaschemaReady;
1735
- }
1736
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1737
- const cached = schemaCache.get(cacheKey);
1738
- if (cached !== void 0) {
1739
- return cached;
1740
- }
1783
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1741
1784
  let parsedSchema;
1742
1785
  if (typeof schema === "string") {
1743
1786
  try {
@@ -1755,18 +1798,27 @@ async function compileSchema(schema, options = {}) {
1755
1798
  } else {
1756
1799
  parsedSchema = schema;
1757
1800
  }
1801
+ const dialect = detectDialect(parsedSchema);
1802
+ const ajv = await getAjv(dialect);
1803
+ const cacheKey = `${dialect}:${baseKey}`;
1804
+ const cached = schemaCache.get(cacheKey);
1805
+ if (cached !== void 0) {
1806
+ return cached;
1807
+ }
1758
1808
  try {
1759
1809
  if (options.aliases && options.aliases.length > 0) {
1760
1810
  for (const alias of options.aliases) {
1761
1811
  if (alias && ajv.getSchema(alias) === void 0) {
1762
1812
  try {
1763
- ajv.addSchema(parsedSchema, alias);
1813
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
1814
+ ajv.addSchema(parsedSchema, alias);
1815
+ }
1764
1816
  } catch {
1765
1817
  }
1766
1818
  }
1767
1819
  }
1768
1820
  }
1769
- const validator = await ajv.compileAsync(parsedSchema);
1821
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1770
1822
  schemaCache.set(cacheKey, validator);
1771
1823
  return validator;
1772
1824
  } catch (error) {
@@ -1836,8 +1888,39 @@ async function validateFile(filePath, validator) {
1836
1888
  }
1837
1889
  async function validateSchema(schema) {
1838
1890
  try {
1839
- const validator = await compileSchema(schema);
1840
- validateData({}, validator);
1891
+ let parsedSchema;
1892
+ if (typeof schema === "string") {
1893
+ try {
1894
+ parsedSchema = JSON.parse(schema);
1895
+ } catch {
1896
+ parsedSchema = parse(schema);
1897
+ }
1898
+ } else if (Buffer.isBuffer(schema)) {
1899
+ const content = schema.toString("utf-8");
1900
+ try {
1901
+ parsedSchema = JSON.parse(content);
1902
+ } catch {
1903
+ parsedSchema = parse(content);
1904
+ }
1905
+ } else {
1906
+ parsedSchema = schema;
1907
+ }
1908
+ const dialect = detectDialect(parsedSchema);
1909
+ const ajv = await getAjv(dialect);
1910
+ const metaValid = ajv.validateSchema(parsedSchema);
1911
+ if (!metaValid && ajv.errors) {
1912
+ const diagnostics = ajv.errors.map(
1913
+ (error) => createDiagnostic(
1914
+ error.instancePath || "",
1915
+ error.message || "Schema meta-validation failed",
1916
+ error.keyword || "unknown",
1917
+ "ERROR",
1918
+ "ajv"
1919
+ )
1920
+ );
1921
+ return { valid: false, diagnostics, source: "ajv" };
1922
+ }
1923
+ await compileSchema(parsedSchema);
1841
1924
  return {
1842
1925
  valid: true,
1843
1926
  diagnostics: [],
@@ -1908,14 +1991,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
1908
1991
  throw error;
1909
1992
  }
1910
1993
  }
1911
- var ajvInstance, metaschemaReady, schemaCache;
1994
+ var ajvInstances, metaschemaReady, schemaCache;
1912
1995
  var init_validator = __esm({
1913
1996
  "src/schema/validator.ts"() {
1914
1997
  init_telemetry();
1998
+ init_ajv_formats();
1915
1999
  init_errors();
1916
2000
  init_registry();
1917
2001
  init_utils();
1918
- metaschemaReady = null;
2002
+ ajvInstances = /* @__PURE__ */ new Map();
2003
+ metaschemaReady = /* @__PURE__ */ new Map();
1919
2004
  schemaCache = /* @__PURE__ */ new Map();
1920
2005
  }
1921
2006
  });
@@ -3870,6 +3955,224 @@ var init_capabilities2 = __esm({
3870
3955
  }
3871
3956
  });
3872
3957
 
3958
+ // src/foundry/signals/config-reload-endpoint.ts
3959
+ function createConfigReloadEndpoint(options) {
3960
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3961
+ return async (payload, req) => {
3962
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
3963
+ const authResult = await auth(req);
3964
+ if (!authResult.authenticated) {
3965
+ if (logger) {
3966
+ logger.warn("Config reload endpoint: authentication failed", {
3967
+ correlation_id: correlationId,
3968
+ reason: authResult.reason
3969
+ });
3970
+ }
3971
+ if (telemetry) {
3972
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3973
+ correlation_id: correlationId
3974
+ });
3975
+ }
3976
+ return {
3977
+ status: "error",
3978
+ error: "authentication_failed",
3979
+ message: authResult.reason || "Authentication required",
3980
+ statusCode: 401
3981
+ };
3982
+ }
3983
+ const identity = authResult.identity || "unknown";
3984
+ if (rateLimit) {
3985
+ const rateLimitResult = await rateLimit(identity);
3986
+ if (!rateLimitResult.allowed) {
3987
+ if (logger) {
3988
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3989
+ correlation_id: correlationId,
3990
+ identity
3991
+ });
3992
+ }
3993
+ if (telemetry) {
3994
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3995
+ correlation_id: correlationId
3996
+ });
3997
+ }
3998
+ return {
3999
+ status: "error",
4000
+ error: "rate_limit_exceeded",
4001
+ message: "Rate limit exceeded. Please try again later.",
4002
+ statusCode: 429
4003
+ };
4004
+ }
4005
+ }
4006
+ if (telemetry) {
4007
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
4008
+ correlation_id: correlationId
4009
+ });
4010
+ }
4011
+ try {
4012
+ const config = await loader();
4013
+ if (validator) {
4014
+ const validation = await validator(config);
4015
+ if (!validation.valid) {
4016
+ if (logger) {
4017
+ logger.warn("Config reload endpoint: validation failed", {
4018
+ correlation_id: correlationId,
4019
+ error_count: validation.errors?.length ?? 0
4020
+ });
4021
+ }
4022
+ if (telemetry) {
4023
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
4024
+ correlation_id: correlationId,
4025
+ reason: "validation_failed"
4026
+ });
4027
+ }
4028
+ return {
4029
+ status: "error",
4030
+ error: "validation_failed",
4031
+ message: "Configuration validation failed",
4032
+ validation_errors: validation.errors,
4033
+ statusCode: 422
4034
+ };
4035
+ }
4036
+ }
4037
+ if (onReload2) {
4038
+ await onReload2(config);
4039
+ }
4040
+ if (telemetry) {
4041
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
4042
+ correlation_id: correlationId
4043
+ });
4044
+ }
4045
+ if (logger) {
4046
+ logger.info("Config reload endpoint: reload accepted", {
4047
+ correlation_id: correlationId,
4048
+ reason: payload.reason
4049
+ });
4050
+ }
4051
+ return {
4052
+ status: "reloaded",
4053
+ correlation_id: correlationId,
4054
+ message: "Configuration reloaded",
4055
+ statusCode: 200
4056
+ };
4057
+ } catch (error) {
4058
+ if (logger) {
4059
+ logger.warn("Config reload endpoint: reload failed", {
4060
+ correlation_id: correlationId,
4061
+ error: error instanceof Error ? error.message : String(error)
4062
+ });
4063
+ }
4064
+ if (telemetry) {
4065
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
4066
+ correlation_id: correlationId,
4067
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
4068
+ });
4069
+ }
4070
+ return {
4071
+ status: "error",
4072
+ error: "reload_failed",
4073
+ message: error instanceof Error ? error.message : String(error),
4074
+ statusCode: 500
4075
+ };
4076
+ }
4077
+ };
4078
+ }
4079
+ function generateCorrelationId2() {
4080
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4081
+ }
4082
+ var init_config_reload_endpoint = __esm({
4083
+ "src/foundry/signals/config-reload-endpoint.ts"() {
4084
+ }
4085
+ });
4086
+
4087
+ // src/appidentity/runtime.ts
4088
+ function detectRuntime() {
4089
+ const versions = process.versions;
4090
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
4091
+ return { name: "bun", version: versions.bun };
4092
+ }
4093
+ if (typeof versions.node === "string" && versions.node.length > 0) {
4094
+ return { name: "node", version: versions.node };
4095
+ }
4096
+ return { name: "unknown" };
4097
+ }
4098
+ function buildRuntimeInfo(options = {}) {
4099
+ const runtime = detectRuntime();
4100
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
4101
+ const vendor = options.vendor ?? options.identity?.app.vendor;
4102
+ return {
4103
+ service: {
4104
+ name: serviceName,
4105
+ vendor,
4106
+ version: options.version
4107
+ },
4108
+ runtime,
4109
+ platform: {
4110
+ os: process.platform,
4111
+ arch: process.arch
4112
+ }
4113
+ };
4114
+ }
4115
+ var init_runtime = __esm({
4116
+ "src/appidentity/runtime.ts"() {
4117
+ }
4118
+ });
4119
+
4120
+ // src/foundry/signals/control-discovery-endpoint.ts
4121
+ function createControlDiscoveryEndpoint(options) {
4122
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
4123
+ return async (req) => {
4124
+ if (auth) {
4125
+ const authResult = await auth(req);
4126
+ if (!authResult.authenticated) {
4127
+ if (logger) {
4128
+ logger.warn("Control discovery endpoint: authentication failed", {
4129
+ reason: authResult.reason
4130
+ });
4131
+ }
4132
+ if (telemetry) {
4133
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4134
+ service: identity.app.binary_name
4135
+ });
4136
+ }
4137
+ return {
4138
+ status: "error",
4139
+ error: "authentication_failed",
4140
+ message: authResult.reason || "Authentication required",
4141
+ statusCode: 401
4142
+ };
4143
+ }
4144
+ }
4145
+ if (telemetry) {
4146
+ telemetry.emit("fulmen.control.discovery.served", {
4147
+ service: identity.app.binary_name
4148
+ });
4149
+ }
4150
+ const runtime = buildRuntimeInfo({ identity, version });
4151
+ return {
4152
+ status: "ok",
4153
+ service: {
4154
+ name: identity.app.binary_name,
4155
+ vendor: identity.app.vendor,
4156
+ version
4157
+ },
4158
+ runtime: {
4159
+ name: runtime.runtime.name,
4160
+ version: runtime.runtime.version,
4161
+ platform: runtime.platform.os,
4162
+ arch: runtime.platform.arch
4163
+ },
4164
+ auth_summary: authSummary,
4165
+ endpoints,
4166
+ statusCode: 200
4167
+ };
4168
+ };
4169
+ }
4170
+ var init_control_discovery_endpoint = __esm({
4171
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4172
+ init_runtime();
4173
+ }
4174
+ });
4175
+
3873
4176
  // src/foundry/signals/convenience.ts
3874
4177
  async function onShutdown(manager, handler, options = {}) {
3875
4178
  await manager.register("SIGTERM", handler, options);
@@ -4129,7 +4432,7 @@ var init_guards = __esm({
4129
4432
  function createSignalEndpoint(options) {
4130
4433
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4131
4434
  return async (payload, req) => {
4132
- const correlationId = payload.correlation_id ?? generateCorrelationId2();
4435
+ const correlationId = payload.correlation_id ?? generateCorrelationId3();
4133
4436
  const authResult = await auth(req);
4134
4437
  if (!authResult.authenticated) {
4135
4438
  if (logger) {
@@ -4252,7 +4555,7 @@ function normalizeSignalName(signal) {
4252
4555
  }
4253
4556
  return `SIG${upper}`;
4254
4557
  }
4255
- function generateCorrelationId2() {
4558
+ function generateCorrelationId3() {
4256
4559
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4257
4560
  }
4258
4561
  function createBearerTokenAuth(expectedToken) {
@@ -4719,6 +5022,8 @@ var init_signals = __esm({
4719
5022
  "src/foundry/signals/index.ts"() {
4720
5023
  init_capabilities2();
4721
5024
  init_catalog();
5025
+ init_config_reload_endpoint();
5026
+ init_control_discovery_endpoint();
4722
5027
  init_convenience();
4723
5028
  init_double_tap();
4724
5029
  init_guards();
@@ -4864,7 +5169,9 @@ __export(foundry_exports, {
4864
5169
  clearMimeTypeCache: () => clearMimeTypeCache,
4865
5170
  clearPatternCache: () => clearPatternCache,
4866
5171
  createBearerTokenAuth: () => createBearerTokenAuth,
5172
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4867
5173
  createConfigReloadHandler: () => createConfigReloadHandler,
5174
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4868
5175
  createDoubleTapTracker: () => createDoubleTapTracker,
4869
5176
  createSignalEndpoint: () => createSignalEndpoint,
4870
5177
  createSignalManager: () => createSignalManager,
@@ -5587,6 +5894,7 @@ var init_cli = __esm({
5587
5894
  // src/schema/index.ts
5588
5895
  var init_schema = __esm({
5589
5896
  "src/schema/index.ts"() {
5897
+ init_ajv_formats();
5590
5898
  init_cli();
5591
5899
  init_errors();
5592
5900
  init_export();