@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
package/dist/index.js CHANGED
@@ -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, parseDocument } from 'yaml';
@@ -5,7 +6,9 @@ import { dirname, join, relative, extname, basename } 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 { pipeline, 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';
@@ -42,6 +45,27 @@ var init_constants = __esm({
42
45
  MAX_ANCESTOR_SEARCH_DEPTH = 20;
43
46
  }
44
47
  });
48
+ function applyFulmenAjvFormats(ajv, options = {}) {
49
+ const mode = options.mode ?? "fast";
50
+ const formats = options.formats ?? DEFAULT_FORMATS;
51
+ addFormats(ajv, { mode, formats });
52
+ return ajv;
53
+ }
54
+ var DEFAULT_FORMATS;
55
+ var init_ajv_formats = __esm({
56
+ "src/schema/ajv-formats.ts"() {
57
+ DEFAULT_FORMATS = [
58
+ "date-time",
59
+ "email",
60
+ "hostname",
61
+ "ipv4",
62
+ "ipv6",
63
+ "uri",
64
+ "uri-reference",
65
+ "uuid"
66
+ ];
67
+ }
68
+ });
45
69
 
46
70
  // src/schema/errors.ts
47
71
  var errors_exports = {};
@@ -1494,20 +1518,14 @@ async function loadMetaSchema(draft) {
1494
1518
  const content = await readFile(metaSchemaPath, "utf-8");
1495
1519
  return JSON.parse(content);
1496
1520
  }
1497
- async function loadVocabularySchemas() {
1521
+ async function loadVocabularySchemas(draft) {
1522
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1523
+ return [];
1524
+ }
1498
1525
  const __filename3 = fileURLToPath(import.meta.url);
1499
1526
  const __dirname4 = dirname(__filename3);
1500
- const vocabDir = join(
1501
- __dirname4,
1502
- "..",
1503
- "..",
1504
- "schemas",
1505
- "crucible-ts",
1506
- "meta",
1507
- "draft-2020-12",
1508
- "meta"
1509
- );
1510
- const vocabFiles = [
1527
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1528
+ const vocabFiles = draft === "draft-2020-12" ? [
1511
1529
  "core.json",
1512
1530
  "applicator.json",
1513
1531
  "unevaluated.json",
@@ -1515,6 +1533,13 @@ async function loadVocabularySchemas() {
1515
1533
  "meta-data.json",
1516
1534
  "format-annotation.json",
1517
1535
  "content.json"
1536
+ ] : [
1537
+ "core.json",
1538
+ "applicator.json",
1539
+ "validation.json",
1540
+ "meta-data.json",
1541
+ "format.json",
1542
+ "content.json"
1518
1543
  ];
1519
1544
  const schemas = [];
1520
1545
  for (const file of vocabFiles) {
@@ -1569,47 +1594,65 @@ async function loadReferencedSchema(uri) {
1569
1594
  }
1570
1595
  return JSON.parse(content);
1571
1596
  }
1572
- function getAjv() {
1573
- if (!ajvInstance) {
1574
- ajvInstance = new Ajv({
1575
- strict: false,
1576
- allErrors: true,
1577
- verbose: true,
1578
- // Allow schemas with $id to be added without replacing existing ones
1579
- addUsedSchema: false,
1580
- // Enable async schema loading for YAML references
1581
- loadSchema: loadReferencedSchema
1582
- });
1583
- addFormats(ajvInstance, {
1584
- mode: "fast",
1585
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1586
- });
1587
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
1588
- if (ajvInstance) {
1589
- for (const vocabSchema of vocabSchemas) {
1590
- try {
1591
- ajvInstance.addMetaSchema(vocabSchema);
1592
- } catch {
1593
- }
1594
- }
1595
- ajvInstance.addMetaSchema(metaSchema);
1597
+ function detectDialect(schema) {
1598
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1599
+ const maybeSchema = schema;
1600
+ const declared = maybeSchema.$schema;
1601
+ if (typeof declared === "string") {
1602
+ if (declared.includes("draft-04")) return "draft-04";
1603
+ if (declared.includes("draft-06")) return "draft-06";
1604
+ if (declared.includes("draft-07")) return "draft-07";
1605
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
1606
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
1607
+ }
1608
+ }
1609
+ return "draft-2020-12";
1610
+ }
1611
+ function createAjv(dialect) {
1612
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1613
+ const ajv = new AjvCtor({
1614
+ strict: false,
1615
+ allErrors: true,
1616
+ verbose: true,
1617
+ // Allow schemas with $id to be added without replacing existing ones
1618
+ addUsedSchema: false,
1619
+ // draft-04 uses "id"; later drafts use "$id"
1620
+ schemaId: dialect === "draft-04" ? "id" : "$id",
1621
+ // Enable async schema loading for YAML references
1622
+ loadSchema: loadReferencedSchema
1623
+ });
1624
+ applyFulmenAjvFormats(ajv);
1625
+ return ajv;
1626
+ }
1627
+ async function getAjv(dialect) {
1628
+ const existing = ajvInstances.get(dialect);
1629
+ if (existing) {
1630
+ const ready = metaschemaReady.get(dialect);
1631
+ if (ready) await ready;
1632
+ return existing;
1633
+ }
1634
+ const ajv = createAjv(dialect);
1635
+ ajvInstances.set(dialect, ajv);
1636
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
1637
+ for (const vocabSchema of vocabSchemas) {
1638
+ try {
1639
+ ajv.addMetaSchema(vocabSchema);
1640
+ } catch {
1596
1641
  }
1597
- }).catch((error) => {
1598
- throw new Error(`Failed to load metaschemas: ${error}`);
1599
- });
1600
- }
1601
- return ajvInstance;
1642
+ }
1643
+ try {
1644
+ ajv.addMetaSchema(metaSchema);
1645
+ } catch {
1646
+ }
1647
+ }).catch((error) => {
1648
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
1649
+ });
1650
+ metaschemaReady.set(dialect, readyPromise);
1651
+ await readyPromise;
1652
+ return ajv;
1602
1653
  }
