@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
package/templates/node/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Connecta deployment
|
|
2
2
|
|
|
3
|
-
This is the prescribed Node deployment
|
|
4
|
-
|
|
3
|
+
This is the prescribed Node deployment. It runs locally from source and as
|
|
4
|
+
a container. Install, provision a machine token, then start it:
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
7
|
npm install
|
|
8
|
-
|
|
8
|
+
npm run --silent provision-token -- "local-machine"
|
|
9
|
+
# Save the returned cta_ token privately. It is shown only once.
|
|
10
|
+
npm start
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
The manifest approves only `esbuild@0.28.2` to run its install script. `tsx`
|
|
@@ -15,8 +17,39 @@ warning, and npm 12 does not block the script. A later esbuild version needs a
|
|
|
15
17
|
new explicit review and approval; do not replace the pinned entry with a broad
|
|
16
18
|
package-name approval.
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
`Authorization: Bearer
|
|
20
|
+
Point an MCP client at `http://localhost:8787/mcp` with
|
|
21
|
+
`Authorization: Bearer <returned-cta-token>`. `CONNECTA_TOKEN` is a client and
|
|
22
|
+
doctor variable only. Setting it never configures server authentication.
|
|
23
|
+
|
|
24
|
+
`accessTokens(storage)` is always installed. With no stored tokens and no
|
|
25
|
+
Clerk keys, the server can start and answer `/health`, but every MCP request
|
|
26
|
+
is refused. There is no open startup or configured static bearer fallback.
|
|
27
|
+
|
|
28
|
+
## Provision machine tokens
|
|
29
|
+
|
|
30
|
+
Run `npm run --silent provision-token -- "machine-name"` from this project
|
|
31
|
+
on a trusted machine. `src/provision-token.ts` imports the installed package's
|
|
32
|
+
`AccessTokenManager`, then calls `manager.create(name, "local-provisioning")`
|
|
33
|
+
on `sqliteStorage(openSqlite(process.env.CONNECTA_DATABASE || "./.connecta.sqlite"))`.
|
|
34
|
+
The server uses that same database path. If you override it, use the same
|
|
35
|
+
`CONNECTA_DATABASE` for provisioning and startup:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
export CONNECTA_DATABASE=/absolute/path/connecta.sqlite
|
|
39
|
+
npm run --silent provision-token -- "build-agent"
|
|
40
|
+
npm start
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The local commands read the process environment; they do not load `.env`.
|
|
44
|
+
Compose reads `.env` for the container commands below. Keep the returned
|
|
45
|
+
secret in a client secret store and out of source control and logs. Each run
|
|
46
|
+
creates a new token; it cannot recover an old secret. Only the hash persists.
|
|
47
|
+
The token is unbound to a human principal and has no interactive management
|
|
48
|
+
authority. Its name is a label, and deployment identity and pool rules decide
|
|
49
|
+
its access on every request. To revoke it locally, use `manager.list()` to
|
|
50
|
+
find its id, then `manager.revoke(id, "local-provisioning")` against that same
|
|
51
|
+
storage. An explicitly permitted Clerk operator can also manage tokens.
|
|
52
|
+
Never expose this provisioning script as an HTTP route or run it at startup.
|
|
20
53
|
|
|
21
54
|
## Run it in Docker
|
|
22
55
|
|
|
@@ -24,20 +57,48 @@ Same source, same configuration, one long-lived service:
|
|
|
24
57
|
|
|
25
58
|
```sh
|
|
26
59
|
cp .env.example .env
|
|
27
|
-
# edit .env
|
|
28
|
-
|
|
29
|
-
docker compose
|
|
60
|
+
# edit .env for the database path and any optional modules
|
|
61
|
+
docker compose build
|
|
62
|
+
docker compose run --rm --no-deps connecta npm run --silent provision-token -- "container-machine"
|
|
63
|
+
# Save the returned cta_ token privately for clients and doctor.
|
|
64
|
+
docker compose up -d
|
|
30
65
|
```
|
|
31
66
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
67
|
+
The provisioning container writes through the installed package API into the
|
|
68
|
+
same named volume and `CONNECTA_DATABASE` as the service. An empty volume
|
|
69
|
+
admits no machine client. Compose never passes `CONNECTA_TOKEN` to the server;
|
|
70
|
+
no environment value can substitute for a stored token.
|
|
35
71
|
|
|
36
72
|
Compose reads `.env`, publishes `PORT` (8787 by default), and keeps state on
|
|
37
73
|
the named volume `connecta-state`, mounted at `/data`. `docker compose down`
|
|
38
74
|
stops the service and keeps state; `down -v` wipes it. `/health` is always
|
|
39
75
|
open, so the container's health probe never carries the bearer token.
|
|
40
76
|
|
|
77
|
+
## Storage
|
|
78
|
+
|
|
79
|
+
Every piece of state lives in one SQLite file, `CONNECTA_DATABASE`
|
|
80
|
+
(`./.connecta.sqlite` locally, `/data/connecta.sqlite` in the container):
|
|
81
|
+
downstream OAuth grants, sealed vault credentials, catalogs, result paging,
|
|
82
|
+
access tokens and activity. `@zackbart/connecta/sqlite` uses
|
|
83
|
+
Node's built-in `node:sqlite` (Node 22.13 or later), creates its tables on
|
|
84
|
+
first use, and commits one row per write. Back it up as one file, together
|
|
85
|
+
with its `-wal` file while the server runs. Node 22 and 23 print an
|
|
86
|
+
experimental-feature warning for `node:sqlite` once at startup.
|
|
87
|
+
|
|
88
|
+
Upgrading from 0.28, which kept state in `.connecta-state.json`: stop the
|
|
89
|
+
server, then copy the old file into the database once, before the first start:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
npx connecta migrate-state .connecta-state.json .connecta.sqlite
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
In the container, run it against the volume, e.g. `docker compose run --rm
|
|
96
|
+
connecta npx connecta migrate-state /data/connecta-state.json
|
|
97
|
+
/data/connecta.sqlite`. It copies every live entry with its expiry, keeps any
|
|
98
|
+
key the database already holds, and never changes the old file; delete that
|
|
99
|
+
once the deployment is verified. The 0.28 activity log
|
|
100
|
+
(`.connecta-activity.jsonl`) is not imported; history starts fresh.
|
|
101
|
+
|
|
41
102
|
A build with no lockfile in the context resolves the pinned Connecta version
|
|
42
103
|
itself, and a lockfile npm writes inside the image never reaches this project.
|
|
43
104
|
So commit the `package-lock.json` that the `npm install` above wrote on this
|
|
@@ -46,20 +107,19 @@ reproducible `npm ci` path.
|
|
|
46
107
|
|
|
47
108
|
## Select optional modules
|
|
48
109
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
110
|
+
Every optional module is code in `src/connecta.config.ts`, switched by an
|
|
111
|
+
environment variable: set it and the module is on, leave it empty and it is
|
|
112
|
+
off. `.env.example` lists each one, and `npm run typecheck` checks every module
|
|
113
|
+
whether or not it is switched on.
|
|
56
114
|
|
|
57
115
|
The template explicitly enables `ui: operatorUi()` from
|
|
58
|
-
`@zackbart/connecta/ui`. Open `http://localhost:8787/` and supply
|
|
59
|
-
|
|
116
|
+
`@zackbart/connecta/ui`. Open `http://localhost:8787/` and supply a
|
|
117
|
+
stored machine token to inspect Connections. Sign in with Clerk for human
|
|
118
|
+
connection management. Omit that option and import for an API-only
|
|
60
119
|
server. OAuth callbacks remain in core even with no UI.
|
|
61
120
|
|
|
62
|
-
The page labels
|
|
121
|
+
The page labels each tool as a read or write. Trusted programs may write;
|
|
122
|
+
read-only pools use `call_destructive_tool`, with approval controlled by the host. For
|
|
63
123
|
classified connection failures it offers a fixed repair prompt, and its
|
|
64
124
|
endpoint section has client setup commands for `/mcp` and any pool available
|
|
65
125
|
to the signed-in identity. Those commands contain no bearer token.
|
|
@@ -69,21 +129,37 @@ their URLs, `description`, `pageTitle`, `favicon`, and `theme`. The theme is
|
|
|
69
129
|
five tokens — `accent`, `radius`, `fontFamily`, `monoFamily`, and
|
|
70
130
|
`colorScheme` — and every other color is mixed from them, so one accent themes
|
|
71
131
|
the whole page. A value that fails its gate falls back to the default, and the
|
|
72
|
-
startup warning names it. `src/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
132
|
+
startup warning names it. Pass it to `operatorUi()` in `src/connecta.config.ts`.
|
|
133
|
+
|
|
134
|
+
Connection management needs an interactive identity. A machine access token
|
|
135
|
+
never authorizes browser credential mutations. `@clerk/backend`
|
|
136
|
+
ships as a dependency of this template. To enable Clerk, set both
|
|
137
|
+
`CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` (one without the other refuses to
|
|
138
|
+
start) and set `PUBLIC_URL`.
|
|
139
|
+
Enable `aud_claim_enabled: true` in Clerk's instance OAuth application settings
|
|
140
|
+
(`PATCH /v1/instance/oauth_application_settings` in Clerk's Backend API), then
|
|
141
|
+
read the setting back. Both JWT and opaque OAuth tokens are supported.
|
|
142
|
+
Leave `allowedOAuthClientIds` omitted or `[]` to require bound tokens.
|
|
143
|
+
|
|
144
|
+
Connecta's protected-resource metadata advertises the exact public `/mcp` or
|
|
145
|
+
`/mcp/<pool>` URL as `resource` and Clerk as the authorization server. MCP
|
|
146
|
+
hosts use that URL in their standard OAuth `resource` parameter. Enable the
|
|
147
|
+
Clerk registration methods your hosts need, including DCR or CIMD. Reconnect
|
|
148
|
+
after enabling audience issuance and confirm the new token's `aud` matches
|
|
149
|
+
the metadata URL, MCP initialization succeeds, and refresh retains the same
|
|
150
|
+
audience. Clerk's raw verification response exposes `aud` for opaque tokens.
|
|
151
|
+
|
|
152
|
+
An explicit `allowedOAuthClientIds` list is only a fallback for unbound tokens
|
|
153
|
+
from clients dedicated to this deployment. It never overrides a mismatched
|
|
154
|
+
audience. New DCR registrations need new entries, so prefer audience binding
|
|
155
|
+
for standard host onboarding. Clerk session tokens work only on operator
|
|
156
|
+
routes. [Inbound auth](https://github.com/zackbart/connecta/blob/main/documentation/auth.md#clerk-oauth-tokens-and-operator-sessions)
|
|
157
|
+
explains configuration, verification, and fixed rejection reason codes.
|
|
158
|
+
Machine clients use connecta-issued `cta_` access tokens
|
|
159
|
+
through `accessTokens(storage)`, which `src/connecta.config.ts` configures on
|
|
160
|
+
the database. Grant explicit `identity.accessTokenManagement` permissions to
|
|
161
|
+
let a signed-in human mint them. Trusted local provisioning above works without
|
|
162
|
+
Clerk and never requires opening the server.
|
|
87
163
|
|
|
88
164
|
Set the code-owned identity resolvers deliberately. `connectorAccess` governs
|
|
89
165
|
use; `credentialAdministration` permits shared-auth changes, and
|
|
@@ -94,20 +170,19 @@ connector where each person should connect their own downstream account.
|
|
|
94
170
|
|
|
95
171
|
### Credential vault
|
|
96
172
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`CONNECTA_CREDENTIAL_KEY` to a base64 32-byte AES key:
|
|
173
|
+
Set `CONNECTA_CREDENTIAL_KEY` to a base64 32-byte AES key and
|
|
174
|
+
`src/connecta.config.ts` passes `encryptedCredentialVault(storage, key)` over the
|
|
175
|
+
same database:
|
|
101
176
|
|
|
102
177
|
```sh
|
|
103
178
|
node -e "console.log(crypto.randomBytes(32).toString('base64'))"
|
|
104
179
|
```
|
|
105
180
|
|
|
106
|
-
Keep this key outside the
|
|
181
|
+
Keep this key outside the database. Losing it makes saved values unreadable;
|
|
107
182
|
upgrades must reuse it. The shipped `time` connector declares no credential
|
|
108
183
|
slot. Add `credential: { label: "API token" }` to an `api()` connector and read
|
|
109
|
-
it through `await ctx.credential?.get()`, or use
|
|
110
|
-
|
|
184
|
+
it through `await ctx.credential?.get()`, or use Notion's explicit
|
|
185
|
+
`surface: "api"` integration interface, which declares its own slot. Authorized humans manage the slot inside that
|
|
111
186
|
connection on `/`; there is no separate Credentials tab.
|
|
112
187
|
|
|
113
188
|
In 0.25.0, this vault also seals downstream OAuth tokens on their first read.
|
|
@@ -122,26 +197,26 @@ static credential recovery reports unavailable instead of offering a dead link.
|
|
|
122
197
|
|
|
123
198
|
### Activity history and diagnostics
|
|
124
199
|
|
|
125
|
-
|
|
126
|
-
`
|
|
127
|
-
tab appears for authorized readers
|
|
128
|
-
|
|
200
|
+
Set `CONNECTA_ACTIVITY=on` and `src/connecta.config.ts` records activity with
|
|
201
|
+
`sqliteActivityStore(database, { retentionDays: 90 })` from
|
|
202
|
+
`@zackbart/connecta/sqlite`. The Activity tab appears for authorized readers.
|
|
203
|
+
Leave it empty to record no activity.
|
|
129
204
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
results, generated code, or raw errors. Adjust retention in that file if needed.
|
|
205
|
+
Activity shares the one database file. Each write prunes a bounded batch of
|
|
206
|
+
rows older than `retentionDays`, so nothing has to be scheduled; change the
|
|
207
|
+
number in `src/connecta.config.ts`. It records no arguments, results, generated code, or raw errors.
|
|
134
208
|
|
|
135
209
|
Diagnostics are independent. Keep the default logger or provide your own;
|
|
136
210
|
`logger: "silent"` suppresses diagnostic output explicitly.
|
|
137
211
|
|
|
138
212
|
The UI displays connections and current permissions. Configuration still owns
|
|
139
|
-
the connector set, tool definitions, and access rules.
|
|
140
|
-
team roster
|
|
213
|
+
the connector set, tool definitions, and access rules. Access token management
|
|
214
|
+
requires explicit Clerk operator permission. There is no team roster or policy editor.
|
|
141
215
|
|
|
142
216
|
## Deployment contract
|
|
143
217
|
|
|
144
|
-
- Edit `src/
|
|
218
|
+
- Edit `src/connecta.config.ts` for connectors, auth, storage, the public URL,
|
|
219
|
+
and optional modules. `src/index.ts` only starts it.
|
|
145
220
|
- Keep the required `executor: quickJsExecutor()` configuration; a deployment
|
|
146
221
|
without an executor refuses to boot.
|
|
147
222
|
- Keep secrets in environment variables or an external secret store.
|
|
@@ -156,19 +231,19 @@ Verify a change with:
|
|
|
156
231
|
```sh
|
|
157
232
|
npm run typecheck
|
|
158
233
|
# In another terminal, while the server is running (npm start or compose):
|
|
159
|
-
CONNECTA_TOKEN=
|
|
234
|
+
CONNECTA_TOKEN='cta_REPLACE_WITH_RETURNED_SECRET' npm run doctor
|
|
160
235
|
```
|
|
161
236
|
|
|
162
|
-
Doctor checks health, the executor, and the exact prescribed
|
|
237
|
+
Doctor checks health, the executor, and the exact prescribed six-tool
|
|
163
238
|
model-facing surface by running a harmless sandbox program. It reads the bearer
|
|
164
239
|
from `CONNECTA_TOKEN`; it never accepts the secret as a command-line argument.
|
|
165
240
|
Remote URLs must use HTTPS.
|
|
166
241
|
|
|
167
242
|
A fully-wired deployment reports exactly the same line as a bare one —
|
|
168
|
-
connector count, QuickJS executed,
|
|
243
|
+
connector count, QuickJS executed, six tools, plus any catalog drift:
|
|
169
244
|
|
|
170
245
|
```text
|
|
171
|
-
Connecta doctor passed: 1 connector(s), QuickJS executed, prescribed
|
|
246
|
+
Connecta doctor passed: 1 connector(s), QuickJS executed, prescribed six-tool surface.
|
|
172
247
|
```
|
|
173
248
|
|
|
174
249
|
`QuickJS` is this deployment's sandbox, reported by the deployment itself —
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# One service, no database: state is
|
|
1
|
+
# One service, no database server: state is one SQLite file on a named volume.
|
|
2
2
|
#
|
|
3
|
-
# cp .env.example .env
|
|
3
|
+
# cp .env.example .env
|
|
4
|
+
# docker compose build
|
|
5
|
+
# docker compose run --rm --no-deps connecta npm run --silent provision-token -- "machine-name"
|
|
4
6
|
# docker compose up -d --build
|
|
5
7
|
#
|
|
6
8
|
# Compose reads `.env` from this directory, so one file configures both the
|
|
@@ -14,23 +16,20 @@ services:
|
|
|
14
16
|
ports:
|
|
15
17
|
- "${PORT:-8787}:${PORT:-8787}"
|
|
16
18
|
environment:
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
CONNECTA_TOKEN: ${CONNECTA_TOKEN:?set CONNECTA_TOKEN in .env}
|
|
19
|
+
# accessTokens(storage) is always installed; an empty database is closed.
|
|
20
|
+
# CONNECTA_TOKEN belongs to clients and is never configured on this server.
|
|
20
21
|
PORT: ${PORT:-8787}
|
|
21
22
|
# Downstream OAuth calls back to this origin. Unset is fine until this
|
|
22
23
|
# deployment is reachable from somewhere other than this machine.
|
|
23
24
|
PUBLIC_URL: ${PUBLIC_URL:-}
|
|
24
|
-
# Keep the
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# until then these are unread. The activity log joins the state file on
|
|
29
|
-
# the volume for the same reason it does.
|
|
25
|
+
# Keep the database under /data so it lands on the volume below.
|
|
26
|
+
CONNECTA_DATABASE: ${CONNECTA_DATABASE:-/data/connecta.sqlite}
|
|
27
|
+
# Optional modules. Each switches its module in src/connecta.config.ts
|
|
28
|
+
# on when set and off when empty.
|
|
30
29
|
CLERK_PUBLISHABLE_KEY: ${CLERK_PUBLISHABLE_KEY:-}
|
|
31
30
|
CLERK_SECRET_KEY: ${CLERK_SECRET_KEY:-}
|
|
32
31
|
CONNECTA_CREDENTIAL_KEY: ${CONNECTA_CREDENTIAL_KEY:-}
|
|
33
|
-
|
|
32
|
+
CONNECTA_ACTIVITY: ${CONNECTA_ACTIVITY:-}
|
|
34
33
|
volumes:
|
|
35
34
|
- connecta-state:/data
|
|
36
35
|
restart: unless-stopped
|
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
"version": "0.0.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=22.
|
|
7
|
+
"node": ">=22.13.0"
|
|
8
8
|
},
|
|
9
9
|
"allowScripts": {
|
|
10
10
|
"esbuild@0.28.2": true
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"doctor": "connecta doctor",
|
|
14
|
+
"provision-token": "tsx src/provision-token.ts",
|
|
14
15
|
"start": "tsx src/index.ts",
|
|
15
16
|
"typecheck": "tsc --noEmit"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
18
|
-
"@
|
|
19
|
+
"@clerk/backend": "^3.12.0",
|
|
20
|
+
"@zackbart/connecta": "0.29.0",
|
|
19
21
|
"quickjs-emscripten": "0.32.0"
|
|
20
22
|
},
|
|
21
23
|
"devDependencies": {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This deployment's configuration: connectors, authentication, storage, public
|
|
3
|
+
* origin, and the optional operator modules. Add application logic only inside
|
|
4
|
+
* deliberate api() connector handlers.
|
|
5
|
+
*
|
|
6
|
+
* Every optional module is type-checked code switched by the environment
|
|
7
|
+
* (.env.example lists each variable): set it and the module is on, leave it
|
|
8
|
+
* empty and it is off. README.md § "Select optional modules" covers each one.
|
|
9
|
+
* Connecta validates the result when src/index.ts starts it; a mistake refuses
|
|
10
|
+
* to boot rather than running in a shape nobody chose.
|
|
11
|
+
*/
|
|
12
|
+
import { api, defineConfig } from "@zackbart/connecta";
|
|
13
|
+
import { activityHistory } from "@zackbart/connecta/activity";
|
|
14
|
+
import { accessTokens } from "@zackbart/connecta/auth/access-tokens";
|
|
15
|
+
import { clerkAuth } from "@zackbart/connecta/auth/clerk";
|
|
16
|
+
import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
|
|
17
|
+
import { quickJsExecutor } from "@zackbart/connecta/quickjs";
|
|
18
|
+
import { openSqlite, sqliteActivityStore, sqliteStorage } from "@zackbart/connecta/sqlite";
|
|
19
|
+
import { operatorUi } from "@zackbart/connecta/ui";
|
|
20
|
+
|
|
21
|
+
/** The variables this file reads (see .env.example). */
|
|
22
|
+
export interface Env {
|
|
23
|
+
PORT?: string;
|
|
24
|
+
PUBLIC_URL?: string;
|
|
25
|
+
CONNECTA_DATABASE?: string;
|
|
26
|
+
CLERK_PUBLISHABLE_KEY?: string;
|
|
27
|
+
CLERK_SECRET_KEY?: string;
|
|
28
|
+
CONNECTA_CREDENTIAL_KEY?: string;
|
|
29
|
+
CONNECTA_ACTIVITY?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default defineConfig((env: Env) => {
|
|
33
|
+
// Empty is unset: an untouched `.env` passes through Compose as "".
|
|
34
|
+
const set = (name: keyof Env) => env[name] || undefined;
|
|
35
|
+
const publicUrl = set("PUBLIC_URL") ?? `http://localhost:${set("PORT") ?? 8787}`;
|
|
36
|
+
|
|
37
|
+
// Operator sign-in. Only a Clerk-authenticated human may manage connector
|
|
38
|
+
// credentials. Machine access tokens never grant interactive authority.
|
|
39
|
+
// Both keys, or neither.
|
|
40
|
+
const clerkPublishableKey = set("CLERK_PUBLISHABLE_KEY");
|
|
41
|
+
const clerkSecretKey = set("CLERK_SECRET_KEY");
|
|
42
|
+
if (Boolean(clerkPublishableKey) !== Boolean(clerkSecretKey)) {
|
|
43
|
+
throw new Error("Operator sign-in needs both CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY.");
|
|
44
|
+
}
|
|
45
|
+
const clerk =
|
|
46
|
+
clerkPublishableKey && clerkSecretKey
|
|
47
|
+
? clerkAuth({
|
|
48
|
+
publishableKey: clerkPublishableKey,
|
|
49
|
+
secretKey: clerkSecretKey,
|
|
50
|
+
// Enable Clerk aud_claim_enabled; hosts request the MCP URL as
|
|
51
|
+
// resource. Bound JWT and opaque tokens are the default.
|
|
52
|
+
publicUrl,
|
|
53
|
+
// Restrict who may sign in with `allowedDomains: ["acme.com"]`, or
|
|
54
|
+
// `gate` for anything a domain cannot express. Absent, every
|
|
55
|
+
// authenticated Clerk user is admitted.
|
|
56
|
+
})
|
|
57
|
+
: undefined;
|
|
58
|
+
// Every piece of state — OAuth grants, sealed credentials, catalogs,
|
|
59
|
+
// paging, access tokens, activity — lives in this one file.
|
|
60
|
+
const database = openSqlite(set("CONNECTA_DATABASE") ?? "./.connecta.sqlite");
|
|
61
|
+
const storage = sqliteStorage(database);
|
|
62
|
+
|
|
63
|
+
// The credential vault. A connector that declares a `credential` slot
|
|
64
|
+
// becomes editable inside its connection on /, encrypted in the database
|
|
65
|
+
// with this key — so keep the key out of that file and out of source.
|
|
66
|
+
const credentialKey = set("CONNECTA_CREDENTIAL_KEY");
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
storage,
|
|
70
|
+
publicUrl,
|
|
71
|
+
// Required: model-written programs run in a bounded QuickJS child.
|
|
72
|
+
executor: quickJsExecutor(),
|
|
73
|
+
auth: clerk ? [clerk] : [],
|
|
74
|
+
// Always installed: an empty database refuses machine requests. Provision
|
|
75
|
+
// locally with `npm run provision-token -- "machine-name"`, or grant an
|
|
76
|
+
// interactive human `identity.accessTokenManagement` for later issuance.
|
|
77
|
+
// v0.23 client tokens survive `connecta migrate-state` on this storage.
|
|
78
|
+
accessTokens: accessTokens(storage),
|
|
79
|
+
// Connection management permissions default to none, so the template
|
|
80
|
+
// grants them. Split members from operators with `connectorAccess`,
|
|
81
|
+
// `activityAccess`, and `accessTokenManagement`, each derived from the
|
|
82
|
+
// authenticated identity and never from an MCP argument.
|
|
83
|
+
identity: {
|
|
84
|
+
credentialAdministration: () => "all",
|
|
85
|
+
personalConnection: () => "all",
|
|
86
|
+
},
|
|
87
|
+
vault: credentialKey ? encryptedCredentialVault(storage, credentialKey) : undefined,
|
|
88
|
+
// Payload-free activity history at /activity: who called what, when, how
|
|
89
|
+
// long it took, and whether it worked. Each write prunes rows older than
|
|
90
|
+
// `retentionDays`; the number is yours to choose.
|
|
91
|
+
activity:
|
|
92
|
+
set("CONNECTA_ACTIVITY") === "on"
|
|
93
|
+
? activityHistory({
|
|
94
|
+
store: sqliteActivityStore(database, { retentionDays: 90 }),
|
|
95
|
+
deploymentId: "production",
|
|
96
|
+
})
|
|
97
|
+
: undefined,
|
|
98
|
+
// The operator UI at /. Branding is code too: operatorUi({ branding }).
|
|
99
|
+
ui: operatorUi(),
|
|
100
|
+
connectors: [
|
|
101
|
+
api("time", {
|
|
102
|
+
description: "Time — current timestamp",
|
|
103
|
+
// A connector that needs an operator-managed secret declares a slot —
|
|
104
|
+
// credential: { label: "API token" },
|
|
105
|
+
// — and reads it inside a handler with `await ctx.credential?.get()`.
|
|
106
|
+
// Telling the time needs no secret, so this one declares nothing.
|
|
107
|
+
tools: [
|
|
108
|
+
{
|
|
109
|
+
name: "get_now",
|
|
110
|
+
description: "Return the current time as an ISO 8601 timestamp.",
|
|
111
|
+
inputSchema: { type: "object", properties: {} },
|
|
112
|
+
annotations: { readOnlyHint: true },
|
|
113
|
+
handler: async () => ({ now: new Date().toISOString() }),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
}),
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
});
|
|
@@ -1,159 +1,14 @@
|
|
|
1
|
-
import { bearerToken } from "@zackbart/connecta/auth/bearer";
|
|
2
|
-
import { operatorUi } from "@zackbart/connecta/ui";
|
|
3
|
-
// import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
|
|
4
|
-
// import { activityHistory } from "@zackbart/connecta/activity";
|
|
5
1
|
/**
|
|
6
|
-
* Prescribed Connecta deployment.
|
|
7
|
-
*
|
|
8
|
-
* Keep this file as deployment configuration: connectors, authentication,
|
|
9
|
-
* storage, and public origin. Add application logic only inside deliberate
|
|
10
|
-
* api() connector handlers.
|
|
11
|
-
*
|
|
12
|
-
* The optional modules — operator sign-in, credential vault, activity — ship
|
|
13
|
-
* here as commented configuration, because each needs a secret or a retention
|
|
14
|
-
* decision this file cannot make for you. Uncomment the block you want;
|
|
15
|
-
* README.md § "Select optional modules" walks through all three.
|
|
16
|
-
*
|
|
17
|
-
* Environment (see .env.example):
|
|
18
|
-
* CONNECTA_TOKEN required inbound bearer token
|
|
19
|
-
* PORT listen port (default 8787)
|
|
20
|
-
* PUBLIC_URL public origin; downstream OAuth calls back to it
|
|
21
|
-
* CONNECTA_STATE_FILE fileStorage path (the container points it at /data)
|
|
22
|
-
* CLERK_PUBLISHABLE_KEY operator sign-in, once the Clerk block is on
|
|
23
|
-
* CLERK_SECRET_KEY operator sign-in, once the Clerk block is on
|
|
24
|
-
* CONNECTA_CREDENTIAL_KEY vault key, once the credentials block is on
|
|
25
|
-
* CONNECTA_ACTIVITY_FILE activity log, once the activity block is on
|
|
2
|
+
* Prescribed Connecta deployment. Configuration lives in src/connecta.config.ts;
|
|
3
|
+
* this file only starts it. The environment it reads is in .env.example.
|
|
26
4
|
*/
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import
|
|
30
|
-
// import { accessTokens } from "@zackbart/connecta/auth/access-tokens";
|
|
31
|
-
// import { artifacts, kvArtifactStore } from "@zackbart/connecta/artifacts";
|
|
32
|
-
// Operator sign-in. Needs `npm install @clerk/backend` — it is an optional
|
|
33
|
-
// peer, so it does not install with Connecta.
|
|
34
|
-
// import { clerkAuth } from "@zackbart/connecta/auth/clerk";
|
|
35
|
-
// Payload-free activity history, kept beside the state file.
|
|
36
|
-
// import { fileActivityStore } from "./file-activity.js";
|
|
5
|
+
import { createConnecta } from "@zackbart/connecta";
|
|
6
|
+
import { listen } from "@zackbart/connecta/node";
|
|
7
|
+
import connectaConfig from "./connecta.config.js";
|
|
37
8
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"CONNECTA_TOKEN is required. Refusing to start without inbound auth.",
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
const port = Number(process.env.PORT ?? 8787);
|
|
45
|
-
// Empty is unset: an untouched `.env` passes through Compose as "", and an
|
|
46
|
-
// empty state path or public origin is worse than the local default.
|
|
47
|
-
const stateFile = process.env.CONNECTA_STATE_FILE || "./.connecta-state.json";
|
|
48
|
-
const publicUrl = process.env.PUBLIC_URL || `http://localhost:${port}`;
|
|
49
|
-
const storage = fileStorage(stateFile);
|
|
50
|
-
// Optional artifact pages and refresh jobs use the same CAS-capable state file:
|
|
51
|
-
// const artifactModule = artifacts({ store: kvArtifactStore(storage) });
|
|
52
|
-
|
|
53
|
-
// Operator sign-in. A bearer token is a client key: it may call tools and read
|
|
54
|
-
// connector status, but only a Clerk-authenticated human may write a visible
|
|
55
|
-
// connector's credential. Without this block the operator UI still renders —
|
|
56
|
-
// an operator pastes the bearer to read it — and connections stay read-only.
|
|
57
|
-
// const clerkPublishableKey = process.env.CLERK_PUBLISHABLE_KEY;
|
|
58
|
-
// const clerkSecretKey = process.env.CLERK_SECRET_KEY;
|
|
59
|
-
// if (!clerkPublishableKey || !clerkSecretKey) {
|
|
60
|
-
// throw new Error(
|
|
61
|
-
// "Operator sign-in needs CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY.",
|
|
62
|
-
// );
|
|
63
|
-
// }
|
|
64
|
-
|
|
65
|
-
const connecta = createConnecta({
|
|
66
|
-
storage,
|
|
67
|
-
// Reuse this storage to keep v0.23 client tokens without rotating secrets.
|
|
68
|
-
// accessTokens: accessTokens(storage),
|
|
69
|
-
auth: [
|
|
70
|
-
bearerToken(token, { subjectId: "operator" }),
|
|
71
|
-
// clerkAuth({
|
|
72
|
-
// publishableKey: clerkPublishableKey,
|
|
73
|
-
// secretKey: clerkSecretKey,
|
|
74
|
-
// publicUrl,
|
|
75
|
-
// // Restrict who may sign in — or use `gate` for anything a domain
|
|
76
|
-
// // cannot express. Absent, every authenticated Clerk user is admitted.
|
|
77
|
-
// // allowedDomains: ["acme.com"],
|
|
78
|
-
// }),
|
|
79
|
-
],
|
|
80
|
-
// Code-owned identity resolvers. Connection management permissions default to
|
|
81
|
-
// none, so the template grants them. The commented pair is the optional
|
|
82
|
-
// member/operator split for Clerk-backed deployments: connector access is
|
|
83
|
-
// derived from the authenticated identity and cannot be selected by an MCP
|
|
84
|
-
// argument. Leave them commented for the all-visible,
|
|
85
|
-
// all-interactive-users-read-activity behavior.
|
|
86
|
-
identity: {
|
|
87
|
-
credentialAdministration: () => "all",
|
|
88
|
-
personalConnection: () => "all",
|
|
89
|
-
// connectorAccess: ({ principal }) =>
|
|
90
|
-
// principal?.id === "user_admin" ? "all" : ["time"],
|
|
91
|
-
// activityAccess: ({ id }) => id === "user_admin",
|
|
92
|
-
// accessTokenManagement: ({ principal }) => principal?.id === "user_admin",
|
|
93
|
-
},
|
|
94
|
-
publicUrl,
|
|
95
|
-
// Required: model-written programs run in a bounded QuickJS child.
|
|
96
|
-
executor: quickJsExecutor(),
|
|
97
|
-
// Credential vault. A connector that declares a `credential` slot becomes
|
|
98
|
-
// editable inside its connection on /, and its value is encrypted in the state file
|
|
99
|
-
// with this key — so keep the key out of that file and out of source:
|
|
100
|
-
// node -e "console.log(crypto.randomBytes(32).toString('base64'))"
|
|
101
|
-
// Rotating a credential takes effect on the next call; no restart.
|
|
102
|
-
// vault: encryptedCredentialVault(storage, process.env.CONNECTA_CREDENTIAL_KEY!),
|
|
103
|
-
// Payload-free activity history at /activity: who called what, when, how
|
|
104
|
-
// long it took, and whether it worked. Never arguments, results, generated
|
|
105
|
-
// code, or raw error messages. Commented because retention is yours to
|
|
106
|
-
// choose — see src/file-activity.ts.
|
|
107
|
-
// activity: activityHistory({
|
|
108
|
-
// store: fileActivityStore(
|
|
109
|
-
// process.env.CONNECTA_ACTIVITY_FILE || "./.connecta-activity.jsonl",
|
|
110
|
-
// ),
|
|
111
|
-
// deploymentId: "production",
|
|
112
|
-
// }),
|
|
113
|
-
// The operator UI at /. Its branding is code, like everything else here:
|
|
114
|
-
// name, owner, description, favicon, and five theme tokens that every
|
|
115
|
-
// other color is mixed from. A value that fails its check falls back to the
|
|
116
|
-
// default, and the startup warning names it.
|
|
117
|
-
// ui: operatorUi({
|
|
118
|
-
// branding: {
|
|
119
|
-
// productName: "Acme Tools",
|
|
120
|
-
// ownerName: "Acme",
|
|
121
|
-
// theme: {
|
|
122
|
-
// accent: "#2f5fe0", // hex only
|
|
123
|
-
// radius: 10, // pixels, or a CSS length such as "0.5rem"
|
|
124
|
-
// fontFamily: "Inter, system-ui, sans-serif",
|
|
125
|
-
// monoFamily: "ui-monospace, monospace",
|
|
126
|
-
// colorScheme: "system", // or "light" | "dark"
|
|
127
|
-
// },
|
|
128
|
-
// },
|
|
129
|
-
// }),
|
|
130
|
-
ui: operatorUi(),
|
|
131
|
-
// artifacts: artifactModule,
|
|
132
|
-
connectors: [
|
|
133
|
-
api("time", {
|
|
134
|
-
description: "Time — current timestamp",
|
|
135
|
-
// A connector that needs an operator-managed secret declares a slot —
|
|
136
|
-
// credential: { label: "API token" },
|
|
137
|
-
// — and reads it inside a handler with `await ctx.credential?.get()`.
|
|
138
|
-
// Telling the time needs no secret, so this one declares nothing.
|
|
139
|
-
tools: [
|
|
140
|
-
{
|
|
141
|
-
name: "get_now",
|
|
142
|
-
description: "Return the current time as an ISO 8601 timestamp.",
|
|
143
|
-
inputSchema: { type: "object", properties: {} },
|
|
144
|
-
annotations: { readOnlyHint: true },
|
|
145
|
-
handler: async () => ({ now: new Date().toISOString() }),
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
}),
|
|
149
|
-
],
|
|
150
|
-
});
|
|
9
|
+
const config = connectaConfig(process.env);
|
|
10
|
+
const connecta = createConnecta(config);
|
|
11
|
+
const port = Number(process.env.PORT || 8787);
|
|
151
12
|
|
|
152
13
|
listen(connecta, port);
|
|
153
|
-
console.log(`connecta listening on port ${port}; MCP at ${publicUrl}/mcp`);
|
|
154
|
-
// The deployment owns this timer; core starts no background work. Uncomment
|
|
155
|
-
// with artifactModule above. Each tick starts at most 10 due pages.
|
|
156
|
-
// const refreshTimer = setInterval(() => {
|
|
157
|
-
// void artifactModule.runDue().catch((error) => console.error("artifact refresh failed", error));
|
|
158
|
-
// }, 60 * 60 * 1000);
|
|
159
|
-
// refreshTimer.unref();
|
|
14
|
+
console.log(`connecta listening on port ${port}; MCP at ${config.publicUrl}/mcp`);
|