@vellumai/credential-executor 0.10.7 → 0.10.8-dev.202607102228.5945895

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 (65) hide show
  1. package/Dockerfile +1 -1
  2. package/node_modules/@vellumai/service-contracts/package.json +1 -2
  3. package/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
  4. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +0 -2
  5. package/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
  6. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +3 -5
  7. package/node_modules/@vellumai/service-contracts/src/index.ts +2 -4
  8. package/node_modules/@vellumai/service-contracts/src/rpc.ts +4 -447
  9. package/package.json +2 -3
  10. package/src/__tests__/bulk-set-credentials.test.ts +1 -1
  11. package/src/__tests__/local-standalone.test.ts +5 -36
  12. package/src/__tests__/managed-integration.test.ts +112 -91
  13. package/src/__tests__/managed-reconnect.test.ts +2 -2
  14. package/src/__tests__/transport.test.ts +23 -27
  15. package/src/cli.ts +1 -1
  16. package/src/index.ts +8 -88
  17. package/src/main.ts +228 -340
  18. package/src/paths.ts +4 -20
  19. package/src/server.ts +52 -469
  20. package/node_modules/@vellumai/service-contracts/src/__tests__/grants.test.ts +0 -686
  21. package/node_modules/@vellumai/service-contracts/src/grants.ts +0 -184
  22. package/node_modules/@vellumai/service-contracts/src/rendering.ts +0 -135
  23. package/src/__tests__/command-executor.test.ts +0 -1879
  24. package/src/__tests__/command-validator.test.ts +0 -1405
  25. package/src/__tests__/command-workspace.test.ts +0 -1050
  26. package/src/__tests__/grant-store.test.ts +0 -689
  27. package/src/__tests__/http-executor.test.ts +0 -1336
  28. package/src/__tests__/http-policy.test.ts +0 -1069
  29. package/src/__tests__/local-materializers.test.ts +0 -860
  30. package/src/__tests__/local-token-refresh.test.ts +0 -361
  31. package/src/__tests__/manage-secure-command-tool.test.ts +0 -134
  32. package/src/__tests__/managed-lazy-getters.test.ts +0 -359
  33. package/src/__tests__/managed-materializers.test.ts +0 -1028
  34. package/src/__tests__/managed-rejection.test.ts +0 -43
  35. package/src/__tests__/toolstore.test.ts +0 -773
  36. package/src/audit/store.ts +0 -188
  37. package/src/commands/auth-adapters.ts +0 -169
  38. package/src/commands/egress-hooks.ts +0 -203
  39. package/src/commands/executor.ts +0 -1155
  40. package/src/commands/output-scan.ts +0 -157
  41. package/src/commands/profiles.ts +0 -286
  42. package/src/commands/validator.ts +0 -702
  43. package/src/commands/workspace.ts +0 -550
  44. package/src/grants/index.ts +0 -17
  45. package/src/grants/persistent-store.ts +0 -309
  46. package/src/grants/rpc-handlers.ts +0 -293
  47. package/src/grants/temporary-store.ts +0 -289
  48. package/src/http/audit.ts +0 -84
  49. package/src/http/executor.ts +0 -684
  50. package/src/http/path-template.ts +0 -245
  51. package/src/http/policy.ts +0 -238
  52. package/src/http/response-filter.ts +0 -233
  53. package/src/managed-errors.ts +0 -9
  54. package/src/managed-lazy-getters.ts +0 -106
  55. package/src/managed-main.ts +0 -822
  56. package/src/materializers/local-oauth-lookup.ts +0 -98
  57. package/src/materializers/local-token-refresh.ts +0 -287
  58. package/src/materializers/local.ts +0 -316
  59. package/src/materializers/managed-platform.ts +0 -295
  60. package/src/subjects/local.ts +0 -177
  61. package/src/subjects/managed.ts +0 -311
  62. package/src/subjects/policy.ts +0 -79
  63. package/src/toolstore/integrity.ts +0 -94
  64. package/src/toolstore/manifest.ts +0 -154
  65. package/src/toolstore/publish.ts +0 -571
