@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,3 +1,4 @@
1
+ import addFormats from 'ajv-formats';
1
2
  import { spawn } from 'child_process';
2
3
  import { readFile, writeFile, access, mkdir } from 'fs/promises';
3
4
  import { parse, stringify } from 'yaml';
@@ -5,7 +6,9 @@ import { dirname, join, relative, extname } from 'path';
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';
@@ -33,6 +36,27 @@ var init_constants = __esm({
33
36
  MAX_ANCESTOR_SEARCH_DEPTH = 20;
34
37
  }
35
38
  });
39
+ function applyFulmenAjvFormats(ajv, options = {}) {
40
+ const mode = options.mode ?? "fast";
41
+ const formats = options.formats ?? DEFAULT_FORMATS;
42
+ addFormats(ajv, { mode, formats });
43
+ return ajv;
44
+ }
45
+ var DEFAULT_FORMATS;
46
+ var init_ajv_formats = __esm({
47
+ "src/schema/ajv-formats.ts"() {
48
+ DEFAULT_FORMATS = [
49
+ "date-time",
50
+ "email",
51
+ "hostname",
52
+ "ipv4",
53
+ "ipv6",
54
+ "uri",
55
+ "uri-reference",
56
+ "uuid"
57
+ ];
58
+ }
59
+ });
36
60
 
37
61
  // src/schema/errors.ts
38
62
  var errors_exports = {};
@@ -1485,20 +1509,14 @@ async function loadMetaSchema(draft) {
1485
1509
  const content = await readFile(metaSchemaPath, "utf-8");
1486
1510
  return JSON.parse(content);
1487
1511
  }
1488
- async function loadVocabularySchemas() {
1512
+ async function loadVocabularySchemas(draft) {
1513
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1514
+ return [];
1515
+ }
1489
1516
  const __filename3 = fileURLToPath(import.meta.url);
1490
1517
  const __dirname4 = dirname(__filename3);
1491
- const vocabDir = join(
1492
- __dirname4,
1493
- "..",
1494
- "..",
1495
- "schemas",
1496
- "crucible-ts",
1497
- "meta",
1498
- "draft-2020-12",
1499
- "meta"
1500
- );
1501
- const vocabFiles = [
1518
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1519
+ const vocabFiles = draft === "draft-2020-12" ? [
1502
1520
  "core.json",
1503
1521
  "applicator.json",
1504
1522
  "unevaluated.json",
@@ -1506,6 +1524,13 @@ async function loadVocabularySchemas() {
1506
1524
  "meta-data.json",
1507
1525
  "format-annotation.json",
1508
1526
  "content.json"
1527
+ ] : [
1528
+ "core.json",
1529
+ "applicator.json",
1530
+ "validation.json",
1531
+ "meta-data.json",
1532
+ "format.json",
1533
+ "content.json"
1509
1534
  ];
1510
1535
  const schemas = [];
1511
1536
  for (const file of vocabFiles) {
@@ -1560,47 +1585,65 @@ async function loadReferencedSchema(uri) {
1560
1585
  }
1561
1586
  return JSON.parse(content);
1562
1587
  }
1563
- function getAjv() {
1564
- if (!ajvInstance) {
1565
- ajvInstance = new Ajv({
1566
- strict: false,
1567
- allErrors: true,
1568
- verbose: true,
1569
- // Allow schemas with $id to be added without replacing existing ones
1570
- addUsedSchema: false,
1571
- // Enable async schema loading for YAML references
1572
- loadSchema: loadReferencedSchema
1573
- });
1574
- addFormats(ajvInstance, {
1575
- mode: "fast",
1576
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1577
- });
1578
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
1579
- if (ajvInstance) {
1580
- for (const vocabSchema of vocabSchemas) {
1581
- try {
1582
- ajvInstance.addMetaSchema(vocabSchema);
1583
- } catch {
1584
- }
1585
- }
1586
- ajvInstance.addMetaSchema(metaSchema);
1587
- }
1588
- }).catch((error) => {
1589
- throw new Error(`Failed to load metaschemas: ${error}`);
1590
- });
1588
+ function detectDialect(schema) {
1589
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1590
+ const maybeSchema = schema;
1591
+ const declared = maybeSchema.$schema;
1592
+ if (typeof declared === "string") {
1593
+ if (declared.includes("draft-04")) return "draft-04";
1594
+ if (declared.includes("draft-06")) return "draft-06";
1595
+ if (declared.includes("draft-07")) return "draft-07";
1596
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
1597
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
1598
+ }
1591
1599
  }
1592
- return ajvInstance;
1600
+ return "draft-2020-12";
1601
+ }
1602
+ function createAjv(dialect) {
1603
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1604
+ const ajv = new AjvCtor({
1605
+ strict: false,
1606
+ allErrors: true,
1607
+ verbose: true,
1608
+ // Allow schemas with $id to be added without replacing existing ones
1609
+ addUsedSchema: false,
1610
+ // draft-04 uses "id"; later drafts use "$id"
1611
+ schemaId: dialect === "draft-04" ? "id" : "$id",
1612
+ // Enable async schema loading for YAML references
1613
+ loadSchema: loadReferencedSchema
1614
+ });
1615
+ applyFulmenAjvFormats(ajv);
1616
+ return ajv;
1617
+ }
1618
+ async function getAjv(dialect) {
1619
+ const existing = ajvInstances.get(dialect);
1620
+ if (existing) {
1621
+ const ready = metaschemaReady.get(dialect);
1622
+ if (ready) await ready;
1623
+ return existing;
1624
+ }
1625
+ const ajv = createAjv(dialect);
1626
+ ajvInstances.set(dialect, ajv);
1627
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1628
+ for (const vocabSchema of vocabSchemas) {
1629
+ try {
1630
+ ajv.addMetaSchema(vocabSchema);
1631
+ } catch {
1632
+ }
1633
+ }
1634
+ try {
1635
+ ajv.addMetaSchema(metaSchema);
1636
+ } catch {
1637
+ }
1638
+ }).catch((error) => {
1639
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1640
+ });
1641
+ metaschemaReady.set(dialect, readyPromise);
1642
+ await readyPromise;
1643
+ return ajv;
1593
1644
  }
