@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
@@ -3,7 +3,7 @@ import { metrics } from '../index.js';
3
3
  import { b as MetricsRegistry } from '../../registry-_yEKhTTl.js';
4
4
  import { S as SignalManager } from '../../manager-CH3fX7zO.js';
5
5
  import { IncomingMessage, ServerResponse, Server } from 'node:http';
6
- import '../../types-BJswWpQC.js';
6
+ import '../../types-DdoeE7F5.js';
7
7
 
8
8
  /**
9
9
  * Prometheus exporter constants
@@ -1,4 +1,5 @@
1
1
  import 'crypto';
2
+ import addFormats from 'ajv-formats';
2
3
  import { spawn } from 'child_process';
3
4
  import { readFile, writeFile, access, mkdir } from 'fs/promises';
4
5
  import { parse, stringify } from 'yaml';
@@ -6,7 +7,9 @@ import { dirname, join, relative, extname } 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';
@@ -117,6 +120,27 @@ var init_serialization = __esm({
117
120
  init_severity();
118
121
  }
119
122
  });
123
+ function applyFulmenAjvFormats(ajv, options = {}) {
124
+ const mode = options.mode ?? "fast";
125
+ const formats = options.formats ?? DEFAULT_FORMATS;
126
+ addFormats(ajv, { mode, formats });
127
+ return ajv;
128
+ }
129
+ var DEFAULT_FORMATS;
130
+ var init_ajv_formats = __esm({
131
+ "src/schema/ajv-formats.ts"() {
132
+ DEFAULT_FORMATS = [
133
+ "date-time",
134
+ "email",
135
+ "hostname",
136
+ "ipv4",
137
+ "ipv6",
138
+ "uri",
139
+ "uri-reference",
140
+ "uuid"
141
+ ];
142
+ }
143
+ });
120
144
 
121
145
  // src/schema/errors.ts
122
146
  var errors_exports = {};
@@ -1572,20 +1596,14 @@ async function loadMetaSchema(draft) {
1572
1596
  const content = await readFile(metaSchemaPath, "utf-8");
1573
1597
  return JSON.parse(content);
1574
1598
  }
1575
- async function loadVocabularySchemas() {
1599
+ async function loadVocabularySchemas(draft) {
1600
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1601
+ return [];
1602
+ }
1576
1603
  const __filename3 = fileURLToPath(import.meta.url);
1577
1604
  const __dirname4 = dirname(__filename3);
1578
- const vocabDir = join(
1579
- __dirname4,
1580
- "..",
1581
- "..",
1582
- "schemas",
1583
- "crucible-ts",
1584
- "meta",
1585
- "draft-2020-12",
1586
- "meta"
1587
- );
1588
- const vocabFiles = [
1605
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1606
+ const vocabFiles = draft === "draft-2020-12" ? [
1589
1607
  "core.json",
1590
1608
  "applicator.json",
1591
1609
  "unevaluated.json",
@@ -1593,6 +1611,13 @@ async function loadVocabularySchemas() {
1593
1611
  "meta-data.json",
1594
1612
  "format-annotation.json",
1595
1613
  "content.json"
1614
+ ] : [
1615
+ "core.json",
1616
+ "applicator.json",
1617
+ "validation.json",
1618
+ "meta-data.json",
1619
+ "format.json",
1620
+ "content.json"
1596
1621
  ];
1597
1622
  const schemas = [];
1598
1623
  for (const file of vocabFiles) {
@@ -1647,47 +1672,65 @@ async function loadReferencedSchema(uri) {
1647
1672
  }
1648
1673
  return JSON.parse(content);
1649
1674
  }
1650
- function getAjv() {
1651
- if (!ajvInstance) {
1652
- ajvInstance = new Ajv({
1653
- strict: false,
1654
- allErrors: true,
1655
- verbose: true,
1656
- // Allow schemas with $id to be added without replacing existing ones
1657
- addUsedSchema: false,
1658
- // Enable async schema loading for YAML references
1659
- loadSchema: loadReferencedSchema
1660
- });
1661
- addFormats(ajvInstance, {
1662
- mode: "fast",
1663
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1664
- });
1665
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
1666
- if (ajvInstance) {
1667
- for (const vocabSchema of vocabSchemas) {
1668
- try {
1669
- ajvInstance.addMetaSchema(vocabSchema);
1670
- } catch {
1671
- }
1672
- }
1673
- ajvInstance.addMetaSchema(metaSchema);
1675
+ function detectDialect(schema) {
1676
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1677
+ const maybeSchema = schema;
1678
+ const declared = maybeSchema.$schema;
1679
+ if (typeof declared === "string") {
1680
+ if (declared.includes("draft-04")) return "draft-04";
1681
+ if (declared.includes("draft-06")) return "draft-06";
1682
+ if (declared.includes("draft-07")) return "draft-07";
1683
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
1684
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
1685
+ }
1686
+ }
1687
+ return "draft-2020-12";
1688
+ }
1689
+ function createAjv(dialect) {
1690
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1691
+ const ajv = new AjvCtor({
1692
+ strict: false,
1693
+ allErrors: true,
1694
+ verbose: true,
1695
+ // Allow schemas with $id to be added without replacing existing ones
1696
+ addUsedSchema: false,
1697
+ // draft-04 uses "id"; later drafts use "$id"
1698
+ schemaId: dialect === "draft-04" ? "id" : "$id",
1699
+ // Enable async schema loading for YAML references
1700
+ loadSchema: loadReferencedSchema
1701
+ });
1702
+ applyFulmenAjvFormats(ajv);
1703
+ return ajv;
1704
+ }
1705
+ async function getAjv(dialect) {
1706
+ const existing = ajvInstances.get(dialect);
1707
+ if (existing) {
1708
+ const ready = metaschemaReady.get(dialect);
1709
+ if (ready) await ready;
1710
+ return existing;
1711
+ }
1712
+ const ajv = createAjv(dialect);
1713
+ ajvInstances.set(dialect, ajv);
1714
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1715
+ for (const vocabSchema of vocabSchemas) {
1716
+ try {
1717
+ ajv.addMetaSchema(vocabSchema);
1718
+ } catch {
1674
1719
  }
1675
- }).catch((error) => {
1676
- throw new Error(`Failed to load metaschemas: ${error}`);
1677
- });
1678
- }
1679
- return ajvInstance;
1720
+ }
1721
+ try {
1722
+ ajv.addMetaSchema(metaSchema);
1723
+ } catch {
1724
+ }
1725
+ }).catch((error) => {
1726
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1727
+ });
1728
+ metaschemaReady.set(dialect, readyPromise);
1729
+ await readyPromise;
1730
+ return ajv;
1680
1731
  }
1681
1732
  async function compileSchema(schema, options = {}) {
1682
- const ajv = getAjv();
1683
- if (metaschemaReady) {
1684
- await metaschemaReady;
1685
- }
1686
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1687
- const cached = schemaCache.get(cacheKey);
1688
- if (cached !== void 0) {
1689
- return cached;
1690
- }
1733
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1691
1734
  let parsedSchema;
1692
1735
  if (typeof schema === "string") {
1693
1736
  try {
@@ -1705,18 +1748,27 @@ async function compileSchema(schema, options = {}) {
1705
1748
  } else {
1706
1749
  parsedSchema = schema;
1707
1750
  }
1751
+ const dialect = detectDialect(parsedSchema);
1752
+ const ajv = await getAjv(dialect);
1753
+ const cacheKey = `${dialect}:${baseKey}`;
1754
+ const cached = schemaCache.get(cacheKey);
1755
+ if (cached !== void 0) {
1756
+ return cached;
1757
+ }
1708
1758
  try {
1709
1759
  if (options.aliases && options.aliases.length > 0) {
1710
1760
  for (const alias of options.aliases) {
1711
1761
  if (alias && ajv.getSchema(alias) === void 0) {
1712
1762
  try {
1713
- ajv.addSchema(parsedSchema, alias);
1763
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
1764
+ ajv.addSchema(parsedSchema, alias);
1765
+ }
1714
1766
  } catch {
1715
1767
  }
1716
1768
  }
1717
1769
  }
1718
1770
  }
1719
- const validator = await ajv.compileAsync(parsedSchema);
1771
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1720
1772
  schemaCache.set(cacheKey, validator);
1721
1773
  return validator;
1722
1774
  } catch (error) {
@@ -1786,8 +1838,39 @@ async function validateFile(filePath, validator) {
1786
1838
  }
1787
1839
  async function validateSchema(schema) {
1788
1840
  try {
1789
- const validator = await compileSchema(schema);
1790
- validateData({}, validator);
1841
+ let parsedSchema;
1842
+ if (typeof schema === "string") {
1843
+ try {
1844
+ parsedSchema = JSON.parse(schema);
1845
+ } catch {
1846
+ parsedSchema = parse(schema);
1847
+ }
1848
+ } else if (Buffer.isBuffer(schema)) {
1849
+ const content = schema.toString("utf-8");
1850
+ try {
1851
+ parsedSchema = JSON.parse(content);
1852
+ } catch {
1853
+ parsedSchema = parse(content);
1854
+ }
1855
+ } else {
1856
+ parsedSchema = schema;
1857
+ }
1858
+ const dialect = detectDialect(parsedSchema);
1859
+ const ajv = await getAjv(dialect);
1860
+ const metaValid = ajv.validateSchema(parsedSchema);
1861
+ if (!metaValid && ajv.errors) {
1862
+ const diagnostics = ajv.errors.map(
1863
+ (error) => createDiagnostic(
1864
+ error.instancePath || "",
1865
+ error.message || "Schema meta-validation failed",
1866
+ error.keyword || "unknown",
1867
+ "ERROR",
1868
+ "ajv"
1869
+ )
1870
+ );
1871
+ return { valid: false, diagnostics, source: "ajv" };
1872
+ }
1873
+ await compileSchema(parsedSchema);
1791
1874
  return {
1792
1875
  valid: true,
1793
1876
  diagnostics: [],
@@ -1858,14 +1941,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
1858
1941
  throw error;
1859
1942
  }
1860
1943
  }
1861
- var ajvInstance, metaschemaReady, schemaCache;
1944
+ var ajvInstances, metaschemaReady, schemaCache;
1862
1945
  var init_validator = __esm({
1863
1946
  "src/schema/validator.ts"() {
1864
1947
  init_telemetry();
1948
+ init_ajv_formats();
1865
1949
  init_errors();
1866
1950
  init_registry();
1867
1951
  init_utils();
1868
- metaschemaReady = null;
1952
+ ajvInstances = /* @__PURE__ */ new Map();
1953
+ metaschemaReady = /* @__PURE__ */ new Map();
1869
1954
  schemaCache = /* @__PURE__ */ new Map();
