@enactprotocol/shared 1.2.11 → 2.0.0

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 (134) hide show
  1. package/README.md +44 -0
  2. package/package.json +16 -58
  3. package/src/config.ts +476 -0
  4. package/src/constants.ts +36 -0
  5. package/src/execution/command.ts +314 -0
  6. package/src/execution/index.ts +73 -0
  7. package/src/execution/runtime.ts +308 -0
  8. package/src/execution/types.ts +379 -0
  9. package/src/execution/validation.ts +508 -0
  10. package/src/index.ts +237 -30
  11. package/src/manifest/index.ts +36 -0
  12. package/src/manifest/loader.ts +187 -0
  13. package/src/manifest/parser.ts +173 -0
  14. package/src/manifest/validator.ts +309 -0
  15. package/src/paths.ts +108 -0
  16. package/src/registry.ts +219 -0
  17. package/src/resolver.ts +345 -0
  18. package/src/types/index.ts +30 -0
  19. package/src/types/manifest.ts +255 -0
  20. package/src/types.ts +5 -188
  21. package/src/utils/fs.ts +281 -0
  22. package/src/utils/logger.ts +270 -59
  23. package/src/utils/version.ts +304 -36
  24. package/tests/config.test.ts +515 -0
  25. package/tests/execution/command.test.ts +317 -0
  26. package/tests/execution/validation.test.ts +384 -0
  27. package/tests/fixtures/invalid-tool.yaml +4 -0
  28. package/tests/fixtures/valid-tool.md +62 -0
  29. package/tests/fixtures/valid-tool.yaml +40 -0
  30. package/tests/index.test.ts +8 -0
  31. package/tests/manifest/loader.test.ts +291 -0
  32. package/tests/manifest/parser.test.ts +345 -0
  33. package/tests/manifest/validator.test.ts +394 -0
  34. package/tests/manifest-types.test.ts +358 -0
  35. package/tests/paths.test.ts +153 -0
  36. package/tests/registry.test.ts +231 -0
  37. package/tests/resolver.test.ts +272 -0
  38. package/tests/utils/fs.test.ts +388 -0
  39. package/tests/utils/logger.test.ts +480 -0
  40. package/tests/utils/version.test.ts +390 -0
  41. package/tsconfig.json +12 -0
  42. package/tsconfig.tsbuildinfo +1 -0
  43. package/dist/LocalToolResolver.d.ts +0 -84
  44. package/dist/LocalToolResolver.js +0 -353
  45. package/dist/api/enact-api.d.ts +0 -130
  46. package/dist/api/enact-api.js +0 -428
  47. package/dist/api/index.d.ts +0 -2
  48. package/dist/api/index.js +0 -2
  49. package/dist/api/types.d.ts +0 -103
  50. package/dist/api/types.js +0 -1
  51. package/dist/constants.d.ts +0 -7
  52. package/dist/constants.js +0 -10
  53. package/dist/core/DaggerExecutionProvider.d.ts +0 -169
  54. package/dist/core/DaggerExecutionProvider.js +0 -1029
  55. package/dist/core/DirectExecutionProvider.d.ts +0 -23
  56. package/dist/core/DirectExecutionProvider.js +0 -406
  57. package/dist/core/EnactCore.d.ts +0 -162
  58. package/dist/core/EnactCore.js +0 -597
  59. package/dist/core/NativeExecutionProvider.d.ts +0 -9
  60. package/dist/core/NativeExecutionProvider.js +0 -16
  61. package/dist/core/index.d.ts +0 -3
  62. package/dist/core/index.js +0 -3
  63. package/dist/exec/index.d.ts +0 -3
  64. package/dist/exec/index.js +0 -3
  65. package/dist/exec/logger.d.ts +0 -11
  66. package/dist/exec/logger.js +0 -57
  67. package/dist/exec/validate.d.ts +0 -5
  68. package/dist/exec/validate.js +0 -167
  69. package/dist/index.d.ts +0 -21
  70. package/dist/index.js +0 -25
  71. package/dist/lib/enact-direct.d.ts +0 -150
  72. package/dist/lib/enact-direct.js +0 -159
  73. package/dist/lib/index.d.ts +0 -1
  74. package/dist/lib/index.js +0 -1
  75. package/dist/security/index.d.ts +0 -3
  76. package/dist/security/index.js +0 -3
  77. package/dist/security/security.d.ts +0 -23
  78. package/dist/security/security.js +0 -137
  79. package/dist/security/sign.d.ts +0 -103
  80. package/dist/security/sign.js +0 -666
  81. package/dist/security/verification-enforcer.d.ts +0 -53
  82. package/dist/security/verification-enforcer.js +0 -204
  83. package/dist/services/McpCoreService.d.ts +0 -98
  84. package/dist/services/McpCoreService.js +0 -124
  85. package/dist/services/index.d.ts +0 -1
  86. package/dist/services/index.js +0 -1
  87. package/dist/types.d.ts +0 -132
  88. package/dist/types.js +0 -3
  89. package/dist/utils/config.d.ts +0 -111
  90. package/dist/utils/config.js +0 -342
  91. package/dist/utils/env-loader.d.ts +0 -54
  92. package/dist/utils/env-loader.js +0 -270
  93. package/dist/utils/help.d.ts +0 -36
  94. package/dist/utils/help.js +0 -248
  95. package/dist/utils/index.d.ts +0 -7
  96. package/dist/utils/index.js +0 -7
  97. package/dist/utils/logger.d.ts +0 -35
  98. package/dist/utils/logger.js +0 -75
  99. package/dist/utils/silent-monitor.d.ts +0 -67
  100. package/dist/utils/silent-monitor.js +0 -242
  101. package/dist/utils/timeout.d.ts +0 -5
  102. package/dist/utils/timeout.js +0 -23
  103. package/dist/utils/version.d.ts +0 -4
  104. package/dist/utils/version.js +0 -35
  105. package/dist/web/env-manager-server.d.ts +0 -29
  106. package/dist/web/env-manager-server.js +0 -367
  107. package/dist/web/index.d.ts +0 -1
  108. package/dist/web/index.js +0 -1
  109. package/src/LocalToolResolver.ts +0 -424
  110. package/src/api/enact-api.ts +0 -604
  111. package/src/api/index.ts +0 -2
  112. package/src/api/types.ts +0 -114
  113. package/src/core/DaggerExecutionProvider.ts +0 -1357
  114. package/src/core/DirectExecutionProvider.ts +0 -484
  115. package/src/core/EnactCore.ts +0 -847
  116. package/src/core/index.ts +0 -3
  117. package/src/exec/index.ts +0 -3
  118. package/src/exec/logger.ts +0 -63
  119. package/src/exec/validate.ts +0 -238
  120. package/src/lib/enact-direct.ts +0 -254
  121. package/src/lib/index.ts +0 -1
  122. package/src/services/McpCoreService.ts +0 -201
  123. package/src/services/index.ts +0 -1
  124. package/src/utils/config.ts +0 -438
  125. package/src/utils/env-loader.ts +0 -370
  126. package/src/utils/help.ts +0 -257
  127. package/src/utils/index.ts +0 -7
  128. package/src/utils/silent-monitor.ts +0 -328
  129. package/src/utils/timeout.ts +0 -26
  130. package/src/web/env-manager-server.ts +0 -465
  131. package/src/web/index.ts +0 -1
  132. package/src/web/static/app.js +0 -663
  133. package/src/web/static/index.html +0 -117
  134. package/src/web/static/style.css +0 -291
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @enactprotocol/shared
2
+
3
+ Core business logic and utilities for Enact.
4
+
5
+ ## Overview
6
+
7
+ This package provides:
8
+ - Manifest parsing (enact.yaml and enact.md)
9
+ - Configuration management (~/.enact/config.yaml)
10
+ - Environment variable management (package-scoped)
11
+ - Tool resolution (local, user-level, registry)
12
+ - Trust store and policy enforcement
13
+ - Execution engine interfaces
14
+ - Registry client
15
+
16
+ ## Status
17
+
18
+ Currently in Phase 1 (scaffolding). Full implementation will be completed in Phase 3.
19
+
20
+ ## Dependencies
21
+
22
+ - `@enactprotocol/security` - For cryptographic operations
23
+
24
+ ## Development
25
+
26
+ ```bash
27
+ # Build
28
+ bun run build
29
+
30
+ # Test
31
+ bun test
32
+
33
+ # Type check
34
+ bun run typecheck
35
+ ```
36
+
37
+ ## Planned Features (Phase 3)
38
+
39
+ - [ ] Configuration system (paths, config, env, trust)
40
+ - [ ] Manifest parser with YAML/Markdown support
41
+ - [ ] Tool resolution with caching strategy
42
+ - [ ] Execution providers (Dagger and Direct)
43
+ - [ ] Registry HTTP client
44
+ - [ ] Comprehensive utilities (logger, tarball, version, fs)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enactprotocol/shared",
3
- "version": "1.2.11",
4
- "description": "Shared utilities and core functionality for Enact Protocol",
3
+ "version": "2.0.0",
4
+ "description": "Core business logic and utilities for Enact",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -9,68 +9,26 @@
9
9
  ".": {
10
10
  "import": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts"
12
- },
13
- "./core": {
14
- "import": "./dist/core/index.js",
15
- "types": "./dist/core/index.d.ts"
16
- },
17
- "./exec": {
18
- "import": "./dist/exec/index.js",
19
- "types": "./dist/exec/index.d.ts"
20
- },
21
- "./utils": {
22
- "import": "./dist/utils/index.js",
23
- "types": "./dist/utils/index.d.ts"
24
- },
25
- "./services": {
26
- "import": "./dist/services/index.js",
27
- "types": "./dist/services/index.d.ts"
28
- },
29
- "./web": {
30
- "import": "./dist/web/index.js",
31
- "types": "./dist/web/index.d.ts"
32
- },
33
- "./api": {
34
- "import": "./dist/api/index.js",
35
- "types": "./dist/api/index.d.ts"
36
- },
37
- "./lib": {
38
- "import": "./dist/lib/index.js",
39
- "types": "./dist/lib/index.d.ts"
40
12
  }