1594
1645
  async function compileSchema(schema, options = {}) {
1595
- const ajv = getAjv();
1596
- if (metaschemaReady) {
1597
- await metaschemaReady;
1598
- }
1599
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1600
- const cached = schemaCache.get(cacheKey);
1601
- if (cached !== void 0) {
1602
- return cached;
1603
- }
1646
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1604
1647
  let parsedSchema;
1605
1648
  if (typeof schema === "string") {
1606
1649
  try {
@@ -1618,18 +1661,27 @@ async function compileSchema(schema, options = {}) {
1618
1661
  } else {
1619
1662
  parsedSchema = schema;
1620
1663
  }
1664
+ const dialect = detectDialect(parsedSchema);
1665
+ const ajv = await getAjv(dialect);
1666
+ const cacheKey = `${dialect}:${baseKey}`;
1667
+ const cached = schemaCache.get(cacheKey);
1668
+ if (cached !== void 0) {
1669
+ return cached;
1670
+ }
1621
1671
  try {
1622
1672
  if (options.aliases && options.aliases.length > 0) {
1623
1673
  for (const alias of options.aliases) {
1624
1674
  if (alias && ajv.getSchema(alias) === void 0) {
1625
1675
  try {
1626
- ajv.addSchema(parsedSchema, alias);
1676
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
1677
+ ajv.addSchema(parsedSchema, alias);
1678
+ }
1627
1679
  } catch {
1628
1680
  }
1629
1681
  }
1630
1682
  }
1631
1683
  }
1632
- const validator = await ajv.compileAsync(parsedSchema);
1684
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1633
1685
  schemaCache.set(cacheKey, validator);
1634
1686
  return validator;
1635
1687
  } catch (error) {
@@ -1699,8 +1751,39 @@ async function validateFile(filePath, validator) {
1699
1751
  }
1700
1752
  async function validateSchema(schema) {
1701
1753
  try {
1702
- const validator = await compileSchema(schema);
1703
- validateData({}, validator);
1754
+ let parsedSchema;
1755
+ if (typeof schema === "string") {
1756
+ try {
1757
+ parsedSchema = JSON.parse(schema);
1758
+ } catch {
1759
+ parsedSchema = parse(schema);
1760
+ }
1761
+ } else if (Buffer.isBuffer(schema)) {
1762
+ const content = schema.toString("utf-8");
1763
+ try {
1764
+ parsedSchema = JSON.parse(content);
1765
+ } catch {
1766
+ parsedSchema = parse(content);
1767
+ }
1768
+ } else {
1769
+ parsedSchema = schema;
1770
+ }
1771
+ const dialect = detectDialect(parsedSchema);
1772
+ const ajv = await getAjv(dialect);
1773
+ const metaValid = ajv.validateSchema(parsedSchema);
1774
+ if (!metaValid && ajv.errors) {
1775
+ const diagnostics = ajv.errors.map(
1776
+ (error) => createDiagnostic(
1777
+ error.instancePath || "",
1778
+ error.message || "Schema meta-validation failed",
1779
+ error.keyword || "unknown",
1780
+ "ERROR",
1781
+ "ajv"
1782
+ )
1783
+ );
1784
+ return { valid: false, diagnostics, source: "ajv" };
1785
+ }
1786
+ await compileSchema(parsedSchema);
1704
1787
  return {
1705
1788
  valid: true,
1706
1789
  diagnostics: [],
@@ -1771,14 +1854,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
1771
1854
  throw error;
1772
1855
  }
1773
1856
  }
1774
- var ajvInstance, metaschemaReady, schemaCache;
1857
+ var ajvInstances, metaschemaReady, schemaCache;
1775
1858
  var init_validator = __esm({
1776
1859
  "src/schema/validator.ts"() {
1777
1860
  init_telemetry();
1861
+ init_ajv_formats();
1778
1862
  init_errors();
1779
1863
  init_registry();
1780
1864
  init_utils();
1781
- metaschemaReady = null;
1865
+ ajvInstances = /* @__PURE__ */ new Map();
1866
+ metaschemaReady = /* @__PURE__ */ new Map();
1782
1867
  schemaCache = /* @__PURE__ */ new Map();
1783
1868
  }
1784
1869
  });
@@ -3733,6 +3818,224 @@ var init_capabilities2 = __esm({
3733
3818
  }
3734
3819
  });
3735
3820
 
3821
+ // src/foundry/signals/config-reload-endpoint.ts
3822
+ function createConfigReloadEndpoint(options) {
3823
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3824
+ return async (payload, req) => {
3825
+ const correlationId = payload.correlation_id ?? generateCorrelationId();
3826
+ const authResult = await auth(req);
3827
+ if (!authResult.authenticated) {
3828
+ if (logger) {
3829
+ logger.warn("Config reload endpoint: authentication failed", {
3830
+ correlation_id: correlationId,
3831
+ reason: authResult.reason
3832
+ });
3833
+ }
3834
+ if (telemetry) {
3835
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3836
+ correlation_id: correlationId
3837
+ });
3838
+ }
3839
+ return {
3840
+ status: "error",
3841
+ error: "authentication_failed",
3842
+ message: authResult.reason || "Authentication required",
3843
+ statusCode: 401
3844
+ };
3845
+ }
3846
+ const identity = authResult.identity || "unknown";
3847
+ if (rateLimit) {
3848
+ const rateLimitResult = await rateLimit(identity);
3849
+ if (!rateLimitResult.allowed) {
3850
+ if (logger) {
3851
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3852
+ correlation_id: correlationId,
3853
+ identity
3854
+ });
3855
+ }
3856
+ if (telemetry) {
3857
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3858
+ correlation_id: correlationId
3859
+ });
3860
+ }
3861
+ return {
3862
+ status: "error",
3863
+ error: "rate_limit_exceeded",
3864
+ message: "Rate limit exceeded. Please try again later.",
3865
+ statusCode: 429
3866
+ };
3867
+ }
3868
+ }
3869
+ if (telemetry) {
3870
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
3871
+ correlation_id: correlationId
3872
+ });
3873
+ }
3874
+ try {
3875
+ const config = await loader();
3876
+ if (validator) {
3877
+ const validation = await validator(config);
3878
+ if (!validation.valid) {
3879
+ if (logger) {
3880
+ logger.warn("Config reload endpoint: validation failed", {
3881
+ correlation_id: correlationId,
3882
+ error_count: validation.errors?.length ?? 0
3883
+ });
3884
+ }
3885
+ if (telemetry) {
3886
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
3887
+ correlation_id: correlationId,
3888
+ reason: "validation_failed"
3889
+ });
3890
+ }
3891
+ return {
3892
+ status: "error",
3893
+ error: "validation_failed",
3894
+ message: "Configuration validation failed",
3895
+ validation_errors: validation.errors,
3896
+ statusCode: 422
3897
+ };
3898
+ }
3899
+ }
3900
+ if (onReload2) {
3901
+ await onReload2(config);
3902
+ }
3903
+ if (telemetry) {
3904
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
3905
+ correlation_id: correlationId
3906
+ });
3907
+ }
3908
+ if (logger) {
3909
+ logger.info("Config reload endpoint: reload accepted", {
3910
+ correlation_id: correlationId,
3911
+ reason: payload.reason
3912
+ });
3913
+ }
3914
+ return {
3915
+ status: "reloaded",
3916
+ correlation_id: correlationId,
3917
+ message: "Configuration reloaded",
3918
+ statusCode: 200
3919
+ };
3920
+ } catch (error) {
3921
+ if (logger) {
3922
+ logger.warn("Config reload endpoint: reload failed", {
3923
+ correlation_id: correlationId,
3924
+ error: error instanceof Error ? error.message : String(error)
3925
+ });
3926
+ }
3927
+ if (telemetry) {
3928
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
3929
+ correlation_id: correlationId,
3930
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
3931
+ });
3932
+ }
3933
+ return {
3934
+ status: "error",
3935
+ error: "reload_failed",
3936
+ message: error instanceof Error ? error.message : String(error),
3937
+ statusCode: 500
3938
+ };
3939
+ }
3940
+ };
3941
+ }
3942
+ function generateCorrelationId() {
3943
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
3944
+ }
3945
+ var init_config_reload_endpoint = __esm({
3946
+ "src/foundry/signals/config-reload-endpoint.ts"() {
3947
+ }
3948
+ });
3949
+
3950
+ // src/appidentity/runtime.ts
3951
+ function detectRuntime() {
3952
+ const versions = process.versions;
3953
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
3954
+ return { name: "bun", version: versions.bun };
3955
+ }
3956
+ if (typeof versions.node === "string" && versions.node.length > 0) {
3957
+ return { name: "node", version: versions.node };
3958
+ }
3959
+ return { name: "unknown" };
3960
+ }
3961
+ function buildRuntimeInfo(options = {}) {
3962
+ const runtime = detectRuntime();
3963
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
3964
+ const vendor = options.vendor ?? options.identity?.app.vendor;
3965
+ return {
3966
+ service: {
3967
+ name: serviceName,
3968
+ vendor,
3969
+ version: options.version
3970
+ },
3971
+ runtime,
3972
+ platform: {
3973
+ os: process.platform,
3974
+ arch: process.arch
3975
+ }
3976
+ };
3977
+ }
3978
+ var init_runtime = __esm({
3979
+ "src/appidentity/runtime.ts"() {
3980
+ }
3981
+ });
3982
+
3983
+ // src/foundry/signals/control-discovery-endpoint.ts
3984
+ function createControlDiscoveryEndpoint(options) {
3985
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
3986
+ return async (req) => {
3987
+ if (auth) {
3988
+ const authResult = await auth(req);
3989
+ if (!authResult.authenticated) {
3990
+ if (logger) {
3991
+ logger.warn("Control discovery endpoint: authentication failed", {
3992
+ reason: authResult.reason
3993
+ });
3994
+ }
3995
+ if (telemetry) {
3996
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
3997
+ service: identity.app.binary_name
3998
+ });
3999
+ }
4000
+ return {
4001
+ status: "error",
4002
+ error: "authentication_failed",
4003
+ message: authResult.reason || "Authentication required",
4004
+ statusCode: 401
4005
+ };
4006
+ }
4007
+ }
4008
+ if (telemetry) {
4009
+ telemetry.emit("fulmen.control.discovery.served", {
4010
+ service: identity.app.binary_name
4011
+ });
4012
+ }
4013
+ const runtime = buildRuntimeInfo({ identity, version });
4014
+ return {
4015
+ status: "ok",
4016
+ service: {
4017
+ name: identity.app.binary_name,
4018
+ vendor: identity.app.vendor,
4019
+ version
4020
+ },
4021
+ runtime: {
4022
+ name: runtime.runtime.name,
4023
+ version: runtime.runtime.version,
4024
+ platform: runtime.platform.os,
4025
+ arch: runtime.platform.arch
4026
+ },
4027
+ auth_summary: authSummary,
4028
+ endpoints,
4029
+ statusCode: 200
4030
+ };
4031
+ };
4032
+ }
4033
+ var init_control_discovery_endpoint = __esm({
4034
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4035
+ init_runtime();
4036
+ }
4037
+ });
4038
+
3736
4039
  // src/foundry/signals/convenience.ts
