@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,7 +1,7 @@
1
1
  import { b as FulmenErrorOptions, F as FulmenError } from '../fulmen-error-B_kX8jSC.js';
2
2
  import { L as Logger } from '../logger-Eycya5Mz.js';
3
3
  import { b as MetricsRegistry } from '../registry-_yEKhTTl.js';
4
- import { C as CompiledValidator, S as SchemaValidationResult } from '../types-BJswWpQC.js';
4
+ import { C as CompiledValidator, S as SchemaValidationResult } from '../types-DdoeE7F5.js';
5
5
 
6
6
  /**
7
7
  * Pathfinder types - Core type definitions for filesystem traversal
@@ -1,5 +1,6 @@
1
1
  import { crc32, xxhash128, createXXHash128, createCRC32 } from 'hash-wasm';
2
2
  import { createHash, randomUUID } from 'crypto';
3
+ import addFormats from 'ajv-formats';
3
4
  import { spawn } from 'child_process';
4
5
  import fs2, { readFile, writeFile, access, mkdir, lstat, realpath } from 'fs/promises';
5
6
  import { parse, stringify } from 'yaml';
@@ -7,7 +8,9 @@ import path3, { dirname, join, relative, extname, resolve } from 'path';
7
8
  import { fileURLToPath } from 'url';
8
9
  import glob from 'fast-glob';
9
10
  import Ajv from 'ajv';
10
- import addFormats from 'ajv-formats';
11
+ import Ajv2019 from 'ajv/dist/2019';
12
+ import Ajv2020 from 'ajv/dist/2020';
13
+ import AjvDraft04 from 'ajv-draft-04';
11
14
  import { Readable } from 'stream';
12
15
  import picomatch from 'picomatch';
13
16
  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';
@@ -406,6 +409,27 @@ var init_serialization = __esm({
406
409
  init_severity();
407
410
  }
408
411
  });
412
+ function applyFulmenAjvFormats(ajv, options = {}) {
413
+ const mode = options.mode ?? "fast";
414
+ const formats = options.formats ?? DEFAULT_FORMATS;
415
+ addFormats(ajv, { mode, formats });
416
+ return ajv;
417
+ }
418
+ var DEFAULT_FORMATS;
419
+ var init_ajv_formats = __esm({
420
+ "src/schema/ajv-formats.ts"() {
421
+ DEFAULT_FORMATS = [
422
+ "date-time",
423
+ "email",
424
+ "hostname",
425
+ "ipv4",
426
+ "ipv6",
427
+ "uri",
428
+ "uri-reference",
429
+ "uuid"
430
+ ];
431
+ }
432
+ });
409
433
 
410
434
  // src/schema/errors.ts
411
435
  var errors_exports = {};
@@ -1858,20 +1882,14 @@ async function loadMetaSchema(draft) {
1858
1882
  const content = await readFile(metaSchemaPath, "utf-8");
1859
1883
  return JSON.parse(content);
1860
1884
  }
1861
- async function loadVocabularySchemas() {
1885
+ async function loadVocabularySchemas(draft) {
1886
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
1887
+ return [];
1888
+ }
1862
1889
  const __filename3 = fileURLToPath(import.meta.url);
1863
1890
  const __dirname4 = dirname(__filename3);
1864
- const vocabDir = join(
1865
- __dirname4,
1866
- "..",
1867
- "..",
1868
- "schemas",
1869
- "crucible-ts",
1870
- "meta",
1871
- "draft-2020-12",
1872
- "meta"
1873
- );
1874
- const vocabFiles = [
1891
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
1892
+ const vocabFiles = draft === "draft-2020-12" ? [
1875
1893
  "core.json",
1876
1894
  "applicator.json",
1877
1895
  "unevaluated.json",
@@ -1879,6 +1897,13 @@ async function loadVocabularySchemas() {
1879
1897
  "meta-data.json",
1880
1898
  "format-annotation.json",
1881
1899
  "content.json"
1900
+ ] : [
1901
+ "core.json",
1902
+ "applicator.json",
1903
+ "validation.json",
1904
+ "meta-data.json",
1905
+ "format.json",
1906
+ "content.json"
1882
1907
  ];
1883
1908
  const schemas = [];
1884
1909
  for (const file of vocabFiles) {
@@ -1933,47 +1958,65 @@ async function loadReferencedSchema(uri) {
1933
1958
  }
1934
1959
  return JSON.parse(content);
1935
1960
  }
1936
- function getAjv() {
1937
- if (!ajvInstance) {
1938
- ajvInstance = new Ajv({
1939
- strict: false,
1940
- allErrors: true,
1941
- verbose: true,
1942
- // Allow schemas with $id to be added without replacing existing ones
1943
- addUsedSchema: false,
1944
- // Enable async schema loading for YAML references
1945
- loadSchema: loadReferencedSchema
1946
- });
1947
- addFormats(ajvInstance, {
1948
- mode: "fast",
1949
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
1950
- });
1951
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
1952
- if (ajvInstance) {
1953
- for (const vocabSchema of vocabSchemas) {
1954
- try {
1955
- ajvInstance.addMetaSchema(vocabSchema);
1956
- } catch {
1957
- }
1958
- }
1959
- ajvInstance.addMetaSchema(metaSchema);
1961
+ function detectDialect(schema) {
1962
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
1963
+ const maybeSchema = schema;
1964
+ const declared = maybeSchema.$schema;
1965
+ if (typeof declared === "string") {
1966
+ if (declared.includes("draft-04")) return "draft-04";
1967
+ if (declared.includes("draft-06")) return "draft-06";
1968
+ if (declared.includes("draft-07")) return "draft-07";
1969
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
1970
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
1971
+ }
1972
+ }
1973
+ return "draft-2020-12";
1974
+ }
1975
+ function createAjv(dialect) {
1976
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
1977
+ const ajv = new AjvCtor({
1978
+ strict: false,
1979
+ allErrors: true,
1980
+ verbose: true,
1981
+ // Allow schemas with $id to be added without replacing existing ones
1982
+ addUsedSchema: false,
1983
+ // draft-04 uses "id"; later drafts use "$id"
1984
+ schemaId: dialect === "draft-04" ? "id" : "$id",
1985
+ // Enable async schema loading for YAML references
1986
+ loadSchema: loadReferencedSchema
1987
+ });
1988
+ applyFulmenAjvFormats(ajv);
1989
+ return ajv;
1990
+ }
1991
+ async function getAjv(dialect) {
1992
+ const existing = ajvInstances.get(dialect);
1993
+ if (existing) {
1994
+ const ready = metaschemaReady.get(dialect);
1995
+ if (ready) await ready;
1996
+ return existing;
1997
+ }
1998
+ const ajv = createAjv(dialect);
1999
+ ajvInstances.set(dialect, ajv);
2000
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
2001
+ for (const vocabSchema of vocabSchemas) {
2002
+ try {
2003
+ ajv.addMetaSchema(vocabSchema);
2004
+ } catch {
1960
2005
  }
1961
- }).catch((error) => {
1962
- throw new Error(`Failed to load metaschemas: ${error}`);
1963
- });
1964
- }
1965
- return ajvInstance;
2006
+ }
2007
+ try {
2008
+ ajv.addMetaSchema(metaSchema);
2009
+ } catch {
2010
+ }
2011
+ }).catch((error) => {
2012
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
2013
+ });
2014
+ metaschemaReady.set(dialect, readyPromise);
2015
+ await readyPromise;
2016
+ return ajv;
1966
2017
  }
1967
2018
  async function compileSchema(schema, options = {}) {
1968
- const ajv = getAjv();
1969
- if (metaschemaReady) {
1970
- await metaschemaReady;
1971
- }
1972
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1973
- const cached = schemaCache.get(cacheKey);
1974
- if (cached !== void 0) {
1975
- return cached;
1976
- }
2019
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
1977
2020
  let parsedSchema;
1978
2021
  if (typeof schema === "string") {
1979
2022
  try {
@@ -1991,18 +2034,27 @@ async function compileSchema(schema, options = {}) {
1991
2034
  } else {
1992
2035
  parsedSchema = schema;
1993
2036
  }
2037
+ const dialect = detectDialect(parsedSchema);
2038
+ const ajv = await getAjv(dialect);
2039
+ const cacheKey = `${dialect}:${baseKey}`;
2040
+ const cached = schemaCache.get(cacheKey);
2041
+ if (cached !== void 0) {
2042
+ return cached;
2043
+ }
1994
2044
  try {
1995
2045
  if (options.aliases && options.aliases.length > 0) {
1996
2046
  for (const alias of options.aliases) {
1997
2047
  if (alias && ajv.getSchema(alias) === void 0) {
1998
2048
  try {
1999
- ajv.addSchema(parsedSchema, alias);
2049
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
2050
+ ajv.addSchema(parsedSchema, alias);
2051
+ }
2000
2052
  } catch {
2001
2053
  }
2002
2054
  }
2003
2055
  }
2004
2056
  }
2005
- const validator = await ajv.compileAsync(parsedSchema);
2057
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
2006
2058
  schemaCache.set(cacheKey, validator);
2007
2059
  return validator;
2008
2060
  } catch (error) {
@@ -2072,8 +2124,39 @@ async function validateFile(filePath, validator) {
2072
2124
  }
2073
2125
  async function validateSchema(schema) {
2074
2126
  try {
2075
- const validator = await compileSchema(schema);
2076
- validateData({}, validator);
2127
+ let parsedSchema;
2128
+ if (typeof schema === "string") {
2129
+ try {
2130
+ parsedSchema = JSON.parse(schema);
2131
+ } catch {
2132
+ parsedSchema = parse(schema);
2133
+ }
2134
+ } else if (Buffer.isBuffer(schema)) {
2135
+ const content = schema.toString("utf-8");
2136
+ try {
2137
+ parsedSchema = JSON.parse(content);
2138
+ } catch {
2139
+ parsedSchema = parse(content);
2140
+ }
2141
+ } else {
2142
+ parsedSchema = schema;
2143
+ }
2144
+ const dialect = detectDialect(parsedSchema);
2145
+ const ajv = await getAjv(dialect);
2146
+ const metaValid = ajv.validateSchema(parsedSchema);
2147
+ if (!metaValid && ajv.errors) {
2148
+ const diagnostics = ajv.errors.map(
2149
+ (error) => createDiagnostic(
2150
+ error.instancePath || "",
2151
+ error.message || "Schema meta-validation failed",
2152
+ error.keyword || "unknown",
2153
+ "ERROR",
2154
+ "ajv"
2155
+ )
2156
+ );
2157
+ return { valid: false, diagnostics, source: "ajv" };
2158
+ }
2159
+ await compileSchema(parsedSchema);
2077
2160
  return {
2078
2161
  valid: true,
2079
2162
  diagnostics: [],
@@ -2144,14 +2227,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
2144
2227
  throw error;
2145
2228
  }
2146
2229
  }
2147
- var ajvInstance, metaschemaReady, schemaCache;
2230
+ var ajvInstances, metaschemaReady, schemaCache;
2148
2231
  var init_validator = __esm({
2149
2232
  "src/schema/validator.ts"() {
2150
2233
  init_telemetry();
2234
+ init_ajv_formats();
2151
2235
  init_errors2();
2152
2236
  init_registry();
2153
2237
  init_utils();
2154
- metaschemaReady = null;
2238
+ ajvInstances = /* @__PURE__ */ new Map();
2239
+ metaschemaReady = /* @__PURE__ */ new Map();
2155
2240
  schemaCache = /* @__PURE__ */ new Map();
