@mccaskie-labs/workbench-config 0.1.2 → 0.1.4
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.
- package/CHANGELOG.md +12 -0
- package/capabilities/ai.json +4 -3
- package/capabilities/gateway.json +9 -3
- package/dist/capabilities.gen.d.ts +44 -4
- package/dist/capabilities.gen.js +51 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @mccaskie-labs/workbench-config
|
|
2
2
|
|
|
3
|
+
## 0.1.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- The `ai` manifest is built (step 4.7): `Ai.countTokens` in its package surface and an `ai.budget` probe.
|
|
8
|
+
|
|
9
|
+
## 0.1.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- The `gateway` manifest is now `built` (step 4.5): the `X-Workbench-Env` request header, the per-key rate limit (300 a minute), the `not_found` and `internal_error` codes, and the `gateway.server-timing` probe.
|
|
14
|
+
|
|
3
15
|
## 0.1.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/capabilities/ai.json
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
"component": "ai",
|
|
5
5
|
"title": "Claude through the Gateway",
|
|
6
6
|
"summary": "Text, structured output and streaming from Claude, metered against the project's budget. Apps never hold an Anthropic key.",
|
|
7
|
-
"status": "
|
|
7
|
+
"status": "built",
|
|
8
8
|
"surfaces": {
|
|
9
9
|
"gateway": ["POST /anthropic/v1/messages", "POST /anthropic/v1/messages/count_tokens"],
|
|
10
|
-
"package": ["createAi", "Ai.text", "Ai.structured", "Ai.stream", "Ai.budget", "createFakeAi"]
|
|
10
|
+
"package": ["createAi", "Ai.text", "Ai.structured", "Ai.stream", "Ai.countTokens", "Ai.budget", "createFakeAi"]
|
|
11
11
|
},
|
|
12
12
|
"config": [
|
|
13
13
|
{
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
{ "id": "ai.structured", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.003 },
|
|
65
65
|
{ "id": "ai.text.truncation-throws", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.001 },
|
|
66
66
|
{ "id": "ai.model-not-allowed", "half": "runtime", "expect": "refused_as_expected", "refusal": "model_not_allowed", "cost_hint_usd": 0 },
|
|
67
|
-
{ "id": "ai.count-tokens", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 }
|
|
67
|
+
{ "id": "ai.count-tokens", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 },
|
|
68
|
+
{ "id": "ai.budget", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 }
|
|
68
69
|
]
|
|
69
70
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"component": "gateway",
|
|
5
5
|
"title": "Workbench Gateway: keys, headers, budgets and errors",
|
|
6
6
|
"summary": "What every Gateway endpoint shares: project keys by environment and scope, the request and response headers, the monthly budget check and the error codes. Apps hold one Workbench key and never a vendor key.",
|
|
7
|
-
"status": "
|
|
7
|
+
"status": "built",
|
|
8
8
|
"surfaces": {
|
|
9
9
|
"gateway": ["GET /v1/health", "GET /v1/usage", "GET /v1/capabilities"],
|
|
10
10
|
"package": ["DEFAULT_GATEWAY_URL"]
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
{ "name": "X-Workbench-Job", "required": true, "pattern": "^[a-z0-9._-]{1,64}$", "description": "What the call is for, e.g. article.write. Recorded with usage" },
|
|
39
39
|
{ "name": "X-Workbench-Prompt-Version", "required": false, "description": "The app's prompt version, recorded with usage (audit F17)" },
|
|
40
40
|
{ "name": "X-Workbench-Subject", "required": false, "pattern": "^[a-z0-9_-]+:[^\\s]{1,128}$", "description": "type:id of the thing the call is about" },
|
|
41
|
-
{ "name": "Idempotency-Key", "required": false, "description": "Email only: a repeat within 24 hours returns the stored result" }
|
|
41
|
+
{ "name": "Idempotency-Key", "required": false, "description": "Email only: a repeat within 24 hours returns the stored result" },
|
|
42
|
+
{ "name": "X-Workbench-Env", "required": false, "pattern": "^(development|staging|production)$", "description": "The caller's WORKBENCH_ENV. The packages send it; a key from another environment is then refused with unauthorised" }
|
|
42
43
|
],
|
|
43
44
|
"response_headers": ["X-Workbench-Request-Id", "Server-Timing"],
|
|
44
45
|
"server_timing_metrics": ["gw", "vendor_fb", "vendor", "total"]
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
"limits": [
|
|
47
48
|
{ "id": "gateway.key-cache", "value": 60, "unit": "seconds", "per": "isolate", "enforced_by": "gateway" },
|
|
48
49
|
{ "id": "gateway.budget-cache", "value": 60, "unit": "seconds", "per": "isolate", "enforced_by": "gateway" },
|
|
50
|
+
{ "id": "gateway.rate-limit", "value": 300, "unit": "requests", "per": "minute per key", "enforced_by": "gateway", "refusal": "rate_limited" },
|
|
49
51
|
{ "id": "gateway.job-format", "value": "[a-z0-9._-]{1,64}", "unit": "pattern", "per": "request", "enforced_by": "gateway", "refusal": "invalid_request" }
|
|
50
52
|
],
|
|
51
53
|
"errors": [
|
|
@@ -56,15 +58,19 @@
|
|
|
56
58
|
{ "code": "rate_limited", "http": 429, "retryable": true, "meaning": "Per-key rate limit; it stops runaway loops, budgets limit spend" },
|
|
57
59
|
{ "code": "not_available", "http": 501, "retryable": false, "meaning": "Not built yet, e.g. bulk email" },
|
|
58
60
|
{ "code": "upstream_error", "http": 502, "retryable": true, "meaning": "The vendor failed after the Gateway's retries" },
|
|
59
|
-
{ "code": "upstream_timeout", "http": 504, "retryable": true, "meaning": "The vendor didn't answer in time" }
|
|
61
|
+
{ "code": "upstream_timeout", "http": 504, "retryable": true, "meaning": "The vendor didn't answer in time" },
|
|
62
|
+
{ "code": "not_found", "http": 404, "retryable": false, "meaning": "No such path. Every documented endpoint checks the key and scope first, even one not built yet" },
|
|
63
|
+
{ "code": "internal_error", "http": 500, "retryable": true, "meaning": "The Gateway failed, for example it couldn't read the platform database; nothing was sent to a vendor" }
|
|
60
64
|
],
|
|
61
65
|
"rules": [
|
|
62
66
|
{ "id": "gateway.no-vendor-keys", "kind": "must_not", "text": "Hold a vendor key (Anthropic, Google, OpenAI, Serper, email) in an app. The Gateway key is the only Workbench credential an app has.", "enforced_by": "review", "probe": "gateway.no-vendor-keys.scan" },
|
|
63
67
|
{ "id": "gateway.key-per-environment", "kind": "must", "text": "Use the key issued for the Worker's own environment. A staging key is refused by production and the other way round.", "enforced_by": "gateway", "probe": "gateway.wrong-environment" },
|
|
68
|
+
{ "id": "gateway.timing-headers", "kind": "must", "text": "Every Gateway response carries X-Workbench-Request-Id and Server-Timing with gw, vendor_fb, vendor and total, refusals included.", "enforced_by": "gateway", "probe": "gateway.server-timing" },
|
|
64
69
|
{ "id": "gateway.request-id-kept", "kind": "must", "text": "Keep X-Workbench-Request-Id with any output stored or any error logged, so it can be matched to a usage row.", "enforced_by": "review", "probe": "gateway.health" }
|
|
65
70
|
],
|
|
66
71
|
"probes": [
|
|
67
72
|
{ "id": "gateway.health", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 },
|
|
73
|
+
{ "id": "gateway.server-timing", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 },
|
|
68
74
|
{ "id": "gateway.bad-key", "half": "runtime", "expect": "refused_as_expected", "refusal": "unauthorised", "smoke": true, "cost_hint_usd": 0 },
|
|
69
75
|
{ "id": "gateway.wrong-scope", "half": "runtime", "expect": "refused_as_expected", "refusal": "forbidden_scope", "cost_hint_usd": 0 },
|
|
70
76
|
{ "id": "gateway.wrong-environment", "half": "runtime", "expect": "refused_as_expected", "refusal": "unauthorised", "cost_hint_usd": 0 },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const MANIFEST_VERSION: "
|
|
1
|
+
export declare const MANIFEST_VERSION: "9005150c45b1";
|
|
2
2
|
export declare const capabilities: {
|
|
3
3
|
readonly ai: {
|
|
4
4
|
readonly $schema: "./schema.json";
|
|
@@ -6,10 +6,10 @@ export declare const capabilities: {
|
|
|
6
6
|
readonly component: "ai";
|
|
7
7
|
readonly title: "Claude through the Gateway";
|
|
8
8
|
readonly summary: "Text, structured output and streaming from Claude, metered against the project's budget. Apps never hold an Anthropic key.";
|
|
9
|
-
readonly status: "
|
|
9
|
+
readonly status: "built";
|
|
10
10
|
readonly surfaces: {
|
|
11
11
|
readonly gateway: readonly ["POST /anthropic/v1/messages", "POST /anthropic/v1/messages/count_tokens"];
|
|
12
|
-
readonly package: readonly ["createAi", "Ai.text", "Ai.structured", "Ai.stream", "Ai.budget", "createFakeAi"];
|
|
12
|
+
readonly package: readonly ["createAi", "Ai.text", "Ai.structured", "Ai.stream", "Ai.countTokens", "Ai.budget", "createFakeAi"];
|
|
13
13
|
};
|
|
14
14
|
readonly config: readonly [{
|
|
15
15
|
readonly name: "WORKBENCH_GATEWAY_KEY";
|
|
@@ -129,6 +129,12 @@ export declare const capabilities: {
|
|
|
129
129
|
readonly expect: "pass";
|
|
130
130
|
readonly smoke: true;
|
|
131
131
|
readonly cost_hint_usd: 0;
|
|
132
|
+
}, {
|
|
133
|
+
readonly id: "ai.budget";
|
|
134
|
+
readonly half: "runtime";
|
|
135
|
+
readonly expect: "pass";
|
|
136
|
+
readonly smoke: true;
|
|
137
|
+
readonly cost_hint_usd: 0;
|
|
132
138
|
}];
|
|
133
139
|
};
|
|
134
140
|
readonly config: {
|
|
@@ -214,7 +220,7 @@ export declare const capabilities: {
|
|
|
214
220
|
readonly component: "gateway";
|
|
215
221
|
readonly title: "Workbench Gateway: keys, headers, budgets and errors";
|
|
216
222
|
readonly summary: "What every Gateway endpoint shares: project keys by environment and scope, the request and response headers, the monthly budget check and the error codes. Apps hold one Workbench key and never a vendor key.";
|
|
217
|
-
readonly status: "
|
|
223
|
+
readonly status: "built";
|
|
218
224
|
readonly surfaces: {
|
|
219
225
|
readonly gateway: readonly ["GET /v1/health", "GET /v1/usage", "GET /v1/capabilities"];
|
|
220
226
|
readonly package: readonly ["DEFAULT_GATEWAY_URL"];
|
|
@@ -267,6 +273,11 @@ export declare const capabilities: {
|
|
|
267
273
|
readonly name: "Idempotency-Key";
|
|
268
274
|
readonly required: false;
|
|
269
275
|
readonly description: "Email only: a repeat within 24 hours returns the stored result";
|
|
276
|
+
}, {
|
|
277
|
+
readonly name: "X-Workbench-Env";
|
|
278
|
+
readonly required: false;
|
|
279
|
+
readonly pattern: "^(development|staging|production)$";
|
|
280
|
+
readonly description: "The caller's WORKBENCH_ENV. The packages send it; a key from another environment is then refused with unauthorised";
|
|
270
281
|
}];
|
|
271
282
|
readonly response_headers: readonly ["X-Workbench-Request-Id", "Server-Timing"];
|
|
272
283
|
readonly server_timing_metrics: readonly ["gw", "vendor_fb", "vendor", "total"];
|
|
@@ -283,6 +294,13 @@ export declare const capabilities: {
|
|
|
283
294
|
readonly unit: "seconds";
|
|
284
295
|
readonly per: "isolate";
|
|
285
296
|
readonly enforced_by: "gateway";
|
|
297
|
+
}, {
|
|
298
|
+
readonly id: "gateway.rate-limit";
|
|
299
|
+
readonly value: 300;
|
|
300
|
+
readonly unit: "requests";
|
|
301
|
+
readonly per: "minute per key";
|
|
302
|
+
readonly enforced_by: "gateway";
|
|
303
|
+
readonly refusal: "rate_limited";
|
|
286
304
|
}, {
|
|
287
305
|
readonly id: "gateway.job-format";
|
|
288
306
|
readonly value: "[a-z0-9._-]{1,64}";
|
|
@@ -331,6 +349,16 @@ export declare const capabilities: {
|
|
|
331
349
|
readonly http: 504;
|
|
332
350
|
readonly retryable: true;
|
|
333
351
|
readonly meaning: "The vendor didn't answer in time";
|
|
352
|
+
}, {
|
|
353
|
+
readonly code: "not_found";
|
|
354
|
+
readonly http: 404;
|
|
355
|
+
readonly retryable: false;
|
|
356
|
+
readonly meaning: "No such path. Every documented endpoint checks the key and scope first, even one not built yet";
|
|
357
|
+
}, {
|
|
358
|
+
readonly code: "internal_error";
|
|
359
|
+
readonly http: 500;
|
|
360
|
+
readonly retryable: true;
|
|
361
|
+
readonly meaning: "The Gateway failed, for example it couldn't read the platform database; nothing was sent to a vendor";
|
|
334
362
|
}];
|
|
335
363
|
readonly rules: readonly [{
|
|
336
364
|
readonly id: "gateway.no-vendor-keys";
|
|
@@ -344,6 +372,12 @@ export declare const capabilities: {
|
|
|
344
372
|
readonly text: "Use the key issued for the Worker's own environment. A staging key is refused by production and the other way round.";
|
|
345
373
|
readonly enforced_by: "gateway";
|
|
346
374
|
readonly probe: "gateway.wrong-environment";
|
|
375
|
+
}, {
|
|
376
|
+
readonly id: "gateway.timing-headers";
|
|
377
|
+
readonly kind: "must";
|
|
378
|
+
readonly text: "Every Gateway response carries X-Workbench-Request-Id and Server-Timing with gw, vendor_fb, vendor and total, refusals included.";
|
|
379
|
+
readonly enforced_by: "gateway";
|
|
380
|
+
readonly probe: "gateway.server-timing";
|
|
347
381
|
}, {
|
|
348
382
|
readonly id: "gateway.request-id-kept";
|
|
349
383
|
readonly kind: "must";
|
|
@@ -357,6 +391,12 @@ export declare const capabilities: {
|
|
|
357
391
|
readonly expect: "pass";
|
|
358
392
|
readonly smoke: true;
|
|
359
393
|
readonly cost_hint_usd: 0;
|
|
394
|
+
}, {
|
|
395
|
+
readonly id: "gateway.server-timing";
|
|
396
|
+
readonly half: "runtime";
|
|
397
|
+
readonly expect: "pass";
|
|
398
|
+
readonly smoke: true;
|
|
399
|
+
readonly cost_hint_usd: 0;
|
|
360
400
|
}, {
|
|
361
401
|
readonly id: "gateway.bad-key";
|
|
362
402
|
readonly half: "runtime";
|
package/dist/capabilities.gen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by `npm run capabilities:build` from capabilities/*.json. Do not edit.
|
|
2
2
|
// check:capabilities fails in Checks when this file is stale.
|
|
3
|
-
export const MANIFEST_VERSION = "
|
|
3
|
+
export const MANIFEST_VERSION = "9005150c45b1";
|
|
4
4
|
export const capabilities = {
|
|
5
5
|
"ai": {
|
|
6
6
|
"$schema": "./schema.json",
|
|
@@ -8,7 +8,7 @@ export const capabilities = {
|
|
|
8
8
|
"component": "ai",
|
|
9
9
|
"title": "Claude through the Gateway",
|
|
10
10
|
"summary": "Text, structured output and streaming from Claude, metered against the project's budget. Apps never hold an Anthropic key.",
|
|
11
|
-
"status": "
|
|
11
|
+
"status": "built",
|
|
12
12
|
"surfaces": {
|
|
13
13
|
"gateway": [
|
|
14
14
|
"POST /anthropic/v1/messages",
|
|
@@ -19,6 +19,7 @@ export const capabilities = {
|
|
|
19
19
|
"Ai.text",
|
|
20
20
|
"Ai.structured",
|
|
21
21
|
"Ai.stream",
|
|
22
|
+
"Ai.countTokens",
|
|
22
23
|
"Ai.budget",
|
|
23
24
|
"createFakeAi"
|
|
24
25
|
]
|
|
@@ -183,6 +184,13 @@ export const capabilities = {
|
|
|
183
184
|
"expect": "pass",
|
|
184
185
|
"smoke": true,
|
|
185
186
|
"cost_hint_usd": 0
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "ai.budget",
|
|
190
|
+
"half": "runtime",
|
|
191
|
+
"expect": "pass",
|
|
192
|
+
"smoke": true,
|
|
193
|
+
"cost_hint_usd": 0
|
|
186
194
|
}
|
|
187
195
|
]
|
|
188
196
|
},
|
|
@@ -303,7 +311,7 @@ export const capabilities = {
|
|
|
303
311
|
"component": "gateway",
|
|
304
312
|
"title": "Workbench Gateway: keys, headers, budgets and errors",
|
|
305
313
|
"summary": "What every Gateway endpoint shares: project keys by environment and scope, the request and response headers, the monthly budget check and the error codes. Apps hold one Workbench key and never a vendor key.",
|
|
306
|
-
"status": "
|
|
314
|
+
"status": "built",
|
|
307
315
|
"surfaces": {
|
|
308
316
|
"gateway": [
|
|
309
317
|
"GET /v1/health",
|
|
@@ -386,6 +394,12 @@ export const capabilities = {
|
|
|
386
394
|
"name": "Idempotency-Key",
|
|
387
395
|
"required": false,
|
|
388
396
|
"description": "Email only: a repeat within 24 hours returns the stored result"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"name": "X-Workbench-Env",
|
|
400
|
+
"required": false,
|
|
401
|
+
"pattern": "^(development|staging|production)$",
|
|
402
|
+
"description": "The caller's WORKBENCH_ENV. The packages send it; a key from another environment is then refused with unauthorised"
|
|
389
403
|
}
|
|
390
404
|
],
|
|
391
405
|
"response_headers": [
|
|
@@ -414,6 +428,14 @@ export const capabilities = {
|
|
|
414
428
|
"per": "isolate",
|
|
415
429
|
"enforced_by": "gateway"
|
|
416
430
|
},
|
|
431
|
+
{
|
|
432
|
+
"id": "gateway.rate-limit",
|
|
433
|
+
"value": 300,
|
|
434
|
+
"unit": "requests",
|
|
435
|
+
"per": "minute per key",
|
|
436
|
+
"enforced_by": "gateway",
|
|
437
|
+
"refusal": "rate_limited"
|
|
438
|
+
},
|
|
417
439
|
{
|
|
418
440
|
"id": "gateway.job-format",
|
|
419
441
|
"value": "[a-z0-9._-]{1,64}",
|
|
@@ -471,6 +493,18 @@ export const capabilities = {
|
|
|
471
493
|
"http": 504,
|
|
472
494
|
"retryable": true,
|
|
473
495
|
"meaning": "The vendor didn't answer in time"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"code": "not_found",
|
|
499
|
+
"http": 404,
|
|
500
|
+
"retryable": false,
|
|
501
|
+
"meaning": "No such path. Every documented endpoint checks the key and scope first, even one not built yet"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"code": "internal_error",
|
|
505
|
+
"http": 500,
|
|
506
|
+
"retryable": true,
|
|
507
|
+
"meaning": "The Gateway failed, for example it couldn't read the platform database; nothing was sent to a vendor"
|
|
474
508
|
}
|
|
475
509
|
],
|
|
476
510
|
"rules": [
|
|
@@ -488,6 +522,13 @@ export const capabilities = {
|
|
|
488
522
|
"enforced_by": "gateway",
|
|
489
523
|
"probe": "gateway.wrong-environment"
|
|
490
524
|
},
|
|
525
|
+
{
|
|
526
|
+
"id": "gateway.timing-headers",
|
|
527
|
+
"kind": "must",
|
|
528
|
+
"text": "Every Gateway response carries X-Workbench-Request-Id and Server-Timing with gw, vendor_fb, vendor and total, refusals included.",
|
|
529
|
+
"enforced_by": "gateway",
|
|
530
|
+
"probe": "gateway.server-timing"
|
|
531
|
+
},
|
|
491
532
|
{
|
|
492
533
|
"id": "gateway.request-id-kept",
|
|
493
534
|
"kind": "must",
|
|
@@ -504,6 +545,13 @@ export const capabilities = {
|
|
|
504
545
|
"smoke": true,
|
|
505
546
|
"cost_hint_usd": 0
|
|
506
547
|
},
|
|
548
|
+
{
|
|
549
|
+
"id": "gateway.server-timing",
|
|
550
|
+
"half": "runtime",
|
|
551
|
+
"expect": "pass",
|
|
552
|
+
"smoke": true,
|
|
553
|
+
"cost_hint_usd": 0
|
|
554
|
+
},
|
|
507
555
|
{
|
|
508
556
|
"id": "gateway.bad-key",
|
|
509
557
|
"half": "runtime",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mccaskie-labs/workbench-config",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Workbench app configuration: WORKBENCH_ENV, typed variables that fail by name, secrets that are never printed, and the capabilities manifest.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|