3737
4040
  async function onShutdown(manager, handler, options = {}) {
3738
4041
  await manager.register("SIGTERM", handler, options);
@@ -3992,7 +4295,7 @@ var init_guards = __esm({
3992
4295
  function createSignalEndpoint(options) {
3993
4296
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
3994
4297
  return async (payload, req) => {
3995
- const correlationId = payload.correlation_id ?? generateCorrelationId();
4298
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
3996
4299
  const authResult = await auth(req);
3997
4300
  if (!authResult.authenticated) {
3998
4301
  if (logger) {
@@ -4115,7 +4418,7 @@ function normalizeSignalName(signal) {
4115
4418
  }
4116
4419
  return `SIG${upper}`;
4117
4420
  }
4118
- function generateCorrelationId() {
4421
+ function generateCorrelationId2() {
4119
4422
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4120
4423
  }
4121
4424
  function createBearerTokenAuth(expectedToken) {
@@ -4582,6 +4885,8 @@ var init_signals = __esm({
4582
4885
  "src/foundry/signals/index.ts"() {
4583
4886
  init_capabilities2();
4584
4887
  init_catalog();
4888
+ init_config_reload_endpoint();
4889
+ init_control_discovery_endpoint();
4585
4890
  init_convenience();
4586
4891
  init_double_tap();
4587
4892
  init_guards();
@@ -4727,7 +5032,9 @@ __export(foundry_exports, {
4727
5032
  clearMimeTypeCache: () => clearMimeTypeCache,
4728
5033
  clearPatternCache: () => clearPatternCache,
4729
5034
  createBearerTokenAuth: () => createBearerTokenAuth,
5035
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4730
5036
  createConfigReloadHandler: () => createConfigReloadHandler,
5037
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4731
5038
  createDoubleTapTracker: () => createDoubleTapTracker,
4732
5039
  createSignalEndpoint: () => createSignalEndpoint,
4733
5040
  createSignalManager: () => createSignalManager,
@@ -5759,6 +6066,7 @@ var init_cli = __esm({
5759
6066
  // src/schema/index.ts
5760
6067
  var init_schema = __esm({
5761
6068
  "src/schema/index.ts"() {
6069
+ init_ajv_formats();
5762
6070
  init_cli();
5763
6071
  init_errors();
5764
6072
  init_export();
@@ -5875,7 +6183,8 @@ async function getEnvVar(key, options) {
5875
6183
 
5876
6184
  // src/appidentity/index.ts
5877
6185
  init_loader2();
6186
+ init_runtime();
5878
6187
 
5879
- export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, MAX_ANCESTOR_SEARCH_DEPTH, buildEnvVar, clearEmbeddedIdentity, clearIdentityCache, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity };
6188
+ export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, MAX_ANCESTOR_SEARCH_DEPTH, buildEnvVar, buildRuntimeInfo, clearEmbeddedIdentity, clearIdentityCache, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity };
5880
6189
  //# sourceMappingURL=index.js.map
5881
6190
  //# sourceMappingURL=index.js.map