@fulmenhq/tsfulmen 0.2.2 → 0.2.4
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.
- package/CHANGELOG.md +42 -0
- package/README.md +9 -8
- package/dist/appidentity/index.d.ts +30 -108
- package/dist/appidentity/index.js +251 -8
- package/dist/appidentity/index.js.map +1 -1
- package/dist/config/index.d.ts +46 -1
- package/dist/config/index.js +309 -10
- package/dist/config/index.js.map +1 -1
- package/dist/crucible/index.js +249 -7
- package/dist/crucible/index.js.map +1 -1
- package/dist/errors/index.js +249 -7
- package/dist/errors/index.js.map +1 -1
- package/dist/foundry/index.d.ts +2 -1
- package/dist/foundry/index.js +250 -8
- package/dist/foundry/index.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +252 -9
- package/dist/index.js.map +1 -1
- package/dist/pathfinder/index.js +249 -7
- package/dist/pathfinder/index.js.map +1 -1
- package/dist/reports/license-inventory.csv +185 -40
- package/dist/schema/index.d.ts +14 -1
- package/dist/schema/index.js +250 -8
- package/dist/schema/index.js.map +1 -1
- package/dist/signals/index.d.ts +483 -395
- package/dist/signals/index.js +250 -8
- package/dist/signals/index.js.map +1 -1
- package/dist/telemetry/http/index.js +250 -7
- package/dist/telemetry/http/index.js.map +1 -1
- package/dist/telemetry/index.js +249 -7
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/prometheus/index.js +251 -7
- package/dist/telemetry/prometheus/index.js.map +1 -1
- package/dist/types-Dv5TERCM.d.ts +108 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
12
12
|
|
|
13
13
|
_No unreleased changes._
|
|
14
14
|
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## [0.2.4] - 2026-02-01
|
|
18
|
+
|
|
19
|
+
### Infrastructure
|
|
20
|
+
|
|
21
|
+
- **Automated Release Workflow** - Added GitHub Actions workflow with npm OIDC trusted publishing
|
|
22
|
+
- Automatic GitHub release creation on tag push
|
|
23
|
+
- npm package publishing via OIDC (no NPM_TOKEN secrets required)
|
|
24
|
+
- Automatic provenance attestation for supply chain security
|
|
25
|
+
- Deployment protection with manual approval gate
|
|
26
|
+
- Post-release verification (npm install + smoke test)
|
|
27
|
+
- No manual npm publish or GitHub release creation required
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## [0.2.3] - 2026-01-28
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **Vitest Upgrade** - Updated test framework from v2.1.9 to v4.0.18
|
|
36
|
+
- Resolves 70 Go stdlib CVEs in bundled esbuild binaries (dev/CI dependencies only)
|
|
37
|
+
- Critical vulnerabilities reduced from 6 to 0
|
|
38
|
+
- Test execution 32% faster (38s vs 56s)
|
|
39
|
+
- No breaking changes to test suite
|
|
40
|
+
|
|
41
|
+
### Security
|
|
42
|
+
|
|
43
|
+
- **CVE Remediation** - Eliminated critical Go 1.20.12 vulnerabilities in vite's nested esbuild
|
|
44
|
+
- Before: 94 findings (6 critical, 34 high, 54 medium)
|
|
45
|
+
- After: 24 findings (0 critical, 10 high, 14 medium)
|
|
46
|
+
- Remaining findings are recent 2025 CVEs in Go 1.25.5 (not legacy issues)
|
|
47
|
+
- **Note**: All vulnerabilities are in dev/CI tooling (esbuild binaries); production code surface has 0 findings
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## [0.2.2] - 2026-01-28
|
|
52
|
+
|
|
53
|
+
**Skipped** - Version number consumed by npm publish workflow reading local package.json before version bump completed. No package published to npm under this version.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
15
57
|
## [0.2.1] - 2026-01-27
|
|
16
58
|
|
|
17
59
|
### Changed
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Every team writes their own HTTP status helpers, exit code enums, and country co
|
|
|
8
8
|
- **Cross-language parity**: Same exit codes, signals, and schemas as gofulmen, rsfulmen, pyfulmen
|
|
9
9
|
- **Type-safe**: Full TypeScript types with strict mode throughout
|
|
10
10
|
|
|
11
|
-
**Lifecycle Phase**: `beta` | **Version**: 0.2.
|
|
11
|
+
**Lifecycle Phase**: `beta` | **Version**: 0.2.3 | **Test Coverage**: 71%
|
|
12
12
|
|
|
13
13
|
**Install**: `bun add @fulmenhq/tsfulmen` (or `npm install @fulmenhq/tsfulmen`)
|
|
14
14
|
|
|
@@ -44,6 +44,7 @@ Every team writes their own HTTP status helpers, exit code enums, and country co
|
|
|
44
44
|
- ✅ **Application Identity** - .fulmen/app.yaml discovery with caching and validation (93 tests)
|
|
45
45
|
- ✅ **Pathfinder** - Filesystem traversal, repository root discovery with security boundaries, checksums, and observability (70 tests)
|
|
46
46
|
- ✅ **Three-Layer Config Loading** - Defaults → User → Env pattern with schema validation (6 tests)
|
|
47
|
+
- ✅ **Workhorse Control-Plane** - Config reload endpoint, control discovery, runtime info for service orchestration
|
|
47
48
|
|
|
48
49
|
## Installation
|
|
49
50
|
|
|
@@ -299,13 +300,13 @@ console.log(hex.checksum); // "sha256:2cf24dba..."
|
|
|
299
300
|
|
|
300
301
|
**Supported Formats**:
|
|
301
302
|
|
|
302
|
-
| Binary
|
|
303
|
-
|
|
|
304
|
-
| base64
|
|
305
|
-
| base64url
|
|
306
|
-
| base64_raw
|
|
307
|
-
| hex
|
|
308
|
-
| base32, base32hex |
|
|
303
|
+
| Binary | Text |
|
|
304
|
+
| ----------------- | ------------------ |
|
|
305
|
+
| base64 | utf-8 |
|
|
306
|
+
| base64url | utf-16le, utf-16be |
|
|
307
|
+
| base64_raw | iso-8859-1, ascii |
|
|
308
|
+
| hex | |
|
|
309
|
+
| base32, base32hex | |
|
|
309
310
|
|
|
310
311
|
**Features**: Padding control, line wrapping, whitespace handling, checksum computation (sha256, xxh3-128), structured `FulencodeError` with error codes.
|
|
311
312
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { I as Identity, L as LoadIdentityOptions } from '../types-Dv5TERCM.js';
|
|
2
|
+
export { A as AppIdentity, a as IdentityMetadata, P as PythonMetadata, R as RepositoryCategory } from '../types-Dv5TERCM.js';
|
|
1
3
|
import { F as FulmenError } from '../fulmen-error-B_kX8jSC.js';
|
|
2
4
|
import { b as SchemaValidationDiagnostic } from '../types-DdoeE7F5.js';
|
|
3
5
|
|
|
@@ -28,113 +30,6 @@ declare const APP_IDENTITY_SCHEMA_ID = "config/repository/app-identity/v1.0.0/ap
|
|
|
28
30
|
*/
|
|
29
31
|
declare const MAX_ANCESTOR_SEARCH_DEPTH = 20;
|
|
30
32
|
|
|
31
|
-
/**
|
|
32
|
-
* Application Identity Types
|
|
33
|
-
*
|
|
34
|
-
* TypeScript interfaces matching the Crucible app-identity schema v1.0.0
|
|
35
|
-
*/
|
|
36
|
-
/**
|
|
37
|
-
* Repository category taxonomy from Fulmen standards
|
|
38
|
-
*/
|
|
39
|
-
type RepositoryCategory = "cli" | "workhorse" | "service" | "library" | "pipeline" | "codex" | "sdk";
|
|
40
|
-
/**
|
|
41
|
-
* Python-specific packaging metadata
|
|
42
|
-
* Field names use snake_case to match Crucible schema
|
|
43
|
-
*/
|
|
44
|
-
interface PythonMetadata {
|
|
45
|
-
readonly distribution_name?: string;
|
|
46
|
-
readonly package_name?: string;
|
|
47
|
-
readonly console_scripts?: ReadonlyArray<{
|
|
48
|
-
readonly name: string;
|
|
49
|
-
readonly entry_point: string;
|
|
50
|
-
}>;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Required application identity fields
|
|
54
|
-
*
|
|
55
|
-
* All fields are readonly to enforce immutability
|
|
56
|
-
* Field names use snake_case to match Crucible schema
|
|
57
|
-
*/
|
|
58
|
-
interface AppIdentity {
|
|
59
|
-
/**
|
|
60
|
-
* Lowercase kebab-case binary/executable name
|
|
61
|
-
* Pattern: ^[a-z][a-z0-9-]{0,62}[a-z0-9]$
|
|
62
|
-
*/
|
|
63
|
-
readonly binary_name: string;
|
|
64
|
-
/**
|
|
65
|
-
* Lowercase alphanumeric vendor namespace (no hyphens)
|
|
66
|
-
* Pattern: ^[a-z][a-z0-9]{0,62}[a-z0-9]$
|
|
67
|
-
*/
|
|
68
|
-
readonly vendor: string;
|
|
69
|
-
/**
|
|
70
|
-
* Uppercase environment variable prefix (must end with _)
|
|
71
|
-
* Pattern: ^[A-Z][A-Z0-9_]*_$
|
|
72
|
-
*/
|
|
73
|
-
readonly env_prefix: string;
|
|
74
|
-
/**
|
|
75
|
-
* Filesystem-safe config directory name
|
|
76
|
-
* Pattern: ^[a-z][a-z0-9-]{0,62}[a-z0-9]$
|
|
77
|
-
*/
|
|
78
|
-
readonly config_name: string;
|
|
79
|
-
/**
|
|
80
|
-
* One-line application description (10-200 chars)
|
|
81
|
-
*/
|
|
82
|
-
readonly description: string;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Optional metadata fields
|
|
86
|
-
*
|
|
87
|
-
* Additional properties are allowed for extensibility
|
|
88
|
-
* Field names use snake_case to match Crucible schema
|
|
89
|
-
*/
|
|
90
|
-
interface IdentityMetadata {
|
|
91
|
-
readonly project_url?: string;
|
|
92
|
-
readonly support_email?: string;
|
|
93
|
-
readonly license?: string;
|
|
94
|
-
readonly repository_category?: RepositoryCategory;
|
|
95
|
-
readonly telemetry_namespace?: string;
|
|
96
|
-
readonly registry_id?: string;
|
|
97
|
-
readonly python?: PythonMetadata;
|
|
98
|
-
readonly [key: string]: unknown;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Complete application identity document
|
|
102
|
-
*/
|
|
103
|
-
interface Identity {
|
|
104
|
-
readonly app: AppIdentity;
|
|
105
|
-
readonly metadata?: IdentityMetadata;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Options for loading identity
|
|
109
|
-
*/
|
|
110
|
-
interface LoadIdentityOptions {
|
|
111
|
-
/**
|
|
112
|
-
* Explicit path override
|
|
113
|
-
* Highest priority in discovery algorithm
|
|
114
|
-
*/
|
|
115
|
-
readonly path?: string;
|
|
116
|
-
/**
|
|
117
|
-
* Test injection - bypass filesystem and discovery
|
|
118
|
-
* Never cached
|
|
119
|
-
*/
|
|
120
|
-
readonly identity?: Identity;
|
|
121
|
-
/**
|
|
122
|
-
* Starting directory for ancestor search
|
|
123
|
-
* Defaults to process.cwd()
|
|
124
|
-
*/
|
|
125
|
-
readonly startDir?: string;
|
|
126
|
-
/**
|
|
127
|
-
* Force reload, bypass cache
|
|
128
|
-
* Useful for testing cache behavior
|
|
129
|
-
*/
|
|
130
|
-
readonly skipCache?: boolean;
|
|
131
|
-
/**
|
|
132
|
-
* Skip schema validation (dangerous)
|
|
133
|
-
* Only use for testing or when identity is pre-validated
|
|
134
|
-
*/
|
|
135
|
-
readonly skipValidation?: boolean;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
33
|
/**
|
|
139
34
|
* Embedded Identity Registration
|
|
140
35
|
*
|
|
@@ -383,4 +278,31 @@ declare function clearIdentityCache(): void;
|
|
|
383
278
|
*/
|
|
384
279
|
declare function loadIdentity(options?: LoadIdentityOptions): Promise<Identity>;
|
|
385
280
|
|
|
386
|
-
|
|
281
|
+
type RuntimeName = "bun" | "node" | "unknown";
|
|
282
|
+
interface RuntimeInfo {
|
|
283
|
+
service: {
|
|
284
|
+
name: string;
|
|
285
|
+
vendor?: string;
|
|
286
|
+
version?: string;
|
|
287
|
+
};
|
|
288
|
+
runtime: {
|
|
289
|
+
name: RuntimeName;
|
|
290
|
+
version?: string;
|
|
291
|
+
};
|
|
292
|
+
platform: {
|
|
293
|
+
os: NodeJS.Platform;
|
|
294
|
+
arch: string;
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
interface BuildRuntimeInfoOptions {
|
|
298
|
+
identity?: Identity;
|
|
299
|
+
version?: string;
|
|
300
|
+
serviceName?: string;
|
|
301
|
+
vendor?: string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Build a minimal runtime info payload suitable for discovery endpoints.
|
|
305
|
+
*/
|
|
306
|
+
declare function buildRuntimeInfo(options?: BuildRuntimeInfoOptions): RuntimeInfo;
|
|
307
|
+
|
|
308
|
+
export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, type BuildRuntimeInfoOptions, type ConfigIdentifiers, Identity, LoadIdentityOptions, MAX_ANCESTOR_SEARCH_DEPTH, type RuntimeInfo, type RuntimeName, buildEnvVar, buildRuntimeInfo, clearEmbeddedIdentity, clearIdentityCache, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import addFormats from 'ajv-formats';
|
|
1
2
|
import { spawn } from 'child_process';
|
|
2
3
|
import { readFile, writeFile, access, mkdir } from 'fs/promises';
|
|
3
4
|
import { parse, stringify } from 'yaml';
|
|
@@ -8,7 +9,6 @@ import Ajv from 'ajv';
|
|
|
8
9
|
import Ajv2019 from 'ajv/dist/2019';
|
|
9
10
|
import Ajv2020 from 'ajv/dist/2020';
|
|
10
11
|
import AjvDraft04 from 'ajv-draft-04';
|
|
11
|
-
import addFormats from 'ajv-formats';
|
|
12
12
|
import { Readable } from 'stream';
|
|
13
13
|
import picomatch from 'picomatch';
|
|
14
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';
|
|
@@ -36,6 +36,27 @@ var init_constants = __esm({
|
|
|
36
36
|
MAX_ANCESTOR_SEARCH_DEPTH = 20;
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
+
function applyFulmenAjvFormats(ajv, options = {}) {
|
|
40
|
+
const mode = options.mode ?? "fast";
|
|
41
|
+
const formats = options.formats ?? DEFAULT_FORMATS;
|
|
42
|
+
addFormats(ajv, { mode, formats });
|
|
43
|
+
return ajv;
|
|
44
|
+
}
|
|
45
|
+
var DEFAULT_FORMATS;
|
|
46
|
+
var init_ajv_formats = __esm({
|
|
47
|
+
"src/schema/ajv-formats.ts"() {
|
|
48
|
+
DEFAULT_FORMATS = [
|
|
49
|
+
"date-time",
|
|
50
|
+
"email",
|
|
51
|
+
"hostname",
|
|
52
|
+
"ipv4",
|
|
53
|
+
"ipv6",
|
|
54
|
+
"uri",
|
|
55
|
+
"uri-reference",
|
|
56
|
+
"uuid"
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
});
|
|
39
60
|
|
|
40
61
|
// src/schema/errors.ts
|
|
41
62
|
var errors_exports = {};
|
|
@@ -1591,10 +1612,7 @@ function createAjv(dialect) {
|
|
|
1591
1612
|
// Enable async schema loading for YAML references
|
|
1592
1613
|
loadSchema: loadReferencedSchema
|
|
1593
1614
|
});
|
|
1594
|
-
|
|
1595
|
-
mode: "fast",
|
|
1596
|
-
formats: ["date-time", "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference"]
|
|
1597
|
-
});
|
|
1615
|
+
applyFulmenAjvFormats(ajv);
|
|
1598
1616
|
return ajv;
|
|
1599
1617
|
}
|
|
1600
1618
|
async function getAjv(dialect) {
|
|
@@ -1840,6 +1858,7 @@ var ajvInstances, metaschemaReady, schemaCache;
|
|
|
1840
1858
|
var init_validator = __esm({
|
|
1841
1859
|
"src/schema/validator.ts"() {
|
|
1842
1860
|
init_telemetry();
|
|
1861
|
+
init_ajv_formats();
|
|
1843
1862
|
init_errors();
|
|
1844
1863
|
init_registry();
|
|
1845
1864
|
init_utils();
|
|
@@ -3799,6 +3818,224 @@ var init_capabilities2 = __esm({
|
|
|
3799
3818
|
}
|
|
3800
3819
|
});
|
|
3801
3820
|
|
|
3821
|
+
// src/foundry/signals/config-reload-endpoint.ts
|
|
3822
|
+
function createConfigReloadEndpoint(options) {
|
|
3823
|
+
const { loader, validator, onReload: onReload2, auth, rateLimit, logger, telemetry } = options;
|
|
3824
|
+
return async (payload, req) => {
|
|
3825
|
+
const correlationId = payload.correlation_id ?? generateCorrelationId();
|
|
3826
|
+
const authResult = await auth(req);
|
|
3827
|
+
if (!authResult.authenticated) {
|
|
3828
|
+
if (logger) {
|
|
3829
|
+
logger.warn("Config reload endpoint: authentication failed", {
|
|
3830
|
+
correlation_id: correlationId,
|
|
3831
|
+
reason: authResult.reason
|
|
3832
|
+
});
|
|
3833
|
+
}
|
|
3834
|
+
if (telemetry) {
|
|
3835
|
+
telemetry.emit("fulmen.config.http_endpoint.auth_failed", {
|
|
3836
|
+
correlation_id: correlationId
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
return {
|
|
3840
|
+
status: "error",
|
|
3841
|
+
error: "authentication_failed",
|
|
3842
|
+
message: authResult.reason || "Authentication required",
|
|
3843
|
+
statusCode: 401
|
|
3844
|
+
};
|
|
3845
|
+
}
|
|
3846
|
+
const identity = authResult.identity || "unknown";
|
|
3847
|
+
if (rateLimit) {
|
|
3848
|
+
const rateLimitResult = await rateLimit(identity);
|
|
3849
|
+
if (!rateLimitResult.allowed) {
|
|
3850
|
+
if (logger) {
|
|
3851
|
+
logger.warn("Config reload endpoint: rate limit exceeded", {
|
|
3852
|
+
correlation_id: correlationId,
|
|
3853
|
+
identity
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
if (telemetry) {
|
|
3857
|
+
telemetry.emit("fulmen.config.http_endpoint.rate_limited", {
|
|
3858
|
+
correlation_id: correlationId
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
return {
|
|
3862
|
+
status: "error",
|
|
3863
|
+
error: "rate_limit_exceeded",
|
|
3864
|
+
message: "Rate limit exceeded. Please try again later.",
|
|
3865
|
+
statusCode: 429
|
|
3866
|
+
};
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
if (telemetry) {
|
|
3870
|
+
telemetry.emit("fulmen.config.http_endpoint.reload_requested", {
|
|
3871
|
+
correlation_id: correlationId
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
try {
|
|
3875
|
+
const config = await loader();
|
|
3876
|
+
if (validator) {
|
|
3877
|
+
const validation = await validator(config);
|
|
3878
|
+
if (!validation.valid) {
|
|
3879
|
+
if (logger) {
|
|
3880
|
+
logger.warn("Config reload endpoint: validation failed", {
|
|
3881
|
+
correlation_id: correlationId,
|
|
3882
|
+
error_count: validation.errors?.length ?? 0
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3885
|
+
if (telemetry) {
|
|
3886
|
+
telemetry.emit("fulmen.config.http_endpoint.reload_rejected", {
|
|
3887
|
+
correlation_id: correlationId,
|
|
3888
|
+
reason: "validation_failed"
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3891
|
+
return {
|
|
3892
|
+
status: "error",
|
|
3893
|
+
error: "validation_failed",
|
|
3894
|
+
message: "Configuration validation failed",
|
|
3895
|
+
validation_errors: validation.errors,
|
|
3896
|
+
statusCode: 422
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
if (onReload2) {
|
|
3901
|
+
await onReload2(config);
|
|
3902
|
+
}
|
|
3903
|
+
if (telemetry) {
|
|
3904
|
+
telemetry.emit("fulmen.config.http_endpoint.reload_accepted", {
|
|
3905
|
+
correlation_id: correlationId
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3908
|
+
if (logger) {
|
|
3909
|
+
logger.info("Config reload endpoint: reload accepted", {
|
|
3910
|
+
correlation_id: correlationId,
|
|
3911
|
+
reason: payload.reason
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
return {
|
|
3915
|
+
status: "reloaded",
|
|
3916
|
+
correlation_id: correlationId,
|
|
3917
|
+
message: "Configuration reloaded",
|
|
3918
|
+
statusCode: 200
|
|
3919
|
+
};
|
|
3920
|
+
} catch (error) {
|
|
3921
|
+
if (logger) {
|
|
3922
|
+
logger.warn("Config reload endpoint: reload failed", {
|
|
3923
|
+
correlation_id: correlationId,
|
|
3924
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3925
|
+
});
|
|
3926
|
+
}
|
|
3927
|
+
if (telemetry) {
|
|
3928
|
+
telemetry.emit("fulmen.config.http_endpoint.reload_error", {
|
|
3929
|
+
correlation_id: correlationId,
|
|
3930
|
+
error_type: error instanceof Error ? error.constructor.name : "unknown"
|
|
3931
|
+
});
|
|
3932
|
+
}
|
|
3933
|
+
return {
|
|
3934
|
+
status: "error",
|
|
3935
|
+
error: "reload_failed",
|
|
3936
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3937
|
+
statusCode: 500
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
};
|
|
3941
|
+
}
|
|
3942
|
+
function generateCorrelationId() {
|
|
3943
|
+
return `cfg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
3944
|
+
}
|
|
3945
|
+
var init_config_reload_endpoint = __esm({
|
|
3946
|
+
"src/foundry/signals/config-reload-endpoint.ts"() {
|
|
3947
|
+
}
|
|
3948
|
+
});
|
|
3949
|
+
|
|
3950
|
+
// src/appidentity/runtime.ts
|
|
3951
|
+
function detectRuntime() {
|
|
3952
|
+
const versions = process.versions;
|
|
3953
|
+
if (typeof versions.bun === "string" && versions.bun.length > 0) {
|
|
3954
|
+
return { name: "bun", version: versions.bun };
|
|
3955
|
+
}
|
|
3956
|
+
if (typeof versions.node === "string" && versions.node.length > 0) {
|
|
3957
|
+
return { name: "node", version: versions.node };
|
|
3958
|
+
}
|
|
3959
|
+
return { name: "unknown" };
|
|
3960
|
+
}
|
|
3961
|
+
function buildRuntimeInfo(options = {}) {
|
|
3962
|
+
const runtime = detectRuntime();
|
|
3963
|
+
const serviceName = options.serviceName ?? options.identity?.app.binary_name ?? "unknown-service";
|
|
3964
|
+
const vendor = options.vendor ?? options.identity?.app.vendor;
|
|
3965
|
+
return {
|
|
3966
|
+
service: {
|
|
3967
|
+
name: serviceName,
|
|
3968
|
+
vendor,
|
|
3969
|
+
version: options.version
|
|
3970
|
+
},
|
|
3971
|
+
runtime,
|
|
3972
|
+
platform: {
|
|
3973
|
+
os: process.platform,
|
|
3974
|
+
arch: process.arch
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
var init_runtime = __esm({
|
|
3979
|
+
"src/appidentity/runtime.ts"() {
|
|
3980
|
+
}
|
|
3981
|
+
});
|
|
3982
|
+
|
|
3983
|
+
// src/foundry/signals/control-discovery-endpoint.ts
|
|
3984
|
+
function createControlDiscoveryEndpoint(options) {
|
|
3985
|
+
const { identity, version, endpoints, auth, authSummary, logger, telemetry } = options;
|
|
3986
|
+
return async (req) => {
|
|
3987
|
+
if (auth) {
|
|
3988
|
+
const authResult = await auth(req);
|
|
3989
|
+
if (!authResult.authenticated) {
|
|
3990
|
+
if (logger) {
|
|
3991
|
+
logger.warn("Control discovery endpoint: authentication failed", {
|
|
3992
|
+
reason: authResult.reason
|
|
3993
|
+
});
|
|
3994
|
+
}
|
|
3995
|
+
if (telemetry) {
|
|
3996
|
+
telemetry.emit("fulmen.control.discovery.auth_failed", {
|
|
3997
|
+
service: identity.app.binary_name
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
return {
|
|
4001
|
+
status: "error",
|
|
4002
|
+
error: "authentication_failed",
|
|
4003
|
+
message: authResult.reason || "Authentication required",
|
|
4004
|
+
statusCode: 401
|
|
4005
|
+
};
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
if (telemetry) {
|
|
4009
|
+
telemetry.emit("fulmen.control.discovery.served", {
|
|
4010
|
+
service: identity.app.binary_name
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
const runtime = buildRuntimeInfo({ identity, version });
|
|
4014
|
+
return {
|
|
4015
|
+
status: "ok",
|
|
4016
|
+
service: {
|
|
4017
|
+
name: identity.app.binary_name,
|
|
4018
|
+
vendor: identity.app.vendor,
|
|
4019
|
+
version
|
|
4020
|
+
},
|
|
4021
|
+
runtime: {
|
|
4022
|
+
name: runtime.runtime.name,
|
|
4023
|
+
version: runtime.runtime.version,
|
|
4024
|
+
platform: runtime.platform.os,
|
|
4025
|
+
arch: runtime.platform.arch
|
|
4026
|
+
},
|
|
4027
|
+
auth_summary: authSummary,
|
|
4028
|
+
endpoints,
|
|
4029
|
+
statusCode: 200
|
|
4030
|
+
};
|
|
4031
|
+
};
|
|
4032
|
+
}
|
|
4033
|
+
var init_control_discovery_endpoint = __esm({
|
|
4034
|
+
"src/foundry/signals/control-discovery-endpoint.ts"() {
|
|
4035
|
+
init_runtime();
|
|
4036
|
+
}
|
|
4037
|
+
});
|
|
4038
|
+
|
|
3802
4039
|
// src/foundry/signals/convenience.ts
|
|
3803
4040
|
async function onShutdown(manager, handler, options = {}) {
|
|
3804
4041
|
await manager.register("SIGTERM", handler, options);
|
|
@@ -4058,7 +4295,7 @@ var init_guards = __esm({
|
|
|
4058
4295
|
function createSignalEndpoint(options) {
|
|
4059
4296
|
const { manager, auth, rateLimit, logger, telemetry, allowedSignals } = options;
|
|
4060
4297
|
return async (payload, req) => {
|
|
4061
|
-
const correlationId = payload.correlation_id ??
|
|
4298
|
+
const correlationId = payload.correlation_id ?? generateCorrelationId2();
|
|
4062
4299
|
const authResult = await auth(req);
|
|
4063
4300
|
if (!authResult.authenticated) {
|
|
4064
4301
|
if (logger) {
|
|
@@ -4181,7 +4418,7 @@ function normalizeSignalName(signal) {
|
|
|
4181
4418
|
}
|
|
4182
4419
|
return `SIG${upper}`;
|
|
4183
4420
|
}
|
|
4184
|
-
function
|
|
4421
|
+
function generateCorrelationId2() {
|
|
4185
4422
|
return `sig-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
4186
4423
|
}
|
|
4187
4424
|
function createBearerTokenAuth(expectedToken) {
|
|
@@ -4648,6 +4885,8 @@ var init_signals = __esm({
|
|
|
4648
4885
|
"src/foundry/signals/index.ts"() {
|
|
4649
4886
|
init_capabilities2();
|
|
4650
4887
|
init_catalog();
|
|
4888
|
+
init_config_reload_endpoint();
|
|
4889
|
+
init_control_discovery_endpoint();
|
|
4651
4890
|
init_convenience();
|
|
4652
4891
|
init_double_tap();
|
|
4653
4892
|
init_guards();
|
|
@@ -4793,7 +5032,9 @@ __export(foundry_exports, {
|
|
|
4793
5032
|
clearMimeTypeCache: () => clearMimeTypeCache,
|
|
4794
5033
|
clearPatternCache: () => clearPatternCache,
|
|
4795
5034
|
createBearerTokenAuth: () => createBearerTokenAuth,
|
|
5035
|
+
createConfigReloadEndpoint: () => createConfigReloadEndpoint,
|
|
4796
5036
|
createConfigReloadHandler: () => createConfigReloadHandler,
|
|
5037
|
+
createControlDiscoveryEndpoint: () => createControlDiscoveryEndpoint,
|
|
4797
5038
|
createDoubleTapTracker: () => createDoubleTapTracker,
|
|
4798
5039
|
createSignalEndpoint: () => createSignalEndpoint,
|
|
4799
5040
|
createSignalManager: () => createSignalManager,
|
|
@@ -5825,6 +6066,7 @@ var init_cli = __esm({
|
|
|
5825
6066
|
// src/schema/index.ts
|
|
5826
6067
|
var init_schema = __esm({
|
|
5827
6068
|
"src/schema/index.ts"() {
|
|
6069
|
+
init_ajv_formats();
|
|
5828
6070
|
init_cli();
|
|
5829
6071
|
init_errors();
|
|
5830
6072
|
init_export();
|
|
@@ -5941,7 +6183,8 @@ async function getEnvVar(key, options) {
|
|
|
5941
6183
|
|
|
5942
6184
|
// src/appidentity/index.ts
|
|
5943
6185
|
init_loader2();
|
|
6186
|
+
init_runtime();
|
|
5944
6187
|
|
|
5945
|
-
export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, MAX_ANCESTOR_SEARCH_DEPTH, buildEnvVar, clearEmbeddedIdentity, clearIdentityCache, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity };
|
|
6188
|
+
export { APP_IDENTITY_DIR, APP_IDENTITY_ENV_VAR, APP_IDENTITY_FILENAME, APP_IDENTITY_SCHEMA_ID, AppIdentityError, MAX_ANCESTOR_SEARCH_DEPTH, buildEnvVar, buildRuntimeInfo, clearEmbeddedIdentity, clearIdentityCache, getBinaryName, getCachedIdentity, getConfigIdentifiers, getConfigName, getEmbeddedIdentity, getEnvPrefix, getEnvVar, getTelemetryNamespace, getVendor, hasEmbeddedIdentity, loadIdentity, registerEmbeddedIdentity };
|
|
5946
6189
|
//# sourceMappingURL=index.js.map
|
|
5947
6190
|
//# sourceMappingURL=index.js.map
|