@knitli/openapi-mcp 0.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 (116) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +362 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +249 -0
  6. package/dist/compile.d.ts +14 -0
  7. package/dist/compile.js +132 -0
  8. package/dist/compiler.d.ts +13 -0
  9. package/dist/compiler.js +7 -0
  10. package/dist/conformance/fixtures.d.ts +24 -0
  11. package/dist/conformance/fixtures.js +119 -0
  12. package/dist/conformance/index.d.ts +2 -0
  13. package/dist/conformance/index.js +2 -0
  14. package/dist/conformance/runtime-suite.d.ts +31 -0
  15. package/dist/conformance/runtime-suite.js +343 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/load.d.ts +31 -0
  19. package/dist/load.js +20 -0
  20. package/dist/operations.d.ts +27 -0
  21. package/dist/operations.js +231 -0
  22. package/dist/permissions.d.ts +31 -0
  23. package/dist/permissions.js +89 -0
  24. package/dist/release/compile-release.d.ts +33 -0
  25. package/dist/release/compile-release.js +1620 -0
  26. package/dist/release/load-v4.d.ts +29 -0
  27. package/dist/release/load-v4.js +297 -0
  28. package/dist/release/manifest-builder.d.ts +19 -0
  29. package/dist/release/manifest-builder.js +28 -0
  30. package/dist/release/publish.d.ts +45 -0
  31. package/dist/release/publish.js +346 -0
  32. package/dist/release/reference-map.d.ts +53 -0
  33. package/dist/release/reference-map.js +347 -0
  34. package/dist/release/schema-v4.d.ts +5 -0
  35. package/dist/release/schema-v4.js +91 -0
  36. package/dist/runtime/action-permit.d.ts +11 -0
  37. package/dist/runtime/action-permit.js +1 -0
  38. package/dist/runtime/classify.d.ts +16 -0
  39. package/dist/runtime/classify.js +279 -0
  40. package/dist/runtime/credential-binding.d.ts +3 -0
  41. package/dist/runtime/credential-binding.js +101 -0
  42. package/dist/runtime/digest.d.ts +8 -0
  43. package/dist/runtime/digest.js +80 -0
  44. package/dist/runtime/errors.d.ts +14 -0
  45. package/dist/runtime/errors.js +15 -0
  46. package/dist/runtime/index.d.ts +20 -0
  47. package/dist/runtime/index.js +14 -0
  48. package/dist/runtime/manifest.d.ts +33 -0
  49. package/dist/runtime/manifest.js +489 -0
  50. package/dist/runtime/prepared-call.d.ts +39 -0
  51. package/dist/runtime/prepared-call.js +566 -0
  52. package/dist/runtime/references.d.ts +7 -0
  53. package/dist/runtime/references.js +197 -0
  54. package/dist/runtime/runtime.d.ts +25 -0
  55. package/dist/runtime/runtime.js +1272 -0
  56. package/dist/runtime/schema-keywords.d.ts +2 -0
  57. package/dist/runtime/schema-keywords.js +30 -0
  58. package/dist/runtime/schema-resolver.d.ts +10 -0
  59. package/dist/runtime/schema-resolver.js +237 -0
  60. package/dist/runtime/serialize.d.ts +12 -0
  61. package/dist/runtime/serialize.js +877 -0
  62. package/dist/runtime/store.d.ts +52 -0
  63. package/dist/runtime/store.js +582 -0
  64. package/dist/runtime/strict-json.d.ts +19 -0
  65. package/dist/runtime/strict-json.js +539 -0
  66. package/dist/runtime/types.d.ts +457 -0
  67. package/dist/runtime/types.js +1 -0
  68. package/dist/runtime/verify-record.d.ts +6 -0
  69. package/dist/runtime/verify-record.js +492 -0
  70. package/dist/runtime/versions.d.ts +63 -0
  71. package/dist/runtime/versions.js +86 -0
  72. package/dist/safety.d.ts +28 -0
  73. package/dist/safety.js +60 -0
  74. package/dist/schema.d.ts +5 -0
  75. package/dist/schema.js +52 -0
  76. package/dist/schemas.d.ts +9 -0
  77. package/dist/schemas.js +16 -0
  78. package/dist/sign.d.ts +20 -0
  79. package/dist/sign.js +55 -0
  80. package/dist/sqlite/auth.d.ts +33 -0
  81. package/dist/sqlite/auth.js +973 -0
  82. package/dist/sqlite/catalog-store.d.ts +20 -0
  83. package/dist/sqlite/catalog-store.js +370 -0
  84. package/dist/sqlite/destination-guard.d.ts +24 -0
  85. package/dist/sqlite/destination-guard.js +140 -0
  86. package/dist/sqlite/generation-store.d.ts +12 -0
  87. package/dist/sqlite/generation-store.js +721 -0
  88. package/dist/sqlite/guarded-fetch.d.ts +24 -0
  89. package/dist/sqlite/guarded-fetch.js +626 -0
  90. package/dist/sqlite/index.d.ts +5 -0
  91. package/dist/sqlite/index.js +5 -0
  92. package/dist/sqlite/oauth-resource.d.ts +2 -0
  93. package/dist/sqlite/oauth-resource.js +15 -0
  94. package/dist/sqlite/release-completion.d.ts +5 -0
  95. package/dist/sqlite/release-completion.js +80 -0
  96. package/dist/stdio/action-broker.d.ts +15 -0
  97. package/dist/stdio/action-broker.js +136 -0
  98. package/dist/stdio/authorizer.d.ts +30 -0
  99. package/dist/stdio/authorizer.js +484 -0
  100. package/dist/stdio/config.d.ts +77 -0
  101. package/dist/stdio/config.js +144 -0
  102. package/dist/stdio/exact-policy.d.ts +41 -0
  103. package/dist/stdio/exact-policy.js +361 -0
  104. package/dist/stdio/index.d.ts +6 -0
  105. package/dist/stdio/index.js +148 -0
  106. package/dist/stdio/render.d.ts +8 -0
  107. package/dist/stdio/render.js +253 -0
  108. package/dist/stdio/request-state.d.ts +18 -0
  109. package/dist/stdio/request-state.js +79 -0
  110. package/dist/stdio/search.d.ts +10 -0
  111. package/dist/stdio/search.js +76 -0
  112. package/dist/stdio/server.d.ts +29 -0
  113. package/dist/stdio/server.js +309 -0
  114. package/dist/types.d.ts +32 -0
  115. package/dist/types.js +1 -0
  116. package/package.json +131 -0
