@zackbart/connecta 0.24.4 → 0.26.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/AGENTS.md +43 -12
- package/CHANGELOG.md +524 -0
- package/README.md +32 -15
- package/SECURITY.md +6 -5
- package/bin/connecta.mjs +5 -1
- package/dist/activity.d.ts +10 -3
- package/dist/activity.js +10 -1
- package/dist/advertised-schema.js +53 -0
- package/dist/artifacts/connector.d.ts +55 -0
- package/dist/artifacts/connector.js +724 -0
- package/dist/artifacts/document.js +76 -0
- package/dist/artifacts/guide.js +140 -0
- package/dist/artifacts/html-scan.js +329 -0
- package/dist/artifacts/json.js +68 -0
- package/dist/artifacts/kv-store.d.ts +20 -0
- package/dist/artifacts/kv-store.js +140 -0
- package/dist/artifacts/markdown.js +449 -0
- package/dist/artifacts/module.d.ts +37 -0
- package/dist/artifacts/module.js +60 -0
- package/dist/artifacts/operations.d.ts +195 -0
- package/dist/artifacts/operations.js +944 -0
- package/dist/artifacts/refresh.d.ts +53 -0
- package/dist/artifacts/refresh.js +235 -0
- package/dist/artifacts/routes.js +194 -0
- package/dist/artifacts/types.d.ts +214 -0
- package/dist/artifacts/types.js +22 -0
- package/dist/artifacts/validate.d.ts +62 -0
- package/dist/artifacts/validate.js +652 -0
- package/dist/artifacts.d.ts +6 -0
- package/dist/artifacts.js +9 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +152 -106
- package/dist/catalog.js +297 -38
- package/dist/concurrency.js +24 -23
- package/dist/connector-access.d.ts +18 -4
- package/dist/connector-access.js +65 -20
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +19 -0
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +2 -2
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +330 -240
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/errors.d.ts +16 -1
- package/dist/errors.js +28 -0
- package/dist/execute.js +745 -282
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +145 -63
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +255 -212
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +72 -7
- package/dist/index.js +281 -26
- package/dist/invocation.js +371 -286
- package/dist/meta-tools.js +213 -94
- package/dist/module-contracts.d.ts +21 -1
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +103 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +128 -3
- package/dist/program-source.js +321 -0
- package/dist/providers/notion.js +2 -2
- package/dist/providers/revenuecat.js +32 -5
- package/dist/providers/stripe.d.ts +3 -2
- package/dist/providers/vercel.js +1 -1
- package/dist/registry.d.ts +43 -7
- package/dist/registry.js +434 -288
- package/dist/resumable.js +1291 -0
- package/dist/routes/activity.js +74 -154
- package/dist/routes/actor-labels.js +114 -0
- package/dist/routes/credentials.js +196 -163
- package/dist/routes/mcp.js +228 -112
- package/dist/routes/oauth-management.js +73 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +83 -0
- package/dist/routes/shared.d.ts +25 -1
- package/dist/routes/shared.js +20 -2
- package/dist/routes/ui-data.js +211 -0
- package/dist/routes/ui.js +76 -35
- package/dist/run-journal.js +347 -0
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/shared-read.js +106 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +137 -89
- package/dist/skills.js +28 -9
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +39 -43
- package/dist/tool-safety.d.ts +27 -1
- package/dist/tool-safety.js +31 -0
- package/dist/types.d.ts +45 -7
- package/dist/ui.d.ts +25 -4
- package/dist/ui.js +77 -167
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +367 -16
- package/documentation/auth.md +163 -2
- package/documentation/code-mode.md +274 -56
- package/documentation/meta-tools.md +200 -44
- package/ethos.md +46 -47
- package/examples/worker/AGENTS.md +6 -0
- package/examples/worker/README.md +224 -14
- package/examples/worker/src/d1-activity-row.ts +14 -2
- package/examples/worker/src/d1-activity.ts +4 -2
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +42 -7
- package/examples/worker/src/r2-artifact-blobs.ts +29 -0
- package/examples/worker/wrangler.jsonc +21 -5
- package/package.json +16 -4
- package/templates/node/AGENTS.md +9 -2
- package/templates/node/README.md +31 -3
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +27 -0
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -96
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -188
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
"name": "connecta-example",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"compatibility_date": "2025-01-01",
|
|
6
|
-
"compatibility_flags": ["nodejs_compat"],
|
|
6
|
+
"compatibility_flags": ["nodejs_compat", "enable_request_signal"],
|
|
7
7
|
"observability": { "enabled": true },
|
|
8
8
|
|
|
9
|
+
// Uncomment with artifacts and D1 in src/index.ts. Every hourly tick runs
|
|
10
|
+
// at most 10 due pages; each page's own daily/weekly schedule lives in data.
|
|
11
|
+
// "triggers": { "crons": ["0 * * * *"] },
|
|
12
|
+
|
|
9
13
|
// Plain vars. Secrets (CREDENTIAL_ENCRYPTION_KEY, DOWNSTREAM_TOKEN) are set with
|
|
10
14
|
// `wrangler secret put <NAME>`, not here.
|
|
11
15
|
"vars": {
|
|
@@ -27,15 +31,27 @@
|
|
|
27
31
|
{ "binding": "CONNECTA_KV", "id": "replace-with-kv-namespace-id" }
|
|
28
32
|
],
|
|
29
33
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
+
// D1 databases, off by default so this example deploys without one.
|
|
35
|
+
// Uncomment the array and keep the entries you use.
|
|
36
|
+
//
|
|
37
|
+
// ACTIVITY_DB: activity history. Create with `wrangler d1 create
|
|
38
|
+
// connecta-activity`, paste the id, apply the schema in README.md, then
|
|
39
|
+
// uncomment the matching lines in src/index.ts.
|
|
40
|
+
//
|
|
41
|
+
// STORAGE_DB: strongly consistent storage with compare-and-set, in place of
|
|
42
|
+
// CONNECTA_KV. Create with `wrangler d1 create connecta-storage`, paste the
|
|
43
|
+
// id, apply the schema in README.md § "Strongly consistent storage", then
|
|
44
|
+
// pass `d1Storage(env.STORAGE_DB)` as `storage` in src/index.ts.
|
|
34
45
|
// "d1_databases": [
|
|
35
46
|
// {
|
|
36
47
|
// "binding": "ACTIVITY_DB",
|
|
37
48
|
// "database_name": "connecta-activity",
|
|
38
49
|
// "database_id": "replace-with-d1-database-id"
|
|
50
|
+
// },
|
|
51
|
+
// {
|
|
52
|
+
// "binding": "STORAGE_DB",
|
|
53
|
+
// "database_name": "connecta-storage",
|
|
54
|
+
// "database_id": "replace-with-d1-database-id"
|
|
39
55
|
// }
|
|
40
56
|
// ],
|
|
41
57
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
|
-
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of
|
|
6
|
+
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of eight meta-tools.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=22.0.0"
|
|
@@ -110,13 +110,17 @@
|
|
|
110
110
|
"./auth/bearer": {
|
|
111
111
|
"types": "./dist/auth/bearer.d.ts",
|
|
112
112
|
"import": "./dist/auth/bearer.js"
|
|
113
|
+
},
|
|
114
|
+
"./artifacts": {
|
|
115
|
+
"types": "./dist/artifacts.d.ts",
|
|
116
|
+
"import": "./dist/artifacts.js"
|
|
113
117
|
}
|
|
114
118
|
},
|
|
115
119
|
"scripts": {
|
|
116
120
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
117
121
|
"build:operator-ui": "node scripts/build-operator-ui.mjs",
|
|
118
122
|
"check:operator-ui": "node scripts/build-operator-ui.mjs --check",
|
|
119
|
-
"build": "npm run build:operator-ui && npm run clean && tsc -p tsconfig.build.json",
|
|
123
|
+
"build": "npm run build:operator-ui && npm run clean && tsc -p tsconfig.build.json && node scripts/prune-declarations.mjs",
|
|
120
124
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.operator-ui.json --noEmit",
|
|
121
125
|
"test": "vitest run",
|
|
122
126
|
"test:browser:install": "playwright install chromium",
|
|
@@ -129,9 +133,16 @@
|
|
|
129
133
|
"check:examples": "tsc -p tsconfig.template.node.json && tsc -p tsconfig.examples.worker.json",
|
|
130
134
|
"check:security": "npm audit --omit=dev --audit-level=moderate",
|
|
131
135
|
"check:package": "node scripts/check-package.mjs",
|
|
136
|
+
"check:declarations": "node scripts/check-declarations.mjs --dist .",
|
|
137
|
+
"check:bundle": "node scripts/bundle-size.mjs",
|
|
132
138
|
"providers:check": "tsx scripts/drift-check.mjs --specs --docs",
|
|
133
139
|
"drift:check": "tsx scripts/drift-check.mjs",
|
|
134
|
-
"
|
|
140
|
+
"eval:selftest": "tsx eval/selftest.ts",
|
|
141
|
+
"eval:agent": "tsx eval/run-agent.ts",
|
|
142
|
+
"eval:smoke": "tsx eval/run-smoke.ts",
|
|
143
|
+
"eval:perf": "tsx eval/run-perf.ts",
|
|
144
|
+
"eval:report": "tsx eval/report.ts",
|
|
145
|
+
"check": "npm run check:docs && npm run check:operator-ui && npm run check:lint && npm run check:unused && npm run typecheck && npm run test && npm run test:browser && npm run build && npm run check:declarations && npm run check:bundle && npm run check:examples",
|
|
135
146
|
"release:check": "npm run check && npm run check:security && npm run check:package",
|
|
136
147
|
"prepack": "npm run check"
|
|
137
148
|
},
|
|
@@ -139,6 +150,7 @@
|
|
|
139
150
|
"@cfworker/json-schema": "^4.1.1",
|
|
140
151
|
"@modelcontextprotocol/client": "2.0.0",
|
|
141
152
|
"@modelcontextprotocol/server": "2.0.0",
|
|
153
|
+
"effect": "4.0.0-rc.117",
|
|
142
154
|
"zod": "^4.4.3"
|
|
143
155
|
},
|
|
144
156
|
"peerDependencies": {
|
package/templates/node/AGENTS.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
This repository is deployment configuration, not a copy of Connecta itself.
|
|
4
4
|
|
|
5
5
|
- Edit `src/index.ts` for connectors, authentication, storage, and public URL.
|
|
6
|
-
- Keep `executor: quickJsExecutor()` for the prescribed
|
|
7
|
-
surface.
|
|
6
|
+
- Keep `executor: quickJsExecutor()` for the prescribed eight-tool code-first
|
|
7
|
+
surface. File storage supports resumable writes, so a program pauses at a
|
|
8
|
+
write until `resume_execution` approves it.
|
|
8
9
|
- Keep credentials in environment variables or an external secret store.
|
|
9
10
|
Never commit `.env`, `.connecta-state.json`, `.connecta-activity.jsonl`,
|
|
10
11
|
tokens, or credential values.
|
|
@@ -19,6 +20,12 @@ This repository is deployment configuration, not a copy of Connecta itself.
|
|
|
19
20
|
alone never grants it. Configured bearer auth is a client option, not a human
|
|
20
21
|
management identity. Connecta-issued access tokens are removed.
|
|
21
22
|
`src/file-activity.ts` remains the deployment-owned history store.
|
|
23
|
+
- Brand the operator UI in `operatorUi({ branding })`: product and owner names,
|
|
24
|
+
description, favicon, and `theme` (`accent`, `radius`, `fontFamily`,
|
|
25
|
+
`monoFamily`, `colorScheme`). Ask the deployment's owner for their brand
|
|
26
|
+
rather than leaving the default. The commented block above
|
|
27
|
+
`ui: operatorUi()` in `src/index.ts` shows the shape; README "Select optional
|
|
28
|
+
modules" covers the rest.
|
|
22
29
|
- Run `npm run typecheck` after configuration changes. With the server running,
|
|
23
30
|
run `CONNECTA_TOKEN=... npm run doctor` before calling setup complete.
|
|
24
31
|
- `Dockerfile` and `docker-compose.yml` containerize *this* source; they are
|
package/templates/node/README.md
CHANGED
|
@@ -46,11 +46,32 @@ reproducible `npm ci` path.
|
|
|
46
46
|
|
|
47
47
|
## Select optional modules
|
|
48
48
|
|
|
49
|
+
For scheduled artifact pages, uncomment the `artifacts()` module and hourly
|
|
50
|
+
`setInterval` block in `src/index.ts`. The module uses the same CAS-capable
|
|
51
|
+
state file and exposes `runDue()` for the timer; core starts no job on its own.
|
|
52
|
+
Each tick starts at most 10 due pages. Refresh programs can call only shared
|
|
53
|
+
connectors' explicitly read-only tools within the program owner's current
|
|
54
|
+
grants. Revoking refresh or pool access stops future runs. Failed runs leave the last good data
|
|
55
|
+
and mark the viewer stale.
|
|
56
|
+
|
|
49
57
|
The template explicitly enables `ui: operatorUi()` from
|
|
50
58
|
`@zackbart/connecta/ui`. Open `http://localhost:8787/` and supply the configured
|
|
51
59
|
bearer to inspect Connections. Omit that option and import for an API-only
|
|
52
60
|
server. OAuth callbacks remain in core even with no UI.
|
|
53
61
|
|
|
62
|
+
The page labels whether each tool runs in programs or needs approval. For
|
|
63
|
+
classified connection failures it offers a fixed repair prompt, and its
|
|
64
|
+
endpoint section has client setup commands for `/mcp` and any pool available
|
|
65
|
+
to the signed-in identity. Those commands contain no bearer token.
|
|
66
|
+
|
|
67
|
+
Branding belongs in `operatorUi({ branding })`: `productName`, `ownerName`,
|
|
68
|
+
their URLs, `description`, `pageTitle`, `favicon`, and `theme`. The theme is
|
|
69
|
+
five tokens — `accent`, `radius`, `fontFamily`, `monoFamily`, and
|
|
70
|
+
`colorScheme` — and every other color is mixed from them, so one accent themes
|
|
71
|
+
the whole page. A value that fails its gate falls back to the default, and the
|
|
72
|
+
startup warning names it. `src/index.ts` carries the full shape commented out
|
|
73
|
+
above `ui: operatorUi()`.
|
|
74
|
+
|
|
54
75
|
Connection management needs an interactive identity. A configured bearer is a
|
|
55
76
|
client key and never authorizes browser credential mutations. To enable Clerk:
|
|
56
77
|
|
|
@@ -89,6 +110,12 @@ it through `await ctx.credential?.get()`, or use a provider such as `notion()`
|
|
|
89
110
|
that declares its own slot. Authorized humans manage the slot inside that
|
|
90
111
|
connection on `/`; there is no separate Credentials tab.
|
|
91
112
|
|
|
113
|
+
In 0.25.0, this vault also seals downstream OAuth tokens on their first read.
|
|
114
|
+
That is a one-way state migration for those tokens: rolling back to 0.24.x
|
|
115
|
+
requires authorizing each OAuth connector again. See the
|
|
116
|
+
[0.25.0 changelog](../../CHANGELOG.md#0250--2026-09-23) before upgrading a
|
|
117
|
+
deployment with existing OAuth connections.
|
|
118
|
+
|
|
92
119
|
A saved replacement takes effect on the next call. Connecta tests credentials
|
|
93
120
|
only on an explicit action and otherwise fails at use. Without a vault or UI,
|
|
94
121
|
static credential recovery reports unavailable instead of offering a dead link.
|
|
@@ -132,16 +159,17 @@ npm run typecheck
|
|
|
132
159
|
CONNECTA_TOKEN=dev-token npm run doctor
|
|
133
160
|
```
|
|
134
161
|
|
|
135
|
-
Doctor checks health, the executor, and the exact prescribed
|
|
162
|
+
Doctor checks health, the executor, and the exact prescribed eight-tool
|
|
136
163
|
model-facing surface by running a harmless sandbox program. It reads the bearer
|
|
137
164
|
from `CONNECTA_TOKEN`; it never accepts the secret as a command-line argument.
|
|
138
165
|
Remote URLs must use HTTPS.
|
|
139
166
|
|
|
140
167
|
A fully-wired deployment reports exactly the same line as a bare one —
|
|
141
|
-
connector count, QuickJS executed,
|
|
168
|
+
connector count, QuickJS executed, eight tools, plus any catalog drift, and
|
|
169
|
+
a note if resumable writes are off:
|
|
142
170
|
|
|
143
171
|
```text
|
|
144
|
-
Connecta doctor passed: 1 connector(s), QuickJS executed, prescribed
|
|
172
|
+
Connecta doctor passed: 1 connector(s), QuickJS executed, prescribed eight-tool surface.
|
|
145
173
|
```
|
|
146
174
|
|
|
147
175
|
`QuickJS` is this deployment's sandbox, reported by the deployment itself —
|
|
@@ -27,6 +27,7 @@ import { operatorUi } from "@zackbart/connecta/ui";
|
|
|
27
27
|
import { api, createConnecta } from "@zackbart/connecta";
|
|
28
28
|
import { fileStorage, listen } from "@zackbart/connecta/node";
|
|
29
29
|
import { quickJsExecutor } from "@zackbart/connecta/quickjs";
|
|
30
|
+
// import { artifacts, kvArtifactStore } from "@zackbart/connecta/artifacts";
|
|
30
31
|
// Operator sign-in. Needs `npm install @clerk/backend` — it is an optional
|
|
31
32
|
// peer, so it does not install with Connecta.
|
|
32
33
|
// import { clerkAuth } from "@zackbart/connecta/auth/clerk";
|
|
@@ -45,6 +46,8 @@ const port = Number(process.env.PORT ?? 8787);
|
|
|
45
46
|
const stateFile = process.env.CONNECTA_STATE_FILE || "./.connecta-state.json";
|
|
46
47
|
const publicUrl = process.env.PUBLIC_URL || `http://localhost:${port}`;
|
|
47
48
|
const storage = fileStorage(stateFile);
|
|
49
|
+
// Optional artifact pages and refresh jobs use the same CAS-capable state file:
|
|
50
|
+
// const artifactModule = artifacts({ store: kvArtifactStore(storage) });
|
|
48
51
|
|
|
49
52
|
// Operator sign-in. A bearer token is a client key: it may call tools and read
|
|
50
53
|
// connector status, but only a Clerk-authenticated human may write a visible
|
|
@@ -103,7 +106,25 @@ const connecta = createConnecta({
|
|
|
103
106
|
// ),
|
|
104
107
|
// deploymentId: "production",
|
|
105
108
|
// }),
|
|
109
|
+
// The operator UI at /. Its branding is code, like everything else here:
|
|
110
|
+
// name, owner, description, favicon, and five theme tokens that every
|
|
111
|
+
// other color is mixed from. A value that fails its check falls back to the
|
|
112
|
+
// default, and the startup warning names it.
|
|
113
|
+
// ui: operatorUi({
|
|
114
|
+
// branding: {
|
|
115
|
+
// productName: "Acme Tools",
|
|
116
|
+
// ownerName: "Acme",
|
|
117
|
+
// theme: {
|
|
118
|
+
// accent: "#2f5fe0", // hex only
|
|
119
|
+
// radius: 10, // pixels, or a CSS length such as "0.5rem"
|
|
120
|
+
// fontFamily: "Inter, system-ui, sans-serif",
|
|
121
|
+
// monoFamily: "ui-monospace, monospace",
|
|
122
|
+
// colorScheme: "system", // or "light" | "dark"
|
|
123
|
+
// },
|
|
124
|
+
// },
|
|
125
|
+
// }),
|
|
106
126
|
ui: operatorUi(),
|
|
127
|
+
// artifacts: artifactModule,
|
|
107
128
|
connectors: [
|
|
108
129
|
api("time", {
|
|
109
130
|
description: "Time — current timestamp",
|
|
@@ -126,3 +147,9 @@ const connecta = createConnecta({
|
|
|
126
147
|
|
|
127
148
|
listen(connecta, port);
|
|
128
149
|
console.log(`connecta listening on port ${port}; MCP at ${publicUrl}/mcp`);
|
|
150
|
+
// The deployment owns this timer; core starts no background work. Uncomment
|
|
151
|
+
// with artifactModule above. Each tick starts at most 10 due pages.
|
|
152
|
+
// const refreshTimer = setInterval(() => {
|
|
153
|
+
// void artifactModule.runDue().catch((error) => console.error("artifact refresh failed", error));
|
|
154
|
+
// }, 60 * 60 * 1000);
|
|
155
|
+
// refreshTimer.unref();
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import type { FetchLike, OAuthClientInformationContext, OAuthClientInformationMixed, OAuthClientMetadata, OAuthClientProvider, OAuthDiscoveryState, OAuthTokens } from "@modelcontextprotocol/client";
|
|
2
|
-
import type { KVStorage } from "../types.js";
|
|
3
|
-
type OAuthRefreshFlightOutcome = {
|
|
4
|
-
status: "refreshed";
|
|
5
|
-
} | {
|
|
6
|
-
status: "retired";
|
|
7
|
-
} | {
|
|
8
|
-
status: "failed";
|
|
9
|
-
error: unknown;
|
|
10
|
-
};
|
|
11
|
-
interface OAuthRefreshFlight {
|
|
12
|
-
done: Promise<OAuthRefreshFlightOutcome>;
|
|
13
|
-
release: (outcome: OAuthRefreshFlightOutcome) => void;
|
|
14
|
-
stopObservingOwnerAbort: () => void;
|
|
15
|
-
mutationId: object;
|
|
16
|
-
writing: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Set once the token endpoint answered with valid tokens: the authorization
|
|
19
|
-
* server has consumed the rotating refresh token, so if the SDK's own
|
|
20
|
-
* saveTokens never arrives (owner cancelled, redirected, or invalidated),
|
|
21
|
-
* `fail` persists this copy itself rather than stranding the marker or
|
|
22
|
-
* letting a contender redeem the retired token.
|
|
23
|
-
*/
|
|
24
|
-
acceptedTokens?: OAuthTokens;
|
|
25
|
-
persist?: (tokens: OAuthTokens) => Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Share one rotating-token redemption within one connector runtime and OAuth
|
|
29
|
-
* generation. The first request still owns the real fetch and response. Its
|
|
30
|
-
* abort signal has one bounded listener until the exact flight settles, so a
|
|
31
|
-
* cancellation after the response cannot strand waiters during token storage.
|
|
32
|
-
* Followers wait for that provider to save tokens, then re-read storage. The
|
|
33
|
-
* map never retains a token response or transport.
|
|
34
|
-
*
|
|
35
|
-
* This is intentionally runtime-local. KVStorage has no atomic coordination
|
|
36
|
-
* operation, so a second isolate can still race the same refresh token.
|
|
37
|
-
*/
|
|
38
|
-
export declare class OAuthRefreshCoordinator {
|
|
39
|
-
private readonly flights;
|
|
40
|
-
/** Opaque identities only: no request promise, signal, callback, or response. */
|
|
41
|
-
private readonly pendingMutations;
|
|
42
|
-
/** One bounded latest-success slot, containing only generation + identity. */
|
|
43
|
-
private successfulRefresh;
|
|
44
|
-
/** Replaced on every map mutation, closing flight/pending ABA across awaits. */
|
|
45
|
-
private stateRevision;
|
|
46
|
-
private advanceStateRevision;
|
|
47
|
-
private observeAuthoritativeGeneration;
|
|
48
|
-
private settle;
|
|
49
|
-
private markMutationPending;
|
|
50
|
-
private finishMutation;
|
|
51
|
-
/** @internal Opaque basis for issuer-aware provider token reads. */
|
|
52
|
-
successfulRefreshIdentity(generation: string): object | undefined;
|
|
53
|
-
coordinatedFetch(provider: KvOAuthProvider, baseFetch: FetchLike, requestSignal?: AbortSignal): FetchLike;
|
|
54
|
-
/** Start storage only while this owner still holds its exact flight. */
|
|
55
|
-
beginMutation(generation: string, flight: OAuthRefreshFlight): boolean;
|
|
56
|
-
/** Publish one exact owner's successful save without disturbing a newer try. */
|
|
57
|
-
succeedMutation(generation: string, flight: OAuthRefreshFlight): void;
|
|
58
|
-
/** Give joined callers a fetch/flow failure, without rejecting the gate. */
|
|
59
|
-
fail(generation: string, flight: OAuthRefreshFlight, error: unknown): void;
|
|
60
|
-
/** Finish an exact failed credential write, then publish its failure. */
|
|
61
|
-
failMutation(generation: string, flight: OAuthRefreshFlight, error: unknown): void;
|
|
62
|
-
/** Force reauthorization fences and wakes every waiter on the retired epoch. */
|
|
63
|
-
retire(generation: string): void;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Physical key for an OAuth value in one authorization epoch. Legacy values
|
|
67
|
-
* keep their historical names so upgrades can read an existing grant. Modern
|
|
68
|
-
* values get an epoch-specific namespace: a stale write or delete can then
|
|
69
|
-
* affect only its own flow, even if it lands after a replacement flow.
|
|
70
|
-
*/
|
|
71
|
-
export declare function oauthValueStorageKey(key: string, generation: string | null): string;
|
|
72
|
-
/**
|
|
73
|
-
* OAuthClientProvider implemented over KVStorage for a single downstream
|
|
74
|
-
* connector. Keys live in the connector's namespace as `oauth:<field>`.
|
|
75
|
-
*
|
|
76
|
-
* Headless twist: redirectToAuthorization() cannot navigate a user agent, so it
|
|
77
|
-
* STORES the authorization URL; the registry surfaces it as status
|
|
78
|
-
* "auth_required" and the operator opens it. The /oauth/callback/<id> route then
|
|
79
|
-
* drives transport.finishAuth(code).
|
|
80
|
-
*/
|
|
81
|
-
export declare class KvOAuthProvider implements OAuthClientProvider {
|
|
82
|
-
private readonly connectorId;
|
|
83
|
-
private readonly storage;
|
|
84
|
-
private readonly redirectUri;
|
|
85
|
-
private readonly refreshCoordinator?;
|
|
86
|
-
private readonly allowAuthorization;
|
|
87
|
-
/**
|
|
88
|
-
* The reset generation this provider's flow started under. Every OAuth value
|
|
89
|
-
* it writes carries this epoch, so a late write can land after a reset without
|
|
90
|
-
* becoming readable under the new generation.
|
|
91
|
-
*/
|
|
92
|
-
private capturedGeneration;
|
|
93
|
-
private refreshFlight;
|
|
94
|
-
/** Tokens this request's issuer-aware auth flow decided to refresh. */
|
|
95
|
-
private refreshBasis;
|
|
96
|
-
constructor(connectorId: string, storage: KVStorage, redirectUri: string, refreshCoordinator?: OAuthRefreshCoordinator | undefined, allowAuthorization?: boolean);
|
|
97
|
-
/**
|
|
98
|
-
* Stamp the force-reauth generation the current connect flow started under.
|
|
99
|
-
* Called by the connector once per connect, before c.connect(). The callback
|
|
100
|
-
* path captures the generation stored beside its verified state instead.
|
|
101
|
-
*/
|
|
102
|
-
captureGeneration(gen: string): void;
|
|
103
|
-
/** The generation captured for this flow, before a concurrent reset. */
|
|
104
|
-
flowGeneration(): Promise<string>;
|
|
105
|
-
/** @internal Record the refresh attempt this provider owns. */
|
|
106
|
-
captureRefreshFlight(generation: string, flight: OAuthRefreshFlight): void;
|
|
107
|
-
private failRefreshFlight;
|
|
108
|
-
/** True when another request saved a refresh result after this flow's read. */
|
|
109
|
-
refreshBasisChanged(current: OAuthTokens, generation: string): boolean;
|
|
110
|
-
/**
|
|
111
|
-
* The epoch this provider writes under. Direct unit/custom use lazily captures
|
|
112
|
-
* the current generation; connector-driven connect and callback paths stamp it
|
|
113
|
-
* explicitly before the SDK can write.
|
|
114
|
-
*/
|
|
115
|
-
private writeGeneration;
|
|
116
|
-
/**
|
|
117
|
-
* Store a value in the flow's physical epoch namespace. The pre-write check
|
|
118
|
-
* avoids needless stale residue. The namespaced key closes the remaining
|
|
119
|
-
* check-then-write race: a late old write cannot overwrite a replacement
|
|
120
|
-
* flow's value because the two writes have different physical keys.
|
|
121
|
-
*/
|
|
122
|
-
private writeValue;
|
|
123
|
-
/**
|
|
124
|
-
* Read a value only when it belongs to the active generation. Plain legacy
|
|
125
|
-
* values remain readable until the first v2 reset, so upgrades do not discard
|
|
126
|
-
* an existing grant; once a modern epoch exists, untagged residue fails
|
|
127
|
-
* closed.
|
|
128
|
-
*/
|
|
129
|
-
private readValue;
|
|
130
|
-
/**
|
|
131
|
-
* Read credentials only for the authorization server that issued them.
|
|
132
|
-
*
|
|
133
|
-
* Values written before issuer binding are bound on their first read with a
|
|
134
|
-
* validated SDK issuer, preserving an existing grant across this upgrade.
|
|
135
|
-
* A later issuer change is different: publish a new generation before
|
|
136
|
-
* returning no credentials, so every isolate drops the old registration and
|
|
137
|
-
* token set and the SDK starts authorization from scratch.
|
|
138
|
-
*/
|
|
139
|
-
private readIssuerBoundValue;
|
|
140
|
-
/** Attempt every key deletion, then report the first backend failure. */
|
|
141
|
-
private deleteAll;
|
|
142
|
-
private cleanupBacklog;
|
|
143
|
-
private rememberRetiredGeneration;
|
|
144
|
-
private valueKeysForGeneration;
|
|
145
|
-
get redirectUrl(): string;
|
|
146
|
-
get clientMetadata(): OAuthClientMetadata;
|
|
147
|
-
clientInformation(ctx?: OAuthClientInformationContext): Promise<OAuthClientInformationMixed | undefined>;
|
|
148
|
-
saveClientInformation(info: OAuthClientInformationMixed, ctx?: OAuthClientInformationContext): Promise<void>;
|
|
149
|
-
discoveryState(): Promise<OAuthDiscoveryState | undefined>;
|
|
150
|
-
saveDiscoveryState(state: OAuthDiscoveryState): Promise<void>;
|
|
151
|
-
tokens(ctx?: OAuthClientInformationContext): Promise<OAuthTokens | undefined>;
|
|
152
|
-
saveTokens(tokens: OAuthTokens, ctx?: OAuthClientInformationContext): Promise<void>;
|
|
153
|
-
/**
|
|
154
|
-
* OAuth `state`. The SDK calls this (when present) and appends the value to
|
|
155
|
-
* the authorization URL. We generate a fresh random value and persist it so
|
|
156
|
-
* the public /oauth/callback route can prove the callback belongs to a flow
|
|
157
|
-
* WE started. Without it, anyone holding a pending authorization URL could
|
|
158
|
-
* complete consent with their own account (login CSRF) — PKCE does not help,
|
|
159
|
-
* since the verifier belongs to connecta, not the attacker. Cleared in
|
|
160
|
-
* clearPending() once the flow completes.
|
|
161
|
-
*/
|
|
162
|
-
state(): Promise<string>;
|
|
163
|
-
/**
|
|
164
|
-
* Constant-time check of a callback's `state` against the stored one-shot
|
|
165
|
-
* value. Absent stored state or absent candidate → false (fail closed).
|
|
166
|
-
*/
|
|
167
|
-
verifyState(candidate: string | null): Promise<boolean>;
|
|
168
|
-
saveCodeVerifier(verifier: string): Promise<void>;
|
|
169
|
-
codeVerifier(): Promise<string>;
|
|
170
|
-
redirectToAuthorization(authorizationUrl: URL): Promise<void>;
|
|
171
|
-
/** The stored authorization URL, if a flow is pending. */
|
|
172
|
-
pendingAuthorizationUrl(): Promise<string | undefined>;
|
|
173
|
-
/** Clear one-shot flow state after the callback completes. */
|
|
174
|
-
clearPending(): Promise<void>;
|
|
175
|
-
/**
|
|
176
|
-
* Force-reauth epoch shared across isolates through storage. Old numeric
|
|
177
|
-
* generations remain valid strings for migration; new resets use unique
|
|
178
|
-
* nonces, avoiding the lost-update race of read/increment/write.
|
|
179
|
-
*/
|
|
180
|
-
generation(): Promise<string>;
|
|
181
|
-
/** True only after an operator disconnect, until an explicit authorization starts. */
|
|
182
|
-
operatorDisconnected(): Promise<boolean>;
|
|
183
|
-
/** Interpret a generation already read by a connector without another KV lookup. */
|
|
184
|
-
isOperatorDisconnectedGeneration(generation: string): boolean;
|
|
185
|
-
/** Publish a unique active epoch without a read/modify/write race. */
|
|
186
|
-
bumpGeneration(): Promise<string>;
|
|
187
|
-
/**
|
|
188
|
-
* Fence every flow that could still write, then remove all durable and
|
|
189
|
-
* one-shot authorization state. The generation is intentionally retained:
|
|
190
|
-
* it is the epoch fence that tells another isolate not to resurrect
|
|
191
|
-
* credentials it read before this reset.
|
|
192
|
-
*
|
|
193
|
-
* Once the fence is durable, attempt every deletion even if one fails. A
|
|
194
|
-
* partial backend outage should not leave unrelated secrets behind merely
|
|
195
|
-
* because an earlier key happened to be the first failed delete.
|
|
196
|
-
*/
|
|
197
|
-
resetAuthorization(operatorDisconnected?: boolean): Promise<void>;
|
|
198
|
-
invalidateCredentials(scope: "all" | "client" | "tokens" | "verifier" | "discovery"): Promise<void>;
|
|
199
|
-
}
|
|
200
|
-
export {};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { ToolDef } from "./types.js";
|
|
2
|
-
export interface CatalogSnapshot {
|
|
3
|
-
tools: ToolDef[];
|
|
4
|
-
/** SHA-256 over the exact serialized tool array, prefixed with its size. */
|
|
5
|
-
fingerprint: string;
|
|
6
|
-
/** UTF-8 form reused by hashing, size enforcement, and chunking. */
|
|
7
|
-
serializedBytes: Uint8Array;
|
|
8
|
-
}
|
|
9
|
-
/** Fingerprint an already serialized catalog without parsing/reserializing it. */
|
|
10
|
-
export declare function fingerprintSerializedCatalog(serializedTools: string): Promise<{
|
|
11
|
-
fingerprint: string;
|
|
12
|
-
byteLength: number;
|
|
13
|
-
}>;
|
|
14
|
-
/**
|
|
15
|
-
* Capture every persisted/discovered field in one deterministic snapshot.
|
|
16
|
-
* Object key order is allowed to cause a conservative extra write; changing
|
|
17
|
-
* any serialized field must change the digest.
|
|
18
|
-
*/
|
|
19
|
-
export declare function snapshotCatalog(tools: ToolDef[]): Promise<CatalogSnapshot>;
|
package/dist/catalog-limits.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Complete-catalog ceiling shared by dynamically listed connector catalogs. */
|
|
2
|
-
export declare const MAX_CATALOG_TOOLS = 100000;
|
|
3
|
-
/**
|
|
4
|
-
* Large enough to cross a single Workers KV value boundary while keeping the
|
|
5
|
-
* serialized form at one quarter of a 128 MiB Worker heap.
|
|
6
|
-
*/
|
|
7
|
-
export declare const MAX_SERIALIZED_CATALOG_BYTES: number;
|
|
8
|
-
/** Persisted values stay comfortably below platform per-value limits. */
|
|
9
|
-
export declare const MAX_CATALOG_CHUNK_BYTES: number;
|