1603
1654
  async function compileSchema(schema, options = {}) {
1604
- const ajv = getAjv();
1605
- if (metaschemaReady) {
1606
- await metaschemaReady;
1607
- }
1608
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1609
- const cached = schemaCache.get(cacheKey);
1610
- if (cached !== void 0) {
1611
- return cached;
1612
- }
1655
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1613
1656
  let parsedSchema;
1614
1657
  if (typeof schema === "string") {
1615
1658
  try {
@@ -1627,18 +1670,27 @@ async function compileSchema(schema, options = {}) {
1627
1670
  } else {
1628
1671
  parsedSchema = schema;
1629
1672
  }
1673
+ const dialect = detectDialect(parsedSchema);
1674
+ const ajv = await getAjv(dialect);
1675
+ const cacheKey = `${dialect}:${baseKey}`;
1676
+ const cached = schemaCache.get(cacheKey);
1677
+ if (cached !== void 0) {
1678
+ return cached;
1679
+ }
1630
1680
  try {
1631
1681
  if (options.aliases && options.aliases.length > 0) {
1632
1682
  for (const alias of options.aliases) {
1633
1683
  if (alias && ajv.getSchema(alias) === void 0) {
1634
1684
  try {
1635
- ajv.addSchema(parsedSchema, alias);
1685
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
1686
+ ajv.addSchema(parsedSchema, alias);
1687
+ }
1636
1688
  } catch {
1637
1689
  }
1638
1690
  }
1639
1691
  }
1640
1692
  }
1641
- const validator = await ajv.compileAsync(parsedSchema);
1693
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
1642
1694
  schemaCache.set(cacheKey, validator);
1643
1695
  return validator;
1644
1696
  } catch (error) {
@@ -1708,8 +1760,39 @@ async function validateFile(filePath, validator) {
1708
1760
  }
1709
1761
  async function validateSchema(schema) {
1710
1762
  try {
1711
- const validator = await compileSchema(schema);
1712
- validateData({}, validator);
1763
+ let parsedSchema;
1764
+ if (typeof schema === "string") {
1765
+ try {
1766
+ parsedSchema = JSON.parse(schema);
1767
+ } catch {
1768
+ parsedSchema = parse(schema);
1769
+ }
1770
+ } else if (Buffer.isBuffer(schema)) {
1771
+ const content = schema.toString("utf-8");
1772
+ try {
1773
+ parsedSchema = JSON.parse(content);
1774
+ } catch {
1775
+ parsedSchema = parse(content);
1776
+ }
1777
+ } else {
1778
+ parsedSchema = schema;
1779
+ }
1780
+ const dialect = detectDialect(parsedSchema);
1781
+ const ajv = await getAjv(dialect);
1782
+ const metaValid = ajv.validateSchema(parsedSchema);
1783
+ if (!metaValid && ajv.errors) {
1784
+ const diagnostics = ajv.errors.map(
1785
+ (error) => createDiagnostic(
1786
+ error.instancePath || "",
1787
+ error.message || "Schema meta-validation failed",
1788
+ error.keyword || "unknown",
1789
+ "ERROR",
1790
+ "ajv"
1791
+ )
1792
+ );
1793
+ return { valid: false, diagnostics, source: "ajv" };
1794
+ }
1795
+ await compileSchema(parsedSchema);
1713
1796
  return {
1714
1797
  valid: true,
1715
1798
  diagnostics: [],
@@ -1780,14 +1863,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
1780
1863
  throw error;
1781
1864
  }
1782
1865
  }
1783
- var ajvInstance, metaschemaReady, schemaCache;
1866
+ var ajvInstances, metaschemaReady, schemaCache;
1784
1867
  var init_validator = __esm({
1785
1868
  "src/schema/validator.ts"() {
1786
1869
  init_telemetry();
1870
+ init_ajv_formats();
1787
1871
  init_errors();
1788
1872
  init_registry();
1789
1873
  init_utils();
1790
- metaschemaReady = null;
1874
+ ajvInstances = /* @__PURE__ */ new Map();
1875
+ metaschemaReady = /* @__PURE__ */ new Map();
1791
1876
  schemaCache = /* @__PURE__ */ new Map();
1792
1877
  }
1793
1878
  });
@@ -3742,6 +3827,224 @@ var init_capabilities2 = __esm({
3742
3827
  }
3743
3828
  });
3744
3829
 
3830
+ // src/foundry/signals/config-reload-endpoint.ts
3831
+ function createConfigReloadEndpoint(options) {
3832
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
3833
+ return async (payload, req) => {
3834
+ const correlationId = payload.correlation_id ?? generateCorrelationId();
3835
+ const authResult = await auth(req);
3836
+ if (!authResult.authenticated) {
3837
+ if (logger) {
3838
+ logger.warn("Config reload endpoint: authentication failed", {
3839
+ correlation_id: correlationId,
3840
+ reason: authResult.reason
3841
+ });
3842
+ }
3843
+ if (telemetry) {
3844
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
3845
+ correlation_id: correlationId
3846
+ });
3847
+ }
3848
+ return {
3849
+ status: "error",
3850
+ error: "authentication_failed",
3851
+ message: authResult.reason || "Authentication required",
3852
+ statusCode: 401
3853
+ };
3854
+ }
3855
+ const identity = authResult.identity || "unknown";
3856
+ if (rateLimit) {
3857
+ const rateLimitResult = await rateLimit(identity);
3858
+ if (!rateLimitResult.allowed) {
3859
+ if (logger) {
3860
+ logger.warn("Config reload endpoint: rate limit exceeded", {
3861
+ correlation_id: correlationId,
3862
+ identity
3863
+ });
3864
+ }
3865
+ if (telemetry) {
3866
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
3867
+ correlation_id: correlationId
3868
+ });
3869
+ }
3870
+ return {
3871
+ status: "error",
3872
+ error: "rate_limit_exceeded",
3873
+ message: "Rate limit exceeded. Please try again later.",
3874
+ statusCode: 429
3875
+ };
3876
+ }
3877
+ }
3878
+ if (telemetry) {
3879
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
3880
+ correlation_id: correlationId
3881
+ });
3882
+ }
3883
+ try {
3884
+ const config = await loader();
3885
+ if (validator) {
3886
+ const validation = await validator(config);
3887
+ if (!validation.valid) {
3888
+ if (logger) {
3889
+ logger.warn("Config reload endpoint: validation failed", {
3890
+ correlation_id: correlationId,
3891
+ error_count: validation.errors?.length ?? 0
3892
+ });
3893
+ }
3894
+ if (telemetry) {
3895
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
3896
+ correlation_id: correlationId,
3897
+ reason: "validation_failed"
3898
+ });
3899
+ }
3900
+ return {
3901
+ status: "error",
3902
+ error: "validation_failed",
3903
+ message: "Configuration validation failed",
3904
+ validation_errors: validation.errors,
3905
+ statusCode: 422
3906
+ };
3907
+ }
3908
+ }
3909
+ if (onReload2) {
3910
+ await onReload2(config);
3911
+ }
3912
+ if (telemetry) {
3913
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
3914
+ correlation_id: correlationId
3915
+ });
3916
+ }
3917
+ if (logger) {
3918
+ logger.info("Config reload endpoint: reload accepted", {
3919
+ correlation_id: correlationId,
3920
+ reason: payload.reason
3921
+ });
3922
+ }
3923
+ return {
3924
+ status: "reloaded",
3925
+ correlation_id: correlationId,
3926
+ message: "Configuration reloaded",
3927
+ statusCode: 200
3928
+ };
3929
+ } catch (error) {
3930
+ if (logger) {
3931
+ logger.warn("Config reload endpoint: reload failed", {
3932
+ correlation_id: correlationId,
3933
+ error: error instanceof Error ? error.message : String(error)
3934
+ });
3935
+ }
3936
+ if (telemetry) {
3937
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
3938
+ correlation_id: correlationId,
3939
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
3940
+ });
3941
+ }
3942
+ return {
3943
+ status: "error",
3944
+ error: "reload_failed",
3945
+ message: error instanceof Error ? error.message : String(error),
3946
+ statusCode: 500
3947
+ };
3948
+ }
3949
+ };
3950
+ }
3951
+ function generateCorrelationId() {
3952
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
3953
+ }
3954
+ var init_config_reload_endpoint = __esm({
3955
+ "src/foundry/signals/config-reload-endpoint.ts"() {
3956
+ }
3957
+ });
3958
+
3959
+ // src/appidentity/runtime.ts
3960
+ function detectRuntime() {
3961
+ const versions = process.versions;
3962
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
3963
+ return { name: "bun", version: versions.bun };
3964
+ }
3965
+ if (typeof versions.node === "string" && versions.node.length > 0) {
3966
+ return { name: "node", version: versions.node };
3967
+ }
3968
+ return { name: "unknown" };
3969
+ }
3970
+ function buildRuntimeInfo(options = {}) {
3971
+ const runtime = detectRuntime();
3972
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
3973
+ const vendor = options.vendor ?? options.identity?.app.vendor;
3974
+ return {
3975
+ service: {
3976
+ name: serviceName,
3977
+ vendor,
3978
+ version: options.version
3979
+ },
3980
+ runtime,
3981
+ platform: {
3982
+ os: process.platform,
3983
+ arch: process.arch
3984
+ }
3985
+ };
3986
+ }
3987
+ var init_runtime = __esm({
3988
+ "src/appidentity/runtime.ts"() {
3989
+ }
3990
+ });
3991
+
3992
+ // src/foundry/signals/control-discovery-endpoint.ts
3993
+ function createControlDiscoveryEndpoint(options) {
3994
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
3995
+ return async (req) => {
3996
+ if (auth) {
3997
+ const authResult = await auth(req);
3998
+ if (!authResult.authenticated) {
3999
+ if (logger) {
4000
+ logger.warn("Control discovery endpoint: authentication failed", {
4001
+ reason: authResult.reason
4002
+ });
4003
+ }
4004
+ if (telemetry) {
4005
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4006
+ service: identity.app.binary_name
4007
+ });
4008
+ }
4009
+ return {
4010
+ status: "error",
4011
+ error: "authentication_failed",
4012
+ message: authResult.reason || "Authentication required",
4013
+ statusCode: 401
4014
+ };
4015
+ }
4016
+ }
4017
+ if (telemetry) {
4018
+ telemetry.emit("fulmen.control.discovery.served", {
4019
+ service: identity.app.binary_name
4020
+ });
4021
+ }
4022
+ const runtime = buildRuntimeInfo({ identity, version });
4023
+ return {
4024
+ status: "ok",
4025
+ service: {
4026
+ name: identity.app.binary_name,
4027
+ vendor: identity.app.vendor,
4028
+ version
4029
+ },
4030
+ runtime: {
4031
+ name: runtime.runtime.name,
4032
+ version: runtime.runtime.version,
4033
+ platform: runtime.platform.os,
4034
+ arch: runtime.platform.arch
4035
+ },
4036
+ auth_summary: authSummary,
4037
+ endpoints,
4038
+ statusCode: 200
4039
+ };
4040
+ };
4041
+ }
4042
+ var init_control_discovery_endpoint = __esm({
4043
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4044
+ init_runtime();
4045
+ }
4046
+ });
4047
+
3745
4048
  // src/foundry/signals/convenience.ts
