@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,6 +1,7 @@
1
1
  import { access, readFile, mkdir, writeFile } from 'fs/promises';
2
2
  import { dirname, extname, join, relative } from 'path';
3
3
  import { parse, stringify } from 'yaml';
4
+ import addFormats from 'ajv-formats';
4
5
  import { spawn } from 'child_process';
5
6
  import { fileURLToPath } from 'url';
6
7
  import glob from 'fast-glob';
@@ -10,7 +11,9 @@ import { suggest as suggest$1, substringSimilarity, score as score$1, normalize
10
11
  import 'crypto';
11
12
  import { Command } from 'commander';
12
13
  import Ajv from 'ajv';
13
- import addFormats from 'ajv-formats';
14
+ import Ajv2019 from 'ajv/dist/2019';
15
+ import Ajv2020 from 'ajv/dist/2020';
16
+ import AjvDraft04 from 'ajv-draft-04';
14
17
 
15
18
  var __defProp = Object.defineProperty;
16
19
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -726,6 +729,27 @@ var init_types = __esm({
726
729
  "src/telemetry/types.ts"() {
727
730
  }
728
731
  });
732
+ function applyFulmenAjvFormats(ajv, options = {}) {
733
+ const mode = options.mode ?? "fast";
734
+ const formats = options.formats ?? DEFAULT_FORMATS;
735
+ addFormats(ajv, { mode, formats });
736
+ return ajv;
737
+ }
738
+ var DEFAULT_FORMATS;
739
+ var init_ajv_formats = __esm({
740
+ "src/schema/ajv-formats.ts"() {
741
+ DEFAULT_FORMATS = [
742
+ "date-time",
743
+ "email",
744
+ "hostname",
745
+ "ipv4",
746
+ "ipv6",
747
+ "uri",
748
+ "uri-reference",
749
+ "uuid"
750
+ ];
751
+ }
752
+ });
729
753
 
730
754
  // src/schema/errors.ts
731
755
  var errors_exports = {};
@@ -3291,7 +3315,9 @@ __export(foundry_exports, {
3291
3315
  clearMimeTypeCache: () => clearMimeTypeCache,
3292
3316
  clearPatternCache: () => clearPatternCache,
3293
3317
  createBearerTokenAuth: () => createBearerTokenAuth,
3318
+ createConfigReloadEndpoint: () => createConfigReloadEndpoint,
3294
3319
  createConfigReloadHandler: () => createConfigReloadHandler,
3320
+ createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
3295
3321
  createDoubleTapTracker: () => createDoubleTapTracker,
3296
3322
  createSignalEndpoint: () => createSignalEndpoint,
3297
3323
  createSignalManager: () => createSignalManager,
@@ -4347,6 +4373,7 @@ var init_cli = __esm({
4347
4373
  // src/schema/index.ts
4348
4374
  var init_schema = __esm({
4349
4375
  "src/schema/index.ts"() {
4376
+ init_ajv_formats();
4350
4377
  init_cli();
4351
4378
  init_errors();
4352
4379
  init_export();
@@ -4410,20 +4437,14 @@ async function loadMetaSchema(draft) {
4410
4437
  const content = await readFile(metaSchemaPath, "utf-8");
4411
4438
  return JSON.parse(content);
4412
4439
  }
4413
- async function loadVocabularySchemas() {
4440
+ async function loadVocabularySchemas(draft) {
4441
+ if (draft !== "draft-2019-09" && draft !== "draft-2020-12") {
4442
+ return [];
4443
+ }
4414
4444
  const __filename3 = fileURLToPath(import.meta.url);
4415
4445
  const __dirname4 = dirname(__filename3);
4416
- const vocabDir = join(
4417
- __dirname4,
4418
- "..",
4419
- "..",
4420
- "schemas",
4421
- "crucible-ts",
4422
- "meta",
4423
- "draft-2020-12",
4424
- "meta"
4425
- );
4426
- const vocabFiles = [
4446
+ const vocabDir = join(__dirname4, "..", "..", "schemas", "crucible-ts", "meta", draft, "meta");
4447
+ const vocabFiles = draft === "draft-2020-12" ? [
4427
4448
  "core.json",
4428
4449
  "applicator.json",
4429
4450
  "unevaluated.json",
@@ -4431,6 +4452,13 @@ async function loadVocabularySchemas() {
4431
4452
  "meta-data.json",
4432
4453
  "format-annotation.json",
4433
4454
  "content.json"
4455
+ ] : [
4456
+ "core.json",
4457
+ "applicator.json",
4458
+ "validation.json",
4459
+ "meta-data.json",
4460
+ "format.json",
4461
+ "content.json"
4434
4462
  ];
4435
4463
  const schemas = [];
4436
4464
  for (const file of vocabFiles) {
@@ -4485,47 +4513,65 @@ async function loadReferencedSchema(uri) {
4485
4513
  }
4486
4514
  return JSON.parse(content);
4487
4515
  }
4488
- function getAjv() {
4489
- if (!ajvInstance) {
4490
- ajvInstance = new Ajv({
4491
- strict: false,
4492
- allErrors: true,
4493
- verbose: true,
4494
- // Allow schemas with $id to be added without replacing existing ones
4495
- addUsedSchema: false,
4496
- // Enable async schema loading for YAML references
4497
- loadSchema: loadReferencedSchema
4498
- });
4499
- addFormats(ajvInstance, {
4500
- mode: "fast",
4501
- formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
4502
- });
4503
- metaschemaReady = Promise.all([loadVocabularySchemas(), loadMetaSchema("draft-2020-12")]).then(([vocabSchemas, metaSchema]) => {
4504
- if (ajvInstance) {
4505
- for (const vocabSchema of vocabSchemas) {
4506
- try {
4507
- ajvInstance.addMetaSchema(vocabSchema);
4508
- } catch {
4509
- }
4510
- }
4511
- ajvInstance.addMetaSchema(metaSchema);
4512
- }
4513
- }).catch((error) => {
4514
- throw new Error(`Failed to load metaschemas: ${error}`);
4515
- });
4516
+ function detectDialect(schema) {
4517
+ if (schema && typeof schema === "object" && !Array.isArray(schema)) {
4518
+ const maybeSchema = schema;
4519
+ const declared = maybeSchema.$schema;
4520
+ if (typeof declared === "string") {
4521
+ if (declared.includes("draft-04")) return "draft-04";
4522
+ if (declared.includes("draft-06")) return "draft-06";
4523
+ if (declared.includes("draft-07")) return "draft-07";
4524
+ if (declared.includes("draft/2019-09")) return "draft-2019-09";
4525
+ if (declared.includes("draft/2020-12")) return "draft-2020-12";
4526
+ }
4516
4527
  }
4517
- return ajvInstance;
4528
+ return "draft-2020-12";
4529
+ }
4530
+ function createAjv(dialect) {
4531
+ const AjvCtor = dialect === "draft-2020-12" ? Ajv2020 : dialect === "draft-2019-09" ? Ajv2019 : dialect === "draft-04" ? AjvDraft04 : Ajv;
4532
+ const ajv = new AjvCtor({
4533
+ strict: false,
4534
+ allErrors: true,
4535
+ verbose: true,
4536
+ // Allow schemas with $id to be added without replacing existing ones
4537
+ addUsedSchema: false,
4538
+ // draft-04 uses "id"; later drafts use "$id"
4539
+ schemaId: dialect === "draft-04" ? "id" : "$id",
4540
+ // Enable async schema loading for YAML references
4541
+ loadSchema: loadReferencedSchema
4542
+ });
4543
+ applyFulmenAjvFormats(ajv);
4544
+ return ajv;
4545
+ }
4546
+ async function getAjv(dialect) {
4547
+ const existing = ajvInstances.get(dialect);
4548
+ if (existing) {
4549
+ const ready = metaschemaReady.get(dialect);
4550
+ if (ready) await ready;
4551
+ return existing;
4552
+ }
4553
+ const ajv = createAjv(dialect);
4554
+ ajvInstances.set(dialect, ajv);
4555
+ const readyPromise = Promise.all([loadVocabularySchemas(dialect), loadMetaSchema(dialect)]).then(([vocabSchemas, metaSchema]) => {
4556
+ for (const vocabSchema of vocabSchemas) {
4557
+ try {
4558
+ ajv.addMetaSchema(vocabSchema);
4559
+ } catch {
4560
+ }
4561
+ }
4562
+ try {
4563
+ ajv.addMetaSchema(metaSchema);
4564
+ } catch {
4565
+ }
4566
+ }).catch((error) => {
4567
+ throw new Error(`Failed to load metaschemas (${dialect}): ${error}`);
4568
+ });
4569
+ metaschemaReady.set(dialect, readyPromise);
4570
+ await readyPromise;
4571
+ return ajv;
4518
4572
  }
4519
4573
  async function compileSchema(schema, options = {}) {
4520
- const ajv = getAjv();
4521
- if (metaschemaReady) {
4522
- await metaschemaReady;
4523
- }
4524
- const cacheKey = typeof schema === "string" ? schema : JSON.stringify(schema);
4525
- const cached = schemaCache.get(cacheKey);
4526
- if (cached !== void 0) {
4527
- return cached;
4528
- }
4574
+ const baseKey = typeof schema === "string" ? schema : JSON.stringify(schema);
4529
4575
  let parsedSchema;
4530
4576
  if (typeof schema === "string") {
4531
4577
  try {
@@ -4543,18 +4589,27 @@ async function compileSchema(schema, options = {}) {
4543
4589
  } else {
4544
4590
  parsedSchema = schema;
4545
4591
  }
4592
+ const dialect = detectDialect(parsedSchema);
4593
+ const ajv = await getAjv(dialect);
4594
+ const cacheKey = `${dialect}:${baseKey}`;
4595
+ const cached = schemaCache.get(cacheKey);
4596
+ if (cached !== void 0) {
4597
+ return cached;
4598
+ }
4546
4599
  try {
4547
4600
  if (options.aliases && options.aliases.length > 0) {
4548
4601
  for (const alias of options.aliases) {
4549
4602
  if (alias && ajv.getSchema(alias) === void 0) {
4550
4603
  try {
4551
- ajv.addSchema(parsedSchema, alias);
4604
+ if (typeof parsedSchema === "object" && parsedSchema !== null) {
4605
+ ajv.addSchema(parsedSchema, alias);
4606
+ }
4552
4607
  } catch {
4553
4608
  }
4554
4609
  }
4555
4610
  }
4556
4611
  }
4557
- const validator = await ajv.compileAsync(parsedSchema);
4612
+ const validator = typeof parsedSchema === "boolean" ? ajv.compile(parsedSchema) : await ajv.compileAsync(parsedSchema);
4558
4613
  schemaCache.set(cacheKey, validator);
4559
4614
  return validator;
4560
4615
  } catch (error) {
@@ -4624,8 +4679,39 @@ async function validateFile(filePath, validator) {
4624
4679
  }
4625
4680
  async function validateSchema(schema) {
4626
4681
  try {
4627
- const validator = await compileSchema(schema);
4628
- validateData({}, validator);
4682
+ let parsedSchema;
4683
+ if (typeof schema === "string") {
4684
+ try {
4685
+ parsedSchema = JSON.parse(schema);
4686
+ } catch {
4687
+ parsedSchema = parse(schema);
4688
+ }
4689
+ } else if (Buffer.isBuffer(schema)) {
4690
+ const content = schema.toString("utf-8");
4691
+ try {
4692
+ parsedSchema = JSON.parse(content);
4693
+ } catch {
4694
+ parsedSchema = parse(content);
4695
+ }
4696
+ } else {
4697
+ parsedSchema = schema;
4698
+ }
4699
+ const dialect = detectDialect(parsedSchema);
4700
+ const ajv = await getAjv(dialect);
4701
+ const metaValid = ajv.validateSchema(parsedSchema);
4702
+ if (!metaValid && ajv.errors) {
4703
+ const diagnostics = ajv.errors.map(
4704
+ (error) => createDiagnostic(
4705
+ error.instancePath || "",
4706
+ error.message || "Schema meta-validation failed",
4707
+ error.keyword || "unknown",
4708
+ "ERROR",
4709
+ "ajv"
4710
+ )
4711
+ );
4712
+ return { valid: false, diagnostics, source: "ajv" };
4713
+ }
4714
+ await compileSchema(parsedSchema);
4629
4715
  return {
4630
4716
  valid: true,
4631
4717
  diagnostics: [],
@@ -4696,14 +4782,16 @@ async function validateFileBySchemaId(filePath, schemaId, registryOptions) {
4696
4782
  throw error;
4697
4783
  }
4698
4784
  }
4699
- var ajvInstance, metaschemaReady, schemaCache;
4785
+ var ajvInstances, metaschemaReady, schemaCache;
4700
4786
  var init_validator = __esm({
4701
4787
  "src/schema/validator.ts"() {
4702
4788
  init_telemetry();
4789
+ init_ajv_formats();
4703
4790
  init_errors();
4704
4791
  init_registry2();
4705
4792
  init_utils();
4706
- metaschemaReady = null;
4793
+ ajvInstances = /* @__PURE__ */ new Map();
4794
+ metaschemaReady = /* @__PURE__ */ new Map();
4707
4795
  schemaCache = /* @__PURE__ */ new Map();
4708
4796
  }
4709
4797
  });
@@ -4923,6 +5011,224 @@ var init_capabilities2 = __esm({
4923
5011
  }
4924
5012
  });
4925
5013
 
5014
+ // src/foundry/signals/config-reload-endpoint.ts
5015
+ function createConfigReloadEndpoint(options) {
5016
+ const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
5017
+ return async (payload, req) => {
5018
+ const correlationId = payload.correlation_id ?? generateCorrelationId2();
5019
+ const authResult = await auth(req);
5020
+ if (!authResult.authenticated) {
5021
+ if (logger) {
5022
+ logger.warn("Config reload endpoint: authentication failed", {
5023
+ correlation_id: correlationId,
5024
+ reason: authResult.reason
5025
+ });
5026
+ }
5027
+ if (telemetry) {
5028
+ telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
5029
+ correlation_id: correlationId
5030
+ });
5031
+ }
5032
+ return {
5033
+ status: "error",
5034
+ error: "authentication_failed",
5035
+ message: authResult.reason || "Authentication required",
5036
+ statusCode: 401
5037
+ };
5038
+ }
5039
+ const identity = authResult.identity || "unknown";
5040
+ if (rateLimit) {
5041
+ const rateLimitResult = await rateLimit(identity);
5042
+ if (!rateLimitResult.allowed) {
5043
+ if (logger) {
5044
+ logger.warn("Config reload endpoint: rate limit exceeded", {
5045
+ correlation_id: correlationId,
5046
+ identity
5047
+ });
5048
+ }
5049
+ if (telemetry) {
5050
+ telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
5051
+ correlation_id: correlationId
5052
+ });
5053
+ }
5054
+ return {
5055
+ status: "error",
5056
+ error: "rate_limit_exceeded",
5057
+ message: "Rate limit exceeded. Please try again later.",
5058
+ statusCode: 429
5059
+ };
5060
+ }
5061
+ }
5062
+ if (telemetry) {
5063
+ telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
5064
+ correlation_id: correlationId
5065
+ });
5066
+ }
5067
+ try {
5068
+ const config = await loader();
5069
+ if (validator) {
5070
+ const validation = await validator(config);
5071
+ if (!validation.valid) {
5072
+ if (logger) {
5073
+ logger.warn("Config reload endpoint: validation failed", {
5074
+ correlation_id: correlationId,
5075
+ error_count: validation.errors?.length ?? 0
5076
+ });
5077
+ }
5078
+ if (telemetry) {
5079
+ telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
5080
+ correlation_id: correlationId,
5081
+ reason: "validation_failed"
5082
+ });
5083
+ }
5084
+ return {
5085
+ status: "error",
5086
+ error: "validation_failed",
5087
+ message: "Configuration validation failed",
5088
+ validation_errors: validation.errors,
5089
+ statusCode: 422
5090
+ };
5091
+ }
5092
+ }
5093
+ if (onReload2) {
5094
+ await onReload2(config);
5095
+ }
5096
+ if (telemetry) {
5097
+ telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
5098
+ correlation_id: correlationId
5099
+ });
5100
+ }
5101
+ if (logger) {
5102
+ logger.info("Config reload endpoint: reload accepted", {
5103
+ correlation_id: correlationId,
5104
+ reason: payload.reason
5105
+ });
5106
+ }
5107
+ return {
5108
+ status: "reloaded",
5109
+ correlation_id: correlationId,
5110
+ message: "Configuration reloaded",
5111
+ statusCode: 200
5112
+ };
5113
+ } catch (error) {
5114
+ if (logger) {
5115
+ logger.warn("Config reload endpoint: reload failed", {
5116
+ correlation_id: correlationId,
5117
+ error: error instanceof Error ? error.message : String(error)
5118
+ });
5119
+ }
5120
+ if (telemetry) {
5121
+ telemetry.emit("fulmen.config.http_endpoint.reload_error", {
5122
+ correlation_id: correlationId,
5123
+ error_type: error instanceof Error ? error.constructor.name : "unknown"
5124
+ });
5125
+ }
5126
+ return {
5127
+ status: "error",
5128
+ error: "reload_failed",
5129
+ message: error instanceof Error ? error.message : String(error),
5130
+ statusCode: 500
5131
+ };
5132
+ }
5133
+ };
5134
+ }
5135
+ function generateCorrelationId2() {
5136
+ return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
5137
+ }
5138
+ var init_config_reload_endpoint = __esm({
5139
+ "src/foundry/signals/config-reload-endpoint.ts"() {
5140
+ }
5141
+ });
5142
+
5143
+ // src/appidentity/runtime.ts
5144
+ function detectRuntime() {
5145
+ const versions = process.versions;
5146
+ if (typeof versions.bun === "string" && versions.bun.length > 0) {
5147
+ return { name: "bun", version: versions.bun };
5148
+ }
5149
+ if (typeof versions.node === "string" && versions.node.length > 0) {
5150
+ return { name: "node", version: versions.node };
5151
+ }
5152
+ return { name: "unknown" };
5153
+ }
5154
+ function buildRuntimeInfo(options = {}) {
5155
+ const runtime = detectRuntime();
5156
+ const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
5157
+ const vendor = options.vendor ?? options.identity?.app.vendor;
5158
+ return {
5159
+ service: {
5160
+ name: serviceName,
5161
+ vendor,
5162
+ version: options.version
5163
+ },
5164
+ runtime,
5165
+ platform: {
5166
+ os: process.platform,
5167
+ arch: process.arch
5168
+ }
5169
+ };
5170
+ }
5171
+ var init_runtime = __esm({
5172
+ "src/appidentity/runtime.ts"() {
5173
+ }
5174
+ });
5175
+
5176
+ // src/foundry/signals/control-discovery-endpoint.ts
5177
+ function createControlDiscoveryEndpoint(options) {
5178
+ const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
5179
+ return async (req) => {
5180
+ if (auth) {
5181
+ const authResult = await auth(req);
5182
+ if (!authResult.authenticated) {
5183
+ if (logger) {
5184
+ logger.warn("Control discovery endpoint: authentication failed", {
5185
+ reason: authResult.reason
5186
+ });
5187
+ }
5188
+ if (telemetry) {
5189
+ telemetry.emit("fulmen.control.discovery.auth_failed", {
5190
+ service: identity.app.binary_name
5191
+ });
5192
+ }
5193
+ return {
5194
+ status: "error",
5195
+ error: "authentication_failed",
5196
+ message: authResult.reason || "Authentication required",
5197
+ statusCode: 401
5198
+ };
5199
+ }
5200
+ }
5201
+ if (telemetry) {
5202
+ telemetry.emit("fulmen.control.discovery.served", {
5203
+ service: identity.app.binary_name
5204
+ });
5205
+ }
5206
+ const runtime = buildRuntimeInfo({ identity, version });
5207
+ return {
5208
+ status: "ok",
5209
+ service: {
5210
+ name: identity.app.binary_name,
5211
+ vendor: identity.app.vendor,
5212
+ version
5213
+ },
5214
+ runtime: {
5215
+ name: runtime.runtime.name,
5216
+ version: runtime.runtime.version,
5217
+ platform: runtime.platform.os,
5218
+ arch: runtime.platform.arch
5219
+ },
5220
+ auth_summary: authSummary,
5221
+ endpoints,
5222
+ statusCode: 200
5223
+ };
5224
+ };
5225
+ }
5226
+ var init_control_discovery_endpoint = __esm({
5227
+ "src/foundry/signals/control-discovery-endpoint.ts"() {
5228
+ init_runtime();
5229
+ }
5230
+ });
5231
+
4926
5232
  // src/foundry/signals/convenience.ts
4927
5233
  async function onShutdown(manager, handler, options = {}) {
4928
5234
  await manager.register("SIGTERM", handler, options);
@@ -5182,7 +5488,7 @@ var init_guards = __esm({
5182
5488
  function createSignalEndpoint(options) {
5183
5489
  const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
5184
5490
  return async (payload, req) => {
5185
- const correlationId = payload.correlation_id ?? generateCorrelationId2();
5491
+ const correlationId = payload.correlation_id ?? generateCorrelationId3();
5186
5492
  const authResult = await auth(req);
5187
5493
  if (!authResult.authenticated) {
5188
5494
  if (logger) {
@@ -5305,7 +5611,7 @@ function normalizeSignalName(signal) {
5305
5611
  }
5306
5612
  return `SIG${upper}`;
5307
5613
  }
5308
- function generateCorrelationId2() {
5614
+ function generateCorrelationId3() {
5309
5615
  return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
5310
5616
  }
5311
5617
  function createBearerTokenAuth(expectedToken) {
@@ -5772,6 +6078,8 @@ var init_signals = __esm({
5772
6078
  "src/foundry/signals/index.ts"() {
5773
6079
  init_capabilities2();
5774
6080
  init_catalog();
6081
+ init_config_reload_endpoint();
6082
+ init_control_discovery_endpoint();
5775
6083
  init_convenience();
5776
6084
  init_double_tap();
5777
6085
  init_guards();
@@ -5785,6 +6093,6 @@ var init_signals = __esm({
5785
6093
  // src/signals/index.ts
5786
6094
  init_signals();
5787
6095
 
5788
- export { ConfigReloadTracker, SignalManager, createBearerTokenAuth, createConfigReloadHandler, createDoubleTapTracker, createSignalEndpoint, createSignalManager, createSimpleRateLimiter, ensurePOSIX, ensureSignalExitCodesSupported, ensureSupported, ensureWindows, getBehavior, getFallbackMetadata, getHttpFallbackGuidance, getPlatform2 as getPlatform, getPlatformCapabilities2 as getPlatformCapabilities, getSignal, getSignalCatalog, getSignalNumber, getSignalsVersion, getWindowTimeRemaining, getWindowsEvent, handleDoubleTap, handleWindowsFallback, isPOSIX2 as isPOSIX, isWindows2 as isWindows, isWithinWindow, listBehaviors, listSignals, onAnyShutdown, onEmergencyQuit, onReload, onShutdown, onUSR1, onUSR2, requiresFallback, resetDoubleTap, supportsSignal, supportsSignalExitCodes2 as supportsSignalExitCodes };
6096
+ export { ConfigReloadTracker, SignalManager, createBearerTokenAuth, createConfigReloadEndpoint, createConfigReloadHandler, createControlDiscoveryEndpoint, createDoubleTapTracker, createSignalEndpoint, createSignalManager, createSimpleRateLimiter, ensurePOSIX, ensureSignalExitCodesSupported, ensureSupported, ensureWindows, getBehavior, getFallbackMetadata, getHttpFallbackGuidance, getPlatform2 as getPlatform, getPlatformCapabilities2 as getPlatformCapabilities, getSignal, getSignalCatalog, getSignalNumber, getSignalsVersion, getWindowTimeRemaining, getWindowsEvent, handleDoubleTap, handleWindowsFallback, isPOSIX2 as isPOSIX, isWindows2 as isWindows, isWithinWindow, listBehaviors, listSignals, onAnyShutdown, onEmergencyQuit, onReload, onShutdown, onUSR1, onUSR2, requiresFallback, resetDoubleTap, supportsSignal, supportsSignalExitCodes2 as supportsSignalExitCodes };
5789
6097
  //# sourceMappingURL=index.js.map
5790
6098
  //# sourceMappingURL=index.js.map