1870
1955
  }
1871
1956
  });
@@ -3820,6 +3905,224 @@ var init_capabilities2 = __esm({
3820
3905
  }
3821
3906
  });
3822
3907
 
3908
+ // src/foundry/signals/config-reload-endpoint.ts
3909
+ function createConfigReloadEndpoint(options) {
3910
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3911
+ return async (payload, req) => {
3912
+ const correlationId = payload.correlation_id ?? generateCorrelationId();
3913
+ const authResult = await auth(req);
3914
+ if (!authResult.authenticated) {
3915
+ if (logger) {
3916
+ logger.warn("Config reload endpoint: authentication failed", {
3917
+ correlation_id: correlationId,
3918
+ reason: authResult.reason
3919
+ });
3920
+ }
3921
+ if (telemetry) {
3922
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3923
+ correlation_id: correlationId
3924
+ });
3925
+ }
3926
+ return {
3927
+ status: "error",
3928
+ error: "authentication_failed",
3929
+ message: authResult.reason || "Authentication required",
3930
+ statusCode: 401
3931
+ };
3932
+ }
3933
+ const identity = authResult.identity || "unknown";
3934
+ if (rateLimit) {
3935
+ const rateLimitResult = await rateLimit(identity);
3936
+ if (!rateLimitResult.allowed) {
3937
+ if (logger) {
3938
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3939
+ correlation_id: correlationId,
3940
+ identity
3941
+ });
3942
+ }
3943
+ if (telemetry) {
3944
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3945
+ correlation_id: correlationId
3946
+ });
3947
+ }
3948
+ return {
3949
+ status: "error",
3950
+ error: "rate_limit_exceeded",
3951
+ message: "Rate limit exceeded. Please try again later.",
3952
+ statusCode: 429
3953
+ };
3954
+ }
3955
+ }
3956
+ if (telemetry) {
3957
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
3958
+ correlation_id: correlationId
3959
+ });
3960
+ }
3961
+ try {
3962
+ const config = await loader();
3963
+ if (validator) {
3964
+ const validation = await validator(config);
3965
+ if (!validation.valid) {
3966
+ if (logger) {
3967
+ logger.warn("Config reload endpoint: validation failed", {
3968
+ correlation_id: correlationId,
3969
+ error_count: validation.errors?.length ?? 0
3970
+ });
3971
+ }
3972
+ if (telemetry) {
3973
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
3974
+ correlation_id: correlationId,
3975
+ reason: "validation_failed"
3976
+ });
3977
+ }
3978
+ return {
3979
+ status: "error",
3980
+ error: "validation_failed",
3981
+ message: "Configuration validation failed",
3982
+ validation_errors: validation.errors,
3983
+ statusCode: 422
3984
+ };
3985
+ }
3986
+ }
3987
+ if (onReload2) {
3988
+ await onReload2(config);
3989
+ }
3990
+ if (telemetry) {
3991
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
3992
+ correlation_id: correlationId
3993
+ });
3994
+ }
3995
+ if (logger) {
3996
+ logger.info("Config reload endpoint: reload accepted", {
3997
+ correlation_id: correlationId,
3998
+ reason: payload.reason
3999
+ });
4000
+ }
4001
+ return {
4002
+ status: "reloaded",
4003
+ correlation_id: correlationId,
4004
+ message: "Configuration reloaded",
4005
+ statusCode: 200
4006
+ };
4007
+ } catch (error) {
4008
+ if (logger) {
4009
+ logger.warn("Config reload endpoint: reload failed", {
4010
+ correlation_id: correlationId,
4011
+ error: error instanceof Error ? error.message : String(error)
4012
+ });
4013
+ }
4014
+ if (telemetry) {
4015
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
4016
+ correlation_id: correlationId,
4017
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
4018
+ });
4019
+ }
4020
+ return {
4021
+ status: "error",
4022
+ error: "reload_failed",
4023
+ message: error instanceof Error ? error.message : String(error),
4024
+ statusCode: 500
4025
+ };
4026
+ }
4027
+ };
4028
+ }
4029
+ function generateCorrelationId() {
4030
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4031
+ }
4032
+ var init_config_reload_endpoint = __esm({
4033
+ "src/foundry/signals/config-reload-endpoint.ts"() {
4034
+ }
4035
+ });
4036
+
4037
+ // src/appidentity/runtime.ts
4038
+ function detectRuntime() {
4039
+ const versions = process.versions;
4040
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
4041
+ return { name: "bun", version: versions.bun };
4042
+ }
4043
+ if (typeof versions.node === "string" && versions.node.length > 0) {
4044
+ return { name: "node", version: versions.node };
4045
+ }
4046
+ return { name: "unknown" };
4047
+ }
4048
+ function buildRuntimeInfo(options = {}) {
4049
+ const runtime = detectRuntime();
4050
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
4051
+ const vendor = options.vendor ?? options.identity?.app.vendor;
4052
+ return {
4053
+ service: {
4054
+ name: serviceName,
4055
+ vendor,
4056
+ version: options.version
4057
+ },
4058
+ runtime,
4059
+ platform: {
4060
+ os: process.platform,
4061
+ arch: process.arch
4062
+ }
4063
+ };
4064
+ }
4065
+ var init_runtime = __esm({
4066
+ "src/appidentity/runtime.ts"() {
4067
+ }
4068
+ });
4069
+
4070
+ // src/foundry/signals/control-discovery-endpoint.ts
4071
+ function createControlDiscoveryEndpoint(options) {
4072
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
4073
+ return async (req) => {
4074
+ if (auth) {
4075
+ const authResult = await auth(req);
4076
+ if (!authResult.authenticated) {
4077
+ if (logger) {
4078
+ logger.warn("Control discovery endpoint: authentication failed", {
4079
+ reason: authResult.reason
4080
+ });
4081
+ }
4082
+ if (telemetry) {
4083
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4084
+ service: identity.app.binary_name
4085
+ });
4086
+ }
4087
+ return {
4088
+ status: "error",
4089
+ error: "authentication_failed",
4090
+ message: authResult.reason || "Authentication required",
4091
+ statusCode: 401
4092
+ };
4093
+ }
4094
+ }
4095
+ if (telemetry) {
4096
+ telemetry.emit("fulmen.control.discovery.served", {
4097
+ service: identity.app.binary_name
4098
+ });
4099
+ }
4100
+ const runtime = buildRuntimeInfo({ identity, version });
4101
+ return {
4102
+ status: "ok",
4103
+ service: {
4104
+ name: identity.app.binary_name,
4105
+ vendor: identity.app.vendor,
4106
+ version
4107
+ },
4108
+ runtime: {
4109
+ name: runtime.runtime.name,
4110
+ version: runtime.runtime.version,
4111
+ platform: runtime.platform.os,
4112
+ arch: runtime.platform.arch
4113
+ },
4114
+ auth_summary: authSummary,
4115
+ endpoints,
4116
+ statusCode: 200
4117
+ };
4118
+ };
4119
+ }
4120
+ var init_control_discovery_endpoint = __esm({
4121
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4122
+ init_runtime();
4123
+ }
4124
+ });
4125
+
3823
4126
  // src/foundry/signals/convenience.ts