@@ -0,0 +1,484 @@
1
+ import { acceptedContent, inputResponse, } from "@modelcontextprotocol/server";
2
+ import { z } from "zod";
3
+ import { snapshotCredentialAuthorizationBinding } from "../runtime/credential-binding.js";
4
+ import { OpenApiMcpError } from "../runtime/errors.js";
5
+ import { verifyAndSnapshotPreparedCall } from "../runtime/prepared-call.js";
6
+ import { matchesExactPolicy, } from "./exact-policy.js";
7
+ import { renderApproval } from "./render.js";
8
+ import { createVerifiedRequestStateCodec, } from "./request-state.js";
9
+ const PENDING_TTL_MS = 120_000;
10
+ const RECEIPT_TTL_MS = 120_000;
11
+ const ACTIVATION_TTL_MS = 900_000;
12
+ const DEFAULT_LIMITS = Object.freeze({
13
+ pending: 256,
14
+ receipts: 1_024,
15
+ activations: 256,
16
+ });
17
+ const confirmationSchema = z.object({ confirm: z.literal(true) }).strict();
18
+ const activationConfirmationSchema = z
19
+ .object({
20
+ confirm: z.literal(true),
21
+ activatePolicy: z.literal(true),
22
+ })
23
+ .strict();
24
+ function deny(reason) {
25
+ return Object.freeze({ status: "denied", reason });
26
+ }
27
+ function deniedError(reason = "Action authorization denied") {
28
+ return new OpenApiMcpError("ACTION_DENIED", reason);
29
+ }
30
+ function defaultRandom(size) {
31
+ return globalThis.crypto.getRandomValues(new Uint8Array(size));
32
+ }
33
+ function normalizeLimits(value) {
34
+ const result = {
35
+ ...DEFAULT_LIMITS,
36
+ };
37
+ for (const key of Object.keys(DEFAULT_LIMITS)) {
38
+ const proposed = value?.[key];
39
+ if (proposed === undefined)
40
+ continue;
41
+ if (!Number.isSafeInteger(proposed) ||
42
+ proposed < 1 ||
43
+ proposed > DEFAULT_LIMITS[key]) {
44
+ throw new RangeError(`Invalid ${key} authorizer limit`);
45
+ }
46
+ result[key] = proposed;
47
+ }
48
+ return Object.freeze(result);
49
+ }
50
+ function hexRandom(randomBytes) {
51
+ const bytes = randomBytes(16);
52
+ if (!(bytes instanceof Uint8Array) || bytes.byteLength !== 16) {
53
+ throw new TypeError("Random source returned an invalid byte sequence");
54
+ }
55
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
56
+ }
57
+ function exactPayloadMatch(payload, pending) {
58
+ return (payload.pendingId === pending.pendingId &&
59
+ payload.callDigest === pending.callDigest &&
60
+ payload.credentialBindingDigest === pending.credentialBindingDigest &&
61
+ payload.expiresAt === pending.expiresAt);
62
+ }
63
+ function credentialMatchesOwnedCall(credential, call) {
64
+ return (credential.profileId === call.credentialProfileId &&
65
+ credential.profileDigest === call.credentialProfileDigest &&
66
+ credential.slotsDigest === call.reservedSlotsDigest);
67
+ }
68
+ function plainResponses(value) {
69
+ if (typeof value !== "object" || value === null || Array.isArray(value))
70
+ return undefined;
71
+ const prototype = Object.getPrototypeOf(value);
72
+ if (prototype !== Object.prototype && prototype !== null)
73
+ return undefined;
74
+ return value;
75
+ }
76
+ function ownDataValue(object, key) {
77
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
78
+ if (descriptor === undefined ||
79
+ !("value" in descriptor) ||
80
+ !descriptor.enumerable) {
81
+ throw deniedError();
82
+ }
83
+ return descriptor.value;
84
+ }
85
+ function snapshotResponseValue(value, depth = 0) {
86
+ if (value === null ||
87
+ typeof value === "string" ||
88
+ typeof value === "boolean") {
89
+ return value;
90
+ }
91
+ if (typeof value !== "object" || Array.isArray(value) || depth >= 3) {
92
+ throw deniedError();
93
+ }
94
+ const prototype = Object.getPrototypeOf(value);
95
+ if (prototype !== Object.prototype && prototype !== null)
96
+ throw deniedError();
97
+ const keys = Reflect.ownKeys(value);
98
+ if (keys.length > 16 || keys.some((key) => typeof key !== "string")) {
99
+ throw deniedError();
100
+ }
101
+ const copy = Object.create(null);
102
+ for (const key of keys) {
103
+ copy[key] = snapshotResponseValue(ownDataValue(value, key), depth + 1);
104
+ }
105
+ return Object.freeze(copy);
106
+ }
107
+ function snapshotContext(context) {
108
+ if (typeof context !== "object" ||
109
+ context === null ||
110
+ Array.isArray(context)) {
111
+ throw deniedError();
112
+ }
113
+ const prototype = Object.getPrototypeOf(context);
114
+ if (prototype !== Object.prototype && prototype !== null)
115
+ throw deniedError();
116
+ const keys = Reflect.ownKeys(context);
117
+ if (keys.some((key) => typeof key !== "string"))
118
+ throw deniedError();
119
+ const kind = ownDataValue(context, "kind");
120
+ if (kind === "initial" && keys.length === 1) {
121
+ return Object.freeze({ kind: "initial" });
122
+ }
123
+ if (kind === "resume" &&
124
+ keys.length === 3 &&
125
+ keys.includes("requestState") &&
126
+ keys.includes("inputResponses")) {
127
+ const requestState = ownDataValue(context, "requestState");
128
+ if (typeof requestState !== "object" || requestState === null)
129
+ throw deniedError();
130
+ let inputResponses;
131
+ try {
132
+ inputResponses = snapshotResponseValue(ownDataValue(context, "inputResponses"));
133
+ }
134
+ catch {
135
+ // Preserve the registered request-state capability so #resume can burn
136
+ // the corresponding pending entry as a terminal malformed response.
137
+ inputResponses = Object.freeze(Object.create(null));
138
+ }
139
+ return Object.freeze({
140
+ kind,
141
+ requestState: requestState,
142
+ inputResponses,
143
+ });
144
+ }
145
+ throw deniedError();
146
+ }
147
+ class LedgerActionAuthorizer {
148
+ requestStateVerifier;
149
+ #elicitationAvailable;
150
+ #now;
151
+ #randomBytes;
152
+ #limits;
153
+ #policies;
154
+ #pending = new Map();
155
+ #receipts = new Map();
156
+ #decisions = new WeakMap();
157
+ #activations = new Map();
158
+ #requestState;
159
+ constructor(options, policies = []) {
160
+ this.#elicitationAvailable = options.elicitationAvailable;
161
+ this.#now = options.now ?? Date.now;
162
+ this.#randomBytes = options.randomBytes ?? defaultRandom;
163
+ this.#limits = normalizeLimits(options.limits);
164
+ this.#policies = Object.freeze([...policies]);
165
+ const key = this.#randomBytes(32);
166
+ if (!(key instanceof Uint8Array) || key.byteLength !== 32) {
167
+ throw new TypeError("Random source returned an invalid HMAC key");
168
+ }
169
+ this.#requestState = createVerifiedRequestStateCodec({
170
+ key,
171
+ now: this.#now,
172
+ isPending: (payload) => {
173
+ const pending = this.#pending.get(payload.pendingId);
174
+ return pending !== undefined && exactPayloadMatch(payload, pending);
175
+ },
176
+ });
177
+ this.requestStateVerifier = this.#requestState.verify;
178
+ Object.freeze(this);
179
+ }
180
+ async authorize(call, credential, context) {
181
+ let ownedContext;
182
+ try {
183
+ ownedContext = snapshotContext(context);
184
+ // Each helper takes its caller-owned snapshot synchronously before its
185
+ // first await; starting both before awaiting closes the mutation window.
186
+ const callPromise = verifyAndSnapshotPreparedCall(call);
187
+ const credentialPromise = snapshotCredentialAuthorizationBinding(credential, call);
188
+ const [ownedCall, ownedCredential] = await Promise.all([
189
+ callPromise,
190
+ credentialPromise,
191
+ ]);
192
+ if (!credentialMatchesOwnedCall(ownedCredential, ownedCall)) {
193
+ throw deniedError();
194
+ }
195
+ const prepared = ownedCall;
196
+ // Resume is deliberately handled before active-policy matching. An
197
+ // echoed confirmation can never be bypassed by a newly active policy.
198
+ if (ownedContext.kind === "resume") {
199
+ return await this.#resume(prepared, ownedCredential, ownedContext);
200
+ }
201
+ const now = this.#now();
202
+ this.#prune(now);
203
+ const policy = this.#matchingPolicy(prepared, now);
204
+ if (policy !== undefined) {
205
+ const activationExpiresAt = Math.min(policy.template.expiresAt, now + ACTIVATION_TTL_MS);
206
+ const activation = this.#activations.get(this.#activationKey(policy.policyDigest, ownedCredential.bindingDigest));
207
+ if (activation !== undefined && now < activation.expiresAt) {
208
+ return this.#mintReceipt(prepared, ownedCredential, "exact-policy", policy.policyDigest, Math.min(now + RECEIPT_TTL_MS, activation.expiresAt));
209
+ }
210
+ return await this.#requestConfirmation(prepared, ownedCredential, policy, activationExpiresAt);
211
+ }
212
+ return await this.#requestConfirmation(prepared, ownedCredential);
213
+ }
214
+ catch {
215
+ if (ownedContext?.kind === "resume") {
216
+ const payload = this.#requestState.read(ownedContext.requestState);
217
+ const pending = payload === undefined
218
+ ? undefined
219
+ : this.#pending.get(payload.pendingId);
220
+ if (payload !== undefined &&
221
+ pending !== undefined &&
222
+ exactPayloadMatch(payload, pending)) {
223
+ this.#pending.delete(payload.pendingId);
224
+ }
225
+ }
226
+ return deny("Action authorization denied");
227
+ }
228
+ }
229
+ consume(decision, call, credential) {
230
+ const receipt = this.#decisions.get(decision);
231
+ if (receipt === undefined ||
232
+ receipt.decision !== decision ||
233
+ this.#receipts.get(decision.authorizationId) !== receipt ||
234
+ this.#now() >= receipt.expiresAt ||
235
+ decision.callDigest !== receipt.callDigest ||
236
+ decision.credentialBindingDigest !== receipt.credentialBindingDigest ||
237
+ decision.path !== receipt.path ||
238
+ decision.policyDigest !== receipt.policyDigest ||
239
+ call.preparedCallDigest !== receipt.callDigest ||
240
+ credential.bindingDigest !== receipt.credentialBindingDigest) {
241
+ return Promise.reject(deniedError());
242
+ }
243
+ // The capability is spent before this method returns a promise and before
244
+ // any integrity verification can yield, so concurrent callers cannot win.
245
+ this.#receipts.delete(decision.authorizationId);
246
+ this.#decisions.delete(decision);
247
+ return Promise.all([
248
+ verifyAndSnapshotPreparedCall(call),
249
+ snapshotCredentialAuthorizationBinding(credential, call),
250
+ ]).then(([verifiedCall, verifiedCredential]) => {
251
+ if (verifiedCall.preparedCallDigest !== receipt.callDigest ||
252
+ verifiedCredential.bindingDigest !==
253
+ receipt.credentialBindingDigest ||
254
+ !credentialMatchesOwnedCall(verifiedCredential, verifiedCall)) {
255
+ throw deniedError();
256
+ }
257
+ }, () => {
258
+ throw deniedError();
259
+ });
260
+ }
261
+ async #resume(call, credential, context) {
262
+ const payload = this.#requestState.read(context.requestState);
263
+ if (payload === undefined)
264
+ return deny("Invalid confirmation state");
265
+ const pending = this.#pending.get(payload.pendingId);
266
+ if (pending === undefined || !exactPayloadMatch(payload, pending)) {
267
+ return deny("Invalid confirmation state");
268
+ }
269
+ const now = this.#now();
270
+ if (now >= pending.expiresAt ||
271
+ call.preparedCallDigest !== pending.callDigest ||
272
+ credential.bindingDigest !== pending.credentialBindingDigest) {
273
+ this.#pending.delete(pending.pendingId);
274
+ return deny("Confirmation expired or no longer matches");
275
+ }
276
+ const responses = plainResponses(context.inputResponses);
277
+ let response;
278
+ let confirmed = false;
279
+ try {
280
+ response = inputResponse(responses, "confirm");
281
+ if (pending.policy === undefined) {
282
+ confirmed =
283
+ acceptedContent(responses, "confirm", confirmationSchema)?.confirm ===
284
+ true;
285
+ }
286
+ else {
287
+ const content = acceptedContent(responses, "confirm", activationConfirmationSchema);
288
+ confirmed =
289
+ content?.confirm === true && content.activatePolicy === true;
290
+ }
291
+ }
292
+ catch {
293
+ this.#pending.delete(pending.pendingId);
294
+ return deny("Malformed confirmation response");
295
+ }
296
+ if (response.kind !== "elicit" || response.action !== "accept") {
297
+ this.#pending.delete(pending.pendingId);
298
+ return deny("Confirmation was not accepted");
299
+ }
300
+ if (!confirmed) {
301
+ this.#pending.delete(pending.pendingId);
302
+ return deny("Malformed confirmation response");
303
+ }
304
+ this.#prune(now);
305
+ const policy = pending.policy;
306
+ const activationExpiresAt = pending.activationExpiresAt;
307
+ if ((policy === undefined) !== (activationExpiresAt === undefined)) {
308
+ this.#pending.delete(pending.pendingId);
309
+ return deny("Invalid confirmation state");
310
+ }
311
+ const activationKey = policy
312
+ ? this.#activationKey(policy.policyDigest, credential.bindingDigest)
313
+ : undefined;
314
+ const newActivation = activationKey !== undefined && !this.#activations.has(activationKey);
315
+ if (this.#receipts.size >= this.#limits.receipts ||
316
+ (newActivation && this.#activations.size >= this.#limits.activations)) {
317
+ // Capacity denial is retriable and must not spend a valid human answer.
318
+ return deny("Authorization capacity is exhausted");
319
+ }
320
+ let decision;
321
+ try {
322
+ decision = this.#mintReceipt(call, credential, policy ? "exact-policy" : "per-call", policy?.policyDigest, Math.min(now + RECEIPT_TTL_MS, activationExpiresAt ?? Number.MAX_SAFE_INTEGER));
323
+ }
324
+ catch {
325
+ return deny("Authorization capacity is exhausted");
326
+ }
327
+ if (policy !== undefined &&
328
+ activationKey !== undefined &&
329
+ activationExpiresAt !== undefined) {
330
+ this.#activations.set(activationKey, Object.freeze({
331
+ policyDigest: policy.policyDigest,
332
+ credentialBindingDigest: credential.bindingDigest,
333
+ expiresAt: activationExpiresAt,
334
+ }));
335
+ }
336
+ this.#pending.delete(pending.pendingId);
337
+ return decision;
338
+ }
339
+ async #requestConfirmation(call, credential, policy, activationExpiresAt) {
340
+ if (!this.#elicitationAvailable()) {
341
+ return deny("Client elicitation is unavailable");
342
+ }
343
+ const now = this.#now();
344
+ this.#prune(now);
345
+ if (this.#pending.size >= this.#limits.pending) {
346
+ return deny("Authorization capacity is exhausted");
347
+ }
348
+ let pendingId;
349
+ for (let attempt = 0; attempt < 8; attempt += 1) {
350
+ const candidate = hexRandom(this.#randomBytes);
351
+ if (!this.#pending.has(candidate)) {
352
+ pendingId = candidate;
353
+ break;
354
+ }
355
+ }
356
+ if (pendingId === undefined)
357
+ return deny("Authorization identifier collision");
358
+ const expiresAt = now + PENDING_TTL_MS;
359
+ if (policy !== undefined && activationExpiresAt === undefined) {
360
+ return deny("Policy activation is invalid");
361
+ }
362
+ const pending = Object.freeze({
363
+ pendingId,
364
+ callDigest: call.preparedCallDigest,
365
+ credentialBindingDigest: credential.bindingDigest,
366
+ expiresAt,
367
+ ...(policy === undefined ? {} : { policy }),
368
+ ...(activationExpiresAt === undefined ? {} : { activationExpiresAt }),
369
+ });
370
+ const presentation = renderApproval(call, credential, policy === undefined
371
+ ? undefined
372
+ : {
373
+ policyDigest: policy.policyDigest,
374
+ expiresAt: activationExpiresAt,
375
+ template: policy.template,
376
+ });
377
+ this.#pending.set(pendingId, pending);
378
+ try {
379
+ const requestState = await this.#requestState.mint({
380
+ version: 1,
381
+ pendingId,
382
+ callDigest: pending.callDigest,
383
+ credentialBindingDigest: pending.credentialBindingDigest,
384
+ expiresAt,
385
+ });
386
+ return Object.freeze({
387
+ status: "input-required",
388
+ presentation,
389
+ requestState,
390
+ });
391
+ }
392
+ catch {
393
+ if (this.#pending.get(pendingId) === pending) {
394
+ this.#pending.delete(pendingId);
395
+ }
396
+ throw deniedError();
397
+ }
398
+ }
399
+ #matchingPolicy(call, now) {
400
+ if (call.safety !== "action" ||
401
+ (call.actionKind !== "create" && call.actionKind !== "update") ||
402
+ (call.cardinality?.kind !== "single" &&
403
+ call.cardinality?.kind !== "bounded")) {
404
+ return undefined;
405
+ }
406
+ return this.#policies.find((policy) => matchesExactPolicy(policy, call, now));
407
+ }
408
+ #mintReceipt(call, credential, path, policyDigest, expiresAt) {
409
+ const now = this.#now();
410
+ this.#prune(now);
411
+ if (now >= expiresAt || this.#receipts.size >= this.#limits.receipts) {
412
+ throw deniedError("Authorization receipt capacity is exhausted");
413
+ }
414
+ for (let attempt = 0; attempt < 8; attempt += 1) {
415
+ const token = hexRandom(this.#randomBytes);
416
+ const authorizationId = Object.freeze(Object.create(null));
417
+ let collision = false;
418
+ for (const existing of this.#receipts.values()) {
419
+ if (existing.randomId === token) {
420
+ collision = true;
421
+ break;
422
+ }
423
+ }
424
+ if (collision)
425
+ continue;
426
+ const decision = Object.freeze({
427
+ status: "authorized",
428
+ callDigest: call.preparedCallDigest,
429
+ credentialBindingDigest: credential.bindingDigest,
430
+ path,
431
+ authorizationId,
432
+ ...(policyDigest === undefined ? {} : { policyDigest }),
433
+ });
434
+ const receipt = Object.freeze({
435
+ randomId: token,
436
+ decision,
437
+ callDigest: decision.callDigest,
438
+ credentialBindingDigest: decision.credentialBindingDigest,
439
+ path,
440
+ ...(policyDigest === undefined ? {} : { policyDigest }),
441
+ expiresAt,
442
+ });
443
+ this.#receipts.set(authorizationId, receipt);
444
+ this.#decisions.set(decision, receipt);
445
+ return decision;
446
+ }
447
+ throw deniedError("Authorization identifier collision");
448
+ }
449
+ #activationKey(policyDigest, bindingDigest) {
450
+ return `${policyDigest}\0${bindingDigest}`;
451
+ }
452
+ #prune(now) {
453
+ for (const [id, pending] of this.#pending) {
454
+ if (now >= pending.expiresAt)
455
+ this.#pending.delete(id);
456
+ }
457
+ for (const [id, receipt] of this.#receipts) {
458
+ if (now >= receipt.expiresAt) {
459
+ this.#receipts.delete(id);
460
+ this.#decisions.delete(receipt.decision);
461
+ }
462
+ }
463
+ for (const [key, activation] of this.#activations) {
464
+ if (now >= activation.expiresAt)
465
+ this.#activations.delete(key);
466
+ }
467
+ }
468
+ }
469
+ export function createDenyActionAuthorizer() {
470
+ return Object.freeze({
471
+ async authorize() {
472
+ return deny("Action authorization is disabled");
473
+ },
474
+ async consume() {
475
+ throw deniedError("Action authorization is disabled");
476
+ },
477
+ });
478
+ }
479
+ export function createClientElicitationActionAuthorizer(options) {
480
+ return new LedgerActionAuthorizer(options);
481
+ }
482
+ export function createExactPolicyActionAuthorizer(options) {
483
+ return new LedgerActionAuthorizer(options, options.templates);
484
+ }
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ declare const schema: z.ZodObject<{
3
+ version: z.ZodLiteral<1>;
4
+ generationStatePath: z.ZodString;
5
+ catalogs: z.ZodArray<z.ZodObject<{
6
+ catalogId: z.ZodString;
7
+ releaseId: z.ZodString;
8
+ path: z.ZodString;
9
+ profileId: z.ZodString;
10
+ }, z.core.$strict>>;
11
+ trust: z.ZodObject<{
12
+ releaseKeys: z.ZodArray<z.ZodObject<{
13
+ issuer: z.ZodString;
14
+ keyId: z.ZodString;
15
+ publicKey: z.ZodString;
16
+ }, z.core.$strict>>;
17
+ rollbackKeys: z.ZodArray<z.ZodObject<{
18
+ issuer: z.ZodString;
19
+ keyId: z.ZodString;
20
+ publicKey: z.ZodString;
21
+ }, z.core.$strict>>;
22
+ }, z.core.$strict>;
23
+ allowedOrigins: z.ZodArray<z.ZodString>;
24
+ profiles: z.ZodArray<z.ZodObject<{
25
+ profileId: z.ZodString;
26
+ revision: z.ZodNumber;
27
+ allowedOrigins: z.ZodArray<z.ZodString>;
28
+ auth: z.ZodDiscriminatedUnion<[z.ZodObject<{
29
+ type: z.ZodLiteral<"bearer-env">;
30
+ env: z.ZodString;
31
+ }, z.core.$strict>, z.ZodObject<{
32
+ type: z.ZodLiteral<"api-key-env">;
33
+ env: z.ZodString;
34
+ placement: z.ZodEnum<{
35
+ header: "header";
36
+ query: "query";
37
+ }>;
38
+ name: z.ZodString;
39
+ }, z.core.$strict>, z.ZodObject<{
40
+ type: z.ZodLiteral<"oauth2-pkce">;
41
+ authorizationEndpoint: z.ZodString;
42
+ tokenEndpoint: z.ZodString;
43
+ clientId: z.ZodString;
44
+ scopes: z.ZodArray<z.ZodString>;
45
+ resource: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
46
+ }, z.core.$strict>], "type">;
47
+ audience: z.ZodOptional<z.ZodString>;
48
+ scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
49
+ }, z.core.$strict>>;
50
+ limits: z.ZodOptional<z.ZodObject<{
51
+ [x: string]: z.ZodOptional<z.ZodNumber>;
52
+ }, z.core.$strict>>;
53
+ exactPolicies: z.ZodOptional<z.ZodArray<z.ZodObject<{
54
+ version: z.ZodLiteral<1>;
55
+ catalogId: z.ZodString;
56
+ releaseId: z.ZodString;
57
+ manifestDigest: z.ZodString;
58
+ operationId: z.ZodString;
59
+ operationDigest: z.ZodString;
60
+ credentialProfileDigest: z.ZodString;
61
+ actionKind: z.ZodEnum<{
62
+ create: "create";
63
+ update: "update";
64
+ }>;
65
+ cardinality: z.ZodEnum<{
66
+ bounded: "bounded";
67
+ single: "single";
68
+ }>;
69
+ maxAffected: z.ZodNumber;
70
+ expiresAt: z.ZodNumber;
71
+ arguments: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
72
+ }, z.core.$strict>>>;
73
+ }, z.core.$strict>;
74
+ export type OpenApiStdioConfig = z.infer<typeof schema>;
75
+ /** Operator-only input. Error text never contains submitted values or keys. */
76
+ export declare function parseOpenApiStdioConfig(input: unknown): OpenApiStdioConfig;
77
+ export {};