2156
2241
  }
2157
2242
  });
@@ -4106,6 +4191,224 @@ var init_capabilities2 = __esm({
4106
4191
  }
4107
4192
  });
4108
4193
 
4194
+ // src/foundry/signals/config-reload-endpoint.ts
4195
+ function createConfigReloadEndpoint(options) {
4196
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
4197
+ return async (payload, req) => {
4198
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
4199
+ const authResult = await auth(req);
4200
+ if (!authResult.authenticated) {
4201
+ if (logger) {
4202
+ logger.warn("Config reload endpoint: authentication failed", {
4203
+ correlation_id: correlationId,
4204
+ reason: authResult.reason
4205
+ });
4206
+ }
4207
+ if (telemetry) {
4208
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
4209
+ correlation_id: correlationId
4210
+ });
4211
+ }
4212
+ return {
4213
+ status: "error",
4214
+ error: "authentication_failed",
4215
+ message: authResult.reason || "Authentication required",
4216
+ statusCode: 401
4217
+ };
4218
+ }
4219
+ const identity = authResult.identity || "unknown";
4220
+ if (rateLimit) {
4221
+ const rateLimitResult = await rateLimit(identity);
4222
+ if (!rateLimitResult.allowed) {
4223
+ if (logger) {
4224
+ logger.warn("Config reload endpoint: rate limit exceeded", {
4225
+ correlation_id: correlationId,
4226
+ identity
4227
+ });
4228
+ }
4229
+ if (telemetry) {
4230
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
4231
+ correlation_id: correlationId
4232
+ });
4233
+ }
4234
+ return {
4235
+ status: "error",
4236
+ error: "rate_limit_exceeded",
4237
+ message: "Rate limit exceeded. Please try again later.",
4238
+ statusCode: 429
4239
+ };
4240
+ }
4241
+ }
4242
+ if (telemetry) {
4243
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
4244
+ correlation_id: correlationId
4245
+ });
4246
+ }
4247
+ try {
4248
+ const config = await loader();
4249
+ if (validator) {
4250
+ const validation = await validator(config);
4251
+ if (!validation.valid) {
4252
+ if (logger) {
4253
+ logger.warn("Config reload endpoint: validation failed", {
4254
+ correlation_id: correlationId,
4255
+ error_count: validation.errors?.length ?? 0
4256
+ });
4257
+ }
4258
+ if (telemetry) {
4259
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
4260
+ correlation_id: correlationId,
4261
+ reason: "validation_failed"
4262
+ });
4263
+ }
4264
+ return {
4265
+ status: "error",
4266
+ error: "validation_failed",
4267
+ message: "Configuration validation failed",
4268
+ validation_errors: validation.errors,
4269
+ statusCode: 422
4270
+ };
4271
+ }
4272
+ }
4273
+ if (onReload2) {
4274
+ await onReload2(config);
4275
+ }
4276
+ if (telemetry) {
4277
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
4278
+ correlation_id: correlationId
4279
+ });
4280
+ }
4281
+ if (logger) {
4282
+ logger.info("Config reload endpoint: reload accepted", {
4283
+ correlation_id: correlationId,
4284
+ reason: payload.reason
4285
+ });
4286
+ }
4287
+ return {
4288
+ status: "reloaded",
4289
+ correlation_id: correlationId,
4290
+ message: "Configuration reloaded",
4291
+ statusCode: 200
4292
+ };
4293
+ } catch (error) {
4294
+ if (logger) {
4295
+ logger.warn("Config reload endpoint: reload failed", {
4296
+ correlation_id: correlationId,
4297
+ error: error instanceof Error ? error.message : String(error)
4298
+ });
4299
+ }
4300
+ if (telemetry) {
4301
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
4302
+ correlation_id: correlationId,
4303
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
4304
+ });
4305
+ }
4306
+ return {
4307
+ status: "error",
4308
+ error: "reload_failed",
4309
+ message: error instanceof Error ? error.message : String(error),
4310
+ statusCode: 500
4311
+ };
4312
+ }
4313
+ };
4314
+ }
4315
+ function generateCorrelationId2() {
4316
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4317
+ }
4318
+ var init_config_reload_endpoint = __esm({
4319
+ "src/foundry/signals/config-reload-endpoint.ts"() {
4320
+ }
4321
+ });
4322
+
4323
+ // src/appidentity/runtime.ts
4324
+ function detectRuntime() {
4325
+ const versions = process.versions;
4326
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
4327
+ return { name: "bun", version: versions.bun };
4328
+ }
4329
+ if (typeof versions.node === "string" && versions.node.length > 0) {
4330
+ return { name: "node", version: versions.node };
4331
+ }
4332
+ return { name: "unknown" };
4333
+ }
4334
+ function buildRuntimeInfo(options = {}) {
4335
+ const runtime = detectRuntime();
4336
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
4337
+ const vendor = options.vendor ?? options.identity?.app.vendor;
4338
+ return {
4339
+ service: {
4340
+ name: serviceName,
4341
+ vendor,
4342
+ version: options.version
4343
+ },
4344
+ runtime,
4345
+ platform: {
4346
+ os: process.platform,
4347
+ arch: process.arch
4348
+ }
4349
+ };
4350
+ }
4351
+ var init_runtime = __esm({
4352
+ "src/appidentity/runtime.ts"() {
4353
+ }
4354
+ });
4355
+
4356
+ // src/foundry/signals/control-discovery-endpoint.ts
4357
+ function createControlDiscoveryEndpoint(options) {
4358
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
4359
+ return async (req) => {
4360
+ if (auth) {
4361
+ const authResult = await auth(req);
4362
+ if (!authResult.authenticated) {
4363
+ if (logger) {
4364
+ logger.warn("Control discovery endpoint: authentication failed", {
4365
+ reason: authResult.reason
4366
+ });
4367
+ }
4368
+ if (telemetry) {
4369
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
4370
+ service: identity.app.binary_name
4371
+ });
4372
+ }
4373
+ return {
4374
+ status: "error",
4375
+ error: "authentication_failed",
4376
+ message: authResult.reason || "Authentication required",
4377
+ statusCode: 401
4378
+ };
4379
+ }
4380
+ }
4381
+ if (telemetry) {
4382
+ telemetry.emit("fulmen.control.discovery.served", {
4383
+ service: identity.app.binary_name
4384
+ });
4385
+ }
4386
+ const runtime = buildRuntimeInfo({ identity, version });
4387
+ return {
4388
+ status: "ok",
4389
+ service: {
4390
+ name: identity.app.binary_name,
4391
+ vendor: identity.app.vendor,
4392
+ version
4393
+ },
4394
+ runtime: {
4395
+ name: runtime.runtime.name,
4396
+ version: runtime.runtime.version,
4397
+ platform: runtime.platform.os,
4398
+ arch: runtime.platform.arch
4399
+ },
4400
+ auth_summary: authSummary,
4401
+ endpoints,
4402
+ statusCode: 200
4403
+ };
4404
+ };
4405
+ }
4406
+ var init_control_discovery_endpoint = __esm({
4407
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
4408
+ init_runtime();
4409
+ }
4410
+ });
4411
+
4109
4412
  // src/foundry/signals/convenience.ts