41
13
  },
42
- "files": [
43
- "dist/**/*",
44
- "src/**/*"
45
- ],
46
14
  "scripts": {
47
- "build": "tsc",
48
- "dev": "tsc --watch",
15
+ "build": "tsc --build",
16
+ "clean": "rm -rf dist",
49
17
  "test": "bun test",
50
- "test:watch": "bun test --watch",
51
- "test:coverage": "bun test --coverage"
18
+ "typecheck": "tsc --noEmit"
52
19
  },
53
- "keywords": [
54
- "enact",
55
- "enact-protocol",
56
- "shared",
57
- "utilities",
58
- "core"
59
- ],
60
- "author": "EnactProtocol",
61
- "license": "MIT",
62
20
  "dependencies": {
63
- "@dagger.io/dagger": "^0.9.11",
64
- "@enactprotocol/security": "0.2.7",
65
- "dotenv": "^16.5.0",
66
- "pino": "^9.7.0",
67
- "pino-pretty": "^13.0.0",
68
- "strip-ansi": "^7.1.0",
69
- "yaml": "^2.8.0",
70
- "zod": "^3.25.67"
21
+ "@enactprotocol/trust": "2.0.0",
22
+ "js-yaml": "^4.1.1",
23
+ "zod": "^4.1.13"
71
24
  },
