@cef-ai/cli 2.7.5 → 2.8.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 (62) hide show
  1. package/README.md +15 -0
  2. package/dist/cli-program.d.ts +2 -0
  3. package/dist/cli-program.d.ts.map +1 -1
  4. package/dist/cli-program.js +17 -0
  5. package/dist/cli-program.js.map +1 -1
  6. package/dist/commands/cubby.d.ts +7 -0
  7. package/dist/commands/cubby.d.ts.map +1 -1
  8. package/dist/commands/cubby.js +8 -4
  9. package/dist/commands/cubby.js.map +1 -1
  10. package/dist/commands/eval.d.ts +217 -0
  11. package/dist/commands/eval.d.ts.map +1 -0
  12. package/dist/commands/eval.js +860 -0
  13. package/dist/commands/eval.js.map +1 -0
  14. package/dist/commands/eval.test.d.ts +2 -0
  15. package/dist/commands/eval.test.d.ts.map +1 -0
  16. package/dist/commands/eval.test.js +474 -0
  17. package/dist/commands/eval.test.js.map +1 -0
  18. package/dist/commands/push.d.ts +23 -0
  19. package/dist/commands/push.d.ts.map +1 -1
  20. package/dist/commands/push.js +70 -1
  21. package/dist/commands/push.js.map +1 -1
  22. package/dist/lib/access-token.d.ts +33 -0
  23. package/dist/lib/access-token.d.ts.map +1 -1
  24. package/dist/lib/access-token.js +89 -20
  25. package/dist/lib/access-token.js.map +1 -1
  26. package/dist/lib/bucket-owner.d.ts +45 -0
  27. package/dist/lib/bucket-owner.d.ts.map +1 -0
  28. package/dist/lib/bucket-owner.js +107 -0
  29. package/dist/lib/bucket-owner.js.map +1 -0
  30. package/dist/lib/ddc.d.ts +22 -0
  31. package/dist/lib/ddc.d.ts.map +1 -1
  32. package/dist/lib/ddc.js +72 -20
  33. package/dist/lib/ddc.js.map +1 -1
  34. package/dist/lib/eval-auth.d.ts +139 -0
  35. package/dist/lib/eval-auth.d.ts.map +1 -0
  36. package/dist/lib/eval-auth.js +385 -0
  37. package/dist/lib/eval-auth.js.map +1 -0
  38. package/dist/lib/eval-auth.test.d.ts +2 -0
  39. package/dist/lib/eval-auth.test.d.ts.map +1 -0
  40. package/dist/lib/eval-auth.test.js +192 -0
  41. package/dist/lib/eval-auth.test.js.map +1 -0
  42. package/dist/lib/eval-store.d.ts +62 -0
  43. package/dist/lib/eval-store.d.ts.map +1 -0
  44. package/dist/lib/eval-store.js +334 -0
  45. package/dist/lib/eval-store.js.map +1 -0
  46. package/dist/lib/eval-store.test.d.ts +2 -0
  47. package/dist/lib/eval-store.test.d.ts.map +1 -0
  48. package/dist/lib/eval-store.test.js +155 -0
  49. package/dist/lib/eval-store.test.js.map +1 -0
  50. package/dist/lib/eval-target.d.ts +122 -0
  51. package/dist/lib/eval-target.d.ts.map +1 -0
  52. package/dist/lib/eval-target.js +320 -0
  53. package/dist/lib/eval-target.js.map +1 -0
  54. package/dist/lib/eval-target.test.d.ts +2 -0
  55. package/dist/lib/eval-target.test.d.ts.map +1 -0
  56. package/dist/lib/eval-target.test.js +209 -0
  57. package/dist/lib/eval-target.test.js.map +1 -0
  58. package/dist/lib/eval-workflow.d.ts +24 -0
  59. package/dist/lib/eval-workflow.d.ts.map +1 -0
  60. package/dist/lib/eval-workflow.js +91 -0
  61. package/dist/lib/eval-workflow.js.map +1 -0
  62. package/package.json +8 -4