@@ -1,309 +0,0 @@
1
- /**
2
- * CES persistent grant store.
3
- *
4
- * Stores canonical validated grants by stable ID in a `grants.json` file
5
- * inside the CES-private data root. This file is never co-mingled with
6
- * assistant trust files or credential metadata.
7
- *
8
- * Design principles:
9
- * - **Fail closed**: If the store file is unreadable or malformed, all
10
- * reads return empty and all writes throw. The CES must never fall back
11
- * to a permissive default when the persistent state is corrupt.
12
- * - **Atomic writes**: Uses rename-over-tmp to prevent partial writes.
13
- * - **Deduplication**: Grants are keyed by a canonical hash (the `id`
14
- * field) — adding an active grant with an existing ID is a no-op.
15
- * Revoked grants with the same ID are reactivated (upsert).
16
- */
17
-
18
- import {
19
- chmodSync,
20
- existsSync,
21
- mkdirSync,
22
- readFileSync,
23
- renameSync,
24
- writeFileSync,
25
- } from "node:fs";
26
- import { dirname, join } from "node:path";
27
- import { randomUUID } from "node:crypto";
28
-
29
- import { getCesGrantsDir } from "../paths.js";
30
-
31
- // ---------------------------------------------------------------------------
32
- // Types
33
- // ---------------------------------------------------------------------------
34
-
35
- /** A canonical persistent grant stored on disk. */
36
- export interface PersistentGrant {
37
- /** Stable canonical hash identifying this grant. */
38
- id: string;
39
- /** The tool or command pattern this grant authorises. */
40
- tool: string;
41
- /** Glob pattern scoping the grant. */
42
- pattern: string;
43
- /** Scope constraint (directory path or "everywhere"). */
44
- scope: string;
45
- /** When the grant was created (epoch ms). */
46
- createdAt: number;
47
- /** The agent session that created this grant. Backfilled to "unknown" on legacy grants. */
48
- sessionId: string;
49
- /** When the grant was revoked (epoch ms), or undefined if active. */
50
- revokedAt?: number;
51
- /** Human-readable reason for revocation. */
52
- revokedReason?: string;
53
- }
54
-
55
- /** On-disk format for the grants file. */
56
- interface GrantsFile {
57
- version: number;
58
- grants: PersistentGrant[];
59
- }
60
-
61
- const GRANTS_FILE_VERSION = 1;
62
- const GRANTS_FILENAME = "grants.json";
63
-
64
- // ---------------------------------------------------------------------------
65
- // Store implementation
66
- // ---------------------------------------------------------------------------
67
-
68
- export class PersistentGrantStore {
69
- private readonly filePath: string;
70
- /** Set to true when the store detects corruption; blocks all operations. */
71
- private corrupt = false;
72
-
73
- constructor(grantsDir?: string) {
74
- const dir = grantsDir ?? getCesGrantsDir();
75
- this.filePath = join(dir, GRANTS_FILENAME);
76
- }
77
-
78
- // -----------------------------------------------------------------------
79
- // Public API
80
- // -----------------------------------------------------------------------
81
-
82
- /**
83
- * Initialise the store, ensuring the parent directory exists and the
84
- * grants file is readable. If the file does not exist it is created
85
- * with an empty grant list.
86
- *
87
- * Throws if the directory cannot be created or an existing file is
88
- * malformed (fail-closed).
89
- */
90
- init(): void {
91
- const dir = dirname(this.filePath);
92
- if (!existsSync(dir)) {
93
- mkdirSync(dir, { recursive: true });
94
- }
95
-
96
- if (!existsSync(this.filePath)) {
97
- this.writeToDisk([]);
98
- return;
99
- }
100
-
101
- // Validate the existing file is readable and well-formed.
102
- // If it isn't, mark corrupt and throw (fail closed).
103
- const grants = this.loadFromDisk();
104
-
105
- // Migration: backfill sessionId on legacy grants that pre-date the field.
106
- let migrated = false;
107
- for (const grant of grants) {
108
- if (grant.sessionId == null) {
109
- (grant as { sessionId: string }).sessionId = "unknown";
110
- migrated = true;
111
- }
112
- }
113
- if (migrated) {
114
- this.writeToDisk(grants);
115
- }
116
- }
117
-
118
- /**
119
- * Return all persisted grants that are not revoked.
120
- *
121
- * Returns an empty array if the store has never been initialised
122
- * (no file on disk). Throws if the store is corrupt.
123
- */
124
- getAll(): PersistentGrant[] {
125
- return this.getAllIncludingRevoked().filter((g) => g.revokedAt == null);
126
- }
127
-
128
- /**
129
- * Return all persisted grants including revoked ones.
130
- *
131
- * Used by the listing handler to expose the full audit trail.
132
- */
133
- getAllIncludingRevoked(): PersistentGrant[] {
134
- this.assertNotCorrupt();
135
- if (!existsSync(this.filePath)) return [];
136
- return [...this.loadFromDisk()];
137
- }
138
-
139
- /**
140
- * Look up a grant by its canonical ID (active grants only).
141
- *
142
- * Returns `undefined` if not found or revoked. Throws if the store is corrupt.
143
- */
144
- getById(id: string): PersistentGrant | undefined {
145
- return this.getAll().find((g) => g.id === id);
146
- }
147
-
148
- /**
149
- * Add a grant. If an active grant with the same `id` already exists,
150
- * this is a no-op (idempotent deduplication by canonical hash).
151
- *
152
- * If a revoked grant with the same `id` exists, it is reactivated
153
- * with the new grant's fields — this supports the revoke-then-re-approve
154
- * workflow where the same proposal hash is re-granted.
155
- *
156
- * Returns `true` if the grant was newly added or reactivated, `false`
157
- * if it was a duplicate of an already-active grant.
158
- */
159
- add(grant: PersistentGrant): boolean {
160
- this.assertNotCorrupt();
161
- const grants = this.loadFromDisk();
162
- const existing = grants.find((g) => g.id === grant.id);
163
- if (existing) {
164
- // Already active — deduplicate as before.
165
- if (existing.revokedAt == null) {
166
- return false;
167
- }
168
- // Revoked — reactivate with fresh fields.
169
- existing.tool = grant.tool;
170
- existing.pattern = grant.pattern;
171
- existing.scope = grant.scope;
172
- existing.createdAt = grant.createdAt;
173
- existing.sessionId = grant.sessionId;
174
- existing.revokedAt = undefined;
175
- existing.revokedReason = undefined;
176
- this.writeToDisk(grants);
177
- return true;
178
- }
179
- grants.push(grant);
180
- this.writeToDisk(grants);
181
- return true;
182
- }
183
-
184
- /**
185
- * Remove a grant by its canonical ID (hard delete).
186
- *
187
- * Returns `true` if the grant was found and removed, `false` otherwise.
188
- *
189
- * Prefer `markRevoked()` for audit-preserving revocation.
190
- */
191
- remove(id: string): boolean {
192
- this.assertNotCorrupt();
193
- const grants = this.loadFromDisk();
194
- const index = grants.findIndex((g) => g.id === id);
195
- if (index === -1) return false;
196
- grants.splice(index, 1);
197
- this.writeToDisk(grants);
198
- return true;
199
- }
200
-
201
- /**
202
- * Mark a grant as revoked by its canonical ID. The grant remains
203
- * on disk for audit purposes but is excluded from `getAll()` and
204
- * `getById()` lookups.
205
- *
206
- * Returns `true` if the grant was found and marked revoked,
207
- * `false` if the grant does not exist or is already revoked.
208
- */
209
- markRevoked(id: string, reason?: string): boolean {
210
- this.assertNotCorrupt();
211
- const grants = this.loadFromDisk();
212
- const grant = grants.find((g) => g.id === id);
213
- if (!grant || grant.revokedAt != null) return false;
214
- grant.revokedAt = Date.now();
215
- grant.revokedReason = reason;
216
- this.writeToDisk(grants);
217
- return true;
218
- }
219
-
220
- /**
221
- * Check whether a grant with the given ID exists.
222
- */
223
- has(id: string): boolean {
224
- return this.getById(id) !== undefined;
225
- }
226
-
227
- /**
228
- * Remove all grants and reset the store to an empty state.
229
- */
230
- clear(): void {
231
- this.assertNotCorrupt();
232
- this.writeToDisk([]);
233
- }
234
-
235
- // -----------------------------------------------------------------------
236
- // Internals
237
- // -----------------------------------------------------------------------
238
-
239
- private assertNotCorrupt(): void {
240
- if (this.corrupt) {
241
- throw new Error(
242
- "CES persistent grant store is corrupt — refusing to operate (fail closed)",
243
- );
244
- }
245
- }
246
-
247
- private loadFromDisk(): PersistentGrant[] {
248
- try {
249
- const raw = readFileSync(this.filePath, "utf-8");
250
- const data = JSON.parse(raw) as unknown;
251
-
252
- if (
253
- typeof data !== "object" ||
254
- data === null ||
255
- !("version" in data) ||
256
- !("grants" in data)
257
- ) {
258
- this.corrupt = true;
259
- throw new Error(
260
- "CES grants file is malformed: missing version or grants field",
261
- );
262
- }
263
-
264
- const file = data as GrantsFile;
265
-
266
- if (file.version !== GRANTS_FILE_VERSION) {
267
- this.corrupt = true;
268
- throw new Error(
269
- `CES grants file has unsupported version ${file.version} (expected ${GRANTS_FILE_VERSION})`,
270
- );
271
- }
272
-
273
- if (!Array.isArray(file.grants)) {
274
- this.corrupt = true;
275
- throw new Error("CES grants file is malformed: grants is not an array");
276
- }
277
-
278
- return [...file.grants];
279
- } catch (err) {
280
- if (this.corrupt) throw err;
281
- // Any other read/parse error → fail closed
282
- this.corrupt = true;
283
- throw new Error(
284
- `CES persistent grant store failed to read ${this.filePath}: ${err instanceof Error ? err.message : String(err)}`,
285
- );
286
- }
287
- }
288
-
289
- private writeToDisk(grants: PersistentGrant[]): void {
290
- const dir = dirname(this.filePath);
291
- if (!existsSync(dir)) {
292
- mkdirSync(dir, { recursive: true });
293
- }
294
-
295
- const data: GrantsFile = {
296
- version: GRANTS_FILE_VERSION,
297
- grants,
298
- };
299
-
300
- const tmpPath = join(dir, `.tmp-${randomUUID()}`);
301
- writeFileSync(tmpPath, JSON.stringify(data, null, 2), {
302
- mode: 0o600,
303
- });
304
- renameSync(tmpPath, this.filePath);
305
- // Enforce owner-only permissions even if the file already existed
306
- // with wider permissions.
307
- chmodSync(this.filePath, 0o600);
308
- }
309
- }
@@ -1,293 +0,0 @@
1
- /**
2
- * CES RPC handlers for grant and audit management.
3
- *
4
- * Implements the server-side handlers for:
5
- * - `record_grant` — Record a grant decision after guardian approval.
6
- * - `list_grants` — List grants filtered by session, handle, or status.
7
- * - `revoke_grant` — Revoke a specific grant by its stable ID.
8
- * - `list_audit_records` — List audit records with filtering and pagination.
9
- *
10
- * All handlers operate strictly on CES-owned state and never expose raw
11
- * secret material, raw tokens, or raw headers/bodies. Grant records returned
12
- * to the assistant contain only metadata (handle, proposal type, status,
13
- * timestamps).
14
- */
15
-
16
- import type {
17
- ListGrants,
18
- ListGrantsResponse,
19
- RecordGrant,
20
- RecordGrantResponse,
21
- RevokeGrant,
22
- RevokeGrantResponse,
23
- ListAuditRecords,
24
- ListAuditRecordsResponse,
25
- PersistentGrantRecord,
26
- } from "@vellumai/service-contracts/credential-rpc";
27
-
28
- import type { PersistentGrantStore, PersistentGrant } from "./persistent-store.js";
29
- import type { TemporaryGrantStore } from "./temporary-store.js";
30
- import type { AuditStore } from "../audit/store.js";
31
- import type { RpcMethodHandler } from "../server.js";
32
-
33
- // ---------------------------------------------------------------------------
34
- // Grant → PersistentGrantRecord projection
35
- // ---------------------------------------------------------------------------
36
-
37
- /**
38
- * Project a CES internal PersistentGrant into the wire-format
39
- * PersistentGrantRecord. Maps real fields from the persistent store
40
- * schema into the wire contract.
41
- */
42
- function projectGrant(grant: PersistentGrant): PersistentGrantRecord {
43
- return {
44
- grantId: grant.id,
45
- sessionId: grant.sessionId,
46
- credentialHandle: grant.scope,
47
- proposalType:
48
- grant.tool === "http" || grant.tool === "command"
49
- ? grant.tool
50
- : "command",
51
- proposalHash: grant.id,
52
- allowedPurposes: [grant.pattern],
53
- status: grant.revokedAt != null ? "revoked" : "active",
54
- grantedBy: "user",
55
- createdAt: new Date(grant.createdAt).toISOString(),
56
- expiresAt: null,
57
- consumedAt: null,
58
- revokedAt:
59
- grant.revokedAt != null
60
- ? new Date(grant.revokedAt).toISOString()
61
- : null,
62
- };
63
- }
64
-
65
- // ---------------------------------------------------------------------------
66
- // record_grant handler
67
- // ---------------------------------------------------------------------------
68
-
69
- export interface RecordGrantHandlerDeps {
70
- persistentGrantStore: PersistentGrantStore;
71
- temporaryGrantStore: TemporaryGrantStore;
72
- }
73
-
74
- /**
75
- * Create an RPC handler for the `record_grant` method.
76
- *
77
- * Receives a `TemporaryGrantDecision` from the approval bridge and persists
78
- * it as a `PersistentGrant` (for approved decisions) or returns a success
79
- * acknowledgement (for denied decisions). The handler also adds an
80
- * in-memory temporary grant so the caller can immediately retry the
81
- * original tool invocation.
82
- *
83
- * For approved decisions with a TTL of "PT10M", the grant is stored as
84
- * a timed temporary grant. Otherwise it is persisted as a permanent grant.
85
- */
86
- export function createRecordGrantHandler(
87
- deps: RecordGrantHandlerDeps,
88
- ): RpcMethodHandler<RecordGrant, RecordGrantResponse> {
89
- return (request) => {
90
- const { decision, sessionId } = request;
91
-
92
- // Denied decisions are acknowledged but produce no grant record.
93
- if (decision.decision === "denied") {
94
- return { success: true };
95
- }
96
-
97
- const proposal = decision.proposal;
98
- const now = Date.now();
99
- const grantId = decision.proposalHash;
100
-
101
- // Determine the grant type. When omitted (backwards compat), default
102
- // to `always_allow` so existing callers that don't send `grantType`
103
- // continue to create persistent grants.
104
- const grantType = decision.grantType ?? "always_allow";
105
-
106
- // Only `always_allow` creates a persistent grant. All other approved
107
- // decisions create only a temporary grant — this prevents allow_once,
108
- // allow_10m, and allow_conversation from becoming effectively permanent.
109
- if (grantType === "always_allow") {
110
- let pattern: string;
111
- if (proposal.type === "http") {
112
- // Use the templated allowedUrlPatterns (e.g. "https://api.example.com/repos/{:uuid}/pulls")
113
- // so the persistent grant covers future requests with different IDs but the same URL structure.
114
- // Falls back to the exact URL only if allowedUrlPatterns is missing.
115
- const urlPattern = proposal.allowedUrlPatterns?.[0] ?? proposal.url;
116
- pattern = `${proposal.method} ${urlPattern}`;
117
- } else {
118
- pattern = proposal.allowedCommandPatterns?.[0] ?? proposal.command;
119
- }
120
- const persistentGrant: PersistentGrant = {
121
- id: grantId,
122
- tool: proposal.type,
123
- pattern,
124
- scope: proposal.credentialHandle,
125
- createdAt: now,
126
- sessionId,
127
- };
128
- deps.persistentGrantStore.add(persistentGrant);
129
- }
130
-
131
- // Record a temporary grant so the caller can use it immediately.
132
- // For `always_allow`, an `allow_once` temp grant bridges the gap until
133
- // the next policy check hits the persistent store.
134
- if (grantType === "allow_10m") {
135
- deps.temporaryGrantStore.add("allow_10m", decision.proposalHash);
136
- } else if (grantType === "allow_conversation") {
137
- deps.temporaryGrantStore.add("allow_conversation", decision.proposalHash, {
138
- conversationId: request.conversationId ?? sessionId,
139
- });
140
- } else {
141
- // allow_once and always_allow both get a single-use temp grant
142
- // for immediate retry.
143
- deps.temporaryGrantStore.add("allow_once", decision.proposalHash);
144
- }
145
-
146
- // Compute expiry from TTL if present.
147
- let expiresAt: string | null = null;
148
- if (decision.ttl === "PT10M") {
149
- expiresAt = new Date(now + 10 * 60 * 1000).toISOString();
150
- }
151
-
152
- const grantRecord: PersistentGrantRecord = {
153
- grantId,
154
- sessionId,
155
- credentialHandle: proposal.credentialHandle,
156
- proposalType: proposal.type,
157
- proposalHash: decision.proposalHash,
158
- allowedPurposes:
159
- proposal.type === "http"
160
- ? proposal.allowedUrlPatterns ?? [`${proposal.method} ${proposal.url}`]
161
- : proposal.allowedCommandPatterns ?? [proposal.command],
162
- status: "active",
163
- grantedBy: decision.decidedBy,
164
- createdAt: new Date(now).toISOString(),
165
- expiresAt,
166
- consumedAt: null,
167
- revokedAt: null,
168
- };
169
-
170
- return {
171
- success: true,
172
- grant: grantRecord,
173
- };
174
- };
175
- }
176
-
177
- // ---------------------------------------------------------------------------
178
- // list_grants handler
179
- // ---------------------------------------------------------------------------
180
-
181
- export interface ListGrantsHandlerDeps {
182
- persistentGrantStore: PersistentGrantStore;
183
- }
184
-
185
- /**
186
- * Create an RPC handler for the `list_grants` method.
187
- *
188
- * Lists all persistent grants (including revoked, for audit trail),
189
- * optionally filtered by session ID, credential handle, or status.
190
- * Returns wire-format PersistentGrantRecords that never include raw
191
- * secret material.
192
- */
193
- export function createListGrantsHandler(
194
- deps: ListGrantsHandlerDeps,
195
- ): RpcMethodHandler<ListGrants, ListGrantsResponse> {
196
- return (request) => {
197
- // Include revoked grants in the listing for audit visibility.
198
- const allGrants = deps.persistentGrantStore.getAllIncludingRevoked();
199
- const projected = allGrants.map((g) => projectGrant(g));
200
-
201
- let filtered = projected;
202
-
203
- if (request.sessionId) {
204
- filtered = filtered.filter((g) => g.sessionId === request.sessionId);
205
- }
206
-
207
- if (request.credentialHandle) {
208
- filtered = filtered.filter(
209
- (g) => g.credentialHandle === request.credentialHandle,
210
- );
211
- }
212
-
213
- if (request.status) {
214
- filtered = filtered.filter((g) => g.status === request.status);
215
- }
216
-
217
- return { grants: filtered };
218
- };
219
- }
220
-
221
- // ---------------------------------------------------------------------------
222
- // revoke_grant handler
223
- // ---------------------------------------------------------------------------
224
-
225
- export interface RevokeGrantHandlerDeps {
226
- persistentGrantStore: PersistentGrantStore;
227
- }
228
-
229
- /**
230
- * Create an RPC handler for the `revoke_grant` method.
231
- *
232
- * Marks a grant as revoked in the persistent store by its stable ID,
233
- * preserving the record for audit trail. Returns success/failure.
234
- */
235
- export function createRevokeGrantHandler(
236
- deps: RevokeGrantHandlerDeps,
237
- ): RpcMethodHandler<RevokeGrant, RevokeGrantResponse> {
238
- return (request) => {
239
- const revoked = deps.persistentGrantStore.markRevoked(
240
- request.grantId,
241
- request.reason,
242
- );
243
-
244
- if (!revoked) {
245
- return {
246
- success: false,
247
- error: {
248
- code: "GRANT_NOT_FOUND",
249
- message: `No grant found with ID "${request.grantId}" (or already revoked)`,
250
- },
251
- };
252
- }
253
-
254
- return { success: true };
255
- };
256
- }
257
-
258
- // ---------------------------------------------------------------------------
259
- // list_audit_records handler
260
- // ---------------------------------------------------------------------------
261
-
262
- export interface ListAuditRecordsHandlerDeps {
263
- auditStore: AuditStore;
264
- }
265
-
266
- /**
267
- * Create an RPC handler for the `list_audit_records` method.
268
- *
269
- * Lists audit records with optional filtering by session, credential
270
- * handle, or grant ID. Supports limit and cursor-based pagination.
271
- *
272
- * Audit records never contain raw secrets, raw tokens, or raw
273
- * headers/bodies — they are token-free summaries generated at
274
- * execution time.
275
- */
276
- export function createListAuditRecordsHandler(
277
- deps: ListAuditRecordsHandlerDeps,
278
- ): RpcMethodHandler<ListAuditRecords, ListAuditRecordsResponse> {
279
- return (request) => {
280
- const result = deps.auditStore.list({
281
- sessionId: request.sessionId,
282
- credentialHandle: request.credentialHandle,
283
- grantId: request.grantId,
284
- limit: request.limit,
285
- cursor: request.cursor,
286
- });
287
-
288
- return {
289
- records: result.records,
290
- nextCursor: result.nextCursor,
291
- };
292
- };
293
- }