72
25
  "devDependencies": {
73
- "@types/node": "^20.12.12",
74
- "typescript": "^5.4.5"
75
- }
26
+ "@types/js-yaml": "^4.0.9",
27
+ "@types/json-schema": "^7.0.15",
28
+ "@types/node": "^22.10.1",
29
+ "typescript": "^5.7.2"
30
+ },
31
+ "keywords": ["enact", "core", "manifest", "execution"],
32
+ "author": "Enact Protocol",
33
+ "license": "Apache-2.0"
76
34
  }
package/src/config.ts ADDED
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Configuration manager for Enact CLI
3
+ * Handles reading and writing ~/.enact/config.yaml
4
+ */
5
+
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { dirname } from "node:path";
8
+ import yaml from "js-yaml";
9
+ import { getConfigPath, getEnactHome } from "./paths";
10
+
11
+ /**
12
+ * Trust configuration for attestation verification
13
+ *
14
+ * Uses a unified model: all trust is based on cryptographic attestations.
15
+ * Publishers who want their tools trusted should self-sign them.
16
+ * Third-party reviewers can add additional attestations.
17
+ */
18
+ export interface TrustConfig {
19
+ /**
20
+ * List of trusted auditor identities (provider:identity format, e.g., github:alice)
21
+ * Anyone who signs with these identities is trusted - whether they authored
22
+ * the tool (self-attestation) or reviewed it (third-party audit).
23
+ */
24
+ auditors?: string[];
25
+ /** Trust policy: 'require_attestation' blocks without trust, 'prompt' asks user, 'allow' installs anyway */
26
+ policy?: "require_attestation" | "prompt" | "allow";
27
+ /** Minimum number of trusted attestations required */
28
+ minimum_attestations?: number;
29
+ }
30
+
31
+ /**
32
+ * Cache configuration
33
+ */
34
+ export interface CacheConfig {
35
+ /** Maximum cache size in megabytes */
36
+ maxSizeMb?: number;
37
+ /** Cache TTL in seconds */
38
+ ttlSeconds?: number;
39
+ }
40
+
41
+ /**
42
+ * Execution configuration
43
+ */
44
+ export interface ExecutionConfig {
45
+ /** Default timeout for tool execution (e.g., "30s", "5m") */
46
+ defaultTimeout?: string;
47
+ /** Whether to run in verbose mode */
48
+ verbose?: boolean;
49
+ }
50
+
51
+ /**
52
+ * Registry configuration
53
+ */
54
+ export interface RegistryConfig {
55
+ /** Default registry URL */
56
+ url?: string;
57
+ /** Authentication token for registry (stored reference, not actual token) */
58
+ authTokenRef?: string;
59
+ /** Direct authentication token (for local development) */
60
+ authToken?: string;
61
+ }
62
+
63
+ /**
64
+ * Complete Enact configuration
65
+ */
66
+ export interface EnactConfig {
67
+ /** Configuration file version */
68
+ version?: string;
69
+ /** Trust settings for verification */
70
+ trust?: TrustConfig;
71
+ /** Cache settings */
72
+ cache?: CacheConfig;
73
+ /** Execution defaults */
74
+ execution?: ExecutionConfig;
75
+ /** Registry settings */
76
+ registry?: RegistryConfig;
77
+ }
78
+
79
+ /**
80
+ * Default configuration values
81
+ */
82
+ export const DEFAULT_CONFIG: EnactConfig = {
83
+ version: "1.0.0",
84
+ trust: {
85
+ auditors: [],
86
+ policy: "prompt",
87
+ minimum_attestations: 1,
88
+ },
89
+ cache: {
90
+ maxSizeMb: 1024, // 1GB
91
+ ttlSeconds: 86400 * 7, // 7 days
92
+ },
93
+ execution: {
94
+ defaultTimeout: "30s",
95
+ verbose: false,
96
+ },
97
+ registry: {
98
+ url: "https://enact.tools",
99
+ },
100
+ };
101
+
102
+ /**
103
+ * Deep merge two objects, with source values overwriting target values
104
+ */
105
+ function deepMerge(target: EnactConfig, source: Partial<EnactConfig>): EnactConfig {
106
+ const result: EnactConfig = { ...target };
107
+
108
+ // Merge trust config
109
+ if (source.trust !== undefined) {
110
+ result.trust = { ...target.trust, ...source.trust };
111
+ }
112
+
113
+ // Merge cache config
114
+ if (source.cache !== undefined) {
115
+ result.cache = { ...target.cache, ...source.cache };
116
+ }
117
+
118
+ // Merge execution config
119
+ if (source.execution !== undefined) {
120
+ result.execution = { ...target.execution, ...source.execution };
121
+ }
122
+
123
+ // Merge registry config
124
+ if (source.registry !== undefined) {
125
+ result.registry = { ...target.registry, ...source.registry };
126
+ }
127
+
128
+ // Copy top-level primitives
129
+ if (source.version !== undefined) {
130
+ result.version = source.version;
131
+ }
132
+
133
+ return result;
134
+ }
135
+
136
+ /**
137
+ * Load configuration from ~/.enact/config.yaml
138
+ * Returns default config if file doesn't exist or is invalid
139
+ * @returns The loaded configuration merged with defaults
140
+ */
141
+ export function loadConfig(): EnactConfig {
142
+ const configPath = getConfigPath();
143
+
144
+ if (!existsSync(configPath)) {
145
+ return { ...DEFAULT_CONFIG };
146
+ }
147
+
148
+ try {
149
+ const content = readFileSync(configPath, "utf-8");
150
+ const parsed = yaml.load(content) as Partial<EnactConfig> | null;
151
+
152
+ if (!parsed || typeof parsed !== "object") {
153
+ return { ...DEFAULT_CONFIG };
154
+ }
155
+
156
+ // Merge with defaults to ensure all fields exist
157
+ return deepMerge(DEFAULT_CONFIG, parsed);
158
+ } catch {
159
+ // Return defaults on any error (parse error, read error, etc.)
160
+ return { ...DEFAULT_CONFIG };
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Save configuration to ~/.enact/config.yaml
166
+ * Creates the ~/.enact/ directory if it doesn't exist
167
+ * @param config - The configuration to save
168
+ */
169
+ export function saveConfig(config: EnactConfig): void {
170
+ const configPath = getConfigPath();
171
+ const enactHome = getEnactHome();
172
+
173
+ // Ensure ~/.enact/ directory exists
174
+ if (!existsSync(enactHome)) {
175
+ mkdirSync(enactHome, { recursive: true });
176
+ }
177
+
178
+ // Ensure parent directory exists (should be ~/.enact/ but be safe)
179
+ const parentDir = dirname(configPath);
180
+ if (!existsSync(parentDir)) {
181
+ mkdirSync(parentDir, { recursive: true });
182
+ }
183
+
184
+ const yamlContent = yaml.dump(config, {
185
+ indent: 2,
186
+ lineWidth: 120,
187
+ noRefs: true,
188
+ });
189
+
190
+ writeFileSync(configPath, yamlContent, "utf-8");
191
+ }
192
+
193
+ /**
194
+ * Get a configuration value by dot-notation key path
195
+ * @param key - Dot-notation path (e.g., "trust.policy", "cache.maxSizeMb")
196
+ * @param defaultValue - Default value if key doesn't exist
197
+ * @returns The configuration value or default
198
+ */
199
+ export function getConfigValue<T>(key: string, defaultValue: T): T {
200
+ const config = loadConfig();
201
+ const keys = key.split(".");
202
+
203
+ let current: unknown = config;
204
+ for (const k of keys) {
205
+ if (current === null || current === undefined || typeof current !== "object") {
206
+ return defaultValue;
207
+ }
208
+ current = (current as Record<string, unknown>)[k];
209
+ }
210
+
211
+ return current === undefined ? defaultValue : (current as T);
212
+ }
213
+
214
+ /**
215
+ * Set a configuration value by dot-notation key path and persist
216
+ * @param key - Dot-notation path (e.g., "trust.policy", "cache.maxSizeMb")
217
+ * @param value - The value to set
218
+ */
219
+ export function setConfigValue<T>(key: string, value: T): void {
220
+ const config = loadConfig();
221
+ const keys = key.split(".");
222
+ const lastKey = keys.pop();
223
+
224
+ if (!lastKey) {
225
+ throw new Error("Invalid configuration key");
226
+ }
227
+
228
+ // Navigate to parent object, creating intermediate objects as needed
229
+ let current: Record<string, unknown> = config as Record<string, unknown>;
230
+ for (const k of keys) {
231
+ if (current[k] === undefined || current[k] === null || typeof current[k] !== "object") {
232
+ current[k] = {};
233
+ }
234
+ current = current[k] as Record<string, unknown>;
235
+ }
236
+
237
+ // Set the value
238
+ current[lastKey] = value;
239
+
240
+ // Persist
241
+ saveConfig(config);
242
+ }
243
+
244
+ /**
245
+ * Reset configuration to defaults
246
+ * This will overwrite the existing config file
247
+ */
248
+ export function resetConfig(): void {
249
+ saveConfig({ ...DEFAULT_CONFIG });
250
+ }
251
+
252
+ /**
253
+ * Check if a configuration file exists
254
+ * @returns true if ~/.enact/config.yaml exists
255
+ */
256
+ export function configExists(): boolean {
257
+ return existsSync(getConfigPath());
258
+ }
259
+
260
+ // =============================================================================
261
+ // Local Trust Management
262
+ // =============================================================================
263
+
264
+ /**
265
+ * Get list of trusted identities from local config
266
+ * @returns Array of identities in provider:identity format
267
+ */
268
+ export function getTrustedIdentities(): string[] {
269
+ const config = loadConfig();
270
+ return config.trust?.auditors ?? [];
271
+ }
272
+
273
+ /**
274
+ * Add an identity to the local trusted list
275
+ * @param identity - Identity in provider:identity format (e.g., github:alice)
276
+ * @returns true if added, false if already exists
277
+ */
278
+ export function addTrustedIdentity(identity: string): boolean {
279
+ const config = loadConfig();
280
+ const auditors = config.trust?.auditors ?? [];
281
+
282
+ // Check if already exists
283
+ if (auditors.includes(identity)) {
284
+ return false;
285
+ }
286
+
287
+ // Add to list
288
+ auditors.push(identity);
289
+
290
+ // Update config
291
+ if (!config.trust) {
292
+ config.trust = { ...DEFAULT_CONFIG.trust };
293
+ }
294
+ config.trust.auditors = auditors;
295
+
296
+ saveConfig(config);
297
+ return true;
298
+ }
299
+
300
+ /**
301
+ * Remove an identity from the local trusted list
302
+ * @param identity - Identity in provider:identity format
303
+ * @returns true if removed, false if not found
304
+ */
305
+ export function removeTrustedIdentity(identity: string): boolean {
306
+ const config = loadConfig();
307
+ const auditors = config.trust?.auditors ?? [];
308
+
309
+ const index = auditors.indexOf(identity);
310
+ if (index === -1) {
311
+ return false;
312
+ }
313
+
314
+ // Remove from list
315
+ auditors.splice(index, 1);
316
+
317
+ // Update config
318
+ if (!config.trust) {
319
+ config.trust = { ...DEFAULT_CONFIG.trust };
320
+ }
321
+ config.trust.auditors = auditors;
322
+
323
+ saveConfig(config);
324
+ return true;
325
+ }
326
+
327
+ /**
328
+ * Check if an identity is in the local trusted list
329
+ * Supports wildcards like github:my-org/* and *@company.com
330
+ * @param identity - Identity to check
331
+ * @returns true if trusted
332
+ */
333
+ export function isIdentityTrusted(identity: string): boolean {
334
+ const trustedIdentities = getTrustedIdentities();
335
+
336
+ // Check exact match first
337
+ if (trustedIdentities.includes(identity)) {
338
+ return true;
339
+ }
340
+
341
+ // Check wildcard matches (e.g., github:my-org/* matches github:my-org/alice)
342
+ for (const trusted of trustedIdentities) {
343
+ if (trusted.endsWith("/*")) {
344
+ const prefix = trusted.slice(0, -2); // Remove /*
345
+ if (identity.startsWith(`${prefix}/`)) {
346
+ return true;
347
+ }
348
+ }
349
+
350
+ // Check email wildcards (e.g., *@company.com)
351
+ if (trusted.includes("*@")) {
352
+ const domainPart = trusted.split("*@")[1];
353
+ if (identity.endsWith(`@${domainPart}`)) {
354
+ return true;
355
+ }
356
+ }
357
+ }
358
+
359
+ return false;
360
+ }
361
+
362
+ // Legacy aliases for backward compatibility
363
+ /** @deprecated Use getTrustedIdentities instead */
364
+ export const getTrustedAuditors = getTrustedIdentities;
365
+ /** @deprecated Use addTrustedIdentity instead */
366
+ export const addTrustedAuditor = addTrustedIdentity;
367
+ /** @deprecated Use removeTrustedIdentity instead */
368
+ export const removeTrustedAuditor = removeTrustedIdentity;
369
+ /** @deprecated Use isIdentityTrusted instead */
370
+ export const isAuditorTrusted = isIdentityTrusted;
371
+
372
+ /**
373
+ * Convert OIDC identity to provider:identity format
374
+ * @param email - Email from Sigstore certificate
375
+ * @param issuer - OIDC issuer URL (optional, improves accuracy)
376
+ * @param username - Provider username if known (optional)
377
+ * @returns Identity in provider:identity format (e.g., github:keithagroves)
378
+ */
379
+ export function emailToProviderIdentity(email: string, issuer?: string, username?: string): string {
380
+ // If we have a username and can determine the provider, use that
381
+ if (username && issuer) {
382
+ const provider = issuerToProvider(issuer);
383
+ if (provider) {
384
+ return `${provider}:${username}`;
385
+ }
386
+ }
387
+
388
+ // Determine provider from issuer URL if available
389
+ if (issuer) {
390
+ const provider = issuerToProvider(issuer);
391
+ if (provider) {
392
+ // Try to extract username from email for GitHub
393
+ if (provider === "github" && email.endsWith("@users.noreply.github.com")) {
394
+ // GitHub noreply format: "123456+username@users.noreply.github.com"
395
+ // or just "username@users.noreply.github.com"
396
+ const localPart = email.replace("@users.noreply.github.com", "");
397
+ const plusIndex = localPart.indexOf("+");
398
+ const extractedUsername = plusIndex >= 0 ? localPart.slice(plusIndex + 1) : localPart;
399
+ return `github:${extractedUsername}`;
400
+ }
401
+ // Use email as the identity since we don't have username
402
+ return `${provider}:${email}`;
403
+ }
404
+ }
405
+
406
+ // Common OIDC providers and their email domains (fallback)
407
+ const providerMap: Record<string, string> = {
408
+ "@users.noreply.github.com": "github",
409
+ "@github.com": "github",
410
+ "@gmail.com": "google",
411
+ "@googlemail.com": "google",
412
+ "@outlook.com": "microsoft",
413
+ "@hotmail.com": "microsoft",
414
+ "@live.com": "microsoft",
415
+ };
416
+
417
+ // Try to match provider by email domain
418
+ for (const [domain, provider] of Object.entries(providerMap)) {
419
+ if (email.endsWith(domain)) {
420
+ let extractedUsername = email.substring(0, email.length - domain.length);
421
+ // Handle GitHub noreply format: "123456+username@users.noreply.github.com"
422
+ if (provider === "github" && domain === "@users.noreply.github.com") {
423
+ const plusIndex = extractedUsername.indexOf("+");
424
+ if (plusIndex >= 0) {
425
+ extractedUsername = extractedUsername.slice(plusIndex + 1);
426
+ }
427
+ }
428
+ return `${provider}:${extractedUsername}`;
429
+ }
430
+ }
431
+
432
+ // If no match, check for GitHub workflow identity
433
+ // Format: https://github.com/{org}/{workflow}
434
+ if (email.startsWith("https://github.com/")) {
435
+ const path = email.replace("https://github.com/", "");
436
+ return `github:${path}`;
437
+ }
438
+
439
+ // Fall back to email as-is
440
+ return email;
441
+ }
442
+
443
+ /**
444
+ * Convert OIDC issuer URL to provider name
445
+ */
446
+ function issuerToProvider(issuer: string): string | undefined {
447
+ if (issuer.includes("github.com")) return "github";
448
+ if (issuer.includes("accounts.google.com")) return "google";
449
+ if (issuer.includes("login.microsoftonline.com")) return "microsoft";
450
+ if (issuer.includes("gitlab.com")) return "gitlab";
451
+ return undefined;
452
+ }
453
+
454
+ /**
455
+ * Get minimum required attestations from config
456
+ * @returns Minimum number of trusted attestations required
457
+ */
458
+ export function getMinimumAttestations(): number {
459
+ const config = loadConfig();
460
+ return config.trust?.minimum_attestations ?? 1;
461
+ }
462
+
463
+ /**
464
+ * Get trust policy from config
465
+ * @returns Trust policy: 'require_attestation', 'prompt', or 'allow'
466
+ */
467
+ export function getTrustPolicy(): "require_attestation" | "prompt" | "allow" {
468
+ const config = loadConfig();
469
+ // Handle legacy 'require_audit' value (cast to string for comparison)
470
+ const policy = config.trust?.policy as string | undefined;
471
+ if (policy === "require_audit") {
472
+ return "require_attestation";
473
+ }
474
+ // Default to require_attestation - trust must be explicit
475
+ return (policy as "require_attestation" | "prompt" | "allow") ?? "require_attestation";
476
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Enact Constants
3
+ *
4
+ * Centralized configuration for Enact URLs and identifiers.
5
+ * This ensures consistency across all packages.
6
+ *
7
+ * Note: Attestation type constants (ENACT_TOOL_TYPE, ENACT_AUDIT_TYPE, etc.)
8
+ * are defined in @enactprotocol/trust and re-exported here for convenience.
9
+ */
10
+
11
+ // Re-export attestation constants from trust package
12
+ export {
13
+ ENACT_BASE_URL,
14
+ ENACT_TOOL_TYPE,
15
+ ENACT_AUDIT_TYPE,
16
+ ENACT_BUILD_TYPE,
17
+ INTOTO_STATEMENT_TYPE,
18
+ SLSA_PROVENANCE_TYPE,
19
+ } from "@enactprotocol/trust";
20
+
21
+ // ============================================================================
22
+ // Runtime URLs (can be overridden by environment)
23
+ // ============================================================================
24
+
25
+ /**
26
+ * The Enact API base URL (Supabase Edge Functions)
27
+ * Override with ENACT_API_URL environment variable
28
+ */
29
+ import { ENACT_BASE_URL as BASE_URL } from "@enactprotocol/trust";
30
+ export const ENACT_API_URL = process.env.ENACT_API_URL || `${BASE_URL}/api`;
31
+
32
+ /**
33
+ * The Enact web application URL
34
+ * Override with ENACT_WEB_URL environment variable
35
+ */
36
+ export const ENACT_WEB_URL = process.env.ENACT_WEB_URL || BASE_URL;