3746
4049
  async function onShutdown(manager, handler, options = {}) {
3747
4050
  await manager.register("SIGTERM", handler, options);
@@ -4001,7 +4304,7 @@ var init_guards = __esm({
4001
4304
  function createSignalEndpoint(options) {
4002
4305
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4003
4306
  return async (payload, req) => {
4004
- const correlationId = payload.correlation_id ?? generateCorrelationId();
4307
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
4005
4308
  const authResult = await auth(req);
4006
4309
  if (!authResult.authenticated) {
4007
4310
  if (logger) {
@@ -4124,7 +4427,7 @@ function normalizeSignalName(signal) {
4124
4427
  }
4125
4428
  return `SIG${upper}`;
4126
4429
  }
4127
- function generateCorrelationId() {
4430
+ function generateCorrelationId2() {
4128
4431
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4129
4432
  }
4130
4433
  function createBearerTokenAuth(expectedToken) {
@@ -4591,6 +4894,8 @@ var init_signals = __esm({
4591
4894
  "src/foundry/signals/index.ts"() {
4592
4895
  init_capabilities2();
4593
4896
  init_catalog();
4897
+ init_config_reload_endpoint();
4898
+ init_control_discovery_endpoint();
4594
4899
  init_convenience();
4595
4900
  init_double_tap();
4596
4901
  init_guards();
@@ -4736,7 +5041,9 @@ __export(foundry_exports, {
4736
5041
  clearMimeTypeCache: () => clearMimeTypeCache,
4737
5042
  clearPatternCache: () => clearPatternCache,
4738
5043
  createBearerTokenAuth: () => createBearerTokenAuth,
5044
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
4739
5045
  createConfigReloadHandler: () => createConfigReloadHandler,
5046
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
4740
5047
  createDoubleTapTracker: () => createDoubleTapTracker,
4741
5048
  createSignalEndpoint: () => createSignalEndpoint,
4742
5049
  createSignalManager: () => createSignalManager,
@@ -5768,6 +6075,7 @@ var init_cli = __esm({
5768
6075
  // src/schema/index.ts
5769
6076
  var init_schema = __esm({
5770
6077
  "src/schema/index.ts"() {
6078
+ init_ajv_formats();
5771
6079
  init_cli();
5772
6080
  init_errors();
5773
6081
  init_export();
@@ -5884,6 +6192,7 @@ async function getEnvVar(key, options) {
5884
6192
 
5885
6193
  // src/appidentity/index.ts
5886
6194
  init_loader2();
6195
+ init_runtime();
5887
6196
 
5888
6197
  // src/docscribe/normalize.ts
5889
6198
  var decoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
@@ -8050,8 +8359,8 @@ async function scanZip(archive, options) {
8050
8359
  var FULPACK_VERSION = "1.0.0";
8051
8360
 
8052
8361
  // src/index.ts
8053
- var VERSION2 = "0.2.0";
8362
+ var VERSION2 = "0.2.3";
8054
8363
 
8055
- export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, ArchiveFormat, DocScribeError, DocScribeParseError, DocScribeUnsupportedFormatError, ERROR_CODES, EntryType, FULPACK_VERSION, FulpackOperationError, MAX_ANCESTOR_SEARCH_DEPTH, Operation, VERSION2 as VERSION, __internal, buildEnvVar, checkDecompressionBomb, clearEmbeddedIdentity, clearIdentityCache, create, createFulpackError, detectFormat2 as detectFormat, extract, extractHeaders, extractMetadata, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, hasPathTraversal, info, inspectDocument, isAbsolutePath, loadIdentity, normalizeInput, parseFrontmatter, registerEmbeddedIdentity, scan, splitDocuments, stripFrontmatter, validatePath, verify };
8364
+ export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, ArchiveFormat, DocScribeError, DocScribeParseError, DocScribeUnsupportedFormatError, ERROR_CODES, EntryType, FULPACK_VERSION, FulpackOperationError, MAX_ANCESTOR_SEARCH_DEPTH, Operation, VERSION2 as VERSION, __internal, buildEnvVar, buildRuntimeInfo, checkDecompressionBomb, clearEmbeddedIdentity, clearIdentityCache, create, createFulpackError, detectFormat2 as detectFormat, extract, extractHeaders, extractMetadata, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, hasPathTraversal, info, inspectDocument, isAbsolutePath, loadIdentity, normalizeInput, parseFrontmatter, registerEmbeddedIdentity, scan, splitDocuments, stripFrontmatter, validatePath, verify };
8056
8365
  //# sourceMappingURL=index.js.map
8057
8366
  //# sourceMappingURL=index.js.map