@mccaskie-labs/workbench-config 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +5 -0
- package/README.md +33 -2
- package/capabilities/ai.json +69 -0
- package/capabilities/config.json +56 -0
- package/capabilities/gateway.json +75 -0
- package/capabilities/schema.json +414 -0
- package/dist/capabilities.gen.d.ts +403 -0
- package/dist/capabilities.gen.js +554 -0
- package/dist/capability-schema.d.ts +193 -0
- package/dist/capability-schema.js +217 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +34 -0
- package/dist/define-config.d.ts +21 -0
- package/dist/define-config.js +57 -0
- package/dist/dev-vars.d.ts +10 -0
- package/dist/dev-vars.js +23 -0
- package/dist/env.d.ts +8 -0
- package/dist/env.js +16 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +26 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/secrets.d.ts +19 -0
- package/dist/secrets.js +44 -0
- package/package.json +48 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# @mccaskie-labs/workbench-config
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- First release: `workbenchEnv`, `defineConfig`, `workerSecret`, `sharedSecret`, `redact`, `redactAll`, the capabilities manifests (`gateway`, `config`, `ai`) with `capabilitySchema`, and the `workbench-config check` CLI.
|
package/README.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mccaskie-labs/workbench-config
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Configuration for apps built on Workbench, a private platform. Published publicly so Workbench projects can install it with no token; it is not supported for other uses.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install @mccaskie-labs/workbench-config zod
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { z } from 'zod'
|
|
11
|
+
import { defineConfig, workbenchEnv } from '@mccaskie-labs/workbench-config'
|
|
12
|
+
|
|
13
|
+
const config = defineConfig({
|
|
14
|
+
WORKBENCH_GATEWAY_KEY: z.string().startsWith('wbk_'),
|
|
15
|
+
SITE_NAME: z.string().default('my-app'),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const stage = workbenchEnv(env) // 'development' | 'staging' | 'production'; throws if WORKBENCH_ENV is missing
|
|
19
|
+
const { WORKBENCH_GATEWAY_KEY } = config.parse(env)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| Export | What it does |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `workbenchEnv(env)` | Reads `WORKBENCH_ENV`. Throws if it is missing or not one of the three environments. Never reads `NODE_ENV` |
|
|
25
|
+
| `defineConfig(shape).parse(env)` | Validates the named variables with zod. An empty string counts as missing. Throws `WorkbenchConfigError` naming every bad variable; the error never holds a value |
|
|
26
|
+
| `workerSecret(env, name)` | A Worker secret or variable by name; throws, naming it, if it is missing or empty |
|
|
27
|
+
| `sharedSecret(binding, name?)` | Reads a Secrets Store binding; throws if it is empty |
|
|
28
|
+
| `redact(value)`, `redactAll(text, secrets)` | `[redacted]` in place of a secret, for logs |
|
|
29
|
+
| `capabilities`, `MANIFEST_VERSION`, `capabilitySchema` | The Workbench capabilities manifests and their schema. The JSON files are also at `@mccaskie-labs/workbench-config/capabilities/<component>.json` |
|
|
30
|
+
| `DEFAULT_GATEWAY_URL` | `https://gateway.mccaskielabs.dev` |
|
|
31
|
+
|
|
32
|
+
`npx workbench-config check [dir]` compares the variable names in `.dev.vars` with `.dev.vars.example` and prints names only.
|
|
33
|
+
|
|
34
|
+
Runs on Node 24 and the Cloudflare Workers runtime. `zod` 4 is a peer dependency.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./schema.json",
|
|
3
|
+
"format": 1,
|
|
4
|
+
"component": "ai",
|
|
5
|
+
"title": "Claude through the Gateway",
|
|
6
|
+
"summary": "Text, structured output and streaming from Claude, metered against the project's budget. Apps never hold an Anthropic key.",
|
|
7
|
+
"status": "planned",
|
|
8
|
+
"surfaces": {
|
|
9
|
+
"gateway": ["POST /anthropic/v1/messages", "POST /anthropic/v1/messages/count_tokens"],
|
|
10
|
+
"package": ["createAi", "Ai.text", "Ai.structured", "Ai.stream", "Ai.budget", "createFakeAi"]
|
|
11
|
+
},
|
|
12
|
+
"config": [
|
|
13
|
+
{
|
|
14
|
+
"name": "WORKBENCH_GATEWAY_KEY",
|
|
15
|
+
"required": true,
|
|
16
|
+
"secret": true,
|
|
17
|
+
"environments": ["staging", "production"],
|
|
18
|
+
"description": "Project key with the ai scope, delivered as a Worker secret by gw_key_issue"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "WORKBENCH_ENV",
|
|
22
|
+
"required": true,
|
|
23
|
+
"secret": false,
|
|
24
|
+
"environments": ["development", "staging", "production"],
|
|
25
|
+
"description": "Drives key checks; never NODE_ENV"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"allowed": {
|
|
29
|
+
"aliases": [
|
|
30
|
+
{ "alias": "claude-default", "provider": "anthropic", "default_model": "claude-sonnet-5-5" },
|
|
31
|
+
{ "alias": "claude-best", "provider": "anthropic", "default_model": "claude-opus-5-5" },
|
|
32
|
+
{ "alias": "claude-fast", "provider": "anthropic", "default_model": "claude-haiku-5-5" }
|
|
33
|
+
],
|
|
34
|
+
"explicit_models": ["claude-sonnet-5", "claude-opus-5"],
|
|
35
|
+
"server_tools": ["web_search", "web_fetch", "code_execution"],
|
|
36
|
+
"required_headers": ["X-Workbench-Job"]
|
|
37
|
+
},
|
|
38
|
+
"limits": [
|
|
39
|
+
{ "id": "ai.retry-attempts", "value": 3, "unit": "attempts", "per": "request", "enforced_by": "gateway" }
|
|
40
|
+
],
|
|
41
|
+
"errors": [
|
|
42
|
+
{ "code": "model_not_allowed", "http": 400, "retryable": false, "meaning": "Not an alias or an allowlisted model id" },
|
|
43
|
+
{ "code": "budget_exceeded", "http": 402, "retryable": false, "meaning": "Enforced monthly budget reached" }
|
|
44
|
+
],
|
|
45
|
+
"rules": [
|
|
46
|
+
{ "id": "ai.no-partial-success", "kind": "must_not", "text": "Treat a refusal or a max_tokens truncation as a successful result.", "enforced_by": "types", "probe": "ai.text.truncation-throws" },
|
|
47
|
+
{ "id": "ai.store-resolved-model", "kind": "must", "text": "Store the resolved model and request id with any output kept.", "enforced_by": "review", "probe": "ai.text.claude-default" },
|
|
48
|
+
{ "id": "ai.aliases-only", "kind": "must", "text": "Ask for a Workbench alias (or an allowlisted explicit id), never an arbitrary model id.", "enforced_by": "gateway", "probe": "ai.model-not-allowed" }
|
|
49
|
+
],
|
|
50
|
+
"examples": [
|
|
51
|
+
{
|
|
52
|
+
"id": "ai.example.text",
|
|
53
|
+
"title": "One-shot text",
|
|
54
|
+
"lang": "ts",
|
|
55
|
+
"probe": "ai.text.claude-default",
|
|
56
|
+
"code": "const ai = createAi({ key: env.WORKBENCH_GATEWAY_KEY, env: workbenchEnv(env) })\nconst r = await ai.text({ model: 'claude-default', job: 'article.write', prompt })\nawait save({ text: r.text, model: r.model, requestId: r.requestId })"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"probes": [
|
|
60
|
+
{ "id": "ai.text.claude-default", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0.002 },
|
|
61
|
+
{ "id": "ai.text.claude-best", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.004 },
|
|
62
|
+
{ "id": "ai.text.claude-fast", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.001 },
|
|
63
|
+
{ "id": "ai.stream", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.002 },
|
|
64
|
+
{ "id": "ai.structured", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.003 },
|
|
65
|
+
{ "id": "ai.text.truncation-throws", "half": "runtime", "expect": "pass", "cost_hint_usd": 0.001 },
|
|
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 }
|
|
68
|
+
]
|
|
69
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./schema.json",
|
|
3
|
+
"format": 1,
|
|
4
|
+
"component": "config",
|
|
5
|
+
"title": "App configuration with workbench-config",
|
|
6
|
+
"summary": "Reads WORKBENCH_ENV and the app's own variables and secrets. A missing or invalid variable fails at startup, by name, and no value is ever printed.",
|
|
7
|
+
"status": "built",
|
|
8
|
+
"surfaces": {
|
|
9
|
+
"package": [
|
|
10
|
+
"workbenchEnv",
|
|
11
|
+
"defineConfig",
|
|
12
|
+
"sharedSecret",
|
|
13
|
+
"workerSecret",
|
|
14
|
+
"redact",
|
|
15
|
+
"redactAll",
|
|
16
|
+
"WorkbenchConfigError",
|
|
17
|
+
"capabilitySchema",
|
|
18
|
+
"capabilities",
|
|
19
|
+
"MANIFEST_VERSION",
|
|
20
|
+
"compareDevVars"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"config": [
|
|
24
|
+
{
|
|
25
|
+
"name": "WORKBENCH_ENV",
|
|
26
|
+
"required": true,
|
|
27
|
+
"secret": false,
|
|
28
|
+
"environments": ["development", "staging", "production"],
|
|
29
|
+
"description": "development, staging or production. Drives key checks and the email transport; never NODE_ENV (audit F9)"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"allowed": {
|
|
33
|
+
"environments": ["development", "staging", "production"]
|
|
34
|
+
},
|
|
35
|
+
"rules": [
|
|
36
|
+
{ "id": "config.fail-by-name", "kind": "must", "text": "Fail at startup when a required variable is missing or invalid, naming every bad variable in one error.", "enforced_by": "types", "probe": "config.missing-variable-named" },
|
|
37
|
+
{ "id": "config.never-print-values", "kind": "must_not", "text": "Print, log or include in an error the value of any variable, valid or not.", "enforced_by": "types", "probe": "config.value-never-printed" },
|
|
38
|
+
{ "id": "config.no-node-env", "kind": "must_not", "text": "Decide the environment from NODE_ENV or a default. WORKBENCH_ENV is required and has no fallback.", "enforced_by": "types", "probe": "config.env-required" },
|
|
39
|
+
{ "id": "config.dev-vars-example", "kind": "must", "text": "Keep .dev.vars.example listing every variable .dev.vars has, with no values; check with npx workbench-config check.", "enforced_by": "review", "probe": "config.dev-vars-check" }
|
|
40
|
+
],
|
|
41
|
+
"examples": [
|
|
42
|
+
{
|
|
43
|
+
"id": "config.example.define",
|
|
44
|
+
"title": "Declare and read the app's variables",
|
|
45
|
+
"lang": "ts",
|
|
46
|
+
"probe": "config.missing-variable-named",
|
|
47
|
+
"code": "import { z } from 'zod'\nimport { defineConfig, workbenchEnv } from '@mccaskie-labs/workbench-config'\n\nconst config = defineConfig({\n WORKBENCH_GATEWAY_KEY: z.string().startsWith('wbk_'),\n SITE_NAME: z.string().default('wb-probe'),\n})\n\nexport default {\n async fetch(request: Request, env: Env) {\n const stage = workbenchEnv(env)\n const { WORKBENCH_GATEWAY_KEY, SITE_NAME } = config.parse(env)\n return new Response(`${SITE_NAME} on ${stage}`)\n },\n}"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"probes": [
|
|
51
|
+
{ "id": "config.missing-variable-named", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 },
|
|
52
|
+
{ "id": "config.value-never-printed", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 },
|
|
53
|
+
{ "id": "config.env-required", "half": "runtime", "expect": "pass", "cost_hint_usd": 0 },
|
|
54
|
+
{ "id": "config.dev-vars-check", "half": "runtime", "expect": "pass", "cost_hint_usd": 0, "note": "Manual: run npx workbench-config check in the project" }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./schema.json",
|
|
3
|
+
"format": 1,
|
|
4
|
+
"component": "gateway",
|
|
5
|
+
"title": "Workbench Gateway: keys, headers, budgets and errors",
|
|
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": "planned",
|
|
8
|
+
"surfaces": {
|
|
9
|
+
"gateway": ["GET /v1/health", "GET /v1/usage", "GET /v1/capabilities"],
|
|
10
|
+
"package": ["DEFAULT_GATEWAY_URL"]
|
|
11
|
+
},
|
|
12
|
+
"config": [
|
|
13
|
+
{
|
|
14
|
+
"name": "WORKBENCH_GATEWAY_KEY",
|
|
15
|
+
"required": true,
|
|
16
|
+
"secret": true,
|
|
17
|
+
"environments": ["staging", "production"],
|
|
18
|
+
"description": "The project's Gateway key for this environment, delivered as a Worker secret by gw_key_issue. Never in code, .dev.vars.example or logs"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "WORKBENCH_GATEWAY_URL",
|
|
22
|
+
"required": false,
|
|
23
|
+
"secret": false,
|
|
24
|
+
"default": "https://gateway.mccaskielabs.dev",
|
|
25
|
+
"environments": ["development", "staging", "production"],
|
|
26
|
+
"description": "Gateway base URL. Leave unset outside tests"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"allowed": {
|
|
30
|
+
"scopes": ["ai", "images", "search", "email"],
|
|
31
|
+
"key_environments": [
|
|
32
|
+
{ "environment": "development", "prefix": "wbk_dev_" },
|
|
33
|
+
{ "environment": "staging", "prefix": "wbk_stg_" },
|
|
34
|
+
{ "environment": "production", "prefix": "wbk_prd_" }
|
|
35
|
+
],
|
|
36
|
+
"request_headers": [
|
|
37
|
+
{ "name": "Authorization", "required": true, "description": "Bearer <key>. On /anthropic/* the key goes in x-api-key instead, so the official SDK works unchanged" },
|
|
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
|
+
{ "name": "X-Workbench-Prompt-Version", "required": false, "description": "The app's prompt version, recorded with usage (audit F17)" },
|
|
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" }
|
|
42
|
+
],
|
|
43
|
+
"response_headers": ["X-Workbench-Request-Id", "Server-Timing"],
|
|
44
|
+
"server_timing_metrics": ["gw", "vendor_fb", "vendor", "total"]
|
|
45
|
+
},
|
|
46
|
+
"limits": [
|
|
47
|
+
{ "id": "gateway.key-cache", "value": 60, "unit": "seconds", "per": "isolate", "enforced_by": "gateway" },
|
|
48
|
+
{ "id": "gateway.budget-cache", "value": 60, "unit": "seconds", "per": "isolate", "enforced_by": "gateway" },
|
|
49
|
+
{ "id": "gateway.job-format", "value": "[a-z0-9._-]{1,64}", "unit": "pattern", "per": "request", "enforced_by": "gateway", "refusal": "invalid_request" }
|
|
50
|
+
],
|
|
51
|
+
"errors": [
|
|
52
|
+
{ "code": "unauthorised", "http": 401, "retryable": false, "meaning": "No key, a malformed or unknown key, a revoked key, or a key from the wrong environment" },
|
|
53
|
+
{ "code": "forbidden_scope", "http": 403, "retryable": false, "meaning": "The key doesn't carry the scope this endpoint needs" },
|
|
54
|
+
{ "code": "invalid_request", "http": 400, "retryable": false, "meaning": "The body or a required header is missing or malformed" },
|
|
55
|
+
{ "code": "budget_exceeded", "http": 402, "retryable": false, "meaning": "The environment's enforced monthly budget, or the global ceiling, is reached" },
|
|
56
|
+
{ "code": "rate_limited", "http": 429, "retryable": true, "meaning": "Per-key rate limit; it stops runaway loops, budgets limit spend" },
|
|
57
|
+
{ "code": "not_available", "http": 501, "retryable": false, "meaning": "Not built yet, e.g. bulk email" },
|
|
58
|
+
{ "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" }
|
|
60
|
+
],
|
|
61
|
+
"rules": [
|
|
62
|
+
{ "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
|
+
{ "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" },
|
|
64
|
+
{ "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
|
+
],
|
|
66
|
+
"probes": [
|
|
67
|
+
{ "id": "gateway.health", "half": "runtime", "expect": "pass", "smoke": true, "cost_hint_usd": 0 },
|
|
68
|
+
{ "id": "gateway.bad-key", "half": "runtime", "expect": "refused_as_expected", "refusal": "unauthorised", "smoke": true, "cost_hint_usd": 0 },
|
|
69
|
+
{ "id": "gateway.wrong-scope", "half": "runtime", "expect": "refused_as_expected", "refusal": "forbidden_scope", "cost_hint_usd": 0 },
|
|
70
|
+
{ "id": "gateway.wrong-environment", "half": "runtime", "expect": "refused_as_expected", "refusal": "unauthorised", "cost_hint_usd": 0 },
|
|
71
|
+
{ "id": "gateway.revoked-key", "half": "runtime", "expect": "refused_as_expected", "refusal": "unauthorised", "cost_hint_usd": 0, "note": "Stops within 60 seconds of revocation" },
|
|
72
|
+
{ "id": "gateway.budget-enforced", "half": "runtime", "expect": "refused_as_expected", "refusal": "budget_exceeded", "cost_hint_usd": 0.01, "note": "Uses the development key's US$0.01 enforce budget (docs/phase-4-plan.md §3 Q10)" },
|
|
73
|
+
{ "id": "gateway.no-vendor-keys.scan", "half": "runtime", "expect": "pass", "cost_hint_usd": 0, "note": "Manual: grep of the repo and cf_worker_secret_list show only WORKBENCH_GATEWAY_* secrets" }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"$schema": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"const": "./schema.json"
|
|
8
|
+
},
|
|
9
|
+
"format": {
|
|
10
|
+
"type": "number",
|
|
11
|
+
"const": 1
|
|
12
|
+
},
|
|
13
|
+
"component": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": [
|
|
16
|
+
"gateway",
|
|
17
|
+
"ai",
|
|
18
|
+
"images",
|
|
19
|
+
"image-search",
|
|
20
|
+
"config",
|
|
21
|
+
"email",
|
|
22
|
+
"email-inbound",
|
|
23
|
+
"email-sending",
|
|
24
|
+
"database",
|
|
25
|
+
"workers",
|
|
26
|
+
"domains",
|
|
27
|
+
"dns",
|
|
28
|
+
"analytics",
|
|
29
|
+
"access",
|
|
30
|
+
"r2",
|
|
31
|
+
"kv",
|
|
32
|
+
"github"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"title": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1
|
|
38
|
+
},
|
|
39
|
+
"summary": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"minLength": 1
|
|
42
|
+
},
|
|
43
|
+
"status": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"enum": [
|
|
46
|
+
"planned",
|
|
47
|
+
"built",
|
|
48
|
+
"deprecated"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
"surfaces": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"gateway": {
|
|
55
|
+
"minItems": 1,
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"pattern": "^(GET|POST|PUT|PATCH|DELETE) \\/\\S*$"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"tools": {
|
|
63
|
+
"minItems": 1,
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"pattern": "^[a-z]+_[a-z0-9_]+$"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"package": {
|
|
71
|
+
"minItems": 1,
|
|
72
|
+
"type": "array",
|
|
73
|
+
"items": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"minLength": 1
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"additionalProperties": false
|
|
80
|
+
},
|
|
81
|
+
"config": {
|
|
82
|
+
"minItems": 1,
|
|
83
|
+
"type": "array",
|
|
84
|
+
"items": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"properties": {
|
|
87
|
+
"name": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"pattern": "^[A-Z][A-Z0-9_]*$"
|
|
90
|
+
},
|
|
91
|
+
"required": {
|
|
92
|
+
"type": "boolean"
|
|
93
|
+
},
|
|
94
|
+
"secret": {
|
|
95
|
+
"type": "boolean"
|
|
96
|
+
},
|
|
97
|
+
"default": {
|
|
98
|
+
"type": [
|
|
99
|
+
"string",
|
|
100
|
+
"number",
|
|
101
|
+
"boolean"
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
"environments": {
|
|
105
|
+
"minItems": 1,
|
|
106
|
+
"type": "array",
|
|
107
|
+
"items": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"enum": [
|
|
110
|
+
"development",
|
|
111
|
+
"staging",
|
|
112
|
+
"production"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"description": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"minLength": 1
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"required": [
|
|
122
|
+
"name",
|
|
123
|
+
"required",
|
|
124
|
+
"secret",
|
|
125
|
+
"environments",
|
|
126
|
+
"description"
|
|
127
|
+
],
|
|
128
|
+
"additionalProperties": false
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"allowed": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"propertyNames": {
|
|
134
|
+
"type": "string"
|
|
135
|
+
},
|
|
136
|
+
"additionalProperties": {}
|
|
137
|
+
},
|
|
138
|
+
"limits": {
|
|
139
|
+
"minItems": 1,
|
|
140
|
+
"type": "array",
|
|
141
|
+
"items": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"properties": {
|
|
144
|
+
"id": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"pattern": "^[a-z0-9][a-z0-9.-]*$"
|
|
147
|
+
},
|
|
148
|
+
"value": {
|
|
149
|
+
"type": [
|
|
150
|
+
"string",
|
|
151
|
+
"number"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
"unit": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"minLength": 1
|
|
157
|
+
},
|
|
158
|
+
"per": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"minLength": 1
|
|
161
|
+
},
|
|
162
|
+
"enforced_by": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"enum": [
|
|
165
|
+
"types",
|
|
166
|
+
"gateway",
|
|
167
|
+
"control",
|
|
168
|
+
"database"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
"refusal": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"pattern": "^[a-z][a-z0-9_]*$"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"required": [
|
|
177
|
+
"id",
|
|
178
|
+
"value",
|
|
179
|
+
"unit",
|
|
180
|
+
"per",
|
|
181
|
+
"enforced_by"
|
|
182
|
+
],
|
|
183
|
+
"additionalProperties": false
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"actions": {
|
|
187
|
+
"minItems": 1,
|
|
188
|
+
"type": "array",
|
|
189
|
+
"items": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"properties": {
|
|
192
|
+
"tool": {
|
|
193
|
+
"type": "string",
|
|
194
|
+
"pattern": "^[a-z]+_[a-z0-9_]+$"
|
|
195
|
+
},
|
|
196
|
+
"gate": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"enum": [
|
|
199
|
+
"free",
|
|
200
|
+
"two_step",
|
|
201
|
+
"two_step_on_production",
|
|
202
|
+
"two_step_when"
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
"when": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"minLength": 1
|
|
208
|
+
},
|
|
209
|
+
"ships": {
|
|
210
|
+
"type": "string",
|
|
211
|
+
"enum": [
|
|
212
|
+
"3",
|
|
213
|
+
"4",
|
|
214
|
+
"later"
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"required": [
|
|
219
|
+
"tool",
|
|
220
|
+
"gate",
|
|
221
|
+
"ships"
|
|
222
|
+
],
|
|
223
|
+
"additionalProperties": false
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
"errors": {
|
|
227
|
+
"minItems": 1,
|
|
228
|
+
"type": "array",
|
|
229
|
+
"items": {
|
|
230
|
+
"type": "object",
|
|
231
|
+
"properties": {
|
|
232
|
+
"code": {
|
|
233
|
+
"type": "string",
|
|
234
|
+
"pattern": "^[a-z][a-z0-9_]*$"
|
|
235
|
+
},
|
|
236
|
+
"http": {
|
|
237
|
+
"type": "integer",
|
|
238
|
+
"minimum": 400,
|
|
239
|
+
"maximum": 599
|
|
240
|
+
},
|
|
241
|
+
"retryable": {
|
|
242
|
+
"type": "boolean"
|
|
243
|
+
},
|
|
244
|
+
"meaning": {
|
|
245
|
+
"type": "string",
|
|
246
|
+
"minLength": 1
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
"required": [
|
|
250
|
+
"code",
|
|
251
|
+
"http",
|
|
252
|
+
"retryable",
|
|
253
|
+
"meaning"
|
|
254
|
+
],
|
|
255
|
+
"additionalProperties": false
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
"rules": {
|
|
259
|
+
"minItems": 1,
|
|
260
|
+
"type": "array",
|
|
261
|
+
"items": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"properties": {
|
|
264
|
+
"id": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"pattern": "^[a-z0-9][a-z0-9.-]*$"
|
|
267
|
+
},
|
|
268
|
+
"kind": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"enum": [
|
|
271
|
+
"must",
|
|
272
|
+
"must_not"
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
"text": {
|
|
276
|
+
"type": "string",
|
|
277
|
+
"minLength": 1
|
|
278
|
+
},
|
|
279
|
+
"enforced_by": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"enum": [
|
|
282
|
+
"types",
|
|
283
|
+
"gateway",
|
|
284
|
+
"control",
|
|
285
|
+
"database",
|
|
286
|
+
"review"
|
|
287
|
+
]
|
|
288
|
+
},
|
|
289
|
+
"probe": {
|
|
290
|
+
"type": "string",
|
|
291
|
+
"pattern": "^[a-z0-9][a-z0-9.-]*$"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"required": [
|
|
295
|
+
"id",
|
|
296
|
+
"kind",
|
|
297
|
+
"text",
|
|
298
|
+
"enforced_by",
|
|
299
|
+
"probe"
|
|
300
|
+
],
|
|
301
|
+
"additionalProperties": false
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
"examples": {
|
|
305
|
+
"minItems": 1,
|
|
306
|
+
"type": "array",
|
|
307
|
+
"items": {
|
|
308
|
+
"type": "object",
|
|
309
|
+
"properties": {
|
|
310
|
+
"id": {
|
|
311
|
+
"type": "string",
|
|
312
|
+
"pattern": "^[a-z0-9][a-z0-9.-]*$"
|
|
313
|
+
},
|
|
314
|
+
"title": {
|
|
315
|
+
"type": "string",
|
|
316
|
+
"minLength": 1
|
|
317
|
+
},
|
|
318
|
+
"lang": {
|
|
319
|
+
"type": "string",
|
|
320
|
+
"enum": [
|
|
321
|
+
"ts",
|
|
322
|
+
"js",
|
|
323
|
+
"sh",
|
|
324
|
+
"ps1",
|
|
325
|
+
"json",
|
|
326
|
+
"sql"
|
|
327
|
+
]
|
|
328
|
+
},
|
|
329
|
+
"code": {
|
|
330
|
+
"type": "string",
|
|
331
|
+
"minLength": 1
|
|
332
|
+
},
|
|
333
|
+
"probe": {
|
|
334
|
+
"type": "string",
|
|
335
|
+
"pattern": "^[a-z0-9][a-z0-9.-]*$"
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
"required": [
|
|
339
|
+
"id",
|
|
340
|
+
"title",
|
|
341
|
+
"lang",
|
|
342
|
+
"code",
|
|
343
|
+
"probe"
|
|
344
|
+
],
|
|
345
|
+
"additionalProperties": false
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
"probes": {
|
|
349
|
+
"minItems": 1,
|
|
350
|
+
"type": "array",
|
|
351
|
+
"items": {
|
|
352
|
+
"type": "object",
|
|
353
|
+
"properties": {
|
|
354
|
+
"id": {
|
|
355
|
+
"type": "string",
|
|
356
|
+
"pattern": "^[a-z0-9][a-z0-9.-]*$"
|
|
357
|
+
},
|
|
358
|
+
"half": {
|
|
359
|
+
"type": "string",
|
|
360
|
+
"enum": [
|
|
361
|
+
"runtime",
|
|
362
|
+
"drill"
|
|
363
|
+
]
|
|
364
|
+
},
|
|
365
|
+
"expect": {
|
|
366
|
+
"type": "string",
|
|
367
|
+
"enum": [
|
|
368
|
+
"pass",
|
|
369
|
+
"refused_as_expected"
|
|
370
|
+
]
|
|
371
|
+
},
|
|
372
|
+
"refusal": {
|
|
373
|
+
"type": "string",
|
|
374
|
+
"pattern": "^[a-z][a-z0-9_]*$"
|
|
375
|
+
},
|
|
376
|
+
"smoke": {
|
|
377
|
+
"type": "boolean"
|
|
378
|
+
},
|
|
379
|
+
"cost_hint_usd": {
|
|
380
|
+
"type": "number",
|
|
381
|
+
"minimum": 0
|
|
382
|
+
},
|
|
383
|
+
"two_step": {
|
|
384
|
+
"type": "boolean"
|
|
385
|
+
},
|
|
386
|
+
"note": {
|
|
387
|
+
"type": "string",
|
|
388
|
+
"minLength": 1
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
"required": [
|
|
392
|
+
"id",
|
|
393
|
+
"half",
|
|
394
|
+
"expect"
|
|
395
|
+
],
|
|
396
|
+
"additionalProperties": false
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
"required": [
|
|
401
|
+
"$schema",
|
|
402
|
+
"format",
|
|
403
|
+
"component",
|
|
404
|
+
"title",
|
|
405
|
+
"summary",
|
|
406
|
+
"status",
|
|
407
|
+
"surfaces",
|
|
408
|
+
"rules",
|
|
409
|
+
"probes"
|
|
410
|
+
],
|
|
411
|
+
"additionalProperties": false,
|
|
412
|
+
"title": "Workbench capabilities manifest",
|
|
413
|
+
"description": "Generated from capabilitySchema in @mccaskie-labs/workbench-config by `npm run capabilities:build`. Do not edit."
|
|
414
|
+
}
|