@ambitresearch/genie 1.3.1

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 (301) hide show
  1. package/README.md +15 -0
  2. package/dist/auth/bearer.d.ts +49 -0
  3. package/dist/auth/bearer.d.ts.map +1 -0
  4. package/dist/auth/bearer.js +159 -0
  5. package/dist/auth/bearer.js.map +1 -0
  6. package/dist/auth/oauth/authorize.d.ts +19 -0
  7. package/dist/auth/oauth/authorize.d.ts.map +1 -0
  8. package/dist/auth/oauth/authorize.js +111 -0
  9. package/dist/auth/oauth/authorize.js.map +1 -0
  10. package/dist/auth/oauth/config.d.ts +10 -0
  11. package/dist/auth/oauth/config.d.ts.map +1 -0
  12. package/dist/auth/oauth/config.js +16 -0
  13. package/dist/auth/oauth/config.js.map +1 -0
  14. package/dist/auth/oauth/dcr.d.ts +15 -0
  15. package/dist/auth/oauth/dcr.d.ts.map +1 -0
  16. package/dist/auth/oauth/dcr.js +38 -0
  17. package/dist/auth/oauth/dcr.js.map +1 -0
  18. package/dist/auth/oauth/jwt.d.ts +13 -0
  19. package/dist/auth/oauth/jwt.d.ts.map +1 -0
  20. package/dist/auth/oauth/jwt.js +46 -0
  21. package/dist/auth/oauth/jwt.js.map +1 -0
  22. package/dist/auth/oauth/metadata.d.ts +9 -0
  23. package/dist/auth/oauth/metadata.d.ts.map +1 -0
  24. package/dist/auth/oauth/metadata.js +19 -0
  25. package/dist/auth/oauth/metadata.js.map +1 -0
  26. package/dist/auth/oauth/router.d.ts +32 -0
  27. package/dist/auth/oauth/router.d.ts.map +1 -0
  28. package/dist/auth/oauth/router.js +159 -0
  29. package/dist/auth/oauth/router.js.map +1 -0
  30. package/dist/auth/oauth/store.d.ts +55 -0
  31. package/dist/auth/oauth/store.d.ts.map +1 -0
  32. package/dist/auth/oauth/store.js +63 -0
  33. package/dist/auth/oauth/store.js.map +1 -0
  34. package/dist/auth/oauth/token.d.ts +21 -0
  35. package/dist/auth/oauth/token.d.ts.map +1 -0
  36. package/dist/auth/oauth/token.js +77 -0
  37. package/dist/auth/oauth/token.js.map +1 -0
  38. package/dist/auth/oidc/group-policy.d.ts +43 -0
  39. package/dist/auth/oidc/group-policy.d.ts.map +1 -0
  40. package/dist/auth/oidc/group-policy.js +54 -0
  41. package/dist/auth/oidc/group-policy.js.map +1 -0
  42. package/dist/auth/oidc/verifier.d.ts +42 -0
  43. package/dist/auth/oidc/verifier.d.ts.map +1 -0
  44. package/dist/auth/oidc/verifier.js +145 -0
  45. package/dist/auth/oidc/verifier.js.map +1 -0
  46. package/dist/auth/token-cli.d.ts +9 -0
  47. package/dist/auth/token-cli.d.ts.map +1 -0
  48. package/dist/auth/token-cli.js +98 -0
  49. package/dist/auth/token-cli.js.map +1 -0
  50. package/dist/cli.d.ts +3 -0
  51. package/dist/cli.d.ts.map +1 -0
  52. package/dist/cli.js +149 -0
  53. package/dist/cli.js.map +1 -0
  54. package/dist/config/redact.d.ts +20 -0
  55. package/dist/config/redact.d.ts.map +1 -0
  56. package/dist/config/redact.js +77 -0
  57. package/dist/config/redact.js.map +1 -0
  58. package/dist/config/secrets.d.ts +100 -0
  59. package/dist/config/secrets.d.ts.map +1 -0
  60. package/dist/config/secrets.js +130 -0
  61. package/dist/config/secrets.js.map +1 -0
  62. package/dist/framework/html.d.ts +103 -0
  63. package/dist/framework/html.d.ts.map +1 -0
  64. package/dist/framework/html.js +271 -0
  65. package/dist/framework/html.js.map +1 -0
  66. package/dist/framework/interface.d.ts +130 -0
  67. package/dist/framework/interface.d.ts.map +1 -0
  68. package/dist/framework/interface.js +112 -0
  69. package/dist/framework/interface.js.map +1 -0
  70. package/dist/framework/react.d.ts +133 -0
  71. package/dist/framework/react.d.ts.map +1 -0
  72. package/dist/framework/react.js +243 -0
  73. package/dist/framework/react.js.map +1 -0
  74. package/dist/framework/vue.d.ts +99 -0
  75. package/dist/framework/vue.d.ts.map +1 -0
  76. package/dist/framework/vue.js +375 -0
  77. package/dist/framework/vue.js.map +1 -0
  78. package/dist/index.d.ts +3 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +3 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/llm/client.d.ts +81 -0
  83. package/dist/llm/client.d.ts.map +1 -0
  84. package/dist/llm/client.js +141 -0
  85. package/dist/llm/client.js.map +1 -0
  86. package/dist/llm/component-response.d.ts +111 -0
  87. package/dist/llm/component-response.d.ts.map +1 -0
  88. package/dist/llm/component-response.js +167 -0
  89. package/dist/llm/component-response.js.map +1 -0
  90. package/dist/llm/generated-files.d.ts +10 -0
  91. package/dist/llm/generated-files.d.ts.map +1 -0
  92. package/dist/llm/generated-files.js +26 -0
  93. package/dist/llm/generated-files.js.map +1 -0
  94. package/dist/llm/prompts/CHANGELOG.md +70 -0
  95. package/dist/llm/prompts/generate-component.system.md +99 -0
  96. package/dist/llm/prompts/refine-component.system.md +112 -0
  97. package/dist/llm/prompts.d.ts +66 -0
  98. package/dist/llm/prompts.d.ts.map +1 -0
  99. package/dist/llm/prompts.js +78 -0
  100. package/dist/llm/prompts.js.map +1 -0
  101. package/dist/llm/retry.d.ts +183 -0
  102. package/dist/llm/retry.d.ts.map +1 -0
  103. package/dist/llm/retry.js +475 -0
  104. package/dist/llm/retry.js.map +1 -0
  105. package/dist/llm/schema.d.ts +129 -0
  106. package/dist/llm/schema.d.ts.map +1 -0
  107. package/dist/llm/schema.js +147 -0
  108. package/dist/llm/schema.js.map +1 -0
  109. package/dist/llm/validate.d.ts +107 -0
  110. package/dist/llm/validate.d.ts.map +1 -0
  111. package/dist/llm/validate.js +174 -0
  112. package/dist/llm/validate.js.map +1 -0
  113. package/dist/manifest/compiler.d.ts +112 -0
  114. package/dist/manifest/compiler.d.ts.map +1 -0
  115. package/dist/manifest/compiler.js +306 -0
  116. package/dist/manifest/compiler.js.map +1 -0
  117. package/dist/manifest/ensure.d.ts +46 -0
  118. package/dist/manifest/ensure.d.ts.map +1 -0
  119. package/dist/manifest/ensure.js +49 -0
  120. package/dist/manifest/ensure.js.map +1 -0
  121. package/dist/manifest/index.d.ts +13 -0
  122. package/dist/manifest/index.d.ts.map +1 -0
  123. package/dist/manifest/index.js +13 -0
  124. package/dist/manifest/index.js.map +1 -0
  125. package/dist/middleware/plan-guard.d.ts +167 -0
  126. package/dist/middleware/plan-guard.d.ts.map +1 -0
  127. package/dist/middleware/plan-guard.js +190 -0
  128. package/dist/middleware/plan-guard.js.map +1 -0
  129. package/dist/plans/index.d.ts +71 -0
  130. package/dist/plans/index.d.ts.map +1 -0
  131. package/dist/plans/index.js +252 -0
  132. package/dist/plans/index.js.map +1 -0
  133. package/dist/schemas/component.schema.json +114 -0
  134. package/dist/server.d.ts +96 -0
  135. package/dist/server.d.ts.map +1 -0
  136. package/dist/server.js +258 -0
  137. package/dist/server.js.map +1 -0
  138. package/dist/store/empty-manifest.d.ts +58 -0
  139. package/dist/store/empty-manifest.d.ts.map +1 -0
  140. package/dist/store/empty-manifest.js +59 -0
  141. package/dist/store/empty-manifest.js.map +1 -0
  142. package/dist/store/git-host.d.ts +120 -0
  143. package/dist/store/git-host.d.ts.map +1 -0
  144. package/dist/store/git-host.js +796 -0
  145. package/dist/store/git-host.js.map +1 -0
  146. package/dist/store/index.d.ts +5 -0
  147. package/dist/store/index.d.ts.map +1 -0
  148. package/dist/store/index.js +4 -0
  149. package/dist/store/index.js.map +1 -0
  150. package/dist/store/interface.d.ts +322 -0
  151. package/dist/store/interface.d.ts.map +1 -0
  152. package/dist/store/interface.js +100 -0
  153. package/dist/store/interface.js.map +1 -0
  154. package/dist/store/kit-files.d.ts +88 -0
  155. package/dist/store/kit-files.d.ts.map +1 -0
  156. package/dist/store/kit-files.js +199 -0
  157. package/dist/store/kit-files.js.map +1 -0
  158. package/dist/store/local.d.ts +70 -0
  159. package/dist/store/local.d.ts.map +1 -0
  160. package/dist/store/local.js +651 -0
  161. package/dist/store/local.js.map +1 -0
  162. package/dist/store/manifest.d.ts +44 -0
  163. package/dist/store/manifest.d.ts.map +1 -0
  164. package/dist/store/manifest.js +116 -0
  165. package/dist/store/manifest.js.map +1 -0
  166. package/dist/store/viewer-assets.d.ts +20 -0
  167. package/dist/store/viewer-assets.d.ts.map +1 -0
  168. package/dist/store/viewer-assets.js +118 -0
  169. package/dist/store/viewer-assets.js.map +1 -0
  170. package/dist/sync/anchor.d.ts +125 -0
  171. package/dist/sync/anchor.d.ts.map +1 -0
  172. package/dist/sync/anchor.js +210 -0
  173. package/dist/sync/anchor.js.map +1 -0
  174. package/dist/sync/index.d.ts +15 -0
  175. package/dist/sync/index.d.ts.map +1 -0
  176. package/dist/sync/index.js +15 -0
  177. package/dist/sync/index.js.map +1 -0
  178. package/dist/sync/orchestrator.d.ts +160 -0
  179. package/dist/sync/orchestrator.d.ts.map +1 -0
  180. package/dist/sync/orchestrator.js +288 -0
  181. package/dist/sync/orchestrator.js.map +1 -0
  182. package/dist/tools/bind_kit.d.ts +29 -0
  183. package/dist/tools/bind_kit.d.ts.map +1 -0
  184. package/dist/tools/bind_kit.js +70 -0
  185. package/dist/tools/bind_kit.js.map +1 -0
  186. package/dist/tools/conjure.d.ts +141 -0
  187. package/dist/tools/conjure.d.ts.map +1 -0
  188. package/dist/tools/conjure.js +655 -0
  189. package/dist/tools/conjure.js.map +1 -0
  190. package/dist/tools/conjure_screen.d.ts +174 -0
  191. package/dist/tools/conjure_screen.d.ts.map +1 -0
  192. package/dist/tools/conjure_screen.js +458 -0
  193. package/dist/tools/conjure_screen.js.map +1 -0
  194. package/dist/tools/create_kit.d.ts +32 -0
  195. package/dist/tools/create_kit.d.ts.map +1 -0
  196. package/dist/tools/create_kit.js +131 -0
  197. package/dist/tools/create_kit.js.map +1 -0
  198. package/dist/tools/create_project.d.ts +267 -0
  199. package/dist/tools/create_project.d.ts.map +1 -0
  200. package/dist/tools/create_project.js +546 -0
  201. package/dist/tools/create_project.js.map +1 -0
  202. package/dist/tools/delete_files.d.ts +93 -0
  203. package/dist/tools/delete_files.d.ts.map +1 -0
  204. package/dist/tools/delete_files.js +276 -0
  205. package/dist/tools/delete_files.js.map +1 -0
  206. package/dist/tools/delete_project.d.ts +43 -0
  207. package/dist/tools/delete_project.d.ts.map +1 -0
  208. package/dist/tools/delete_project.js +118 -0
  209. package/dist/tools/delete_project.js.map +1 -0
  210. package/dist/tools/get_kit.d.ts +28 -0
  211. package/dist/tools/get_kit.d.ts.map +1 -0
  212. package/dist/tools/get_kit.js +95 -0
  213. package/dist/tools/get_kit.js.map +1 -0
  214. package/dist/tools/get_project.d.ts +37 -0
  215. package/dist/tools/get_project.d.ts.map +1 -0
  216. package/dist/tools/get_project.js +71 -0
  217. package/dist/tools/get_project.js.map +1 -0
  218. package/dist/tools/list_components.d.ts +42 -0
  219. package/dist/tools/list_components.d.ts.map +1 -0
  220. package/dist/tools/list_components.js +125 -0
  221. package/dist/tools/list_components.js.map +1 -0
  222. package/dist/tools/list_files.d.ts +32 -0
  223. package/dist/tools/list_files.d.ts.map +1 -0
  224. package/dist/tools/list_files.js +109 -0
  225. package/dist/tools/list_files.js.map +1 -0
  226. package/dist/tools/list_kits.d.ts +14 -0
  227. package/dist/tools/list_kits.d.ts.map +1 -0
  228. package/dist/tools/list_kits.js +41 -0
  229. package/dist/tools/list_kits.js.map +1 -0
  230. package/dist/tools/list_projects.d.ts +23 -0
  231. package/dist/tools/list_projects.d.ts.map +1 -0
  232. package/dist/tools/list_projects.js +66 -0
  233. package/dist/tools/list_projects.js.map +1 -0
  234. package/dist/tools/plan.d.ts +16 -0
  235. package/dist/tools/plan.d.ts.map +1 -0
  236. package/dist/tools/plan.js +146 -0
  237. package/dist/tools/plan.js.map +1 -0
  238. package/dist/tools/preview.d.ts +289 -0
  239. package/dist/tools/preview.d.ts.map +1 -0
  240. package/dist/tools/preview.js +672 -0
  241. package/dist/tools/preview.js.map +1 -0
  242. package/dist/tools/read_file.d.ts +40 -0
  243. package/dist/tools/read_file.d.ts.map +1 -0
  244. package/dist/tools/read_file.js +114 -0
  245. package/dist/tools/read_file.js.map +1 -0
  246. package/dist/tools/refine.d.ts +220 -0
  247. package/dist/tools/refine.d.ts.map +1 -0
  248. package/dist/tools/refine.js +501 -0
  249. package/dist/tools/refine.js.map +1 -0
  250. package/dist/tools/validate.d.ts +74 -0
  251. package/dist/tools/validate.d.ts.map +1 -0
  252. package/dist/tools/validate.js +224 -0
  253. package/dist/tools/validate.js.map +1 -0
  254. package/dist/tools/write_files.d.ts +158 -0
  255. package/dist/tools/write_files.d.ts.map +1 -0
  256. package/dist/tools/write_files.js +541 -0
  257. package/dist/tools/write_files.js.map +1 -0
  258. package/dist/transport.d.ts +77 -0
  259. package/dist/transport.d.ts.map +1 -0
  260. package/dist/transport.js +448 -0
  261. package/dist/transport.js.map +1 -0
  262. package/dist/ui/card-asset-broker.d.ts +42 -0
  263. package/dist/ui/card-asset-broker.d.ts.map +1 -0
  264. package/dist/ui/card-asset-broker.js +479 -0
  265. package/dist/ui/card-asset-broker.js.map +1 -0
  266. package/dist/ui/grid-resource.d.ts +252 -0
  267. package/dist/ui/grid-resource.d.ts.map +1 -0
  268. package/dist/ui/grid-resource.js +680 -0
  269. package/dist/ui/grid-resource.js.map +1 -0
  270. package/dist/ui/viewer-static/index.html +95 -0
  271. package/dist/ui/viewer-static/viewer.css +451 -0
  272. package/dist/ui/viewer-static/viewer.js +1405 -0
  273. package/dist/validate/full-scan.d.ts +87 -0
  274. package/dist/validate/full-scan.d.ts.map +1 -0
  275. package/dist/validate/full-scan.js +299 -0
  276. package/dist/validate/full-scan.js.map +1 -0
  277. package/dist/validate/index.d.ts +15 -0
  278. package/dist/validate/index.d.ts.map +1 -0
  279. package/dist/validate/index.js +18 -0
  280. package/dist/validate/index.js.map +1 -0
  281. package/dist/validate/marker.d.ts +89 -0
  282. package/dist/validate/marker.d.ts.map +1 -0
  283. package/dist/validate/marker.js +87 -0
  284. package/dist/validate/marker.js.map +1 -0
  285. package/dist/validate/phash.d.ts +135 -0
  286. package/dist/validate/phash.d.ts.map +1 -0
  287. package/dist/validate/phash.js +307 -0
  288. package/dist/validate/phash.js.map +1 -0
  289. package/dist/validate/render.d.ts +38 -0
  290. package/dist/validate/render.d.ts.map +1 -0
  291. package/dist/validate/render.js +155 -0
  292. package/dist/validate/render.js.map +1 -0
  293. package/dist/watch/index.d.ts +12 -0
  294. package/dist/watch/index.d.ts.map +1 -0
  295. package/dist/watch/index.js +12 -0
  296. package/dist/watch/index.js.map +1 -0
  297. package/dist/watch/watcher.d.ts +65 -0
  298. package/dist/watch/watcher.d.ts.map +1 -0
  299. package/dist/watch/watcher.js +238 -0
  300. package/dist/watch/watcher.js.map +1 -0
  301. package/package.json +70 -0