4110
4413
  async function onShutdown(manager, handler, options = {}) {
4111
4414
  await manager.register("SIGTERM", handler, options);
@@ -4365,7 +4668,7 @@ var init_guards = __esm({
4365
4668
  function createSignalEndpoint(options) {
4366
4669
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
4367
4670
  return async (payload, req) => {
4368
- const correlationId = payload.correlation_id ?? generateCorrelationId2();
4671
+ const correlationId = payload.correlation_id ?? generateCorrelationId3();
4369
4672
  const authResult = await auth(req);
4370
4673
  if (!authResult.authenticated) {
4371
4674
  if (logger) {
@@ -4488,7 +4791,7 @@ function normalizeSignalName(signal) {
4488
4791
  }
4489
4792
  return `SIG${upper}`;
4490
4793
  }
4491
- function generateCorrelationId2() {
4794
+ function generateCorrelationId3() {
4492
4795
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
4493
4796
  }
4494
4797
  function createBearerTokenAuth(expectedToken) {
@@ -4955,6 +5258,8 @@ var init_signals = __esm({
4955
5258
  "src/foundry/signals/index.ts"() {
4956
5259
  init_capabilities2();
4957
5260
  init_catalog();
5261
+ init_config_reload_endpoint();
5262
+ init_control_discovery_endpoint();
4958
5263
  init_convenience();
4959
5264
  init_double_tap();
4960
5265
  init_guards();
@@ -5100,7 +5405,9 @@ __export(foundry_exports, {
5100
5405
  clearMimeTypeCache: () => clearMimeTypeCache,
5101
5406
  clearPatternCache: () => clearPatternCache,
5102
5407
  createBearerTokenAuth: () => createBearerTokenAuth,
5408
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
5103
5409
  createConfigReloadHandler: () => createConfigReloadHandler,
5410
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
5104
5411
  createDoubleTapTracker: () => createDoubleTapTracker,
5105
5412
  createSignalEndpoint: () => createSignalEndpoint,
5106
5413
  createSignalManager: () => createSignalManager,
@@ -5823,6 +6130,7 @@ var init_cli = __esm({
5823
6130
  // src/schema/index.ts
5824
6131
  var init_schema = __esm({
5825
6132
  "src/schema/index.ts"() {
6133
+ init_ajv_formats();
5826
6134
  init_cli();
5827
6135
  init_errors2();
5828
6136
  init_export();