3824
4127
  async function onShutdown(manager, handler, options = {}) {
3825
4128
  await manager.register("SIGTERM", handler, options);
@@ -4079,7 +4382,7 @@ var init_guards = __esm({
4079
4382
  function createSignalEndpoint(options) {
4080
4383
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4081
4384
  return async (payload, req) => {
4082
- const correlationId = payload.correlation_id ?? generateCorrelationId();
4385
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
4083
4386
  const authResult = await auth(req);
4084
4387
  if (!authResult.authenticated) {
4085
4388
  if (logger) {
@@ -4202,7 +4505,7 @@ function normalizeSignalName(signal) {
4202
4505
  }
4203
4506
  return `SIG${upper}`;
4204
4507
  }
4205
- function generateCorrelationId() {
4508
+ function generateCorrelationId2() {
4206
4509
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4207
4510
  }
4208
4511
  function createBearerTokenAuth(expectedToken) {
@@ -4669,6 +4972,8 @@ var init_signals = __esm({
4669
4972
  "src/foundry/signals/index.ts"() {
4670
4973
  init_capabilities2();
4671
4974
  init_catalog();
4975
+ init_config_reload_endpoint();
4976
+ init_control_discovery_endpoint();
4672
4977
  init_convenience();
4673
4978
  init_double_tap();
4674
4979
  init_guards();
@@ -4814,7 +5119,9 @@ __export(foundry_exports, {
4814
5119
  clearMimeTypeCache: () => clearMimeTypeCache,
4815
5120
  clearPatternCache: () => clearPatternCache,
4816
5121
  createBearerTokenAuth: () => createBearerTokenAuth,
5122
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4817
5123
  createConfigReloadHandler: () => createConfigReloadHandler,
5124
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4818
5125
  createDoubleTapTracker: () => createDoubleTapTracker,
4819
5126
  createSignalEndpoint: () => createSignalEndpoint,
4820
5127
  createSignalManager: () => createSignalManager,
@@ -5584,6 +5891,7 @@ var init_cli = __esm({
5584
5891
  // src/schema/index.ts
5585
5892
  var init_schema = __esm({
5586
5893
  "src/schema/index.ts"() {
5894
+ init_ajv_formats();
5587
5895
  init_cli();
5588
5896
  init_errors();
5589
5897
  init_export();
@@ -10714,6 +11022,7 @@ __export(appidentity_exports, {
10714
11022
  AppIdentityError: () => AppIdentityError,
10715
11023
  MAX_ANCESTOR_SEARCH_DEPTH: () => MAX_ANCESTOR_SEARCH_DEPTH,
10716
11024
  buildEnvVar: () => buildEnvVar,
11025
+ buildRuntimeInfo: () => buildRuntimeInfo,
10717
11026
  clearEmbeddedIdentity: () => clearEmbeddedIdentity,
10718
11027
  clearIdentityCache: () => clearIdentityCache,
10719
11028
  getBinaryName: () => getBinaryName,
@@ -10736,6 +11045,7 @@ var init_appidentity = __esm({
10736
11045
  init_errors4();
10737
11046
  init_helpers();
10738
11047
  init_loader2();
11048
+ init_runtime();
10739
11049
  }
10740
11050
  });
10741
11051