@@ -0,0 +1,62 @@
1
+ /**
2
+ * S3-gateway-backed `EvalStore`.
3
+ *
4
+ * Datasets live on the S3 gateway, NOT on the DDC content gateway that
5
+ * `cef push` writes to. ROC's own `agentServiceAccess/wellKnown.ts` states why
6
+ * these cannot be the same path: "the content gateway cannot resolve S3 keys:
7
+ * the S3 gateway addresses objects through its own Postgres key->CID index,
8
+ * which the CDN knows nothing about." They are two systems with two key spaces,
9
+ * which is also why `cef eval push` is its own command rather than a flag on
10
+ * `cef push`.
11
+ */
12
+ import { S3Client } from "@aws-sdk/client-s3";
13
+ import type { EvalStore } from "@cef-ai/eval";
14
+ import type { CredentialSource } from "./eval-auth.js";
15
+ export interface S3StoreOptions {
16
+ bucket: string;
17
+ client: S3Client;
18
+ /** Key prefix within the bucket. Defaults to none. */
19
+ root?: string;
20
+ /**
21
+ * The gateway refused `failed`'s credential (403): a client with a fresh
22
+ * one, or undefined when there is none. Each request is retried once.
23
+ */
24
+ renew?: (failed: S3Client) => Promise<S3Client | undefined>;
25
+ }
26
+ /** A refused credential: expired, revoked, or never valid. */
27
+ export declare function isAuthFailure(err: unknown): boolean;
28
+ export declare function s3Store(opts: S3StoreOptions): EvalStore;
29
+ /**
30
+ * Build an S3 client for a gateway endpoint. With a `CredentialSource` the
31
+ * client asks it for a credential, carrying its expiry, so the SDK asks again
32
+ * inside its refresh window (5 min before expiry) and the source mints anew.
33
+ */
34
+ export declare function gatewayClient(cfg: {
35
+ endpoint: string;
36
+ region?: string;
37
+ } & ({
38
+ accessKeyId: string;
39
+ secretAccessKey: string;
40
+ sessionToken?: string;
41
+ credentials?: never;
42
+ } | {
43
+ credentials: CredentialSource;
44
+ })): S3Client;
45
+ /**
46
+ * The store `cef eval` uses: a gateway client over a credential source, and
47
+ * on a 403 one fresh client per refused credential — concurrent requests that
48
+ * fail together share it instead of minting one each.
49
+ */
50
+ export declare function gatewayStore(opts: {
51
+ endpoint: string;
52
+ bucket: string;
53
+ region?: string;
54
+ credentials: CredentialSource;
55
+ root?: string;
56
+ makeClient?: (cfg: {
57
+ endpoint: string;
58
+ region?: string;
59
+ credentials: CredentialSource;
60
+ }) => S3Client;
61
+ }): EvalStore;
62
+ //# sourceMappingURL=eval-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-store.d.ts","sourceRoot":"","sources":["../../src/lib/eval-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAML,QAAQ,EAET,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,CAAC;IACjB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CAC7D;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAInD;AAuBD,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CA8NvD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,CACA;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,GAC5F;IAAE,WAAW,EAAE,gBAAgB,CAAA;CAAE,CACpC,GAAG,QAAQ,CA+CX;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,gBAAgB,CAAA;KAAE,KAAK,QAAQ,CAAC;CACtG,GAAG,SAAS,CAwBZ"}
@@ -0,0 +1,334 @@
1
+ /**
2
+ * S3-gateway-backed `EvalStore`.
3
+ *
4
+ * Datasets live on the S3 gateway, NOT on the DDC content gateway that
5
+ * `cef push` writes to. ROC's own `agentServiceAccess/wellKnown.ts` states why
6
+ * these cannot be the same path: "the content gateway cannot resolve S3 keys:
7
+ * the S3 gateway addresses objects through its own Postgres key->CID index,
8
+ * which the CDN knows nothing about." They are two systems with two key spaces,
9
+ * which is also why `cef eval push` is its own command rather than a flag on
10
+ * `cef push`.
11
+ */
12
+ import { GetObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client, } from "@aws-sdk/client-s3";
13
+ /** A refused credential: expired, revoked, or never valid. */
14
+ export function isAuthFailure(err) {
15
+ const e = err;
16
+ return e?.$metadata?.httpStatusCode === 403
17
+ || e?.name === "InvalidAccessKeyId" || e?.name === "ExpiredToken" || e?.name === "AccessDenied";
18
+ }
19
+ /**
20
+ * `listObjects` on the gateway caps a page at 1000 keys and does NOT signal
21
+ * truncation to a naive caller — a known ceiling recorded in the design. So
22
+ * this paginates explicitly with the continuation token rather than trusting a
23
+ * single response, and every caller gets the whole prefix or an error.
24
+ *
25
+ * Silent truncation is the dangerous shape: a dataset that quietly
26
+ * stops at 1000 cases cuts a version missing the rest, and the experiment
27
+ * built from it looks complete.
28
+ */
29
+ /** The endpoint host, for a warning that has to say WHICH gateway misbehaved. */
30
+ async function endpointLabel(client) {
31
+ try {
32
+ const endpoint = client.config
33
+ .endpoint;
34
+ return (await endpoint?.())?.hostname ?? "the S3 endpoint";
35
+ }
36
+ catch {
37
+ return "the S3 endpoint";
38
+ }
39
+ }
40
+ export function s3Store(opts) {
41
+ const { bucket } = opts;
42
+ let client = opts.client;
43
+ /*
44
+ * ONE RETRY ON A REFUSED CREDENTIAL. The client re-asks its credential
45
+ * provider before expiry on its own; this covers the rest (revoked, a clock
46
+ * that disagrees with the gateway's) without turning a real permission
47
+ * error into a loop.
48
+ */
49
+ const send = async (command) => {
50
+ const used = client;
51
+ try {
52
+ return (await used.send(command));
53
+ }
54
+ catch (err) {
55
+ if (!opts.renew || !isAuthFailure(err))
56
+ throw err;
57
+ const fresh = await opts.renew(used);
58
+ if (!fresh)
59
+ throw err;
60
+ client = fresh;
61
+ return (await fresh.send(command));
62
+ }
63
+ };
64
+ const root = opts.root ? opts.root.replace(/\/*$/, "/") : "";
65
+ const full = (key) => `${root}${key}`;
66
+ const get = async (key) => {
67
+ try {
68
+ const res = await send(new GetObjectCommand({ Bucket: bucket, Key: full(key) }));
69
+ return (await res.Body?.transformToString()) ?? null;
70
+ }
71
+ catch (err) {
72
+ const name = err.name;
73
+ // A genuinely absent object is `null`; anything else (auth, network,
74
+ // a bad bucket) must propagate — swallowing it would make `putCase`'s
75
+ // written-once check silently pass and overwrite a real case.
76
+ if (name === "NoSuchKey" || name === "NotFound")
77
+ return null;
78
+ throw err;
79
+ }
80
+ };
81
+ return {
82
+ async list(prefix) {
83
+ const keys = [];
84
+ let token;
85
+ do {
86
+ const page = await send(new ListObjectsV2Command({
87
+ Bucket: bucket,
88
+ Prefix: full(prefix),
89
+ ContinuationToken: token,
90
+ }));
91
+ for (const obj of page.Contents ?? []) {
92
+ if (obj.Key)
93
+ keys.push(root ? obj.Key.slice(root.length) : obj.Key);
94
+ }
95
+ // Truncated with nowhere to continue from is the exact silent
96
+ // truncation this function exists to prevent: returning the short list
97
+ // reads as a complete one, and the version cut from it is missing cases
98
+ // nobody will notice are gone.
99
+ if (page.IsTruncated && !page.NextContinuationToken) {
100
+ throw new Error(`[eval] listing "${full(prefix)}" in bucket "${bucket}" came back ` +
101
+ `TRUNCATED after ${keys.length} key(s) with no ` +
102
+ `NextContinuationToken, so the rest of the prefix cannot be ` +
103
+ `fetched. Refusing to return a short list — a set that quietly ` +
104
+ `stops cuts a version missing cases, and the experiment built ` +
105
+ `from it looks complete.`);
106
+ }
107
+ token = page.IsTruncated ? page.NextContinuationToken : undefined;
108
+ } while (token);
109
+ return keys.sort();
110
+ },
111
+ /**
112
+ * Child prefixes through `Delimiter: "/"`, paged the same way and with the
113
+ * same refusal of a truncated page that has nowhere to continue — what
114
+ * lets `listDatasets`/`findExperiment` skip every run record in the bucket.
115
+ */
116
+ async listPrefixes(prefix) {
117
+ const out = new Set();
118
+ let token;
119
+ do {
120
+ const page = await send(new ListObjectsV2Command({
121
+ Bucket: bucket,
122
+ Prefix: full(prefix),
123
+ Delimiter: "/",
124
+ ContinuationToken: token,
125
+ }));
126
+ for (const p of page.CommonPrefixes ?? []) {
127
+ if (p.Prefix)
128
+ out.add(root ? p.Prefix.slice(root.length) : p.Prefix);
129
+ }
130
+ if (page.IsTruncated && !page.NextContinuationToken) {
131
+ throw new Error(`[eval] listing the prefixes of "${full(prefix)}" in bucket "${bucket}" came back TRUNCATED ` +
132
+ `with no NextContinuationToken — refusing to return a short list.`);
133
+ }
134
+ token = page.IsTruncated ? page.NextContinuationToken : undefined;
135
+ } while (token);
136
+ return [...out].sort();
137
+ },
138
+ get,
139
+ async put(key, body) {
140
+ await send(new PutObjectCommand({
141
+ Bucket: bucket,
142
+ Key: full(key),
143
+ Body: body,
144
+ ContentType: "application/json",
145
+ }));
146
+ },
147
+ /**
148
+ * Write-if-absent. A real compare-and-set on every store this runs against,
149
+ * including Cere's DDC S3 gateway — verified, not assumed.
150
+ *
151
+ * Two layers, in order:
152
+ *
153
+ * 1. READ FIRST, and return `false` without attempting any write. Catches
154
+ * every collision older than one round trip, and — because it never
155
+ * writes on that path — it is also what keeps a store that ignored the
156
+ * header from corrupting rather than merely losing a cut.
157
+ *
158
+ * 2. `If-None-Match: "*"` on the PUT, with `412` read as "taken". This is
159
+ * the part that actually closes the race, step 1 or no step 1.
160
+ *
161
+ * Step 2 was believed inert on the DDC gateway: it is a hand-written Go
162
+ * service whose documented PutObject path is "stream to DDC, then UPSERT
163
+ * the index row" with no precondition step, its published error model lists
164
+ * no `412`, and nothing in this repo or ROC had ever sent the header. Two
165
+ * company ADR paragraphs contradicted each other on it.
166
+ *
167
+ * Probed against dev rather than left as a caveat: the first conditional
168
+ * PUT succeeded, the SECOND returned `PreconditionFailed` / 412, and the
169
+ * stored bytes were still the first writer's. The gateway honours it, and
170
+ * the ADR paragraph claiming conditional writes exist is the correct one.
171
+ *
172
+ * The unconditional fallback below stays for a store that rejects the
173
+ * header outright with 501 — one unknown header must not be able to take
174
+ * `eval push --cut` down entirely — and step 1 still guards that path. NOT
175
+ * 400: that covers a malformed key, a bad body, a signing problem and much
176
+ * else, and downgrading on it would let two concurrent cutters both
177
+ * believe they won while step 2 was never really tried.
178
+ */
179
+ async putIfAbsent(key, body) {
180
+ if ((await get(key)) !== null)
181
+ return false;
182
+ const command = (conditional) => new PutObjectCommand({
183
+ Bucket: bucket,
184
+ Key: full(key),
185
+ Body: body,
186
+ ContentType: "application/json",
187
+ ...(conditional ? { IfNoneMatch: "*" } : {}),
188
+ });
189
+ try {
190
+ await send(command(true));
191
+ return true;
192
+ }
193
+ catch (err) {
194
+ const code = err.$metadata
195
+ ?.httpStatusCode;
196
+ // 412: the key was taken. 409 (`ConditionalRequestConflict`) means a
197
+ // concurrent write collided and AWS says to retry — reporting it as
198
+ // "taken" sends the caller back to re-list and try the next number,
199
+ // which is the same recovery and is self-correcting if the key turns
200
+ // out to be free after all.
201
+ if (err.name === "PreconditionFailed")
202
+ return false;
203
+ if (code === 412 || code === 409)
204
+ return false;
205
+ // A server that does not IMPLEMENT the header must not take the whole
206
+ // feature down with it: `cef eval push --cut` has to keep working
207
+ // against a gateway that answers 501. 501 ONLY — a 400 is "your request
208
+ // was wrong", which covers a malformed key, a bad body, a signing
209
+ // problem and much else, and downgrading on it means two cutters both
210
+ // believe they won while step 2 was never really tried. And the
211
+ // downgrade is announced: a gateway that regresses out of conditional
212
+ // writes must be visible, not a comment nobody reads.
213
+ if (code === 501) {
214
+ console.warn(`[eval] ${await endpointLabel(client)} answered 501 to a conditional ` +
215
+ `PUT (If-None-Match: *) on "${full(key)}" — falling back to an ` +
216
+ `UNCONDITIONAL write. The read check above still stands, but two ` +
217
+ `concurrent writers can now both believe they won a version or a ` +
218
+ `revision. This gateway no longer supports write-once.`);
219
+ await send(command(false));
220
+ return true;
221
+ }
222
+ throw err;
223
+ }
224
+ },
225
+ /**
226
+ * The claim `storeConformance` refuses to run without, and the one the
227
+ * doc comment above spends forty lines earning: the SERVER adjudicates,
228
+ * through `If-None-Match: "*"` with 412/409 read as "taken".
229
+ *
230
+ * Pinned, not asserted-by-comment: `eval-store.test.ts` drives this
231
+ * `putIfAbsent` and reads the header off the `PutObjectCommand` the store
232
+ * actually sent. Change the mechanism and that test goes red next to this
233
+ * word.
234
+ *
235
+ * One honest caveat, and it is wider than "one write": the 501 branch
236
+ * above is in a per-call `catch`, so against a gateway that answers 501 it
237
+ * fires on EVERY `putIfAbsent` for as long as that lasts. The whole
238
+ * process is writing unconditionally, with only step 1's read between two
239
+ * cutters and a silently replaced manifest. The per-call `console.warn` is
240
+ * the right mitigation precisely because of that — it names the endpoint
241
+ * every time rather than once — but nobody should read this as a single
242
+ * degraded write.
243
+ *
244
+ * The declaration stays `conditional-write` regardless, because it
245
+ * describes what this store SENDS. A store whose declared mechanism
246
+ * flipped mid-run would be a claim nobody could check against the code,
247
+ * which is the one thing the word has to be.
248
+ */
249
+ atomicity: "conditional-write",
250
+ };
251
+ }
252
+ /**
253
+ * Build an S3 client for a gateway endpoint. With a `CredentialSource` the
254
+ * client asks it for a credential, carrying its expiry, so the SDK asks again
255
+ * inside its refresh window (5 min before expiry) and the source mints anew.
256
+ */
257
+ export function gatewayClient(cfg) {
258
+ const credentials = "credentials" in cfg && cfg.credentials
259
+ ? async () => {
260
+ const c = await cfg.credentials.get();
261
+ return {
262
+ accessKeyId: c.accessKeyId,
263
+ secretAccessKey: c.secretAccessKey,
264
+ ...(c.expiresAt !== undefined ? { expiration: new Date(c.expiresAt) } : {}),
265
+ };
266
+ }
267
+ : {
268
+ accessKeyId: cfg.accessKeyId,
269
+ secretAccessKey: cfg.secretAccessKey,
270
+ ...(cfg.sessionToken ? { sessionToken: cfg.sessionToken } : {}),
271
+ };
272
+ const config = {
273
+ endpoint: cfg.endpoint,
274
+ region: cfg.region ?? "us-east-1",
275
+ // The gateway addresses buckets by path, not by virtual host.
276
+ forcePathStyle: true,
277
+ credentials,
278
+ };
279
+ const client = new S3Client(config);
280
+ // The gateway routes on `/<bucket>/<key>` and reads the empty last segment of
281
+ // a bucket-level path (`/<bucket>/`, which is what the AWS SDK sends under
282
+ // `forcePathStyle`) as a key, answering `400 InvalidArgument: key is
283
+ // required`. Keyed commands are left alone: an object key may legitimately
284
+ // end in `/`. Keying off the command input rather than the path shape also
285
+ // survives a `--endpoint` that carries a path prefix, where `/s3/<bucket>/`
286
+ // is indistinguishable from a bucket-and-key path by pattern alone.
287
+ //
288
+ // ROC needs this too and has `removeTrailingBucketSlashForDdcGateway` in
289
+ // `browserBucketStorage.ts`; that one matches on the path shape, so it is the
290
+ // same behaviour for the bare-host endpoints both use.
291
+ client.middlewareStack.add((next) => (args) => {
292
+ const request = args.request;
293
+ const keyed = typeof args.input === "object" && args.input !== null && "Key" in args.input;
294
+ if (request?.path && request.path.length > 1 && request.path.endsWith("/") && !keyed) {
295
+ request.path = request.path.replace(/\/+$/, "");
296
+ }
297
+ return next(args);
298
+ }, { name: "removeTrailingBucketSlashForDdcGateway", step: "build" });
299
+ return client;
300
+ }
301
+ /**
302
+ * The store `cef eval` uses: a gateway client over a credential source, and
303
+ * on a 403 one fresh client per refused credential — concurrent requests that
304
+ * fail together share it instead of minting one each.
305
+ */
306
+ export function gatewayStore(opts) {
307
+ const make = () => (opts.makeClient ?? gatewayClient)({
308
+ endpoint: opts.endpoint, ...(opts.region ? { region: opts.region } : {}), credentials: opts.credentials,
309
+ });
310
+ let current = make();
311
+ let renewing;
312
+ return s3Store({
313
+ bucket: opts.bucket,
314
+ client: current,
315
+ ...(opts.root ? { root: opts.root } : {}),
316
+ renew: async (failed) => {
317
+ if (failed !== current)
318
+ return current;
319
+ renewing ??= (async () => {
320
+ try {
321
+ if (!(await opts.credentials.invalidate()))
322
+ return undefined;
323
+ current = make();
324
+ return current;
325
+ }
326
+ finally {
327
+ renewing = undefined;
328
+ }
329
+ })();
330
+ return renewing;
331
+ },
332
+ });
333
+ }
334
+ //# sourceMappingURL=eval-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-store.js","sourceRoot":"","sources":["../../src/lib/eval-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=eval-store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-store.test.d.ts","sourceRoot":"","sources":["../../src/lib/eval-store.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * `s3Store` against an in-process S3 that behaves like the gateway where it
3
+ * matters: a list page holds at most PAGE keys, and `If-None-Match: *` on an
4
+ * existing key answers 412. A plain memory map would pass a store that reads
5
+ * one page or ignores the condition.
6
+ */
7
+ import { describe, expect, it } from "vitest";
8
+ import { GetObjectCommand, ListObjectsV2Command, PutObjectCommand, } from "@aws-sdk/client-s3";
9
+ import { addCase, createDataset, cutVersion, loadVersion, storeConformance } from "@cef-ai/eval";
10
+ import { mintedCredentials, staticCredentials } from "./eval-auth.js";
11
+ import { gatewayStore, s3Store } from "./eval-store.js";
12
+ const PAGE = 3;
13
+ function fakeS3(opts = {}) {
14
+ const objects = new Map();
15
+ const sent = [];
16
+ const send = async (cmd) => {
17
+ const input = cmd.input;
18
+ sent.push({ name: cmd.constructor.name, input });
19
+ if (cmd instanceof GetObjectCommand) {
20
+ const body = objects.get(String(input.Key));
21
+ if (body === undefined)
22
+ throw Object.assign(new Error("NoSuchKey"), { name: "NoSuchKey" });
23
+ return { Body: { transformToString: async () => body } };
24
+ }
25
+ if (cmd instanceof PutObjectCommand) {
26
+ const key = String(input.Key);
27
+ if (input.IfNoneMatch === "*") {
28
+ if (opts.conditional === "501")
29
+ throw Object.assign(new Error("NotImplemented"), { $metadata: { httpStatusCode: 501 } });
30
+ if (objects.has(key))
31
+ throw Object.assign(new Error("PreconditionFailed"), { name: "PreconditionFailed", $metadata: { httpStatusCode: 412 } });
32
+ }
33
+ objects.set(key, String(input.Body));
34
+ return {};
35
+ }
36
+ if (cmd instanceof ListObjectsV2Command) {
37
+ const prefix = String(input.Prefix ?? "");
38
+ const keys = [...objects.keys()].filter((k) => k.startsWith(prefix)).sort();
39
+ // With a delimiter, S3 rolls every key below a "/" into one CommonPrefix and pages over the mix.
40
+ const entries = input.Delimiter === "/"
41
+ ? [...new Set(keys.map((k) => {
42
+ const slash = k.indexOf("/", prefix.length);
43
+ return slash < 0 ? k : `${k.slice(0, slash + 1)}`;
44
+ }))].sort()
45
+ : keys;
46
+ const start = input.ContinuationToken ? Number(input.ContinuationToken) : 0;
47
+ const page = entries.slice(start, start + PAGE);
48
+ const more = start + PAGE < entries.length;
49
+ return {
50
+ Contents: page.filter((k) => !k.endsWith("/") || input.Delimiter !== "/").map((Key) => ({ Key })),
51
+ CommonPrefixes: input.Delimiter === "/" ? page.filter((k) => k.endsWith("/")).map((Prefix) => ({ Prefix })) : [],
52
+ IsTruncated: more,
53
+ ...(more ? { NextContinuationToken: String(start + PAGE) } : {}),
54
+ };
55
+ }
56
+ throw new Error(`unexpected command ${cmd.constructor.name}`);
57
+ };
58
+ return { client: { send }, objects, sent };
59
+ }
60
+ describe("s3Store", () => {
61
+ for (const check of storeConformance(async () => s3Store({ bucket: "b", client: fakeS3().client }))) {
62
+ // The 1050-key paging check is slow against nothing in particular; paging is covered below.
63
+ if (check.name.includes("pages past"))
64
+ continue;
65
+ it(check.name, check.run);
66
+ }
67
+ it("lists every page, under the root, with the root stripped", async () => {
68
+ const s3 = fakeS3();
69
+ const store = s3Store({ bucket: "b", client: s3.client, root: "team" });
70
+ for (let i = 0; i < 7; i++)
71
+ await store.put(`evals/k${i}`, "x");
72
+ await store.put("other/z", "x");
73
+ expect(await store.list("evals/")).toEqual(["evals/k0", "evals/k1", "evals/k2", "evals/k3", "evals/k4", "evals/k5", "evals/k6"]);
74
+ expect([...s3.objects.keys()].every((k) => k.startsWith("team/"))).toBe(true);
75
+ });
76
+ it("reads an absent key as null and refuses a taken one with a conditional put", async () => {
77
+ const s3 = fakeS3();
78
+ const store = s3Store({ bucket: "b", client: s3.client });
79
+ expect(await store.get("nope")).toBeNull();
80
+ expect(await store.putIfAbsent("k", "1")).toBe(true);
81
+ expect(await store.putIfAbsent("k", "2")).toBe(false);
82
+ expect(await store.get("k")).toBe("1");
83
+ expect(s3.sent.filter((c) => c.name === "PutObjectCommand").every((c) => c.input.IfNoneMatch === "*")).toBe(true);
84
+ });
85
+ it("lists child prefixes across pages, under the root, with the root stripped", async () => {
86
+ const s3 = fakeS3();
87
+ const store = s3Store({ bucket: "b", client: s3.client, root: "team" });
88
+ for (const d of ["a", "b", "c", "d", "e"]) {
89
+ await store.put(`evals/datasets/${d}/header.json`, "{}");
90
+ await store.put(`evals/datasets/${d}/cases/x/r1.json`, "{}");
91
+ }
92
+ await store.put("evals/datasets/loose.json", "{}");
93
+ expect(await store.listPrefixes("evals/datasets/")).toEqual(["a", "b", "c", "d", "e"].map((d) => `evals/datasets/${d}/`));
94
+ expect(s3.sent.filter((c) => c.name === "ListObjectsV2Command").every((c) => c.input.Delimiter === "/")).toBe(true);
95
+ });
96
+ it("falls back to an unconditional write only on 501", async () => {
97
+ const store = s3Store({ bucket: "b", client: fakeS3({ conditional: "501" }).client });
98
+ expect(await store.putIfAbsent("k", "1")).toBe(true);
99
+ });
100
+ it("holds a dataset end to end", async () => {
101
+ const store = s3Store({ bucket: "b", client: fakeS3().client });
102
+ await createDataset(store, { dataset: "triage", workflowId: "wf" });
103
+ for (const id of ["a", "b", "c", "d"])
104
+ await addCase(store, "wf", "triage", { id, input: { id } });
105
+ await cutVersion(store, "wf", "triage");
106
+ expect((await loadVersion(store, "wf", "triage", 1)).cases.map((c) => c.case.id)).toEqual(["a", "b", "c", "d"]);
107
+ });
108
+ it("replaces a refused credential once, shared by every request it failed, and retries each", async () => {
109
+ const backing = fakeS3();
110
+ let n = 0;
111
+ const credentials = mintedCredentials({
112
+ mint: async () => ({ accessKeyId: `k${++n}`, secretAccessKey: "s", expiresAt: Date.now() + 3600_000 }),
113
+ });
114
+ await credentials.get();
115
+ const clients = [];
116
+ const store = gatewayStore({
117
+ endpoint: "https://gw", bucket: "b", credentials,
118
+ makeClient: (cfg) => {
119
+ const send = async (cmd) => {
120
+ const key = (await cfg.credentials.get()).accessKeyId;
121
+ // The first credential was revoked behind our back.
122
+ if (key === "k1") {
123
+ // One request was already in flight on the old credential, and is refused after the renewal.
124
+ if (cmd.input.Key === "slow")
125
+ await new Promise((r) => setTimeout(r, 20));
126
+ throw Object.assign(new Error("InvalidAccessKeyId"), { name: "InvalidAccessKeyId", $metadata: { httpStatusCode: 403 } });
127
+ }
128
+ return backing.client.send(cmd);
129
+ };
130
+ clients.push(`client${clients.length + 1}`);
131
+ return { send };
132
+ },
133
+ });
134
+ await Promise.all([store.get("slow"), store.put("a", "1"), store.put("b", "2"), store.get("a")]);
135
+ expect(await store.get("a")).toBe("1");
136
+ expect(await store.get("b")).toBe("2");
137
+ expect(clients).toEqual(["client1", "client2"]);
138
+ expect(n).toBe(2);
139
+ });
140
+ it("does not retry a refusal it cannot fix", async () => {
141
+ let calls = 0;
142
+ const store = gatewayStore({
143
+ endpoint: "https://gw", bucket: "b", credentials: staticCredentials({ accessKeyId: "AK", secretAccessKey: "SK" }),
144
+ makeClient: () => ({
145
+ send: async () => {
146
+ calls++;
147
+ throw Object.assign(new Error("AccessDenied"), { name: "AccessDenied", $metadata: { httpStatusCode: 403 } });
148
+ },
149
+ }),
150
+ });
151
+ await expect(store.put("a", "1")).rejects.toThrow(/AccessDenied/);
152
+ expect(calls).toBe(1);
153
+ });
154
+ });
155
+ //# sourceMappingURL=eval-store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-store.test.js","sourceRoot":"","sources":["../../src/lib/eval-store.test.ts"],"names":[],"mappings":"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"}