@zackbart/connecta 0.28.1 → 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.
Files changed (319) hide show
  1. package/CHANGELOG.md +788 -5
  2. package/README.md +62 -42
  3. package/bin/connecta.mjs +110 -139
  4. package/bin/meta-tool-names.d.mts +2 -0
  5. package/bin/meta-tool-names.mjs +9 -0
  6. package/bin/version.mjs +3 -0
  7. package/dist/access-tokens.js +53 -63
  8. package/dist/activity-request.js +8 -0
  9. package/dist/activity.d.ts +76 -42
  10. package/dist/activity.js +110 -42
  11. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  12. package/dist/auth/clerk-sdk/client.js +5 -0
  13. package/dist/auth/clerk-sdk/generated.js +1845 -0
  14. package/dist/auth/clerk-transport.js +19 -0
  15. package/dist/auth/clerk.d.ts +17 -7
  16. package/dist/auth/clerk.js +189 -107
  17. package/dist/auth/cloudflare-access.js +25 -26
  18. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  19. package/dist/auth/downstream-client-metadata.js +133 -0
  20. package/dist/auth/downstream-oauth.js +1584 -1580
  21. package/dist/auth/downstream-revocation.js +106 -0
  22. package/dist/auth/oauth-token-response.js +231 -0
  23. package/dist/auth/oauth-v2-migration.js +116 -0
  24. package/dist/auth/static-oauth.js +93 -71
  25. package/dist/auth-elicitation.js +283 -0
  26. package/dist/branding.d.ts +8 -16
  27. package/dist/branding.js +26 -59
  28. package/dist/byte-read-response.js +39 -0
  29. package/dist/call-admission.js +12 -16
  30. package/dist/catalog-cache.js +739 -0
  31. package/dist/catalog-drift.d.ts +50 -15
  32. package/dist/catalog-drift.js +365 -128
  33. package/dist/catalog-fingerprint.js +2 -4
  34. package/dist/catalog-freshness.js +13 -0
  35. package/dist/catalog-limits.js +2 -2
  36. package/dist/catalog-service.js +368 -224
  37. package/dist/catalog.js +43 -84
  38. package/dist/concurrency.js +3 -4
  39. package/dist/config-defaults.d.ts +39 -0
  40. package/dist/config-defaults.js +46 -0
  41. package/dist/config-schema.d.ts +181 -0
  42. package/dist/config-schema.js +435 -0
  43. package/dist/config-value-sources.js +102 -0
  44. package/dist/config.d.ts +631 -0
  45. package/dist/config.js +503 -0
  46. package/dist/connector-access.d.ts +1 -0
  47. package/dist/connector-access.js +13 -15
  48. package/dist/connector-caller.d.ts +8 -0
  49. package/dist/connector-caller.js +15 -6
  50. package/dist/connector-scope.js +1 -1
  51. package/dist/connectors/api-connector.d.ts +59 -7
  52. package/dist/connectors/api-connector.js +106 -19
  53. package/dist/connectors/api.js +4 -1
  54. package/dist/connectors/guarded-fetch.d.ts +144 -0
  55. package/dist/connectors/guarded-fetch.js +84 -35
  56. package/dist/connectors/negotiation-cache.js +46 -0
  57. package/dist/connectors/option-shapes.js +27 -0
  58. package/dist/connectors/remote-mcp.d.ts +30 -4
  59. package/dist/connectors/remote-mcp.js +1774 -320
  60. package/dist/connectors/resource-uri.js +335 -0
  61. package/dist/credential-contract.d.ts +10 -0
  62. package/dist/credential-rules.js +1 -3
  63. package/dist/credential-url.js +108 -0
  64. package/dist/credentials.d.ts +6 -1
  65. package/dist/credentials.js +27 -18
  66. package/dist/d1.d.ts +130 -0
  67. package/dist/d1.js +290 -0
  68. package/dist/describe-config.d.ts +165 -0
  69. package/dist/describe-config.js +315 -0
  70. package/dist/described.js +106 -0
  71. package/dist/downstream-input-context.js +119 -0
  72. package/dist/downstream-input.js +323 -0
  73. package/dist/errors.d.ts +97 -20
  74. package/dist/errors.js +158 -80
  75. package/dist/execute.js +453 -305
  76. package/dist/executor-admission.d.ts +1 -0
  77. package/dist/executor-admission.js +11 -15
  78. package/dist/executor-contract.d.ts +20 -0
  79. package/dist/executor-contract.js +48 -0
  80. package/dist/executor-result.js +24 -9
  81. package/dist/executors/quickjs-child.js +3 -4
  82. package/dist/executors/quickjs-runtime.js +147 -59
  83. package/dist/executors/quickjs.js +29 -35
  84. package/dist/favicon.js +1 -2
  85. package/dist/fix-prompt.js +1 -3
  86. package/dist/guest-runtime.js +188 -0
  87. package/dist/guest-types.d.ts +176 -0
  88. package/dist/guest-types.js +1 -0
  89. package/dist/html-security.js +24 -0
  90. package/dist/identity.js +1 -3
  91. package/dist/inbound-credential.js +52 -0
  92. package/dist/index.d.ts +24 -256
  93. package/dist/index.js +65 -478
  94. package/dist/invocation-auth.js +69 -0
  95. package/dist/invocation.js +230 -73
  96. package/dist/json-schema.d.ts +1 -1
  97. package/dist/mcp-client-context.js +35 -0
  98. package/dist/mcp-result.js +11 -7
  99. package/dist/meta-output.js +76 -0
  100. package/dist/meta-tool-names.d.ts +2 -0
  101. package/dist/meta-tool-names.js +3 -0
  102. package/dist/meta-tools.js +285 -209
  103. package/dist/module-contracts.d.ts +8 -23
  104. package/dist/node.d.ts +0 -2
  105. package/dist/node.js +2 -2
  106. package/dist/oauth-callback-outcome.js +1 -3
  107. package/dist/oauth-handoff.js +117 -0
  108. package/dist/oauth-sealing.js +20 -0
  109. package/dist/operator-record.d.ts +137 -0
  110. package/dist/operator-record.js +420 -0
  111. package/dist/operator-ui/assets.js +31 -0
  112. package/dist/operator-ui/contract.d.ts +72 -0
  113. package/dist/operator-ui/contract.js +1 -0
  114. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  115. package/dist/operator-ui/generated.js +5 -4
  116. package/dist/operator-ui/model.d.ts +12 -87
  117. package/dist/operator-ui/model.js +2 -9
  118. package/dist/operator-ui/setup-commands.js +1 -3
  119. package/dist/operator-ui/view.d.ts +323 -0
  120. package/dist/operator-ui/view.js +62 -189
  121. package/dist/page-styles.js +2 -3
  122. package/dist/program-source.js +14 -2
  123. package/dist/{exempt-writes.js → program-writes.js} +55 -18
  124. package/dist/provider.d.ts +142 -0
  125. package/dist/provider.js +187 -0
  126. package/dist/providers/_shared/google/result-size.js +57 -0
  127. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  128. package/dist/providers/_shared/google/service-account.js +500 -0
  129. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  130. package/dist/providers/_shared/google/workspace.js +518 -0
  131. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  132. package/dist/providers/basecamp/index.js +997 -0
  133. package/dist/providers/basecamp/skill.generated.js +9 -0
  134. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  135. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  136. package/dist/providers/breeze/skill.generated.js +9 -0
  137. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  138. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  139. package/dist/providers/ccb/skill.generated.js +9 -0
  140. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  141. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  142. package/dist/providers/cloudflare/skill.generated.js +10 -0
  143. package/dist/providers/docs/index.d.ts +54 -0
  144. package/dist/providers/docs/index.js +1237 -0
  145. package/dist/providers/docs/skill.generated.js +9 -0
  146. package/dist/providers/drive/index.d.ts +53 -0
  147. package/dist/providers/drive/index.js +1523 -0
  148. package/dist/providers/drive/skill.generated.js +9 -0
  149. package/dist/providers/forms/index.d.ts +54 -0
  150. package/dist/providers/forms/index.js +1217 -0
  151. package/dist/providers/forms/skill.generated.js +11 -0
  152. package/dist/providers/github/auth.d.ts +32 -0
  153. package/dist/providers/github/auth.js +361 -0
  154. package/dist/providers/github/hosted.js +218 -0
  155. package/dist/providers/github/index.d.ts +13 -0
  156. package/dist/providers/github/index.js +140 -0
  157. package/dist/providers/github/key.js +100 -0
  158. package/dist/providers/github/rest.js +321 -0
  159. package/dist/providers/github/scope.d.ts +29 -0
  160. package/dist/providers/github/scope.js +124 -0
  161. package/dist/providers/github/skill.generated.js +8 -0
  162. package/dist/providers/gmail/index.d.ts +51 -0
  163. package/dist/providers/gmail/index.js +1436 -0
  164. package/dist/providers/gmail/skill.generated.js +9 -0
  165. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  166. package/dist/providers/linear/index.js +352 -0
  167. package/dist/providers/linear/skill.generated.js +8 -0
  168. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  169. package/dist/providers/mixpanel/index.js +416 -0
  170. package/dist/providers/mixpanel/skill.generated.js +9 -0
  171. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  172. package/dist/providers/{notion.js → notion/index.js} +226 -239
  173. package/dist/providers/notion/skill.generated.js +10 -0
  174. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  175. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  176. package/dist/providers/overflow/skill.generated.js +8 -0
  177. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  178. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  179. package/dist/providers/planning-center/skill.generated.js +10 -0
  180. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  181. package/dist/providers/revenuecat/index.js +557 -0
  182. package/dist/providers/revenuecat/skill.generated.js +11 -0
  183. package/dist/providers/sheets/index.d.ts +54 -0
  184. package/dist/providers/sheets/index.js +1237 -0
  185. package/dist/providers/sheets/skill.generated.js +10 -0
  186. package/dist/providers/slides/index.d.ts +53 -0
  187. package/dist/providers/slides/index.js +2645 -0
  188. package/dist/providers/slides/skill.generated.js +9 -0
  189. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  190. package/dist/providers/stripe/index.js +266 -0
  191. package/dist/providers/stripe/skill.generated.js +11 -0
  192. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  193. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  194. package/dist/providers/tithely/skill.generated.js +8 -0
  195. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  196. package/dist/providers/vercel/index.js +1038 -0
  197. package/dist/providers/vercel/mcp-ownership.js +212 -0
  198. package/dist/providers/vercel/skill.generated.js +11 -0
  199. package/dist/registry.d.ts +108 -171
  200. package/dist/registry.js +513 -925
  201. package/dist/request-state.js +21 -0
  202. package/dist/resource-read.js +100 -0
  203. package/dist/result-shapes.js +17 -49
  204. package/dist/retired-toolkits.js +1 -1
  205. package/dist/routes/access-tokens.js +3 -6
  206. package/dist/routes/activity-context.js +21 -0
  207. package/dist/routes/activity-disclosure.js +40 -0
  208. package/dist/routes/activity.js +59 -21
  209. package/dist/routes/actor-labels.js +3 -50
  210. package/dist/routes/connect.js +221 -0
  211. package/dist/routes/credentials.js +15 -27
  212. package/dist/routes/mcp.js +356 -130
  213. package/dist/routes/oauth-client-metadata.js +15 -0
  214. package/dist/routes/oauth-management.js +45 -107
  215. package/dist/routes/oauth.js +110 -57
  216. package/dist/routes/operator.js +3 -3
  217. package/dist/routes/shared.d.ts +25 -64
  218. package/dist/routes/shared.js +160 -80
  219. package/dist/routes/skills.js +30 -0
  220. package/dist/routes/ui-config.js +225 -0
  221. package/dist/routes/ui-data.js +40 -35
  222. package/dist/routes/ui.js +66 -56
  223. package/dist/runtime/admission.js +4 -2
  224. package/dist/runtime/call-admission.js +3 -1
  225. package/dist/runtime/connector-scope.js +1 -4
  226. package/dist/runtime/run.js +10 -39
  227. package/dist/runtime/services.js +12 -117
  228. package/dist/runtime/shared-read.js +1 -3
  229. package/dist/runtime/storage.js +8 -1
  230. package/dist/sent-secrets.d.ts +52 -0
  231. package/dist/sent-secrets.js +474 -0
  232. package/dist/server.js +28 -71
  233. package/dist/skills.js +406 -174
  234. package/dist/sqlite.d.ts +48 -0
  235. package/dist/sqlite.js +166 -0
  236. package/dist/storage/keys.js +428 -0
  237. package/dist/storage/memory.js +7 -0
  238. package/dist/storage/sql.d.ts +66 -0
  239. package/dist/storage/sql.js +534 -0
  240. package/dist/timeout.js +4 -2
  241. package/dist/tool-name.js +17 -0
  242. package/dist/tool-safety.d.ts +9 -35
  243. package/dist/tool-safety.js +20 -40
  244. package/dist/types.d.ts +320 -54
  245. package/dist/types.js +0 -1
  246. package/dist/ui.d.ts +7 -19
  247. package/dist/ui.js +76 -90
  248. package/dist/url-safety.js +3 -6
  249. package/dist/usage-guide.js +137 -0
  250. package/dist/validate.d.ts +7 -0
  251. package/dist/validate.js +309 -47
  252. package/dist/version.d.ts +1 -7
  253. package/dist/version.js +3 -7
  254. package/dist/worker.d.ts +11 -0
  255. package/dist/worker.js +434 -0
  256. package/documentation/architecture.md +664 -231
  257. package/documentation/auth.md +936 -546
  258. package/documentation/code-mode.md +501 -312
  259. package/documentation/meta-tools.md +449 -200
  260. package/documentation/operator-tests.md +93 -0
  261. package/documentation/provider-migration-0.29.md +60 -0
  262. package/examples/worker/AGENTS.md +34 -12
  263. package/examples/worker/README.md +286 -255
  264. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  265. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  266. package/examples/worker/src/connecta.config.ts +151 -0
  267. package/examples/worker/src/index.ts +14 -197
  268. package/examples/worker/wrangler.jsonc +15 -35
  269. package/package.json +120 -54
  270. package/templates/node/.dockerignore +1 -2
  271. package/templates/node/.env.example +20 -21
  272. package/templates/node/AGENTS.md +21 -17
  273. package/templates/node/Dockerfile +3 -5
  274. package/templates/node/README.md +133 -58
  275. package/templates/node/docker-compose.yml +11 -12
  276. package/templates/node/package.json +4 -2
  277. package/templates/node/src/connecta.config.ts +119 -0
  278. package/templates/node/src/index.ts +9 -154
  279. package/templates/node/src/provision-token.ts +17 -0
  280. package/AGENTS.md +0 -170
  281. package/dist/artifacts/connector.d.ts +0 -58
  282. package/dist/artifacts/connector.js +0 -730
  283. package/dist/artifacts/document.js +0 -86
  284. package/dist/artifacts/guide.js +0 -140
  285. package/dist/artifacts/html-scan.js +0 -329
  286. package/dist/artifacts/json.js +0 -78
  287. package/dist/artifacts/kv-store.d.ts +0 -20
  288. package/dist/artifacts/kv-store.js +0 -140
  289. package/dist/artifacts/markdown.js +0 -478
  290. package/dist/artifacts/module.d.ts +0 -37
  291. package/dist/artifacts/module.js +0 -66
  292. package/dist/artifacts/operations.d.ts +0 -197
  293. package/dist/artifacts/operations.js +0 -1002
  294. package/dist/artifacts/refresh.d.ts +0 -55
  295. package/dist/artifacts/refresh.js +0 -236
  296. package/dist/artifacts/routes.js +0 -206
  297. package/dist/artifacts/types.d.ts +0 -214
  298. package/dist/artifacts/types.js +0 -22
  299. package/dist/artifacts/validate.d.ts +0 -62
  300. package/dist/artifacts/validate.js +0 -651
  301. package/dist/artifacts.d.ts +0 -6
  302. package/dist/artifacts.js +0 -9
  303. package/dist/auth/bearer.d.ts +0 -11
  304. package/dist/auth/bearer.js +0 -50
  305. package/dist/providers/basecamp.js +0 -445
  306. package/dist/providers/linear.js +0 -217
  307. package/dist/providers/mixpanel.js +0 -247
  308. package/dist/providers/revenuecat.js +0 -317
  309. package/dist/providers/stripe.js +0 -253
  310. package/dist/providers/vercel.js +0 -1253
  311. package/dist/storage/file.d.ts +0 -15
  312. package/dist/storage/file.js +0 -455
  313. package/ethos.md +0 -93
  314. package/examples/worker/src/cloudflare-kv.ts +0 -43
  315. package/examples/worker/src/d1-activity-row.ts +0 -155
  316. package/examples/worker/src/d1-activity.ts +0 -147
  317. package/examples/worker/src/d1-storage.ts +0 -138
  318. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  319. package/templates/node/src/file-activity.ts +0 -182
