@zackbart/connecta 0.28.0 → 0.29.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 +961 -5
- package/README.md +62 -39
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1632 -1509
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +526 -0
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +220 -0
- package/dist/connectors/api-connector.js +214 -0
- package/dist/connectors/api.d.ts +8 -71
- package/dist/connectors/api.js +13 -99
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +108 -25
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1780 -351
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +25 -257
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-partition.js +17 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/basecamp/index.d.ts +43 -0
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/breeze/index.d.ts +34 -0
- package/dist/providers/breeze/index.js +1446 -0
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/ccb/index.d.ts +103 -0
- package/dist/providers/ccb/index.js +1725 -0
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/overflow/index.d.ts +60 -0
- package/dist/providers/overflow/index.js +1535 -0
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/planning-center/index.d.ts +59 -0
- package/dist/providers/planning-center/index.js +2199 -0
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/tithely/index.d.ts +49 -0
- package/dist/providers/tithely/index.js +1003 -0
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +136 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +665 -230
- package/documentation/auth.md +962 -458
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +125 -35
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Bindings for the one-shot Workers KV → D1 copy in scripts/copy-kv-to-d1.mjs.
|
|
2
|
+
// Never deployed: `wrangler deploy` reads wrangler.jsonc, which binds no KV.
|
|
3
|
+
// Both bindings are remote, so the script reads the real namespace and writes
|
|
4
|
+
// the real database through your `wrangler login` session. Fill in the ids,
|
|
5
|
+
// stop traffic/writers, wait for stable KV, copy and verify before deploying,
|
|
6
|
+
// then mark cutover before reopening traffic. README.md § "Upgrading from
|
|
7
|
+
// 0.28" has the steps.
|
|
8
|
+
{
|
|
9
|
+
"name": "connecta-kv-to-d1",
|
|
10
|
+
"compatibility_date": "2025-01-01",
|
|
11
|
+
"kv_namespaces": [
|
|
12
|
+
{
|
|
13
|
+
"binding": "CONNECTA_KV",
|
|
14
|
+
"id": "replace-with-kv-namespace-id",
|
|
15
|
+
"remote": true,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
"d1_databases": [
|
|
19
|
+
{
|
|
20
|
+
"binding": "CONNECTA_DB",
|
|
21
|
+
"database_name": "connecta",
|
|
22
|
+
"database_id": "replace-with-d1-database-id",
|
|
23
|
+
"remote": true,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Run only during the maintenance-window cutover. See README "Upgrading from 0.28".
|
|
3
|
+
|
|
4
|
+
export const HELP = `Usage: node scripts/copy-kv-to-d1.mjs [config] --maintenance [options]
|
|
5
|
+
Config defaults to kv-to-d1.wrangler.jsonc; source is its CONNECTA_KV namespace id.
|
|
6
|
+
1. Bulk-export KV and export D1 with wrangler d1 export. Preserve the credentials key.
|
|
7
|
+
2. Block traffic and all background writers, then drain in-flight requests.
|
|
8
|
+
3. Wait at least 60 seconds, or the longest configured KV cacheTtl, whichever is
|
|
9
|
+
greater. Propagation can take longer. Two consecutive listing/hash passes
|
|
10
|
+
must match; this script checks them before copying or verifying.
|
|
11
|
+
4. Copy BEFORE switching traffic. Resolve invalid/conflict counts under maintenance.
|
|
12
|
+
--overwrite-family <family> may be repeated; there is no blanket overwrite.
|
|
13
|
+
Token/OAuth/vault families also require --confirm-stale-d1 after the operator
|
|
14
|
+
confirms that D1's conflicting rows are stale. Run --verify for exact value
|
|
15
|
+
hashes and expiries; it reports mismatch counts by family, never payloads.
|
|
16
|
+
5. Deploy D1 config under maintenance. Run doctor and check an existing OAuth
|
|
17
|
+
connector, cta_ token, vault credential, and visible activity. Set
|
|
18
|
+
CONNECTA_ACTIVITY="on"; config lives in src/connecta.config.ts.
|
|
19
|
+
6. Run --mark-live before reopening traffic. It permanently seals this source
|
|
20
|
+
in D1. Never rerun afterward. --i-know-this-is-stale overrides the seal only
|
|
21
|
+
after assessing stale KV state, with traffic and writers stopped again.
|
|
22
|
+
Keep backups before deleting KV.
|
|
23
|
+
Options: --verify, --mark-live, --overwrite-family <family>, --confirm-stale-d1,
|
|
24
|
+
--i-know-this-is-stale, --maintenance, --help`;
|
|
25
|
+
|
|
26
|
+
export class MigrationUsageError extends Error {}
|
|
27
|
+
|
|
28
|
+
export function parseArgs(args) {
|
|
29
|
+
const options = { configPath: "kv-to-d1.wrangler.jsonc", overwriteFamilies: [] };
|
|
30
|
+
let hasConfig = false;
|
|
31
|
+
for (let i = 0; i < args.length; i++) {
|
|
32
|
+
const arg = args[i];
|
|
33
|
+
if (arg === "--overwrite-family") {
|
|
34
|
+
const family = args[++i];
|
|
35
|
+
if (!family || family.startsWith("--")) throw new MigrationUsageError("--overwrite-family requires a family");
|
|
36
|
+
options.overwriteFamilies.push(family);
|
|
37
|
+
} else if (arg === "--maintenance") options.maintenance = true;
|
|
38
|
+
else if (arg === "--verify") options.verify = true;
|
|
39
|
+
else if (arg === "--mark-live") options.markLive = true;
|
|
40
|
+
else if (arg === "--confirm-stale-d1") options.confirmStale = true;
|
|
41
|
+
else if (arg === "--i-know-this-is-stale") options.allowStale = true;
|
|
42
|
+
else if (arg === "--help") options.help = true;
|
|
43
|
+
else if (!arg.startsWith("--") && !hasConfig) {
|
|
44
|
+
options.configPath = arg;
|
|
45
|
+
hasConfig = true;
|
|
46
|
+
} else throw new MigrationUsageError("Unknown argument; use --help");
|
|
47
|
+
}
|
|
48
|
+
if (options.help) return options;
|
|
49
|
+
if (!options.maintenance)
|
|
50
|
+
throw new MigrationUsageError(
|
|
51
|
+
"Traffic and writers must be stopped; pass --maintenance after draining and waiting for KV propagation",
|
|
52
|
+
);
|
|
53
|
+
if (
|
|
54
|
+
options.overwriteFamilies.some(
|
|
55
|
+
(family) => family === "access-token" || family === "credential" || family.startsWith("oauth"),
|
|
56
|
+
) &&
|
|
57
|
+
!options.confirmStale
|
|
58
|
+
) {
|
|
59
|
+
throw new MigrationUsageError(
|
|
60
|
+
"Token/OAuth/vault overwrite requires --confirm-stale-d1 after confirming D1 rows are stale",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if ((options.verify || options.markLive) && options.overwriteFamilies.length)
|
|
64
|
+
throw new MigrationUsageError("Verification and cutover marking cannot overwrite families");
|
|
65
|
+
if (options.verify && options.markLive)
|
|
66
|
+
throw new MigrationUsageError("Verify before marking cutover in a separate command");
|
|
67
|
+
return options;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const encode = new TextEncoder();
|
|
71
|
+
const hash = async (value) => {
|
|
72
|
+
const bytes = await crypto.subtle.digest("SHA-256", encode.encode(value));
|
|
73
|
+
return Array.from(new Uint8Array(bytes), (n) => n.toString(16).padStart(2, "0")).join("");
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Keep only entry digests. Sorting makes the comparison independent of list order.
|
|
77
|
+
async function sourceHash(kv) {
|
|
78
|
+
const digests = [];
|
|
79
|
+
let cursor;
|
|
80
|
+
do {
|
|
81
|
+
const page = await kv.list({ limit: 1000, ...(cursor ? { cursor } : {}) });
|
|
82
|
+
for (const key of page.keys) {
|
|
83
|
+
if (key.expiration !== undefined && key.expiration * 1000 <= Date.now()) continue;
|
|
84
|
+
const value = await kv.get(key.name, "text");
|
|
85
|
+
if (value !== null) digests.push(await hash(JSON.stringify([key.name, key.expiration ?? null, value])));
|
|
86
|
+
}
|
|
87
|
+
cursor = page.list_complete ? undefined : page.cursor;
|
|
88
|
+
} while (cursor);
|
|
89
|
+
return hash(digests.sort().join(""));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Injected operations let the Workers suite exercise the real migration flow. */
|
|
93
|
+
export async function runMigration(kv, db, options, operations, output = console) {
|
|
94
|
+
if (!options.maintenance) throw new MigrationUsageError("Migration requires maintenance");
|
|
95
|
+
if (options.markLive) {
|
|
96
|
+
await operations.markKvToD1Live(db, options.source);
|
|
97
|
+
output.log("Cutover sealed in D1. Reopen traffic after deployment verification; never copy stale KV afterward.");
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
if ((await sourceHash(kv)) !== (await sourceHash(kv))) {
|
|
101
|
+
throw new MigrationUsageError("Workers KV source is not stable; keep maintenance active, wait, and repeat");
|
|
102
|
+
}
|
|
103
|
+
const families = {};
|
|
104
|
+
let cursor;
|
|
105
|
+
do {
|
|
106
|
+
const result = await operations.copyKvToD1(kv, db, {
|
|
107
|
+
source: options.source,
|
|
108
|
+
cursor,
|
|
109
|
+
overwriteFamilies: options.overwriteFamilies,
|
|
110
|
+
verify: options.verify,
|
|
111
|
+
allowStale: options.allowStale,
|
|
112
|
+
});
|
|
113
|
+
for (const [family, counts] of Object.entries(result.families)) {
|
|
114
|
+
const into = (families[family] ??= {});
|
|
115
|
+
for (const [field, n] of Object.entries(counts)) into[field] = (into[field] ?? 0) + n;
|
|
116
|
+
}
|
|
117
|
+
cursor = result.cursor;
|
|
118
|
+
} while (cursor);
|
|
119
|
+
output.table(families);
|
|
120
|
+
if (Object.values(families).some((counts) => counts.invalid > 0 || counts.conflicts > 0 || counts.mismatches > 0)) {
|
|
121
|
+
output.error(
|
|
122
|
+
"Invalid entries, conflicts, or verification mismatches remain. Keep maintenance active and resolve the per-family counts.",
|
|
123
|
+
);
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function main() {
|
|
130
|
+
let proxy;
|
|
131
|
+
let operations;
|
|
132
|
+
try {
|
|
133
|
+
const options = parseArgs(process.argv.slice(2));
|
|
134
|
+
if (options.help) {
|
|
135
|
+
console.log(HELP);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const wranglerModule = "wrangler";
|
|
139
|
+
const d1Module = "@zackbart/connecta/d1";
|
|
140
|
+
const { getPlatformProxy, unstable_readConfig } = await import(/* @vite-ignore */ wranglerModule);
|
|
141
|
+
operations = await import(/* @vite-ignore */ d1Module);
|
|
142
|
+
const config = unstable_readConfig({ config: options.configPath });
|
|
143
|
+
const source = config.kv_namespaces?.find((binding) => binding.binding === "CONNECTA_KV")?.id;
|
|
144
|
+
if (!source || source.startsWith("replace-"))
|
|
145
|
+
throw new MigrationUsageError("Configure the CONNECTA_KV namespace id before copying");
|
|
146
|
+
proxy = await getPlatformProxy({ configPath: options.configPath, persist: false });
|
|
147
|
+
const { CONNECTA_KV: kv, CONNECTA_DB: db } = proxy.env;
|
|
148
|
+
if (!kv || !db) throw new MigrationUsageError("Copy config must bind CONNECTA_KV and CONNECTA_DB");
|
|
149
|
+
process.exitCode = await runMigration(kv, db, { ...options, source }, operations);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error(
|
|
152
|
+
error instanceof MigrationUsageError || (operations && error instanceof operations.KvToD1CopyError)
|
|
153
|
+
? error.message
|
|
154
|
+
: "Workers KV to D1 migration stopped; keep maintenance active and inspect the configuration",
|
|
155
|
+
);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
} finally {
|
|
158
|
+
await proxy?.dispose();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (typeof process !== "undefined" && import.meta.url === `file://${process.argv?.[1]}`) await main();
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connecta on Cloudflare Workers: this deployment's configuration.
|
|
3
|
+
*
|
|
4
|
+
* One MCP endpoint aggregating a downstream remote MCP and an HTTP API, guarded
|
|
5
|
+
* by Cloudflare Access. It needs three resources and nothing else: one D1
|
|
6
|
+
* database (CONNECTA_DB) holding every piece of state, the Worker Loader
|
|
7
|
+
* binding (LOADER) behind execute_code, and one secret
|
|
8
|
+
* (CREDENTIAL_ENCRYPTION_KEY) sealing credentials in that database. Access
|
|
9
|
+
* admits the request before this Worker runs and supplies trusted human
|
|
10
|
+
* identity through ctx.access. Machines use stored cta_ tokens inside connecta.
|
|
11
|
+
*
|
|
12
|
+
* Optional modules are type-checked code switched by the environment: the
|
|
13
|
+
* credential vault by the CREDENTIAL_ENCRYPTION_KEY secret, and activity
|
|
14
|
+
* history by the CONNECTA_ACTIVITY var ("on"), in the same database. README.md § "Select optional
|
|
15
|
+
* modules" walks through each.
|
|
16
|
+
*
|
|
17
|
+
* Setup (this example has no package.json of its own — it self-references the
|
|
18
|
+
* installed `@zackbart/connecta` package):
|
|
19
|
+
* 1. `npm install` in the connecta package root (../../ from here) so the
|
|
20
|
+
* package import and wrangler resolve. A copy in its own repository
|
|
21
|
+
* installs `@zackbart/connecta @cloudflare/codemode` instead. Codemode is
|
|
22
|
+
* an optional peer.
|
|
23
|
+
* 2. `wrangler d1 create connecta` and put its id in wrangler.jsonc under
|
|
24
|
+
* `d1_databases`. connecta creates its tables on first use.
|
|
25
|
+
* 3. Set secrets:
|
|
26
|
+
* wrangler secret put DOWNSTREAM_TOKEN
|
|
27
|
+
* wrangler secret put CREDENTIAL_ENCRYPTION_KEY
|
|
28
|
+
* and PUBLIC_URL as a plain var in wrangler.jsonc.
|
|
29
|
+
* 4. Attach Cloudflare Access to this Worker. Enable Managed OAuth and
|
|
30
|
+
* Dynamic Client Registration. Its Allowed redirect URIs must include
|
|
31
|
+
* Claude's https://claude.ai/api/mcp/auth_callback plus ChatGPT's
|
|
32
|
+
* https://chatgpt.com/connector_platform_oauth_redirect and
|
|
33
|
+
* https://chatgpt.com/connector/oauth/* forms (see ../AGENTS.md).
|
|
34
|
+
* 5. Use the Workers Paid plan required by the `worker_loaders` binding.
|
|
35
|
+
* 6. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
|
|
36
|
+
* lives. Point your MCP client at `<PUBLIC_URL>/mcp`.
|
|
37
|
+
*/
|
|
38
|
+
import { api, defineConfig, remoteMcp } from "@zackbart/connecta";
|
|
39
|
+
import { activityHistory } from "@zackbart/connecta/activity";
|
|
40
|
+
import { accessTokens } from "@zackbart/connecta/auth/access-tokens";
|
|
41
|
+
import { cloudflareAccessAuth } from "@zackbart/connecta/auth/cloudflare-access";
|
|
42
|
+
import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
|
|
43
|
+
import { d1ActivityStore, d1Storage } from "@zackbart/connecta/d1";
|
|
44
|
+
import { operatorUi } from "@zackbart/connecta/ui";
|
|
45
|
+
import { workerExecutor } from "@zackbart/connecta/worker";
|
|
46
|
+
|
|
47
|
+
export interface Env {
|
|
48
|
+
/** The one D1 database: OAuth grants, sealed credentials, catalogs, tokens, activity. */
|
|
49
|
+
CONNECTA_DB: D1Database;
|
|
50
|
+
/**
|
|
51
|
+
* Base64 32-byte AES key encrypting operator-managed credentials in D1.
|
|
52
|
+
* Unset means no vault: credential management is unavailable in the operator
|
|
53
|
+
* UI. Never put it in D1, since it is what protects D1.
|
|
54
|
+
*/
|
|
55
|
+
CREDENTIAL_ENCRYPTION_KEY?: string;
|
|
56
|
+
DOWNSTREAM_TOKEN: string;
|
|
57
|
+
PUBLIC_URL: string;
|
|
58
|
+
/** "on" switches on activity history, in CONNECTA_DB. */
|
|
59
|
+
CONNECTA_ACTIVITY?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Worker Loader binding (wrangler.jsonc `worker_loaders`) powering
|
|
62
|
+
* execute_code. Dynamic Workers require the Workers Paid plan.
|
|
63
|
+
*/
|
|
64
|
+
LOADER: WorkerLoader;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default defineConfig((env: Env) => {
|
|
68
|
+
const storage = d1Storage(env.CONNECTA_DB);
|
|
69
|
+
return {
|
|
70
|
+
publicUrl: env.PUBLIC_URL,
|
|
71
|
+
storage,
|
|
72
|
+
// Required adapter owns each run's handles; direct upstream construction throws.
|
|
73
|
+
executor: workerExecutor({ loader: env.LOADER }),
|
|
74
|
+
// Access owns edge admission and proves human identity for MCP and the
|
|
75
|
+
// operator pages. Machines need Access service headers at the edge plus a
|
|
76
|
+
// stored cta_ bearer inside connecta; a service identity alone is refused.
|
|
77
|
+
auth: [cloudflareAccessAuth()],
|
|
78
|
+
// Always installed over the same D1 storage. Empty storage admits no
|
|
79
|
+
// machine client; an Access-authenticated operator can provision tokens.
|
|
80
|
+
accessTokens: accessTokens(storage),
|
|
81
|
+
// Code-owned roster. Access proves the identity; connecta derives
|
|
82
|
+
// connector visibility and management permissions from the stable id it
|
|
83
|
+
// supplies. Add `connectorAccess` and `activityAccess` to split members
|
|
84
|
+
// from operators; here every signed-in human may manage every connector
|
|
85
|
+
// and access token. Machine tokens never receive interactive authority.
|
|
86
|
+
identity: {
|
|
87
|
+
credentialAdministration: () => "all",
|
|
88
|
+
personalConnection: () => "all",
|
|
89
|
+
accessTokenManagement: () => true,
|
|
90
|
+
},
|
|
91
|
+
// Connectors that declare a `credential` slot become editable by every
|
|
92
|
+
// signed-in human who can see that connector, encrypted with this key
|
|
93
|
+
// before anything reaches D1. A saved replacement takes effect on the
|
|
94
|
+
// next call; credentials fail at use.
|
|
95
|
+
vault: env.CREDENTIAL_ENCRYPTION_KEY ? encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY) : undefined,
|
|
96
|
+
// Payload-free activity at /ui/api/activity in CONNECTA_DB. The shared
|
|
97
|
+
// D1 mapping persists the build version and validated client facts, and
|
|
98
|
+
// adds nullable columns to old tables. Each write prunes expired rows.
|
|
99
|
+
activity:
|
|
100
|
+
env.CONNECTA_ACTIVITY === "on"
|
|
101
|
+
? activityHistory({
|
|
102
|
+
store: d1ActivityStore(env.CONNECTA_DB, { retentionDays: 90 }),
|
|
103
|
+
deploymentId: "production",
|
|
104
|
+
})
|
|
105
|
+
: undefined,
|
|
106
|
+
// Branding is code too: operatorUi({ branding }).
|
|
107
|
+
ui: operatorUi(),
|
|
108
|
+
connectors: [
|
|
109
|
+
remoteMcp("notion", {
|
|
110
|
+
url: "https://mcp.notion.com/mcp",
|
|
111
|
+
description: "Notion — pages, databases, comments (static token)",
|
|
112
|
+
auth: {
|
|
113
|
+
type: "headers",
|
|
114
|
+
headers: { Authorization: `Bearer ${env.DOWNSTREAM_TOKEN}` },
|
|
115
|
+
// The vault-backed alternative for a downstream that authenticates
|
|
116
|
+
// with a static key: the operator manages it inside its connection
|
|
117
|
+
// in the operator UI, so no Worker secret holds it.
|
|
118
|
+
// type: "credential",
|
|
119
|
+
// credential: { label: "Notion internal integration token" },
|
|
120
|
+
},
|
|
121
|
+
// Use `authScope: "personal"` with OAuth or credential auth when each
|
|
122
|
+
// Access user connects their own downstream account. Literal headers
|
|
123
|
+
// are deployment-owned and cannot be personal.
|
|
124
|
+
}),
|
|
125
|
+
api("echo", {
|
|
126
|
+
description: "Echo — text transforms",
|
|
127
|
+
// What a vault-backed connector adds — an operator edits this slot
|
|
128
|
+
// inside its connection and the handler reads it with
|
|
129
|
+
// `await ctx.credential?.get()`, so the secret never lives in source:
|
|
130
|
+
// credential: { label: "API token" },
|
|
131
|
+
tools: [
|
|
132
|
+
{
|
|
133
|
+
name: "shout",
|
|
134
|
+
description: "Uppercase the given text.",
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
text: { type: "string", description: "Text to uppercase." },
|
|
139
|
+
},
|
|
140
|
+
required: ["text"],
|
|
141
|
+
},
|
|
142
|
+
annotations: { readOnlyHint: true },
|
|
143
|
+
handler: async (args: { text: string }) => ({
|
|
144
|
+
shouted: args.text.toUpperCase(),
|
|
145
|
+
}),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
}),
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
});
|
|
@@ -1,205 +1,22 @@
|
|
|
1
|
-
import { operatorUi } from "@zackbart/connecta/ui";
|
|
2
|
-
// import { activityHistory } from "@zackbart/connecta/activity";
|
|
3
|
-
import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
|
|
4
1
|
/**
|
|
5
|
-
* connecta on Cloudflare Workers.
|
|
6
|
-
*
|
|
7
|
-
* One MCP endpoint aggregating a downstream remote MCP and an HTTP API, guarded
|
|
8
|
-
* by Cloudflare Access, with OAuth/cache state in a KV namespace. Access
|
|
9
|
-
* authenticates the request before this Worker runs and supplies the trusted
|
|
10
|
-
* identity through ctx.access. The required Worker Loader binding in
|
|
11
|
-
* wrangler.jsonc backs the seven-tool surface.
|
|
12
|
-
*
|
|
13
|
-
* The operator surface is wired here except for activity history, which needs
|
|
14
|
-
* a database this example does not create for you: sign-in and the credential
|
|
15
|
-
* vault are on, and activity is three commented lines below. README.md
|
|
16
|
-
* § "Select optional modules" walks through all three.
|
|
17
|
-
*
|
|
18
|
-
* Setup (this example has no package.json of its own — it self-references the
|
|
19
|
-
* installed `@zackbart/connecta` package):
|
|
20
|
-
* 1. `npm install` in the connecta package root (../../ from here) so the
|
|
21
|
-
* package import and wrangler resolve. A copy in its own repository
|
|
22
|
-
* installs `@zackbart/connecta @cloudflare/codemode` instead. Codemode is
|
|
23
|
-
* an optional peer.
|
|
24
|
-
* 2. Create a KV namespace and put its id in wrangler.jsonc under `kv_namespaces`.
|
|
25
|
-
* 3. Set secrets:
|
|
26
|
-
* wrangler secret put DOWNSTREAM_TOKEN
|
|
27
|
-
* wrangler secret put CREDENTIAL_ENCRYPTION_KEY
|
|
28
|
-
* and PUBLIC_URL as a plain var in wrangler.jsonc.
|
|
29
|
-
* 4. Attach Cloudflare Access to this Worker. Enable Managed OAuth and
|
|
30
|
-
* Dynamic Client Registration. Its Allowed redirect URIs must include
|
|
31
|
-
* Claude's https://claude.ai/api/mcp/auth_callback plus ChatGPT's
|
|
32
|
-
* https://chatgpt.com/connector_platform_oauth_redirect and
|
|
33
|
-
* https://chatgpt.com/connector/oauth/* forms (see ../AGENTS.md).
|
|
34
|
-
* 5. Use the Workers Paid plan required by the `worker_loaders` binding.
|
|
35
|
-
* 6. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
|
|
36
|
-
* lives. Point your MCP client at `<PUBLIC_URL>/mcp`.
|
|
2
|
+
* connecta on Cloudflare Workers. Configuration lives in connecta.config.ts,
|
|
3
|
+
* which also lists setup; this file only starts it.
|
|
37
4
|
*/
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
api,
|
|
41
|
-
createConnecta,
|
|
42
|
-
remoteMcp,
|
|
43
|
-
} from "@zackbart/connecta";
|
|
44
|
-
import { cloudflareAccessAuth } from "@zackbart/connecta/auth/cloudflare-access";
|
|
45
|
-
import { cloudflareKvStorage } from "./cloudflare-kv.js";
|
|
46
|
-
// import { artifacts, kvArtifactStore } from "@zackbart/connecta/artifacts";
|
|
47
|
-
// import { d1Storage } from "./d1-storage.js";
|
|
48
|
-
// Activity history, off by default because it needs a D1 database.
|
|
49
|
-
// import { d1ActivityStore } from "./d1-activity.js";
|
|
50
|
-
|
|
51
|
-
interface Env {
|
|
52
|
-
CONNECTA_KV: KVNamespace;
|
|
53
|
-
/**
|
|
54
|
-
* Base64 32-byte AES key encrypting operator-managed credentials in KV.
|
|
55
|
-
* Unset means no vault: credential management is unavailable in the operator
|
|
56
|
-
* UI. Never put it in KV, since it is what protects KV.
|
|
57
|
-
*/
|
|
58
|
-
CREDENTIAL_ENCRYPTION_KEY: string;
|
|
59
|
-
DOWNSTREAM_TOKEN: string;
|
|
60
|
-
PUBLIC_URL: string;
|
|
61
|
-
/** Uncomment with the `d1_databases` binding to enable activity history. */
|
|
62
|
-
// ACTIVITY_DB: D1Database;
|
|
63
|
-
/**
|
|
64
|
-
* Worker Loader binding (wrangler.jsonc `worker_loaders`) powering
|
|
65
|
-
* execute_code. Dynamic Workers require the Workers Paid plan.
|
|
66
|
-
*/
|
|
67
|
-
LOADER: WorkerLoader;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function build(env: Env) {
|
|
71
|
-
const storage = cloudflareKvStorage(env.CONNECTA_KV);
|
|
72
|
-
// Optional refreshable pages need the D1 binding, not Workers KV:
|
|
73
|
-
// const artifactModule = artifacts({ store: kvArtifactStore(d1Storage(env.STORAGE_DB)) });
|
|
74
|
-
// scheduledArtifacts.module = artifactModule;
|
|
75
|
-
return createConnecta({
|
|
76
|
-
publicUrl: env.PUBLIC_URL,
|
|
77
|
-
storage,
|
|
78
|
-
executor: new DynamicWorkerExecutor({ loader: env.LOADER }),
|
|
79
|
-
auth: [
|
|
80
|
-
// Access owns admission policy. A human identity may use MCP and the
|
|
81
|
-
// operator pages; a service token may use MCP but cannot mutate operator
|
|
82
|
-
// state. Neither path asks connecta to parse a JWT.
|
|
83
|
-
cloudflareAccessAuth(),
|
|
84
|
-
],
|
|
85
|
-
// Optional code-owned roster. Access proves the identity; connecta derives
|
|
86
|
-
// connector visibility and deployment-operator status from the stable id
|
|
87
|
-
// it supplies. A signed-in human may manage auth for every connector this
|
|
88
|
-
// view includes. Omit the block to keep every connector visible and every
|
|
89
|
-
// human a deployment operator.
|
|
90
|
-
// identity: {
|
|
91
|
-
// connectorAccess: ({ principal }) =>
|
|
92
|
-
// principal?.id === "ACCESS_USER_UUID"
|
|
93
|
-
// ? ["notion", "echo"]
|
|
94
|
-
// : ["echo"],
|
|
95
|
-
// activityAccess: ({ id }) => id === "ACCESS_USER_UUID",
|
|
96
|
-
// },
|
|
97
|
-
// Connectors that declare a `credential` slot become editable by every
|
|
98
|
-
// signed-in human who can see that connector, inside its connection on /,
|
|
99
|
-
// encrypted with this key before anything reaches KV. A saved replacement takes
|
|
100
|
-
// effect on the next call — no redeploy, and no liveness probe:
|
|
101
|
-
// credentials fail at use.
|
|
102
|
-
//
|
|
103
|
-
// The key is the vault, not the form: a credential form appears only on a
|
|
104
|
-
// connector that declares a slot. Neither connector below does — Notion
|
|
105
|
-
// here carries a deployment-owned static header and echo has no secret at
|
|
106
|
-
// all — so this example ships the vault ready and nothing to fill in.
|
|
107
|
-
// Declare a slot (see the commented shape on `echo`, or use a provider
|
|
108
|
-
// connector like `notion()`, which declares its own) and the form appears
|
|
109
|
-
// on the next load.
|
|
110
|
-
vault: encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY),
|
|
111
|
-
// Branding is code too: name, owner, description, favicon, and five theme
|
|
112
|
-
// tokens that every other color is mixed from. A value that fails its
|
|
113
|
-
// check falls back to the default, and the startup warning names it.
|
|
114
|
-
// ui: operatorUi({
|
|
115
|
-
// branding: {
|
|
116
|
-
// productName: "Acme Tools",
|
|
117
|
-
// ownerName: "Acme",
|
|
118
|
-
// theme: {
|
|
119
|
-
// accent: "#2f5fe0", // hex only
|
|
120
|
-
// radius: 10, // pixels, or a CSS length such as "0.5rem"
|
|
121
|
-
// fontFamily: "Inter, system-ui, sans-serif",
|
|
122
|
-
// monoFamily: "ui-monospace, monospace",
|
|
123
|
-
// colorScheme: "system", // or "light" | "dark"
|
|
124
|
-
// },
|
|
125
|
-
// },
|
|
126
|
-
// }),
|
|
127
|
-
ui: operatorUi(),
|
|
128
|
-
// artifacts: artifactModule,
|
|
129
|
-
identity: { credentialAdministration: () => "all", personalConnection: () => "all" },
|
|
130
|
-
// Payload-free activity at /activity, off until a database exists to hold
|
|
131
|
-
// it. Uncomment the `d1_databases` binding in wrangler.jsonc, apply the
|
|
132
|
-
// schema in README.md § "Activity history", then these three lines and the
|
|
133
|
-
// import above.
|
|
134
|
-
// activity: activityHistory({
|
|
135
|
-
// store: d1ActivityStore(env.ACTIVITY_DB),
|
|
136
|
-
// deploymentId: "production",
|
|
137
|
-
// }),
|
|
138
|
-
connectors: [
|
|
139
|
-
remoteMcp("notion", {
|
|
140
|
-
url: "https://mcp.notion.com/mcp",
|
|
141
|
-
description: "Notion — pages, databases, comments (static token)",
|
|
142
|
-
auth: {
|
|
143
|
-
type: "headers",
|
|
144
|
-
headers: { Authorization: `Bearer ${env.DOWNSTREAM_TOKEN}` },
|
|
145
|
-
// The vault-backed alternative for a downstream that authenticates
|
|
146
|
-
// with a static key: the operator manages it inside its connection
|
|
147
|
-
// in the operator UI, so no Worker secret holds it.
|
|
148
|
-
// type: "credential",
|
|
149
|
-
// credential: { label: "Notion internal integration token" },
|
|
150
|
-
},
|
|
151
|
-
// Use `authScope: "personal"` with OAuth or credential auth when each
|
|
152
|
-
// Access user connects their own downstream account. Literal headers
|
|
153
|
-
// are deployment-owned and cannot be personal.
|
|
154
|
-
}),
|
|
155
|
-
api("echo", {
|
|
156
|
-
description: "Echo — text transforms",
|
|
157
|
-
// What a vault-backed connector adds — an operator edits this slot
|
|
158
|
-
// inside its connection in the operator UI and the handler reads it with
|
|
159
|
-
// `await ctx.credential?.get()`, so the secret never lives in source
|
|
160
|
-
// or in a Worker variable:
|
|
161
|
-
// credential: { label: "API token" },
|
|
162
|
-
tools: [
|
|
163
|
-
{
|
|
164
|
-
name: "shout",
|
|
165
|
-
description: "Uppercase the given text.",
|
|
166
|
-
inputSchema: {
|
|
167
|
-
type: "object",
|
|
168
|
-
properties: {
|
|
169
|
-
text: { type: "string", description: "Text to uppercase." },
|
|
170
|
-
},
|
|
171
|
-
required: ["text"],
|
|
172
|
-
},
|
|
173
|
-
annotations: { readOnlyHint: true },
|
|
174
|
-
handler: async (args: { text: string }) => ({
|
|
175
|
-
shouted: args.text.toUpperCase(),
|
|
176
|
-
}),
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
}),
|
|
180
|
-
],
|
|
181
|
-
});
|
|
182
|
-
}
|
|
5
|
+
import { createConnecta, type Connecta } from "@zackbart/connecta";
|
|
6
|
+
import connectaConfig, { type Env } from "./connecta.config.js";
|
|
183
7
|
|
|
184
8
|
// Lazy per-isolate singleton: reuses the plain-data tool cache. Downstream MCP
|
|
185
9
|
// clients are request-scoped internally so Worker I/O never crosses requests.
|
|
186
|
-
let
|
|
187
|
-
|
|
10
|
+
let started: Connecta | undefined;
|
|
11
|
+
function start(env: Env) {
|
|
12
|
+
if (!started) {
|
|
13
|
+
const config = connectaConfig(env);
|
|
14
|
+
started = createConnecta(config);
|
|
15
|
+
}
|
|
16
|
+
return started;
|
|
17
|
+
}
|
|
188
18
|
|
|
189
19
|
export default {
|
|
190
|
-
// Pass `ctx` through:
|
|
191
|
-
|
|
192
|
-
// being cancelled with the request.
|
|
193
|
-
async fetch(
|
|
194
|
-
request: Request,
|
|
195
|
-
env: Env,
|
|
196
|
-
ctx: ExecutionContext,
|
|
197
|
-
): Promise<Response> {
|
|
198
|
-
connecta ??= build(env);
|
|
199
|
-
return connecta.fetch(request, env, ctx);
|
|
200
|
-
},
|
|
201
|
-
async scheduled(_event: ScheduledController, env: Env): Promise<void> {
|
|
202
|
-
connecta ??= build(env);
|
|
203
|
-
await scheduledArtifacts.module?.runDue();
|
|
204
|
-
},
|
|
20
|
+
// Pass `ctx` through: deferred work (activity sinks) settles on ctx.waitUntil.
|
|
21
|
+
fetch: (request: Request, env: Env, ctx: ExecutionContext) => start(env).fetch(request, env, ctx),
|
|
205
22
|
};
|
|
@@ -6,14 +6,13 @@
|
|
|
6
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
|
-
|
|
13
9
|
// Plain vars. Secrets (CREDENTIAL_ENCRYPTION_KEY, DOWNSTREAM_TOKEN) are set with
|
|
14
10
|
// `wrangler secret put <NAME>`, not here.
|
|
11
|
+
// CONNECTA_ACTIVITY switches on activity history in src/connecta.config.ts
|
|
12
|
+
// when "on"; its data stays in CONNECTA_DB.
|
|
15
13
|
"vars": {
|
|
16
|
-
"
|
|
14
|
+
// "CONNECTA_ACTIVITY": "on",
|
|
15
|
+
"PUBLIC_URL": "https://connecta.example.workers.dev",
|
|
17
16
|
},
|
|
18
17
|
|
|
19
18
|
// Local-only Access identity. Remove this block to exercise the
|
|
@@ -22,39 +21,20 @@
|
|
|
22
21
|
"access": {
|
|
23
22
|
"dev": {
|
|
24
23
|
"aud": "connecta-local",
|
|
25
|
-
"identity": { "email": "operator@example.com" }
|
|
26
|
-
}
|
|
24
|
+
"identity": { "email": "operator@example.com" },
|
|
25
|
+
},
|
|
27
26
|
},
|
|
28
27
|
|
|
29
|
-
// Create with `wrangler
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
// The one database. Create with `wrangler d1 create connecta` and paste
|
|
29
|
+
// the id. connecta creates its tables on first use; nothing to apply.
|
|
30
|
+
"d1_databases": [
|
|
31
|
+
{
|
|
32
|
+
"binding": "CONNECTA_DB",
|
|
33
|
+
"database_name": "connecta",
|
|
34
|
+
"database_id": "replace-with-d1-database-id",
|
|
35
|
+
},
|
|
32
36
|
],
|
|
33
37
|
|
|
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.
|
|
45
|
-
// "d1_databases": [
|
|
46
|
-
// {
|
|
47
|
-
// "binding": "ACTIVITY_DB",
|
|
48
|
-
// "database_name": "connecta-activity",
|
|
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"
|
|
55
|
-
// }
|
|
56
|
-
// ],
|
|
57
|
-
|
|
58
38
|
// Required by execute_code; Dynamic Workers use the Workers Paid plan.
|
|
59
|
-
"worker_loaders": [{ "binding": "LOADER" }]
|
|
39
|
+
"worker_loaders": [{ "binding": "LOADER" }],
|
|
60
40
|
}
|