@@ -0,0 +1,145 @@
1
+ /**
2
+ * OIDC relying-party token verification (DRO-276 / M5-04).
3
+ *
4
+ * Complements `../oauth/*` (M5-01's self-issued authorization server): this
5
+ * module lets genie ALSO act as an OIDC *relying party* against an adopter's
6
+ * own external Identity Provider (Keycloak, Okta, Auth0, Authentik, etc.),
7
+ * validating a bearer token that IdP issued rather than one genie minted
8
+ * itself. The provider must expose OIDC discovery/JWKS, issue signed JWT
9
+ * access tokens for the genie resource/API using the RFC 9068 `typ: at+jwt`
10
+ * discriminator, and map group membership into a `groups` claim. Opaque
11
+ * access tokens and OIDC ID tokens are not supported. Verification checks the
12
+ * signature plus exact `iss`, configured `aud`, required RFC 9068 claims, and
13
+ * token type before enforcing group membership.
14
+ *
15
+ * Configuration is opt-in via env, mirroring `resolveOAuthSigningKey`'s
16
+ * "refuse to run without it" posture for `../oauth/config.ts`:
17
+ * - GENIE_OIDC_ISSUER — the provider's issuer URL (used to derive its
18
+ * `/.well-known/openid-configuration` + JWKS endpoint).
19
+ * - GENIE_OIDC_AUDIENCE — the resource/API identifier expected in the
20
+ * access token's `aud` claim.
21
+ * Both must be non-empty for OIDC RP mode to activate. When neither variable
22
+ * is present the feature is disabled; a partial or empty configuration is a
23
+ * startup error so an intended auth gate cannot fail open.
24
+ */
25
+ import { createRemoteJWKSet, jwtVerify } from "jose";
26
+ import { isIP } from "node:net";
27
+ import { enforceGroupAccess, REQUIRED_GROUP } from "./group-policy.js";
28
+ export class MissingOidcConfigError extends Error {
29
+ }
30
+ /** Read {@link OidcVerifierConfig} from env. A missing or empty issuer or
31
+ * audience is invalid once either OIDC variable is present. */
32
+ export function resolveOidcConfig(env = process.env) {
33
+ const issuer = env.GENIE_OIDC_ISSUER;
34
+ const audience = env.GENIE_OIDC_AUDIENCE;
35
+ if (!issuer || !audience) {
36
+ throw new MissingOidcConfigError("GENIE_OIDC_ISSUER and GENIE_OIDC_AUDIENCE must both be set to enable OIDC relying-party mode.");
37
+ }
38
+ return { issuer, audience, requiredGroup: env.GENIE_OIDC_REQUIRED_GROUP || REQUIRED_GROUP };
39
+ }
40
+ const DEFAULT_DISCOVERY_TIMEOUT_MS = 10_000;
41
+ function isLoopbackHostname(hostname) {
42
+ const normalized = hostname.replace(/^\[|\]$/g, "").toLowerCase();
43
+ return (normalized === "localhost" ||
44
+ normalized.endsWith(".localhost") ||
45
+ (isIP(normalized) === 4 && normalized.split(".")[0] === "127") ||
46
+ normalized === "::1" ||
47
+ normalized === "0:0:0:0:0:0:0:1" ||
48
+ normalized === "::ffff:127.0.0.1");
49
+ }
50
+ function requireSecureOidcUrl(value, label, allowPlaintextLoopback) {
51
+ const url = new URL(value);
52
+ if (url.username || url.password || url.hash || (label === "issuer" && url.search)) {
53
+ throw new Error(`OIDC ${label} must not contain credentials, a fragment, or an issuer query.`);
54
+ }
55
+ const allowedDevelopmentUrl = allowPlaintextLoopback && url.protocol === "http:" && isLoopbackHostname(url.hostname);
56
+ if (url.protocol !== "https:" && !allowedDevelopmentUrl) {
57
+ throw new Error(`OIDC ${label} must use HTTPS unless it is a loopback development URL.`);
58
+ }
59
+ return url;
60
+ }
61
+ function enforceAccessTokenClaimTypes(claims) {
62
+ for (const claim of ["sub", "client_id", "jti"]) {
63
+ if (typeof claims[claim] !== "string") {
64
+ throw new Error(`OIDC access token claim "${claim}" must be a string.`);
65
+ }
66
+ }
67
+ }
68
+ /** Fetch `${issuer}/.well-known/openid-configuration` and return its
69
+ * `jwks_uri` using the RFC 8414 / OIDC Discovery 1.0 endpoint required by
70
+ * this integration. */
71
+ async function discoverJwksUri(discoveryIssuer, expectedIssuer, timeoutMs, allowPlaintextLoopback) {
72
+ const discoveryUrl = `${discoveryIssuer}/.well-known/openid-configuration`;
73
+ const signal = AbortSignal.timeout(timeoutMs);
74
+ let res;
75
+ try {
76
+ res = await fetch(discoveryUrl, { signal, redirect: "manual" });
77
+ }
78
+ catch (error) {
79
+ if (signal.aborted) {
80
+ throw new Error(`OIDC discovery timed out after ${timeoutMs} ms: GET ${discoveryUrl}`, {
81
+ cause: error,
82
+ });
83
+ }
84
+ throw error;
85
+ }
86
+ if (!res.ok) {
87
+ throw new Error(`OIDC discovery failed: GET ${discoveryUrl} -> ${res.status}`);
88
+ }
89
+ const doc = (await res.json());
90
+ if (doc.issuer !== expectedIssuer) {
91
+ throw new Error(`OIDC discovery issuer mismatch: expected ${expectedIssuer}, received ${String(doc.issuer)}.`);
92
+ }
93
+ if (!doc.jwks_uri) {
94
+ throw new Error(`OIDC discovery document at ${discoveryUrl} is missing jwks_uri.`);
95
+ }
96
+ requireSecureOidcUrl(doc.jwks_uri, "jwks_uri", allowPlaintextLoopback);
97
+ return doc.jwks_uri;
98
+ }
99
+ /**
100
+ * Build a verifier bound to a live provider's JWKS. The provider's `jwks_uri`
101
+ * is resolved once via OIDC discovery; the returned JWK set is fetched lazily
102
+ * and cached across calls by `jose`'s `createRemoteJWKSet` (in-flight
103
+ * de-duplication + background re-fetch on unknown `kid` — no manual caching
104
+ * needed here).
105
+ */
106
+ export async function createOidcVerifier(config) {
107
+ const issuer = config.issuer;
108
+ const issuerUrl = requireSecureOidcUrl(issuer, "issuer", true);
109
+ const allowPlaintextLoopback = issuerUrl.protocol === "http:";
110
+ const discoveryIssuer = issuer.endsWith("/") ? issuer.slice(0, -1) : issuer;
111
+ const discoveryTimeoutMs = config.discoveryTimeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS;
112
+ if (!Number.isFinite(discoveryTimeoutMs) || discoveryTimeoutMs <= 0) {
113
+ throw new Error("discoveryTimeoutMs must be a positive finite number.");
114
+ }
115
+ const jwksUri = await discoverJwksUri(discoveryIssuer, issuer, discoveryTimeoutMs, allowPlaintextLoopback);
116
+ const jwks = createRemoteJWKSet(new URL(jwksUri));
117
+ const requiredGroup = config.requiredGroup ?? REQUIRED_GROUP;
118
+ return {
119
+ issuer,
120
+ audience: config.audience,
121
+ requiredGroup,
122
+ async verify(token) {
123
+ const { payload } = await jwtVerify(token, jwks, {
124
+ issuer,
125
+ audience: config.audience,
126
+ typ: "at+jwt",
127
+ requiredClaims: ["exp", "sub", "client_id", "iat", "jti"],
128
+ });
129
+ const claims = payload;
130
+ enforceAccessTokenClaimTypes(claims);
131
+ enforceGroupAccess(claims, requiredGroup);
132
+ return claims;
133
+ },
134
+ };
135
+ }
136
+ /** Same as {@link createOidcVerifier}, but returns `undefined` only when both
137
+ * OIDC variables are absent. Partial or explicitly empty configuration is a
138
+ * startup error. */
139
+ export async function tryCreateOidcVerifier(env = process.env) {
140
+ if (env.GENIE_OIDC_ISSUER === undefined && env.GENIE_OIDC_AUDIENCE === undefined) {
141
+ return undefined;
142
+ }
143
+ return createOidcVerifier(resolveOidcConfig(env));
144
+ }
145
+ //# sourceMappingURL=verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../../src/auth/oidc/verifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface TokenCliResult {
2
+ /** Text to print to stdout. */
3
+ output: string;
4
+ /** Process exit code the caller should use. */
5
+ exitCode: number;
6
+ }
7
+ /** Run `genie token <subcommand> ...args`. Never calls process.exit. */
8
+ export declare function runTokenCli(argv: string[]): Promise<TokenCliResult>;
9
+ //# sourceMappingURL=token-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-cli.d.ts","sourceRoot":"","sources":["../../src/auth/token-cli.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsCD,wEAAwE;AACxE,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAwDzE"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `genie token` admin CLI (M5-02, DRO-274).
3
+ *
4
+ * Subcommands:
5
+ * genie token create [--sub <id>] [--scope read] [--scope write]
6
+ * genie token list
7
+ * genie token revoke <prefix>
8
+ *
9
+ * Kept a plain argv-in/stdout-out module (no process.exit calls) so cli.ts
10
+ * can unit-test it and control the process lifecycle itself.
11
+ */
12
+ import { createToken, listTokens, revokeToken } from "./bearer.js";
13
+ const TOKEN_HELP = `genie token — manage static Bearer tokens (M5-02)
14
+
15
+ Usage:
16
+ genie token create [--sub <id>] [--scope read] [--scope write]
17
+ genie token list
18
+ genie token revoke <prefix>
19
+
20
+ Notes:
21
+ - "create" prints the plaintext token exactly once. It is not recoverable
22
+ afterward — only its SHA-256 hash is stored.
23
+ - "--scope" may be passed multiple times; defaults to "read" if omitted.
24
+ - "revoke <prefix>" matches any token whose printed prefix starts with
25
+ <prefix> (the value shown by "genie token list").`;
26
+ function parseScopes(argv) {
27
+ const scopes = [];
28
+ let sub;
29
+ const rest = [];
30
+ for (let i = 0; i < argv.length; i++) {
31
+ const arg = argv[i];
32
+ if (arg === undefined)
33
+ continue;
34
+ if (arg === "--scope") {
35
+ const value = argv[++i];
36
+ if (value !== "read" && value !== "write") {
37
+ throw new Error(`--scope must be "read" or "write", got: ${value ?? "<missing>"}`);
38
+ }
39
+ scopes.push(value);
40
+ }
41
+ else if (arg === "--sub") {
42
+ sub = argv[++i];
43
+ }
44
+ else {
45
+ rest.push(arg);
46
+ }
47
+ }
48
+ return { scopes, sub, rest };
49
+ }
50
+ /** Run `genie token <subcommand> ...args`. Never calls process.exit. */
51
+ export async function runTokenCli(argv) {
52
+ const [subcommand, ...rest] = argv;
53
+ if (subcommand === undefined || subcommand === "-h" || subcommand === "--help") {
54
+ return { output: TOKEN_HELP + "\n", exitCode: 0 };
55
+ }
56
+ if (subcommand === "create") {
57
+ const { scopes, sub } = parseScopes(rest);
58
+ const { token, record } = await createToken({
59
+ sub: sub ?? "default",
60
+ scopes: scopes.length > 0 ? scopes : undefined,
61
+ });
62
+ const lines = [
63
+ `Token created (shown once — copy it now, it cannot be displayed again):`,
64
+ "",
65
+ ` ${token}`,
66
+ "",
67
+ ` sub: ${record.sub}`,
68
+ ` scopes: ${record.scopes.join(", ")}`,
69
+ ` prefix: ${record.prefix}`,
70
+ "",
71
+ ];
72
+ return { output: lines.join("\n"), exitCode: 0 };
73
+ }
74
+ if (subcommand === "list") {
75
+ const tokens = await listTokens();
76
+ if (tokens.length === 0) {
77
+ return { output: "No tokens.\n", exitCode: 0 };
78
+ }
79
+ const lines = tokens.map((t) => `${t.prefix}... hash=${t.hash} sub=${t.sub} scopes=${t.scopes.join(",")} createdAt=${t.createdAt} lastUsedAt=${t.lastUsedAt ?? "never"}`);
80
+ return { output: lines.join("\n") + "\n", exitCode: 0 };
81
+ }
82
+ if (subcommand === "revoke") {
83
+ const prefix = rest[0];
84
+ if (prefix === undefined || prefix.trim() === "") {
85
+ return { output: "genie token revoke: missing <prefix> argument.\n", exitCode: 1 };
86
+ }
87
+ const removed = await revokeToken(prefix);
88
+ if (removed === 0) {
89
+ return { output: `No token found matching prefix "${prefix}".\n`, exitCode: 1 };
90
+ }
91
+ return { output: `Revoked ${removed} token(s) matching prefix "${prefix}".\n`, exitCode: 0 };
92
+ }
93
+ return {
94
+ output: `genie token: unknown subcommand "${subcommand}".\n\n${TOKEN_HELP}\n`,
95
+ exitCode: 1,
96
+ };
97
+ }
98
+ //# sourceMappingURL=token-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-cli.js","sourceRoot":"","sources":["../../src/auth/token-cli.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import { createServer, SERVER_INFO } from "./server.js";
3
+ import { resolvePreviewLocality, resolveTransport, startTransport } from "./transport.js";
4
+ import { runTokenCli } from "./auth/token-cli.js";
5
+ import { createRedactingLogger } from "./config/redact.js";
6
+ import { auditLoadedSecrets, loadSecrets } from "./config/secrets.js";
7
+ /** Minimal flag parser — no dependency needed for M0's tiny surface. */
8
+ function parseArgs(argv) {
9
+ const out = { help: false, version: false };
10
+ for (let i = 0; i < argv.length; i++) {
11
+ const arg = argv[i];
12
+ switch (arg) {
13
+ case "--transport":
14
+ out.transport = argv[++i];
15
+ break;
16
+ case "--port":
17
+ out.port = Number(argv[++i]);
18
+ break;
19
+ case "--host":
20
+ out.host = argv[++i];
21
+ break;
22
+ case "--require-bearer-auth":
23
+ out.requireBearerAuth = true;
24
+ break;
25
+ case "--preview-locality": {
26
+ const value = argv[++i];
27
+ if (value === undefined || value.trim() === "" || value.startsWith("-")) {
28
+ throw new Error("--preview-locality requires a value: local or remote.");
29
+ }
30
+ out.previewLocality = value;
31
+ break;
32
+ }
33
+ case "--secrets-from": {
34
+ const value = argv[++i];
35
+ if (value === undefined || value.trim() === "" || value.startsWith("-")) {
36
+ throw new Error("--secrets-from requires a path, e.g. a mounted Docker secret.");
37
+ }
38
+ out.secretsFrom = value;
39
+ break;
40
+ }
41
+ case "-h":
42
+ case "--help":
43
+ out.help = true;
44
+ break;
45
+ case "-v":
46
+ case "--version":
47
+ out.version = true;
48
+ break;
49
+ default:
50
+ // ignore unknown flags for forward-compat
51
+ break;
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+ const HELP = `genie — harness-agnostic MCP server for AI UI-component generation
57
+
58
+ Usage: genie [options]
59
+ genie token <create|list|revoke> [args] Manage static Bearer tokens
60
+
61
+ Options:
62
+ --transport <stdio|http> Transport to use (default: auto-detect by TTY)
63
+ --port <n> HTTP port (default: 3000)
64
+ --host <addr> HTTP host (default: 127.0.0.1)
65
+ --preview-locality <mode> Preview reachability: local or remote
66
+ --require-bearer-auth Require Authorization: Bearer <token> on /mcp (HTTP only)
67
+ --secrets-from <path> Read secrets from an owner-only KEY=VALUE file
68
+ (e.g. a mounted container secret), overriding env
69
+ -v, --version Print version and exit
70
+ -h, --help Show this help
71
+
72
+ Env:
73
+ MCP_TRANSPORT Same as --transport
74
+ GENIE_PREVIEW_LOCALITY Same as --preview-locality
75
+ GENIE_REQUIRE_BEARER_AUTH Same as --require-bearer-auth ("1"/"true")
76
+ GENIE_HOME Root for persisted state, incl. auth/tokens.json
77
+ GENIE_KITS_ROOT Directory where kit tools read/write UI kits
78
+ GENIE_PROJECTS_ROOT Directory where create_project writes project roots
79
+ GENIE_LLM_API_KEY Required secret — LLM endpoint API key
80
+ OAUTH_HS256_KEY Required secret — HS256 signing key for OAuth tokens
81
+ GENIE_GIT_TOKEN Optional secret — git host API token
82
+ OAUTH_CLIENT_SECRET Optional secret — OAuth confidential client secret
83
+
84
+ This scaffold build boots, speaks MCP, and registers ping plus M1 tools:
85
+ kit listing, kit creation, file listing, file reading, validation, and project
86
+ create/list/get/delete.`;
87
+ function resolveRequireBearerAuth(flag) {
88
+ if (flag)
89
+ return true;
90
+ const env = (process.env.GENIE_REQUIRE_BEARER_AUTH ?? "").trim().toLowerCase();
91
+ return env === "1" || env === "true";
92
+ }
93
+ let logger;
94
+ async function main() {
95
+ const argv = process.argv.slice(2);
96
+ if (argv[0] === "token") {
97
+ const result = await runTokenCli(argv.slice(1));
98
+ process.stdout.write(result.output);
99
+ process.exitCode = result.exitCode;
100
+ return;
101
+ }
102
+ const args = parseArgs(argv);
103
+ if (args.help) {
104
+ process.stdout.write(HELP + "\n");
105
+ return;
106
+ }
107
+ if (args.version) {
108
+ process.stdout.write(`${SERVER_INFO.name} ${SERVER_INFO.version}\n`);
109
+ return;
110
+ }
111
+ const loadedSecrets = loadSecrets({ secretsFromPath: args.secretsFrom });
112
+ logger = createRedactingLogger(loadedSecrets);
113
+ auditLoadedSecrets(loadedSecrets, (line) => logger?.info(JSON.parse(line)));
114
+ const transportKind = resolveTransport(args.transport);
115
+ const host = args.host ?? "127.0.0.1";
116
+ const previewLocality = resolvePreviewLocality(transportKind, args.previewLocality);
117
+ const requireBearerAuth = resolveRequireBearerAuth(args.requireBearerAuth);
118
+ const createConfiguredServer = () => createServer({ transportKind, previewLocality });
119
+ const server = createConfiguredServer();
120
+ // Test-only hook (M5-13 / DRO-285 AC4): when set, register N additional
121
+ // no-op tools on the SAME server instance this CLI starts over its real
122
+ // transport, so the harness smoke suites can probe `tools/list` over the
123
+ // exact transport a harness config launches — not an in-memory stand-in.
124
+ // Never set in production; intentionally undocumented outside test code.
125
+ const extraToolCount = Number(process.env.GENIE_TEST_EXTRA_TOOLS ?? "0");
126
+ if (Number.isFinite(extraToolCount) && extraToolCount > 0) {
127
+ for (let i = 0; i < extraToolCount; i++) {
128
+ server.registerTool(`dummy_tool_${i}`, { title: `Dummy ${i}`, description: "Harness smoke-test filler tool.", inputSchema: {} }, () => ({ content: [{ type: "text", text: "dummy" }] }));
129
+ }
130
+ }
131
+ await startTransport(server, {
132
+ kind: transportKind,
133
+ port: args.port,
134
+ host,
135
+ ...(transportKind === "http"
136
+ ? { serverFactory: createConfiguredServer, requireBearerAuth }
137
+ : {}),
138
+ });
139
+ }
140
+ main().catch((err) => {
141
+ if (logger) {
142
+ logger.fatal({ err }, "genie fatal");
143
+ }
144
+ else {
145
+ process.stderr.write(`genie: fatal: ${err instanceof Error ? err.message : String(err)}\n`);
146
+ }
147
+ process.exit(1);
148
+ });
149
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Log redaction (M5-03 / DRO-275, AC4).
3
+ *
4
+ * The configured pino logger combines path-based redaction for known secret
5
+ * keys with value-based scrubbing for arbitrary fields and message strings.
6
+ */
7
+ import { type DestinationStream, type Logger } from "pino";
8
+ import { type LoadedSecret } from "./secrets.js";
9
+ /** Censor string substituted for any redacted value (AC4). */
10
+ export declare const REDACTED = "****";
11
+ /** Pino paths that are always redacted, regardless of their values. */
12
+ export declare const redactOptions: {
13
+ paths: string[];
14
+ censor: string;
15
+ };
16
+ /** Replace every occurrence of each configured secret value in text. */
17
+ export declare function redactSecretValues(text: string, secrets: readonly LoadedSecret[]): string;
18
+ /** Create the server logger with pino path and configured-value redaction. */
19
+ export declare function createRedactingLogger(secrets: readonly LoadedSecret[], destination?: DestinationStream): Logger;
20
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/config/redact.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAa,EAAE,KAAK,iBAAiB,EAAE,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AAEjE,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAErE,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,SAAS,CAAC;AAE/B,uEAAuE;AACvE,eAAO,MAAM,aAAa;;;CASzB,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAMzF;AAyCD,8EAA8E;AAC9E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,WAAW,GAAE,iBAA6D,GACzE,MAAM,CAcR"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Log redaction (M5-03 / DRO-275, AC4).
3
+ *
4
+ * The configured pino logger combines path-based redaction for known secret
5
+ * keys with value-based scrubbing for arbitrary fields and message strings.
6
+ */
7
+ import pino, {} from "pino";
8
+ import { SECRET_DEFINITIONS } from "./secrets.js";
9
+ /** Censor string substituted for any redacted value (AC4). */
10
+ export const REDACTED = "****";
11
+ /** Pino paths that are always redacted, regardless of their values. */
12
+ export const redactOptions = {
13
+ paths: [
14
+ ...SECRET_DEFINITIONS.map((definition) => definition.key),
15
+ ...SECRET_DEFINITIONS.map((definition) => `*.${definition.key}`),
16
+ ...SECRET_DEFINITIONS.map((definition) => `env.${definition.key}`),
17
+ "req.headers.authorization",
18
+ "*.req.headers.authorization",
19
+ ],
20
+ censor: REDACTED,
21
+ };
22
+ /** Replace every occurrence of each configured secret value in text. */
23
+ export function redactSecretValues(text, secrets) {
24
+ let redacted = text;
25
+ for (const { value } of secrets) {
26
+ redacted = redacted.split(value).join(REDACTED);
27
+ }
28
+ return redacted;
29
+ }
30
+ function redactLogValue(value, secrets, seen) {
31
+ if (typeof value === "string")
32
+ return redactSecretValues(value, secrets);
33
+ if (value === null || typeof value !== "object")
34
+ return value;
35
+ const existing = seen.get(value);
36
+ if (existing !== undefined)
37
+ return existing;
38
+ if (value instanceof Error) {
39
+ const redactedError = {
40
+ type: value.name,
41
+ message: redactSecretValues(value.message, secrets),
42
+ stack: value.stack ? redactSecretValues(value.stack, secrets) : undefined,
43
+ };
44
+ seen.set(value, redactedError);
45
+ for (const [key, nestedValue] of Object.entries(value)) {
46
+ redactedError[key] = redactLogValue(nestedValue, secrets, seen);
47
+ }
48
+ return redactedError;
49
+ }
50
+ if (Array.isArray(value)) {
51
+ const redactedArray = [];
52
+ seen.set(value, redactedArray);
53
+ for (const item of value)
54
+ redactedArray.push(redactLogValue(item, secrets, seen));
55
+ return redactedArray;
56
+ }
57
+ const redactedObject = {};
58
+ seen.set(value, redactedObject);
59
+ for (const [key, nestedValue] of Object.entries(value)) {
60
+ redactedObject[key] = redactLogValue(nestedValue, secrets, seen);
61
+ }
62
+ return redactedObject;
63
+ }
64
+ /** Create the server logger with pino path and configured-value redaction. */
65
+ export function createRedactingLogger(secrets, destination = pino.destination({ dest: 2, sync: true })) {
66
+ return pino({
67
+ redact: redactOptions,
68
+ hooks: {
69
+ logMethod(args, method) {
70
+ const seen = new WeakMap();
71
+ const redactedArgs = args.map((arg) => redactLogValue(arg, secrets, seen));
72
+ method.apply(this, redactedArgs);
73
+ },
74
+ },
75
+ }, destination);
76
+ }
77
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/config/redact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Secret loading (M5-03 / DRO-275) — no plaintext secrets at rest.
3
+ *
4
+ * Every secret genie uses is read only from env vars or, when
5
+ * `--secrets-from <path>` is passed (e.g. a mounted Docker/Kubernetes
6
+ * secret), from an owner-only `KEY=VALUE`-per-line file at that path (AC6).
7
+ * Files with group/other permission bits are rejected before reading. Nothing
8
+ * secret is ever hardcoded, and this module never writes a secret value to
9
+ * disk, argv, or a log line.
10
+ *
11
+ * Bootstrap contract (AC2): `loadSecrets()` throws `SecretValidationError`
12
+ * with one message per problem when any *required* secret is:
13
+ * - missing (unset, and absent from a `--secrets-from` file if provided);
14
+ * - shorter than its required minimum length;
15
+ * - present verbatim anywhere in `process.argv` (a value passed as a CLI
16
+ * flag is visible to `ps`/shell history on every other local user, so a
17
+ * secret leaking into argv is treated as fatal, not just discouraged).
18
+ *
19
+ * AC3: `auditLoadedSecrets` logs which secret *names* were loaded (never
20
+ * values) as a single structured line on process.stderr, mirroring the
21
+ * stdout-is-JSON-RPC convention documented in `llm/client.ts`.
22
+ */
23
+ /** Every secret env var genie recognises, and whether it's required to boot. */
24
+ export declare const SECRET_DEFINITIONS: readonly [{
25
+ readonly key: "GENIE_LLM_API_KEY";
26
+ readonly required: true;
27
+ readonly minLength: 16;
28
+ }, {
29
+ readonly key: "OAUTH_HS256_KEY";
30
+ readonly required: true;
31
+ readonly minLength: 32;
32
+ }, {
33
+ readonly key: "GENIE_GIT_TOKEN";
34
+ readonly required: false;
35
+ readonly minLength: 0;
36
+ }, {
37
+ readonly key: "OAUTH_CLIENT_SECRET";
38
+ readonly required: false;
39
+ readonly minLength: 0;
40
+ }];
41
+ /** Union of every secret key name genie knows about. */
42
+ export type SecretKey = (typeof SECRET_DEFINITIONS)[number]["key"];
43
+ /** Minimum acceptable length for a required secret value (AC2). */
44
+ export declare const MIN_SECRET_LENGTH = 16;
45
+ /**
46
+ * Thrown by `loadSecrets` when bootstrap validation fails. Aggregates every
47
+ * problem found (not just the first) so a misconfigured deployment gets the
48
+ * full picture in one failure rather than fixing issues one at a time.
49
+ */
50
+ export declare class SecretValidationError extends Error {
51
+ readonly problems: readonly string[];
52
+ constructor(problems: readonly string[]);
53
+ }
54
+ /**
55
+ * Parse a `KEY=VALUE`-per-line secrets file (AC6), e.g. a mounted secret
56
+ * mounted at `/run/secrets/genie`. Blank lines and `#`-prefixed comments are
57
+ * skipped. Values are not trimmed of surrounding quotes — the file is
58
+ * expected to hold raw values, matching Docker/Kubernetes secret conventions
59
+ * (unlike a shell-sourced `.env`, no quoting/escaping is applied).
60
+ */
61
+ export declare function parseSecretsFile(contents: string): Record<string, string>;
62
+ /** A loaded secret: its name and value, never logged together (AC3). */
63
+ export interface LoadedSecret {
64
+ key: SecretKey;
65
+ value: string;
66
+ }
67
+ export interface LoadSecretsOptions {
68
+ /** Environment to read from. Defaults to `process.env`. */
69
+ env?: NodeJS.ProcessEnv;
70
+ /** argv to scan for leaked secret values. Defaults to `process.argv`. */
71
+ argv?: readonly string[];
72
+ /**
73
+ * Path to a `KEY=VALUE` secrets file (e.g. `--secrets-from`, AC6). When
74
+ * given, values from this file take precedence over env for the same key.
75
+ */
76
+ secretsFromPath?: string;
77
+ /** Injectable file reader, for tests. Defaults to `node:fs`'s `readFileSync`. */
78
+ readFile?: (path: string) => string;
79
+ /** Injectable file stat, for testing mounted-secret permissions. */
80
+ statFile?: (path: string) => {
81
+ mode: number;
82
+ };
83
+ }
84
+ /**
85
+ * Load and validate every known secret (AC1/AC2). Reads from env only,
86
+ * optionally overlaid by a `--secrets-from` file (AC6). Throws
87
+ * `SecretValidationError` if any *required* secret is missing, too short, or
88
+ * leaked into argv verbatim.
89
+ *
90
+ * Returns only the secrets that were actually present (optional secrets are
91
+ * omitted when unset) so callers can distinguish "not configured" from "".
92
+ */
93
+ export declare function loadSecrets(options?: LoadSecretsOptions): LoadedSecret[];
94
+ /**
95
+ * Log which secrets were loaded — key names only, never values — as a single
96
+ * structured line on stderr (stdout is the JSON-RPC stream on the stdio
97
+ * transport; see `llm/client.ts` for the same convention).
98
+ */
99
+ export declare function auditLoadedSecrets(loaded: readonly LoadedSecret[], write?: (line: string) => void): void;
100
+ //# sourceMappingURL=secrets.d.ts.map