@@ -1,11 +1,13 @@
1
1
  # Connecta deployment
2
2
 
3
- This is the prescribed Node deployment — the only one. It runs locally from
4
- source and, unchanged, as a container. Install and run it:
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
- CONNECTA_TOKEN=dev-token npm start
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
- Then point an MCP client at `http://localhost:8787/mcp` with
19
- `Authorization: Bearer dev-token`.
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 — set CONNECTA_TOKEN to a long random value
28
-
29
- docker compose up -d --build
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
- `CONNECTA_TOKEN` ships empty on purpose: `up` fails on it until you set it,
33
- rather than starting a deployment whose bearer is a value published in this
34
- template. Everything else in `.env.example` has a working default.
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
- For scheduled artifact pages, uncomment the `artifacts()` module and hourly
50
- `setInterval` block in `src/index.ts`. The module uses the same CAS-capable
51
- state file and exposes `runDue()` for the timer; core starts no job on its own.
52
- Each tick starts at most 10 due pages. Refresh programs can call only shared
53
- connectors' explicitly read-only tools within the program owner's current
54
- grants. Revoking refresh or pool access stops future runs. Failed runs leave the last good data
55
- and mark the viewer stale.
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 the configured
59
- bearer to inspect Connections. Omit that option and import for an API-only
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 whether each tool runs in programs or needs approval. For
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/index.ts` carries the full shape commented out
73
- above `ui: operatorUi()`.
74
-
75
- Connection management needs an interactive identity. A configured bearer is a
76
- client key and never authorizes browser credential mutations. To enable Clerk:
77
-
78
- ```sh
79
- npm install @clerk/backend
80
- ```
81
-
82
- Set `CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`, enable the corresponding
83
- `clerkAuth` import and auth entry in `src/index.ts`, and set `PUBLIC_URL`.
84
- Enable Dynamic Client Registration on the Clerk instance if MCP clients should
85
- sign in with OAuth. Connecta no longer issues named client access tokens; keep
86
- the configured bearer only for clients that need it.
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
- Import `encryptedCredentialVault` from `@zackbart/connecta/credentials`, then
98
- set `vault: encryptedCredentialVault(storage, credentialKey)` — `storage` is
99
- the `fileStorage` binding `src/index.ts` already passes to `createConnecta`. Set
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 state file. Losing it makes saved values unreadable;
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 a provider such as `notion()`
110
- that declares its own slot. Authorized humans manage the slot inside that
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
- Import `activityHistory` from `@zackbart/connecta/activity` and wire the template's
126
- `fileActivityStore` through `activity: activityHistory({ store })`. The Activity
127
- tab appears for authorized readers when the store supports listing. Omit this
128
- option and its store wiring to record no activity.
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
- `src/file-activity.ts` belongs to the deployment. It appends payload-free events
131
- and periodically retains the newest 5,000, allowing a small slack window between
132
- rewrites. Docker stores the log on the state volume. It records no arguments,
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. There is no token tab,
140
- team roster, or policy editor.
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/index.ts` for connectors, auth, storage, and the public URL.
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=dev-token npm run doctor
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 seven-tool
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, seven tools, plus any catalog drift:
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 seven-tool surface.
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 the fileStorage JSON on a named volume.
1
+ # One service, no database server: state is one SQLite file on a named volume.
2
2
  #
3
- # cp .env.example .env # then set CONNECTA_TOKEN
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
- # Refuse to start without inbound auth — the same rule src/index.ts
18
- # enforces, stated early enough that `up` fails before the build.
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 state file under /data so it lands on the volume below.
25
- CONNECTA_STATE_FILE: ${CONNECTA_STATE_FILE:-/data/connecta-state.json}
26
- # Operator surface. Passed through so the matching blocks in
27
- # src/index.ts work in the container the moment they are uncommented;
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
- CONNECTA_ACTIVITY_FILE: ${CONNECTA_ACTIVITY_FILE:-/data/connecta-activity.jsonl}
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.0.0"
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
- "@zackbart/connecta": "0.28.1",
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 { api, createConnecta } from "@zackbart/connecta";
28
- import { fileStorage, listen } from "@zackbart/connecta/node";
29
- import { quickJsExecutor } from "@zackbart/connecta/quickjs";
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 token = process.env.CONNECTA_TOKEN;
39
- if (!token) {
40
- throw new Error(
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`);