@zackbart/connecta 0.15.1 → 0.16.1
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/AGENTS.md +40 -20
- package/CHANGELOG.md +653 -0
- package/README.md +25 -5
- package/bin/connecta.mjs +44 -3
- package/dist/access-tokens.d.ts +0 -1
- package/dist/access-tokens.js +0 -1
- package/dist/activity.d.ts +50 -2
- package/dist/activity.js +34 -1
- package/dist/apps-shell.d.ts +0 -1
- package/dist/apps-shell.js +0 -1
- package/dist/auth/bearer.d.ts +0 -1
- package/dist/auth/bearer.js +0 -1
- package/dist/auth/clerk.d.ts +0 -1
- package/dist/auth/clerk.js +45 -9
- package/dist/auth/downstream-oauth.d.ts +0 -1
- package/dist/auth/downstream-oauth.js +0 -1
- package/dist/call-admission.d.ts +0 -1
- package/dist/call-admission.js +0 -1
- package/dist/catalog-drift.d.ts +99 -0
- package/dist/catalog-drift.js +251 -0
- package/dist/catalog-fingerprint.d.ts +0 -1
- package/dist/catalog-fingerprint.js +0 -1
- package/dist/catalog-limits.d.ts +0 -1
- package/dist/catalog-limits.js +0 -1
- package/dist/catalog-service.d.ts +0 -1
- package/dist/catalog-service.js +41 -5
- package/dist/catalog.d.ts +11 -1
- package/dist/catalog.js +20 -1
- package/dist/concurrency.d.ts +0 -1
- package/dist/concurrency.js +0 -1
- package/dist/connector-scope.d.ts +0 -1
- package/dist/connector-scope.js +0 -1
- package/dist/connectors/api.d.ts +29 -21
- package/dist/connectors/api.js +45 -19
- package/dist/connectors/guarded-fetch.d.ts +106 -0
- package/dist/connectors/guarded-fetch.js +289 -0
- package/dist/connectors/remote-mcp.d.ts +0 -1
- package/dist/connectors/remote-mcp.js +0 -1
- package/dist/credentials.d.ts +0 -1
- package/dist/credentials.js +0 -1
- package/dist/errors.d.ts +30 -3
- package/dist/errors.js +1 -1
- package/dist/execute.d.ts +0 -1
- package/dist/execute.js +0 -1
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +28 -1
- package/dist/executor-result.d.ts +0 -1
- package/dist/executor-result.js +0 -1
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-child.js +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -1
- package/dist/executors/quickjs-protocol.js +0 -1
- package/dist/executors/quickjs-runtime.d.ts +0 -1
- package/dist/executors/quickjs-runtime.js +0 -1
- package/dist/executors/quickjs.d.ts +0 -1
- package/dist/executors/quickjs.js +2 -1
- package/dist/favicon.d.ts +0 -1
- package/dist/favicon.js +0 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -7
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +0 -1
- package/dist/json-schema.d.ts +0 -1
- package/dist/json-schema.js +0 -1
- package/dist/mcp-result.d.ts +0 -1
- package/dist/mcp-result.js +0 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/operator-ui/generated.d.ts +2 -3
- package/dist/operator-ui/generated.js +3 -4
- package/dist/operator-ui/model.d.ts +10 -1
- package/dist/operator-ui/model.js +0 -1
- package/dist/operator-ui/view.d.ts +167 -0
- package/dist/operator-ui/view.js +270 -0
- package/dist/providers/cloudflare.d.ts +0 -1
- package/dist/providers/cloudflare.js +261 -299
- package/dist/providers/linear.d.ts +26 -5
- package/dist/providers/linear.js +32 -61
- package/dist/providers/mixpanel.d.ts +35 -4
- package/dist/providers/mixpanel.js +53 -79
- package/dist/providers/notion.d.ts +0 -1
- package/dist/providers/notion.js +87 -42
- package/dist/providers/stripe.d.ts +13 -1
- package/dist/providers/stripe.js +31 -60
- package/dist/registry.d.ts +34 -2
- package/dist/registry.js +85 -5
- package/dist/retired-toolkits.d.ts +0 -1
- package/dist/retired-toolkits.js +0 -1
- package/dist/routes/access-tokens.d.ts +0 -1
- package/dist/routes/access-tokens.js +0 -1
- package/dist/routes/activity.d.ts +0 -1
- package/dist/routes/activity.js +0 -1
- package/dist/routes/credentials.d.ts +0 -1
- package/dist/routes/credentials.js +0 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +0 -1
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/ui.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +11 -1
- package/dist/skills.d.ts +0 -1
- package/dist/skills.js +0 -1
- package/dist/storage/file.d.ts +0 -1
- package/dist/storage/file.js +0 -1
- package/dist/storage/memory.d.ts +0 -1
- package/dist/storage/memory.js +0 -1
- package/dist/timeout.d.ts +0 -1
- package/dist/timeout.js +0 -1
- package/dist/tool-safety.d.ts +0 -1
- package/dist/tool-safety.js +0 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +13 -137
- package/dist/validate.d.ts +13 -15
- package/dist/validate.js +24 -11
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/documentation/architecture.md +186 -5
- package/documentation/auth.md +11 -0
- package/documentation/call-admission.md +142 -6
- package/documentation/cloudflare.md +92 -10
- package/documentation/code-first-exploration.md +2 -2
- package/documentation/code-mode.md +14 -14
- package/documentation/connector-guides.md +172 -5
- package/documentation/connectors.md +89 -0
- package/documentation/linear.md +20 -2
- package/documentation/mcp-ui-design.md +2 -2
- package/documentation/meta-tools.md +7 -1
- package/documentation/mixpanel.md +42 -9
- package/documentation/notion.md +23 -2
- package/documentation/operations.md +325 -5
- package/documentation/operator-ui.md +120 -5
- package/documentation/provider-audit.md +160 -0
- package/documentation/provider-conventions.md +667 -0
- package/documentation/request-admission.md +167 -5
- package/documentation/rich-output-design.md +1 -2
- package/documentation/stripe.md +15 -0
- package/documentation/upgrading.md +454 -0
- package/ethos.md +14 -9
- package/examples/worker/README.md +102 -12
- package/examples/worker/src/index.ts +46 -1
- package/examples/worker/wrangler.jsonc +14 -1
- package/package.json +10 -6
- package/templates/node/.dockerignore +10 -0
- package/templates/node/.env.example +40 -3
- package/templates/node/AGENTS.md +16 -1
- package/templates/node/Dockerfile +49 -0
- package/templates/node/README.md +117 -2
- package/templates/node/docker-compose.yml +39 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +182 -0
- package/templates/node/src/index.ts +80 -4
- package/assets/connecta-clay-hero.png +0 -0
- package/dist/access-tokens.d.ts.map +0 -1
- package/dist/access-tokens.js.map +0 -1
- package/dist/activity.d.ts.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/apps-shell.d.ts.map +0 -1
- package/dist/apps-shell.js.map +0 -1
- package/dist/auth/bearer.d.ts.map +0 -1
- package/dist/auth/bearer.js.map +0 -1
- package/dist/auth/clerk.d.ts.map +0 -1
- package/dist/auth/clerk.js.map +0 -1
- package/dist/auth/downstream-oauth.d.ts.map +0 -1
- package/dist/auth/downstream-oauth.js.map +0 -1
- package/dist/call-admission.d.ts.map +0 -1
- package/dist/call-admission.js.map +0 -1
- package/dist/catalog-fingerprint.d.ts.map +0 -1
- package/dist/catalog-fingerprint.js.map +0 -1
- package/dist/catalog-limits.d.ts.map +0 -1
- package/dist/catalog-limits.js.map +0 -1
- package/dist/catalog-service.d.ts.map +0 -1
- package/dist/catalog-service.js.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/catalog.js.map +0 -1
- package/dist/concurrency.d.ts.map +0 -1
- package/dist/concurrency.js.map +0 -1
- package/dist/connector-scope.d.ts.map +0 -1
- package/dist/connector-scope.js.map +0 -1
- package/dist/connectors/api.d.ts.map +0 -1
- package/dist/connectors/api.js.map +0 -1
- package/dist/connectors/remote-mcp.d.ts.map +0 -1
- package/dist/connectors/remote-mcp.js.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js.map +0 -1
- package/dist/executor-admission.d.ts.map +0 -1
- package/dist/executor-admission.js.map +0 -1
- package/dist/executor-result.d.ts.map +0 -1
- package/dist/executor-result.js.map +0 -1
- package/dist/executors/quickjs-child.d.ts.map +0 -1
- package/dist/executors/quickjs-child.js.map +0 -1
- package/dist/executors/quickjs-protocol.d.ts.map +0 -1
- package/dist/executors/quickjs-protocol.js.map +0 -1
- package/dist/executors/quickjs-runtime.d.ts.map +0 -1
- package/dist/executors/quickjs-runtime.js.map +0 -1
- package/dist/executors/quickjs.d.ts.map +0 -1
- package/dist/executors/quickjs.js.map +0 -1
- package/dist/favicon.d.ts.map +0 -1
- package/dist/favicon.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-schema.d.ts.map +0 -1
- package/dist/json-schema.js.map +0 -1
- package/dist/mcp-result.d.ts.map +0 -1
- package/dist/mcp-result.js.map +0 -1
- package/dist/meta-tools.d.ts.map +0 -1
- package/dist/meta-tools.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/operator-ui/generated.d.ts.map +0 -1
- package/dist/operator-ui/generated.js.map +0 -1
- package/dist/operator-ui/model.d.ts.map +0 -1
- package/dist/operator-ui/model.js.map +0 -1
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/linear.d.ts.map +0 -1
- package/dist/providers/linear.js.map +0 -1
- package/dist/providers/mixpanel.d.ts.map +0 -1
- package/dist/providers/mixpanel.js.map +0 -1
- package/dist/providers/notion.d.ts.map +0 -1
- package/dist/providers/notion.js.map +0 -1
- package/dist/providers/stripe.d.ts.map +0 -1
- package/dist/providers/stripe.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/retired-toolkits.d.ts.map +0 -1
- package/dist/retired-toolkits.js.map +0 -1
- package/dist/routes/access-tokens.d.ts.map +0 -1
- package/dist/routes/access-tokens.js.map +0 -1
- package/dist/routes/activity.d.ts.map +0 -1
- package/dist/routes/activity.js.map +0 -1
- package/dist/routes/credentials.d.ts.map +0 -1
- package/dist/routes/credentials.js.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/mcp.js.map +0 -1
- package/dist/routes/oauth.d.ts.map +0 -1
- package/dist/routes/oauth.js.map +0 -1
- package/dist/routes/shared.d.ts.map +0 -1
- package/dist/routes/shared.js.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/storage/file.d.ts.map +0 -1
- package/dist/storage/file.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js.map +0 -1
- package/dist/tool-safety.d.ts.map +0 -1
- package/dist/tool-safety.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/examples/node/README.md +0 -52
- package/examples/node/src/index.ts +0 -71
- package/src/access-tokens.ts +0 -289
- package/src/activity.ts +0 -234
- package/src/apps-shell.ts +0 -370
- package/src/auth/bearer.ts +0 -61
- package/src/auth/clerk.ts +0 -553
- package/src/auth/downstream-oauth.ts +0 -575
- package/src/call-admission.ts +0 -521
- package/src/catalog-fingerprint.ts +0 -52
- package/src/catalog-limits.ts +0 -11
- package/src/catalog-service.ts +0 -1153
- package/src/catalog.ts +0 -790
- package/src/concurrency.ts +0 -39
- package/src/connector-scope.ts +0 -66
- package/src/connectors/api.ts +0 -159
- package/src/connectors/remote-mcp.ts +0 -1090
- package/src/credentials.ts +0 -428
- package/src/errors.ts +0 -364
- package/src/execute.ts +0 -1406
- package/src/executor-admission.ts +0 -328
- package/src/executor-result.ts +0 -107
- package/src/executors/quickjs-child.ts +0 -172
- package/src/executors/quickjs-protocol.ts +0 -99
- package/src/executors/quickjs-runtime.ts +0 -470
- package/src/executors/quickjs.ts +0 -776
- package/src/favicon.ts +0 -53
- package/src/index.ts +0 -666
- package/src/invocation.ts +0 -655
- package/src/json-schema.ts +0 -11
- package/src/mcp-result.ts +0 -43
- package/src/meta-tools.ts +0 -1607
- package/src/node.ts +0 -206
- package/src/operator-ui/browser.css +0 -423
- package/src/operator-ui/browser.ts +0 -1281
- package/src/operator-ui/generated.ts +0 -4
- package/src/operator-ui/model.ts +0 -111
- package/src/providers/cloudflare.ts +0 -3999
- package/src/providers/linear.ts +0 -302
- package/src/providers/mixpanel.ts +0 -229
- package/src/providers/notion.ts +0 -1879
- package/src/providers/stripe.ts +0 -307
- package/src/registry.ts +0 -1085
- package/src/retired-toolkits.ts +0 -14
- package/src/routes/access-tokens.ts +0 -115
- package/src/routes/activity.ts +0 -215
- package/src/routes/credentials.ts +0 -254
- package/src/routes/mcp.ts +0 -429
- package/src/routes/oauth.ts +0 -322
- package/src/routes/shared.ts +0 -247
- package/src/routes/ui.ts +0 -152
- package/src/server.ts +0 -195
- package/src/skills.ts +0 -271
- package/src/storage/file.ts +0 -123
- package/src/storage/memory.ts +0 -44
- package/src/timeout.ts +0 -84
- package/src/tool-safety.ts +0 -15
- package/src/types.ts +0 -523
- package/src/ui.ts +0 -744
- package/src/validate.ts +0 -387
- package/src/version.ts +0 -7
|
@@ -1,7 +1,327 @@
|
|
|
1
1
|
# Operations
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Configuring, running, verifying, and upgrading a deployment — and the map of
|
|
4
|
+
which suite proves what, which is the part an agent changing this repository
|
|
5
|
+
needs most.
|
|
6
|
+
|
|
7
|
+
## Running it
|
|
8
|
+
|
|
9
|
+
`createConnecta(config)` returns `{ fetch, registry, close }`. `fetch` takes
|
|
10
|
+
the Workers `(request, env, ctx)` signature; passing `ctx` through is what lets
|
|
11
|
+
connecta hand deferred work — best-effort activity writes — to `ctx.waitUntil`
|
|
12
|
+
instead of losing it when the response returns.
|
|
13
|
+
|
|
14
|
+
An `executor` is required. A deployment without one throws at construction
|
|
15
|
+
rather than serving a smaller surface
|
|
16
|
+
([#273](https://github.com/zackbart/connecta/issues/273)): Node uses
|
|
17
|
+
`quickJsExecutor()` from `@zackbart/connecta/quickjs`, Workers use
|
|
18
|
+
`new DynamicWorkerExecutor({ loader: env.LOADER })` from
|
|
19
|
+
`@cloudflare/codemode`.
|
|
20
|
+
|
|
21
|
+
Both executor packages are optional peers: they never install with connecta,
|
|
22
|
+
and a deployment installs the one its runtime needs. The manifest publishes the
|
|
23
|
+
range each release supports — `^0.32.0` for `quickjs-emscripten`,
|
|
24
|
+
`^0.4.4 || ^0.5.0` for `@cloudflare/codemode` — so npm answers the version
|
|
25
|
+
question at install time rather than leaving a deployment to discover the skew
|
|
26
|
+
when a program runs ([#376](https://github.com/zackbart/connecta/issues/376)).
|
|
27
|
+
The version this repository tests against is one the published range admits,
|
|
28
|
+
and `test/package-surface.test.ts` fails if the two ever drift apart.
|
|
29
|
+
|
|
30
|
+
There are exactly two deployment shapes.
|
|
31
|
+
[`templates/node/`](../templates/node/) is what `connecta init` copies — the
|
|
32
|
+
one standalone Node project, Docker-ready rather than Docker-only — and
|
|
33
|
+
[`examples/worker/`](../examples/worker/) is the Cloudflare shape. Both ship
|
|
34
|
+
the whole operator feature set; each README walks through its own enablement.
|
|
35
|
+
A third scaffold that is a diff away from either is the shape
|
|
36
|
+
[#344](https://github.com/zackbart/connecta/issues/344) deleted, so do not add
|
|
37
|
+
one.
|
|
38
|
+
|
|
39
|
+
### The CLI
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npx @zackbart/connecta init my-deployment
|
|
43
|
+
cd my-deployment && npm install && npm start
|
|
44
|
+
CONNECTA_TOKEN=… npx connecta doctor --url http://localhost:8787
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`init` copies the template, pins the generated deployment to the CLI package's
|
|
48
|
+
exact version, restores the template `.gitignore` (npm renames it in a
|
|
49
|
+
tarball), and refuses to merge into an existing path.
|
|
50
|
+
|
|
51
|
+
`doctor` verifies a *running* deployment: `/health` reports ok, `tools/list` is
|
|
52
|
+
exactly the seven prescribed names, and `execute_code` actually runs a trivial
|
|
53
|
+
program. The executor it names is the one the deployment reports on `/health`,
|
|
54
|
+
from that executor's own `name` or its constructor name, sanitized and bounded
|
|
55
|
+
on the way out: `QuickJS` on the Node template, `DynamicWorkerExecutor` on the
|
|
56
|
+
Worker example, and `code executed` when an executor identifies as nothing —
|
|
57
|
+
a checker that asserts a sandbox it never saw is worse than one that says it
|
|
58
|
+
does not know ([#368](https://github.com/zackbart/connecta/issues/368)). It
|
|
59
|
+
refuses to send a bearer token over remote plaintext HTTP, and it
|
|
60
|
+
*reports* catalog drift without failing on it — an unclassified downstream tool
|
|
61
|
+
already fails closed onto `call_destructive_tool`, so drift is a maintainer's
|
|
62
|
+
next task rather than a broken deployment
|
|
63
|
+
([#343](https://github.com/zackbart/connecta/issues/343)).
|
|
64
|
+
|
|
65
|
+
### Configuration
|
|
66
|
+
|
|
67
|
+
Structural seams stay top-level; tuning is grouped by subsystem. Every group is
|
|
68
|
+
optional.
|
|
69
|
+
|
|
70
|
+
| Option | Default | What it does |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| `connectors` | — (required) | the connector set ([connectors](./connectors.md)) |
|
|
73
|
+
| `executor` | — (required) | the sandbox `execute_code` runs in ([code mode](./code-mode.md#what-an-executor-must-implement)) |
|
|
74
|
+
| `auth?` | none ⇒ open (dev only) | one `InboundAuth` or an array; bearer providers are checked before Clerk ([inbound auth](./auth.md)) |
|
|
75
|
+
| `storage?` | `memoryStorage()` | the one state seam ([storage](./storage-and-credentials.md)) |
|
|
76
|
+
| `publicUrl?` | per-request origin | public base URL; an HTTPS value also redirects inbound HTTP |
|
|
77
|
+
| `logger?` | `console`, prefixed `[connecta]` | `{ debug, info, warn, error }` |
|
|
78
|
+
| `branding?` | neutral Connecta defaults | operator-page and OAuth result-page labels and marks |
|
|
79
|
+
| `serverInfo?` | `connecta` / package version | `{ name, version, title?, websiteUrl?, icons? }` per the MCP icons spec |
|
|
80
|
+
| `deploymentInfo?` | unset | arbitrary metadata exposed by `/health` |
|
|
81
|
+
| `activity?` | unset | `{ store, readGate?, deploymentId? }` — payload-free activity storage, an optional operator-read gate, and a stable event label |
|
|
82
|
+
| `credentials.encryptionKey?` | unset | base64 32-byte AES key for the connector vault. Without it, connectors declaring `credential` warn and their slots stay unmanageable |
|
|
83
|
+
| `accessTokens?` | unset | `{ maxActive? }` (default 100) for operator-issued MCP bearer tokens. Requires a Clerk provider, or construction throws ([access tokens](./auth.md#operator-issued-access-tokens)) |
|
|
84
|
+
| `discovery.concurrency?` | 4 | connector catalogs/status probes in flight at once |
|
|
85
|
+
| `discovery.catalogTtlSeconds?` | 300 | fresh TTL for cached tool lists |
|
|
86
|
+
| `discovery.persistCatalog?` | true | persist serializable catalogs as a manifest plus revision-addressed chunks |
|
|
87
|
+
| `discovery.staleCatalogSeconds?` | 3600 | how long an expired catalog stays usable as a failure fallback |
|
|
88
|
+
| `discovery.probeTimeoutMs?` | 30_000 | per-connector deadline for catalog fan-out; a timed-out connector degrades alone. Not a tool-call deadline |
|
|
89
|
+
| `calls.defaultTimeoutMs?` | **unset (opt-in)** | deadline for calls that pass no `timeoutMs`. Bounds one attempt, so retries can still extend total duration |
|
|
90
|
+
| `calls.maxResultBytes?` | 50_000 | inline result cap before truncation and `get_result` paging; a connector may override it. Invalid values warn and fall back |
|
|
91
|
+
| `execute.maxEmittedBytes?` | 4_000_000 | aggregate `connecta.emit` bytes per run — a transport bound, not a context bound |
|
|
92
|
+
| `execute.maxEmittedBlocks?` | 32 | content blocks `connecta.emit` accepts per run |
|
|
93
|
+
| `admission.requests?` | 16 active / 32 queued / 5 s / 1 s | global FIFO `/mcp` capacity, taken before auth ([request admission](./request-admission.md)) |
|
|
94
|
+
| `admission.code?` | 2 active / 8 queued / 5 s / 1 s | fallback pool for an executor that owns no `acquire()`; ignored with a warning when it does |
|
|
95
|
+
|
|
96
|
+
Options removed in earlier releases throw with their migration named rather
|
|
97
|
+
than falling back to a default: `toolkits`
|
|
98
|
+
([#178](https://github.com/zackbart/connecta/issues/178)), `credentials.health`
|
|
99
|
+
([#179](https://github.com/zackbart/connecta/issues/179)), `surface` and
|
|
100
|
+
`calls.maxBatchResultBytes`
|
|
101
|
+
([#273](https://github.com/zackbart/connecta/issues/273)), and the flat v0.6
|
|
102
|
+
paths. Silently ignoring a removed option is how a deployment ends up running a
|
|
103
|
+
policy its config file says it has.
|
|
104
|
+
|
|
105
|
+
### Deployment as a release unit
|
|
106
|
+
|
|
107
|
+
Treat the package and each running instance as separate release units:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
@zackbart/connecta release
|
|
111
|
+
↓ exact version
|
|
112
|
+
deployment repository
|
|
113
|
+
src/index.ts connector and auth configuration
|
|
114
|
+
package-lock.json reproducible package graph
|
|
115
|
+
wrangler.jsonc (Worker) domain, bindings
|
|
116
|
+
migrations/ (Worker) deployment-owned D1 schema history
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
An upgrade is an intentional dependency change followed by a normal build.
|
|
120
|
+
Doing it to a deployment somebody else generated — reading its pin, diffing it
|
|
121
|
+
against the template generation it came from, and crossing the version
|
|
122
|
+
boundaries that break construction — is [upgrading](./upgrading.md), which is
|
|
123
|
+
written for the agent sitting inside that deployment rather than inside this
|
|
124
|
+
repository. Instances must not share KV namespaces, D1 databases, secrets, or
|
|
125
|
+
encryption keys. Keeping deployment configuration private is sensible even
|
|
126
|
+
though this package is public.
|
|
127
|
+
|
|
128
|
+
## Verification
|
|
129
|
+
|
|
130
|
+
`npm run check` must pass before anything is claimed done. In order:
|
|
131
|
+
|
|
132
|
+
| Script | What it gates |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `check:docs` | local Markdown targets and fragments — including the `github.com` and `raw.githubusercontent.com` URLs that point back into this repository — guide and ethos size caps, duplicate heading anchors, a resurrected `docs/`, stale manual references |
|
|
135
|
+
| `check:operator-ui` | the committed browser bundle matches its source, byte for byte |
|
|
136
|
+
| `check:lint` | Oxlint's correctness category only — style is authored, not enforced |
|
|
137
|
+
| `check:unused` | Knip's unused-export and dependency gate |
|
|
138
|
+
| `typecheck` | `tsc --noEmit` for the package and the separate DOM-lib browser project |
|
|
139
|
+
| `test` | both vitest projects |
|
|
140
|
+
| `build` | the operator bundle, then `tsc -p tsconfig.build.json` into `dist/` |
|
|
141
|
+
| `check:examples` | the Node template and the Worker example typecheck against the built package |
|
|
142
|
+
|
|
143
|
+
`npm run release:check` adds `check:security` (`npm audit --omit=dev
|
|
144
|
+
--audit-level=moderate`) and `check:package`, and is what CI runs on every push
|
|
145
|
+
and pull request. `check:package` packs the tarball, asserts the required files
|
|
146
|
+
are in it and that no unshippable path leaked in — including any
|
|
147
|
+
Cloudflare-named connector or storage path (`connectors/cloudflare`,
|
|
148
|
+
`storage/cloudflare`) anywhere in the artifact, `dist/` and `examples/` alike —
|
|
149
|
+
derives the shipped guide list from which guides still carry a stub marker,
|
|
150
|
+
hands the packed path list to `scripts/check-packed-links.mjs`, and then runs
|
|
151
|
+
`connecta init` and builds and runs the generated deployment's own container.
|
|
152
|
+
|
|
153
|
+
That last step enforces the packed-link policy, which is one sentence: **every
|
|
154
|
+
relative link in shipped Markdown must resolve to a path the tarball carries,
|
|
155
|
+
and a target that is repository-only is cited as an absolute
|
|
156
|
+
`https://github.com/zackbart/connecta/blob/main/...` URL** (the
|
|
157
|
+
`raw.githubusercontent.com` form for an image, which is how the README hero
|
|
158
|
+
still renders on npmjs.com). The tarball is built output, not a checkout: it
|
|
159
|
+
carries no `eval/`, `test/`, `scripts/`, or `assets/`, so a relative pointer
|
|
160
|
+
into any of them is a dead end for the reader who installed the package, and
|
|
161
|
+
the fix is never to ship those directories — that would undo the trim of
|
|
162
|
+
[#346](https://github.com/zackbart/connecta/issues/346). A repository URL keeps
|
|
163
|
+
the citation verifiable in both directions: an outside reader can follow it, and
|
|
164
|
+
`check:docs` resolves it back to the checkout and fails when the cited file
|
|
165
|
+
moves ([#378](https://github.com/zackbart/connecta/issues/378)). `CHANGELOG.md`
|
|
166
|
+
is exempt from both gates, because release notes quote the paths that existed
|
|
167
|
+
when they shipped.
|
|
168
|
+
|
|
169
|
+
The Worker example ships in the tarball, its Cloudflare KV and D1 adapters
|
|
170
|
+
included: it is the Workers starting template a consumer copies. That is not a
|
|
171
|
+
hole in the published surface, because nothing under `examples/` appears in the
|
|
172
|
+
`exports` map — every export target resolves into `dist/`, so those adapters
|
|
173
|
+
are reference source rather than an importable subpath. They also clear the
|
|
174
|
+
platform-specific gate above on their names (`cloudflare-kv.ts`,
|
|
175
|
+
`d1-activity.ts`) rather than by exemption: that gate is a blunt pattern over
|
|
176
|
+
the whole artifact, so an example file renamed into `storage/cloudflare` would
|
|
177
|
+
fail the pack even though nothing about the published surface had changed.
|
|
178
|
+
|
|
179
|
+
Two more runners are deliberately outside `check`:
|
|
180
|
+
|
|
181
|
+
- `npm run test:browser` — Playwright against a real headless Chromium
|
|
182
|
+
(`npm run test:browser:install` once). It covers the embedded bundle without
|
|
183
|
+
adding a browser download to both CI Node-version jobs.
|
|
184
|
+
- `npm run drift:check` — the maintainer-run provider drift check, with local
|
|
185
|
+
provider credentials exported. No credential goes near CI and nothing files
|
|
186
|
+
itself; findings are read by a human and become issues
|
|
187
|
+
([provider conventions](./provider-conventions.md#the-maintainer-run-drift-check)).
|
|
188
|
+
- `npm run load:admission` — the opt-in capacity matrix and soak
|
|
189
|
+
([request admission](./request-admission.md#measuring-capacity)).
|
|
190
|
+
|
|
191
|
+
Releases: `npm run release:check`, tag `v<version>` matching `package.json`
|
|
192
|
+
exactly (the publish workflow verifies this and fails otherwise), and
|
|
193
|
+
publishing fires on GitHub **Release publication**, not on the tag push.
|
|
194
|
+
|
|
195
|
+
## The test map
|
|
196
|
+
|
|
197
|
+
Suites live in `test/` and run as two vitest projects. `WORKERS_SUITES` holds
|
|
198
|
+
runtime-portable suites; `NODE_ONLY_SUITES` holds Node-bound suites, each with
|
|
199
|
+
a stated reason. The `node` project runs their union; the `workers` project
|
|
200
|
+
re-runs the portable list inside workerd against the Worker example's
|
|
201
|
+
compatibility settings — so a Workers-only regression, the class of bug the
|
|
202
|
+
`CfWorkerJsonSchemaValidator` workaround exists for, fails CI instead of being
|
|
203
|
+
found by hand. `test/suite-partition.test.ts` walks the directory and refuses
|
|
204
|
+
an unclassified, double-classified, stale, or reasonless entry.
|
|
205
|
+
|
|
206
|
+
**New behavior gets a row here.** A suite that is not in this table is either
|
|
207
|
+
new and undocumented or dead, and neither is a state to leave the repository
|
|
208
|
+
in.
|
|
209
|
+
|
|
210
|
+
### Runtime-portable (`WORKERS_SUITES`)
|
|
211
|
+
|
|
212
|
+
| Suite | Covers |
|
|
213
|
+
| --- | --- |
|
|
214
|
+
| `access-tokens.test.ts` | the `AccessTokenManager` — a one-time secret created, authenticated, renamed, and revoked, bounded names and active count, enumerable storage required, a deployment with no Clerk operator refused — and the Clerk-only routes, down to historical activity still resolving a revoked token's name |
|
|
215
|
+
| `activity.test.ts` | payload-free delivery: a rejected async write attaches to `waitUntil` instead of throwing, approved destructive calls record under their real entry point, result-size friction records without retaining the result, and a hallucinated connector id or invented identity is clamped so the event still cannot carry a payload |
|
|
216
|
+
| `api-connector.test.ts` | `api()` — kind, description, tool defs, dispatch, default args, unknown tools, handler throws, argument validation, and the construction contract |
|
|
217
|
+
| `bearer.test.ts` | constant-time bearer compare, case-insensitive scheme, 401 challenges, and the retired audience options refusing rather than silently unbinding |
|
|
218
|
+
| `branding.test.ts` | branding fallbacks and overrides across the operator shells, OAuth result pages, `/favicon.*`, page titles, and escaping — branding is not an injection vector |
|
|
219
|
+
| `call-admission.test.ts` | connector-scoped per-runtime downstream admission ([call admission](./call-admission.md)): independent partitions, exact rolling-window reset, cancellation that charges no budget, bounded partition state, local-refusal health isolation, one shared limiter across direct and program calls, and payload-free `/health` aggregates |
|
|
220
|
+
| `catalog-drift.test.ts` | `vettedCatalog()`, `detectCatalogDrift()`, and `withVettedCatalog()`; drift on the registry surface and on `/health`; the connector seam projected rather than echoed; and the drift types being public |
|
|
221
|
+
| `catalog.test.ts` | lexical ranking and the compact schema renderer — `const`, `allOf` beside siblings, `$ref`, the depth limit, per-schema caching, and 2020-12 keyword compatibility |
|
|
222
|
+
| `clerk.test.ts` | protected-resource metadata, the browser sign-in config, OAuth and session tokens, cached best-effort activity labels with their caps, the hand-applied `azp` rejection, and the `allowedDomains` allowlist including every lookalike that must not be repaired into a match |
|
|
223
|
+
| `cloudflare-provider.test.ts` | `cloudflare()` construction, tool surface, request building, projections, typed failures, and credential test |
|
|
224
|
+
| `cloudflare-registry.test.ts` | the same provider inside a real deployment: discovery, addressing, and admission through the registry |
|
|
225
|
+
| `code-first-surface.test.ts` | the seven-tool surface itself — an executor required and both runtime configurations named, every removed option and removed top-level tool refused, and `connecta.ui` findable before an agent chooses catalog search |
|
|
226
|
+
| `codemode-compat.test.ts` | the `Executor` seam staying structurally compatible with `@cloudflare/codemode`'s `DynamicWorkerExecutor`, enforced by `tsc` |
|
|
227
|
+
| `config.test.ts` | the grouped `ConnectaConfig` boundary — each group forwarding to its internals, malformed admission bounds failing construction, and one complete migration error for legacy own-properties |
|
|
228
|
+
| `credentials.test.ts` | the pure stored-shape classifier (containment, not equality) and the AES-GCM vault: round-trip, ciphertext bound to its connector id, named field sets, masked metadata, wrong-key rejection, deletion, coexistence with OAuth keys |
|
|
229
|
+
| `d1-activity-example.test.ts` | the Worker example's deployment-owned D1 activity store: actor namespace round-trip, payload-free friction reconstructed from the persisted code, and agreement with the package's friction table |
|
|
230
|
+
| `downstream-oauth.test.ts` | `KvOAuthProvider` round-trips and races, `auth_required` versus `error`, `startAuth`/`finishAuth`, callback refusal equality, bounded diagnostics, and HTML escaping |
|
|
231
|
+
| `errors.test.ts` | `ConnectorCallError` codes, retryable defaults and overrides, `retryAfterMs` round-trip, typed-over-heuristic classification, `AbortError` as a retryable timeout, and framing errors |
|
|
232
|
+
| `execute.test.ts` | the code-mode host bridge: identifier sanitization, MCP-result unwrapping, sandbox provider construction, fail-closed filtering of destructive and unannotated tools, and MCP/code-mode invocation parity |
|
|
233
|
+
| `execute-emit.test.ts` | `connecta.emit` (M1–M10) — block validation, budgets, the provider, delivery after the result envelope on success only, and the defaults |
|
|
234
|
+
| `execute-ui.test.ts` | `connecta.ui` (U1–U9) — validation, multiplicity and budget, the provider, `_meta` delivery, and the Apps shell |
|
|
235
|
+
| `executor-admission.test.ts` | the portable bounded FIFO both pools use: active and queue ceilings, stable retryable overload, queue timeout, cancellation removal, idempotent release, shutdown |
|
|
236
|
+
| `guarded-fetch.test.ts` | the guarded transport — construction, request building, destination confinement, and response handling |
|
|
237
|
+
| `guest-api-contract.test.ts` | the executor-independent half of the guest API contract cases, including the serialized truncation envelope, capped logs, truncation reported as success, and an in-flight host call failing when the run ends |
|
|
238
|
+
| `linear-provider.test.ts` / `linear-registry.test.ts` | the Linear proxy's construction, classification, and guide; then the same connector inside a real deployment |
|
|
239
|
+
| `meta-tools.test.ts` | the registry-backed meta-tools: bounded discovery with page and address maxima, concise and full descriptions, compact and JSON schemas, structured errors, `skills` and connector-guide selection, stored-credential drift, catalog-lookup health accounting, `fields` selection, truncation and `get_result` offset validation and character alignment, per-connector `maxResultBytes`, probe timeouts, and empty-query browse of an unavailable or unconfigured catalog |
|
|
240
|
+
| `mixpanel-provider.test.ts` / `mixpanel-registry.test.ts` | the Mixpanel proxy, then the same connector inside a real deployment |
|
|
241
|
+
| `notion-provider.test.ts` / `notion-registry.test.ts` | Notion's tool surface, request construction, lean projections, both pagination conventions, error mapping, and writes; then the connector in a real deployment |
|
|
242
|
+
| `operator-boundary.test.ts` | the operator row of the decisions table, after every mutation route: authentication material managed without moving a declared structure, and the one honest exception — a credential write making a remote catalog appear, which is discovery arriving, not an operator editing the deployment |
|
|
243
|
+
| `operator-store.test.ts` | `src/operator-ui/app/store.ts` against a fake browser: the Clerk listener, `gate()`, the generation fence, and the request path |
|
|
244
|
+
| `provider-conventions.test.ts` | the conventions a test can hold: hand-written providers refusing schemas they cannot enforce (H5), Cloudflare stating its second pagination convention in the schema (H10), and Notion saying it has no escape hatch (H14) |
|
|
245
|
+
| `registry.test.ts` | construction and id validation, startup convention and result-cap warnings, address resolution, tool-cache TTL, and broken-connector isolation |
|
|
246
|
+
| `remote-mcp.test.ts` | `remoteMcp()` against an in-process server through the `_transportFactory` seam: passthrough, downstream `isError`, Workers-safe output-schema validation, request-scoped client reuse and at-most-once scope close; plus the real transport's manual redirect policy, destination guard, credential containment, and downstream session termination |
|
|
247
|
+
| `remote-mcp-pagination.test.ts` | the `tools/list` cursor chain in both directions — exact cursor handoff, first-wins dedup, a failed later page rejecting rather than returning its prefix, the runaway backstops, the tool-metadata re-prime across pages, and paginated catalogs reaching the discovery path |
|
|
248
|
+
| `request-admission.test.ts` | `/mcp` bounded before auth, the stable 503 and `Retry-After`, health and operator responsiveness under saturation, payload-free counters, queued cancellation, shutdown rejection while active work drains, and the separate fallback code pool |
|
|
249
|
+
| `server.test.ts` | end-to-end `/mcp` (401 → initialize instructions → seven tools → usage skill → `call_tool`), the open routes, Clerk `.well-known` metadata with no network, and an end-to-end code-mode run |
|
|
250
|
+
| `server-route-contracts.test.ts` | the route contracts `server.ts` must keep byte-identical: every built-in answered ahead of connector routes inside the security wrapper, open data-free shells with framing denied, per-route auth and same-origin requirements with exact 401/403/405 bodies, and OAuth `verifyState`-before-`finishAuth` ordering |
|
|
251
|
+
| `startup-warnings.test.ts` | every construction-time `logger.warn` and, as importantly, the conditions that must *not* trigger one: open mode with a credential or OAuth connector, `publicUrl` unset beside OAuth, dropped branding and `uiAuth` URLs, a missing `verifyState`, a credential test-hook mismatch, and an unusable `calls.maxResultBytes` |
|
|
252
|
+
| `stripe-provider.test.ts` / `stripe-registry.test.ts` | the Stripe proxy's endpoint modes and admission, then the connector in a real deployment |
|
|
253
|
+
| `ui.test.ts` | the server shell and `/ui/*` routes and the app's pure state rules from `view.ts` — filtering, page routing and capability states, credential management, gated `/ui/data` with broken-connector isolation, and the URL safety gates |
|
|
254
|
+
| `validate.test.ts` | `validateToolInput()` — a returned (not thrown) `invalid_args` naming the path, `additionalProperties: false` enforcement, per-schema validator caching, and an unusable schema passed through with one warning |
|
|
255
|
+
|
|
256
|
+
### Node-bound (`NODE_ONLY_SUITES`)
|
|
257
|
+
|
|
258
|
+
Each entry carries its reason in `vitest.config.ts`; the reason is the
|
|
259
|
+
justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
260
|
+
|
|
261
|
+
| Suite | Covers | Why Node |
|
|
262
|
+
| --- | --- | --- |
|
|
263
|
+
| `deployment-shapes.test.ts` | the Worker as the only example, one Node template that is also its own container, the same source running locally and in the container, the full operator surface in both, a template that cannot start on its own `.env.example`, a Worker README naming every optional peer its entrypoint imports, and the initializer's `.gitignore` staying in step | walks the template and example trees with Node filesystem APIs |
|
|
264
|
+
| `doc-links.test.ts` | the documentation checker itself — local file and fragment resolution, repository URLs resolved back to the checkout, duplicate heading slugs, fenced-code exclusion, and useful failures | spawns the Node checker against filesystem fixtures |
|
|
265
|
+
| `doctor-cli.test.ts` | `connecta doctor`'s executor line end to end — the sandbox the deployment reports is the one named, an unidentifiable executor gets an executor-neutral line, and a hostile name is bounded and stripped before it reaches a terminal | spawns the CLI against a Node HTTP deployment over real sockets |
|
|
266
|
+
| `drift-check.test.ts` | the maintainer drift checker — recorded touched endpoints, a quiet revision bump, clear failures for an unavailable spec/manifest/credential, `$ref` traversal, and one well-formed row per endpoint | spawns the Node checker against filesystem fixtures |
|
|
267
|
+
| `file-storage.test.ts` | `fileStorage()` across instances, logical TTL plus physical pruning without clobbering a newer value, and corrupt-file quarantine | exercises the Node filesystem storage adapter |
|
|
268
|
+
| `guest-api-contract-quickjs.test.ts` | the shared guest-contract cases on the real QuickJS executor | runs the contract cases on the Node QuickJS executor |
|
|
269
|
+
| `node.test.ts` | the `listen()` adapter propagating an HTTP client disconnect through the Web `Request` and the MCP handler into a program's connector call, releasing both admission permits | exercises the Node HTTP adapter over real TCP sockets |
|
|
270
|
+
| `packed-links.test.ts` | the packed-link gate itself — shipped targets and repository URLs accepted, relative links into unshipped paths and directories rejected with the citation to write instead, reference definitions seen, fenced examples ignored, the changelog exempt | spawns the Node packed-link gate against filesystem fixtures |
|
|
271
|
+
| `package-surface.test.ts` | the published boundary — built output shipped, the `exports` map carrying exactly the documented subpaths plus `./package.json`, only generic factories, platform storage kept in examples, Clerk and QuickJS behind optional subpaths, every provider independently importable, and the Cloudflare provider free of bare specifiers | walks the package tree with Node filesystem APIs |
|
|
272
|
+
| `purity.test.ts` | the import-graph guardrail ([architecture](./architecture.md#import-graph-purity)) — the core stays Workers-clean | walks the source import graph with Node filesystem APIs |
|
|
273
|
+
| `quickjs-child-entry.test.ts` | a missing QuickJS child entry failing before `fork()`, with the expected path and the bundler-externalization constraint | mocks Node child-process and filesystem APIs |
|
|
274
|
+
| `quickjs-child-stderr.test.ts` | abnormal child exits retaining only an 8 KiB stderr tail, included in the parent-side diagnostic | mocks Node child-process streams |
|
|
275
|
+
| `quickjs-executor.test.ts` | the child-process sandbox — code normalization, lazy namespace proxies, bounded IPC, separate guest-CPU and wall budgets, saturation, cancellation and shutdown, crash and OOM recovery, host-call hangs, stalled-promise detection | runs the Node QuickJS child-process executor |
|
|
276
|
+
| `quickjs-log-limits.test.ts` | bounded `console.*` capture — per-entry cut, cumulative character and transport budgets, escape-heavy floods preserving the guest result | runs the Node QuickJS child-process executor |
|
|
277
|
+
| `suite-partition.test.ts` | this partition, including itself: every `*.test.ts` in exactly one list, stale entries and empty reasons refused | walks the test directory to guard the partition |
|
|
278
|
+
| `template-file-activity.test.ts` | the Node template's own activity store — persistence across restart, torn-line repair, newest-first paging, and compaction past the slack window | runs it against real files |
|
|
279
|
+
| `upgrade-guide.test.ts` | the [upgrade guide](./upgrading.md)'s claims about somebody else's deployment — the generated file inventory, the seven tool names doctor demands, version boundaries that actually shipped, a bump target that is this release, and the three places a reader finds it | reads the guide, the template tree, and the CLI with Node filesystem APIs |
|
|
280
|
+
| `version.test.ts` | `CONNECTA_VERSION` matching `package.json` | reads `package.json` with Node filesystem APIs |
|
|
281
|
+
|
|
282
|
+
### Outside `npm run check`
|
|
283
|
+
|
|
284
|
+
| Suite | Covers |
|
|
285
|
+
| --- | --- |
|
|
286
|
+
| `browser/operator-ui.spec.ts` | the operator wiring in a real browser: the shell staying open until authentication, credential and access-token and OAuth flows end to end, drift shown without naming a tool, and every failure and empty state |
|
|
287
|
+
| `browser/program-ui.spec.ts` | the Apps shell in a real browser: a bound view merging fixed and declared arguments and correlating concurrent reads, and the one-string payload receiving no read bridge ([program UI read calls](./program-ui-read-calls.md)) |
|
|
288
|
+
|
|
289
|
+
**The `_transportFactory` seam.** `RemoteMcpOptions._transportFactory` is
|
|
290
|
+
internal, not public API: when set, `remoteMcp()` uses that `Transport` instead
|
|
291
|
+
of building an HTTP one. Tests link an in-memory transport to an in-process MCP
|
|
292
|
+
server, so remote-MCP behavior is exercised without a network or a real OAuth
|
|
293
|
+
server. Two consequences worth knowing before you use it: an in-memory
|
|
294
|
+
transport has no session semantics, so anything about `Mcp-Session-Id` needs
|
|
295
|
+
the real HTTP transport, and anything about redirects or destination
|
|
296
|
+
confinement does too.
|
|
297
|
+
|
|
298
|
+
## Troubleshooting
|
|
299
|
+
|
|
300
|
+
- **MCP clients cache the tool list.** After adding a connector or completing a
|
|
301
|
+
downstream OAuth flow, restart the client. It will not re-list on its own.
|
|
302
|
+
Connecta declares a one-hour private `tools/list` cache hint, which is a
|
|
303
|
+
ceiling on how long a well-behaved client may wait, not a promise it will.
|
|
304
|
+
- **`auth_required` that never clears.** Confirm `publicUrl` is set and
|
|
305
|
+
`GET <publicUrl>/oauth/callback/<connectorId>` is reachable from a browser,
|
|
306
|
+
and that storage is durable rather than `memoryStorage()` across restarts.
|
|
307
|
+
Then `authorize_connector` to restart the flow; `force: true` wipes stored
|
|
308
|
+
credentials for a clean retry.
|
|
309
|
+
- **A connector with no `verifyState` refuses every callback.** That is the
|
|
310
|
+
designed behavior, not a bug: handing an unverified code to `finishAuth` is
|
|
311
|
+
the vulnerability. The startup warning names the connector.
|
|
312
|
+
- **401 loops from a client that cannot discover auth.** The client must reach
|
|
313
|
+
the open `/.well-known/oauth-protected-resource` (and the `/mcp` variant);
|
|
314
|
+
confirm CORS and the Clerk keys, and that DCR is enabled on the Clerk
|
|
315
|
+
instance.
|
|
316
|
+
- **No sessions and no server push, by design.** The transport is stateless.
|
|
317
|
+
Scope resolves per request, which is also where the MCP spec has arrived.
|
|
318
|
+
- **A tool that should be callable from a program is not.** Only tools
|
|
319
|
+
explicitly annotated `readOnlyHint: true` are admissible inside the sandbox.
|
|
320
|
+
A missing, false, or contradictory annotation fails closed, every time, and
|
|
321
|
+
the recovery is `call_destructive_tool` — not a wider sandbox.
|
|
322
|
+
- **`check:operator-ui` fails after a UI change.** Run
|
|
323
|
+
`npm run build:operator-ui` and commit the regenerated
|
|
324
|
+
`src/operator-ui/generated.ts` ([operator UI](./operator-ui.md#why-the-bundle-is-committed)).
|
|
325
|
+
- **Upgrade the MCP SDK and Zod together**, then run `npm run release:check`.
|
|
326
|
+
The SDK packages are pinned exactly and paired with Zod 4 to keep the
|
|
327
|
+
optional code-mode peer graph valid.
|
|
@@ -1,7 +1,122 @@
|
|
|
1
1
|
# Operator UI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
The browser surface a human uses to see what a deployment exposes and to manage
|
|
4
|
+
the authentication material behind it. It is a small Preact app compiled by the
|
|
5
|
+
repository's own esbuild step and inlined into a data-free server shell.
|
|
6
|
+
|
|
7
|
+
Read [`ethos.md`](../ethos.md) first. The boundary this subsystem lives inside
|
|
8
|
+
is the operator row in its decisions table: **operator routes may manage
|
|
9
|
+
authentication material for capabilities declared in deployment configuration,
|
|
10
|
+
and may not change the connector set, the tool catalog or annotations, requested
|
|
11
|
+
OAuth scopes, admission policy, authorization rules, or caller tool scope.**
|
|
12
|
+
`test/operator-boundary.test.ts` proves it after every mutation route.
|
|
13
|
+
|
|
14
|
+
Both deployment shapes ship the whole feature set behind it, because pages for
|
|
15
|
+
things a deployment cannot do are worse than no pages
|
|
16
|
+
([#345](https://github.com/zackbart/connecta/issues/345)). The
|
|
17
|
+
[Node template](../templates/node/) carries sign-in, vault, tokens, and
|
|
18
|
+
activity as commented blocks in `src/index.ts` — plus a deployment-owned
|
|
19
|
+
`src/file-activity.ts` that is compiled rather than commented — and the
|
|
20
|
+
[Worker example](../examples/worker/) wires the first three and comments the
|
|
21
|
+
fourth, which needs a D1 database. Each README walks through its own
|
|
22
|
+
enablement.
|
|
23
|
+
|
|
24
|
+
The vault is the one whose page needs a second thing. `/credentials` lists
|
|
25
|
+
connector credential slots, so `credentialManagement` stays `no_slots` — and
|
|
26
|
+
the nav entry stays hidden — until a connector declares `credential`, however
|
|
27
|
+
well-configured the vault is. Neither shape's shipped connectors need a secret,
|
|
28
|
+
so both carry the slot's shape as a comment beside a connector and say so in
|
|
29
|
+
their walkthrough, which is the honest version of the same page count.
|
|
30
|
+
|
|
31
|
+
## The shape
|
|
32
|
+
|
|
33
|
+
| Piece | What it owns |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `src/ui.ts` | The served HTML: branding, gated URLs, CSP-nonced script tags, the four page titles, and `buildUiData` — the `/ui/data` payload. |
|
|
36
|
+
| `src/operator-ui/model.ts` | The transport types both sides share, plus connector filtering. |
|
|
37
|
+
| `src/operator-ui/view.ts` | The app's state shape and every pure rule over it. No DOM, so `test/ui.test.ts` calls it directly. |
|
|
38
|
+
| `src/operator-ui/app/` | The browser app: `store.ts` (state and every request), `main.tsx` (shell, gate, router), and one component file per page. |
|
|
39
|
+
| `src/operator-ui/browser.css` | One stylesheet, inlined into the shell. |
|
|
40
|
+
| `src/operator-ui/generated.ts` | The build output: the bundle and the stylesheet as two exported strings. |
|
|
41
|
+
|
|
42
|
+
The server renders a mount point, not a page. Branding, the Clerk loader, and
|
|
43
|
+
every operator-configured URL stay in `src/ui.ts`, where they are gated before
|
|
44
|
+
they can become an attribute; the bundle renders everything that has a state.
|
|
45
|
+
Two roots share one store: `#operatorNav` and `#operatorContent`.
|
|
46
|
+
|
|
47
|
+
## Rules that are not obvious
|
|
48
|
+
|
|
49
|
+
- **No operator data in the shell.** Every page serves the same markup. Connector,
|
|
50
|
+
credential, token, and activity data arrives only through the authenticated
|
|
51
|
+
`/ui/*` APIs, and the shell is identical whether or not a caller is signed in.
|
|
52
|
+
- **One store, one identity.** `store.ts` is the only file that touches `fetch`,
|
|
53
|
+
`localStorage`, or Clerk. Every request carries the current session's token,
|
|
54
|
+
and every response is dropped unless the identity that asked for it is still
|
|
55
|
+
the one on screen. `resetIdentity` replaces all identity-scoped state at once
|
|
56
|
+
and bumps a generation that work already in flight compares itself against.
|
|
57
|
+
- **Escaping is structural.** Components return elements; nothing builds HTML
|
|
58
|
+
from strings. A value that could be a URL passes `safeHttpHref` before it may
|
|
59
|
+
become an `href`, mirroring the server-side gate in `src/ui.ts`.
|
|
60
|
+
- **Secrets are shown once.** A created access token lives in state only, and
|
|
61
|
+
leaving the page — by navigation or by `pagehide`, which covers the
|
|
62
|
+
back-forward cache — unmounts it.
|
|
63
|
+
- **Every flow has four states.** Loading, error, empty, and success, with no
|
|
64
|
+
dead end: a failed save keeps the form and its typed value, a failed list
|
|
65
|
+
offers a retry, and an empty collection says what would fill it. A mutation
|
|
66
|
+
that fails is still a resolved promise — `mutate` lands the failure in state
|
|
67
|
+
rather than rejecting — so a caller that clears a form must clear it on a
|
|
68
|
+
confirmed success, never on resolution. `createAccessToken` returns that
|
|
69
|
+
answer as a boolean for exactly this reason.
|
|
70
|
+
- **Drift is counts, and absence is its own answer.** The connector card reads
|
|
71
|
+
`catalogDrift` ([#343](https://github.com/zackbart/connecta/issues/343)) as
|
|
72
|
+
four category counts and a timestamp. There is no drill-down, because a tool
|
|
73
|
+
name or a schema here would make an operator page the payload surface the
|
|
74
|
+
drift model refuses to be. A connector with no report renders as *not
|
|
75
|
+
observed*, never as clean: this runtime having seen no refresh is not the
|
|
76
|
+
same claim as a refresh having found nothing.
|
|
77
|
+
|
|
78
|
+
## Working on it
|
|
79
|
+
|
|
80
|
+
Source changes require a rebuild: `npm run build:operator-ui` regenerates
|
|
81
|
+
`src/operator-ui/generated.ts`, and `npm run check:operator-ui` fails when the
|
|
82
|
+
committed artifact is stale. Both run through the same esbuild call, so the
|
|
83
|
+
check compares byte for byte.
|
|
84
|
+
|
|
85
|
+
Tests split along the DOM line, because `test/ui.test.ts` runs in workerd as
|
|
86
|
+
well as Node and there is no DOM in either:
|
|
87
|
+
|
|
88
|
+
- `test/ui.test.ts` — the server shell, the `/ui/*` routes, and the app's pure
|
|
89
|
+
state rules from `view.ts`.
|
|
90
|
+
- `test/operator-store.test.ts` — `store.ts` itself, against a fake browser: the
|
|
91
|
+
Clerk listener, `gate()`, the generation fence, and the request path. The
|
|
92
|
+
rules in `view.ts` prove what an identity change *erases*; this suite proves
|
|
93
|
+
something calls them when the identity actually changes. It typechecks in the
|
|
94
|
+
DOM-lib program (`tsconfig.operator-ui.json`) because it imports the store.
|
|
95
|
+
- `test/browser/operator-ui.spec.ts` — the wiring, in a real browser:
|
|
96
|
+
credential, token, and OAuth flows end to end, including their failure and
|
|
97
|
+
empty states. Run it with `npm run test:browser` (`npm run test:browser:install`
|
|
98
|
+
once, for Chromium). It is not part of `npm run check`.
|
|
99
|
+
|
|
100
|
+
## Why the bundle is committed
|
|
101
|
+
|
|
102
|
+
`src/operator-ui/generated.ts` is generated and checked in. The alternatives
|
|
103
|
+
were considered and lost:
|
|
104
|
+
|
|
105
|
+
- **Build during `prepack`.** The artifact would still have to exist before
|
|
106
|
+
`tsc` runs, so every contributor and every CI job would need the browser build
|
|
107
|
+
before typechecking — and a published tarball would carry a build output
|
|
108
|
+
nobody could diff against its source.
|
|
109
|
+
- **Exclude it from the tarball.** `dist/operator-ui/generated.js` is imported
|
|
110
|
+
by `src/ui.ts`; a deployment that installs the package needs it. Excluding it
|
|
111
|
+
ships a broken import.
|
|
112
|
+
|
|
113
|
+
Committing keeps one prebuilt string that Node and Workers read unchanged, with
|
|
114
|
+
no browser toolchain at install, pack, or deploy time. The cost is a large
|
|
115
|
+
generated diff on UI changes, paid deliberately: the bundle is not minified, so
|
|
116
|
+
what actually reaches an operator's page can be read in review, and
|
|
117
|
+
`check:operator-ui` is what keeps it honest.
|
|
118
|
+
|
|
119
|
+
Preact is a `devDependency` for the same reason. It is inlined into the bundle
|
|
120
|
+
at build time and never appears in a deployment's dependency tree — the package
|
|
121
|
+
has no runtime dependency on it, and `test/package-surface.test.ts` keeps the
|
|
122
|
+
published surface that way.
|