@zackbart/connecta 0.28.1 → 0.30.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 (332) hide show
  1. package/CHANGELOG.md +855 -5
  2. package/README.md +125 -116
  3. package/bin/connecta.mjs +121 -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/argument-redaction.js +230 -0
  12. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  13. package/dist/auth/clerk-sdk/client.js +5 -0
  14. package/dist/auth/clerk-sdk/generated.js +1845 -0
  15. package/dist/auth/clerk-transport.js +19 -0
  16. package/dist/auth/clerk.d.ts +17 -7
  17. package/dist/auth/clerk.js +189 -107
  18. package/dist/auth/cloudflare-access.js +25 -26
  19. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  20. package/dist/auth/downstream-client-metadata.js +133 -0
  21. package/dist/auth/downstream-oauth.js +1584 -1580
  22. package/dist/auth/downstream-revocation.js +106 -0
  23. package/dist/auth/oauth-token-response.js +231 -0
  24. package/dist/auth/oauth-v2-migration.js +116 -0
  25. package/dist/auth/static-oauth.js +93 -71
  26. package/dist/auth-elicitation.js +288 -0
  27. package/dist/authorization-handoff.js +111 -0
  28. package/dist/branding.d.ts +8 -16
  29. package/dist/branding.js +26 -59
  30. package/dist/byte-read-response.js +39 -0
  31. package/dist/call-admission.js +12 -16
  32. package/dist/catalog-cache.js +739 -0
  33. package/dist/catalog-drift.d.ts +50 -15
  34. package/dist/catalog-drift.js +365 -128
  35. package/dist/catalog-fingerprint.js +2 -4
  36. package/dist/catalog-freshness.js +13 -0
  37. package/dist/catalog-limits.js +2 -2
  38. package/dist/catalog-service.js +409 -225
  39. package/dist/catalog.js +43 -84
  40. package/dist/concurrency.js +3 -4
  41. package/dist/config-defaults.d.ts +39 -0
  42. package/dist/config-defaults.js +46 -0
  43. package/dist/config-schema.d.ts +181 -0
  44. package/dist/config-schema.js +435 -0
  45. package/dist/config-value-sources.js +102 -0
  46. package/dist/config.d.ts +633 -0
  47. package/dist/config.js +508 -0
  48. package/dist/connector-access.d.ts +1 -0
  49. package/dist/connector-access.js +13 -15
  50. package/dist/connector-caller.d.ts +8 -0
  51. package/dist/connector-caller.js +15 -6
  52. package/dist/connector-scope.js +1 -1
  53. package/dist/connectors/api-connector.d.ts +59 -7
  54. package/dist/connectors/api-connector.js +106 -19
  55. package/dist/connectors/api.js +4 -1
  56. package/dist/connectors/guarded-fetch.d.ts +144 -0
  57. package/dist/connectors/guarded-fetch.js +87 -38
  58. package/dist/connectors/negotiation-cache.js +46 -0
  59. package/dist/connectors/option-shapes.js +27 -0
  60. package/dist/connectors/remote-mcp.d.ts +30 -4
  61. package/dist/connectors/remote-mcp.js +1774 -320
  62. package/dist/connectors/resource-uri.js +335 -0
  63. package/dist/credential-contract.d.ts +10 -0
  64. package/dist/credential-rules.js +1 -3
  65. package/dist/credential-url.js +108 -0
  66. package/dist/credentials.d.ts +6 -1
  67. package/dist/credentials.js +27 -18
  68. package/dist/d1.d.ts +130 -0
  69. package/dist/d1.js +290 -0
  70. package/dist/describe-config.d.ts +165 -0
  71. package/dist/describe-config.js +315 -0
  72. package/dist/described.js +106 -0
  73. package/dist/downstream-input-context.js +128 -0
  74. package/dist/downstream-input.js +325 -0
  75. package/dist/errors.d.ts +132 -31
  76. package/dist/errors.js +175 -87
  77. package/dist/execute.js +798 -318
  78. package/dist/executor-admission.d.ts +1 -0
  79. package/dist/executor-admission.js +11 -15
  80. package/dist/executor-contract.d.ts +20 -0
  81. package/dist/executor-contract.js +48 -0
  82. package/dist/executor-result.js +30 -13
  83. package/dist/executors/quickjs-child.js +15 -9
  84. package/dist/executors/quickjs-protocol.js +5 -6
  85. package/dist/executors/quickjs-runtime.js +152 -59
  86. package/dist/executors/quickjs.js +49 -39
  87. package/dist/favicon.js +1 -2
  88. package/dist/fix-prompt.js +1 -3
  89. package/dist/guest-runtime.js +238 -0
  90. package/dist/guest-types.d.ts +218 -0
  91. package/dist/guest-types.js +1 -0
  92. package/dist/html-security.js +24 -0
  93. package/dist/identity.js +1 -3
  94. package/dist/inbound-credential.js +52 -0
  95. package/dist/index.d.ts +24 -256
  96. package/dist/index.js +65 -478
  97. package/dist/invocation-auth.js +69 -0
  98. package/dist/invocation.js +262 -79
  99. package/dist/json-schema.d.ts +1 -1
  100. package/dist/mcp-client-context.js +35 -0
  101. package/dist/mcp-result.js +11 -7
  102. package/dist/meta-output.js +61 -0
  103. package/dist/meta-tool-names.d.ts +2 -0
  104. package/dist/meta-tool-names.js +3 -0
  105. package/dist/meta-tools.js +419 -272
  106. package/dist/module-contracts.d.ts +8 -23
  107. package/dist/node.d.ts +0 -2
  108. package/dist/node.js +2 -2
  109. package/dist/oauth-callback-outcome.js +1 -3
  110. package/dist/oauth-handoff.js +194 -0
  111. package/dist/oauth-sealing.js +20 -0
  112. package/dist/operator-record.d.ts +137 -0
  113. package/dist/operator-record.js +420 -0
  114. package/dist/operator-ui/assets.js +31 -0
  115. package/dist/operator-ui/contract.d.ts +72 -0
  116. package/dist/operator-ui/contract.js +1 -0
  117. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  118. package/dist/operator-ui/generated.js +5 -4
  119. package/dist/operator-ui/model.d.ts +12 -87
  120. package/dist/operator-ui/model.js +2 -9
  121. package/dist/operator-ui/setup-commands.js +1 -3
  122. package/dist/operator-ui/view.d.ts +323 -0
  123. package/dist/operator-ui/view.js +62 -189
  124. package/dist/page-styles.js +2 -3
  125. package/dist/program-result.js +5 -0
  126. package/dist/program-source.js +14 -2
  127. package/dist/{exempt-writes.js → program-writes.js} +60 -19
  128. package/dist/provider.d.ts +142 -0
  129. package/dist/provider.js +187 -0
  130. package/dist/providers/_shared/google/result-size.js +57 -0
  131. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  132. package/dist/providers/_shared/google/service-account.js +500 -0
  133. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  134. package/dist/providers/_shared/google/workspace.js +518 -0
  135. package/dist/providers/_shared/request-token.js +83 -0
  136. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  137. package/dist/providers/basecamp/index.js +997 -0
  138. package/dist/providers/basecamp/skill.generated.js +9 -0
  139. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  140. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  141. package/dist/providers/breeze/skill.generated.js +9 -0
  142. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  143. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  144. package/dist/providers/ccb/skill.generated.js +9 -0
  145. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  146. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  147. package/dist/providers/cloudflare/skill.generated.js +10 -0
  148. package/dist/providers/docs/index.d.ts +54 -0
  149. package/dist/providers/docs/index.js +1237 -0
  150. package/dist/providers/docs/skill.generated.js +9 -0
  151. package/dist/providers/drive/index.d.ts +53 -0
  152. package/dist/providers/drive/index.js +1523 -0
  153. package/dist/providers/drive/skill.generated.js +9 -0
  154. package/dist/providers/forms/index.d.ts +54 -0
  155. package/dist/providers/forms/index.js +1217 -0
  156. package/dist/providers/forms/skill.generated.js +11 -0
  157. package/dist/providers/github/auth.d.ts +32 -0
  158. package/dist/providers/github/auth.js +361 -0
  159. package/dist/providers/github/hosted.js +218 -0
  160. package/dist/providers/github/index.d.ts +13 -0
  161. package/dist/providers/github/index.js +140 -0
  162. package/dist/providers/github/key.js +100 -0
  163. package/dist/providers/github/rest.js +321 -0
  164. package/dist/providers/github/scope.d.ts +29 -0
  165. package/dist/providers/github/scope.js +124 -0
  166. package/dist/providers/github/skill.generated.js +8 -0
  167. package/dist/providers/gmail/index.d.ts +51 -0
  168. package/dist/providers/gmail/index.js +1436 -0
  169. package/dist/providers/gmail/skill.generated.js +9 -0
  170. package/dist/providers/infisical/index.d.ts +7 -0
  171. package/dist/providers/infisical/index.js +713 -0
  172. package/dist/providers/infisical/skill.generated.js +8 -0
  173. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  174. package/dist/providers/linear/index.js +352 -0
  175. package/dist/providers/linear/skill.generated.js +8 -0
  176. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  177. package/dist/providers/mixpanel/index.js +416 -0
  178. package/dist/providers/mixpanel/skill.generated.js +9 -0
  179. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  180. package/dist/providers/{notion.js → notion/index.js} +226 -239
  181. package/dist/providers/notion/skill.generated.js +10 -0
  182. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  183. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  184. package/dist/providers/overflow/skill.generated.js +8 -0
  185. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  186. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  187. package/dist/providers/planning-center/skill.generated.js +10 -0
  188. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  189. package/dist/providers/revenuecat/index.js +557 -0
  190. package/dist/providers/revenuecat/skill.generated.js +11 -0
  191. package/dist/providers/sheets/index.d.ts +54 -0
  192. package/dist/providers/sheets/index.js +1237 -0
  193. package/dist/providers/sheets/skill.generated.js +10 -0
  194. package/dist/providers/slides/index.d.ts +53 -0
  195. package/dist/providers/slides/index.js +2645 -0
  196. package/dist/providers/slides/skill.generated.js +9 -0
  197. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  198. package/dist/providers/stripe/index.js +266 -0
  199. package/dist/providers/stripe/skill.generated.js +11 -0
  200. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  201. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  202. package/dist/providers/tithely/skill.generated.js +8 -0
  203. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  204. package/dist/providers/vercel/index.js +1038 -0
  205. package/dist/providers/vercel/mcp-ownership.js +212 -0
  206. package/dist/providers/vercel/skill.generated.js +11 -0
  207. package/dist/registry.d.ts +110 -171
  208. package/dist/registry.js +588 -927
  209. package/dist/request-state.js +21 -0
  210. package/dist/resource-read.js +103 -0
  211. package/dist/result-shapes.js +17 -49
  212. package/dist/retired-toolkits.js +1 -1
  213. package/dist/routes/access-tokens.js +3 -6
  214. package/dist/routes/activity-context.js +21 -0
  215. package/dist/routes/activity-disclosure.js +40 -0
  216. package/dist/routes/activity.js +59 -21
  217. package/dist/routes/actor-labels.js +3 -50
  218. package/dist/routes/connect.js +221 -0
  219. package/dist/routes/credentials.js +15 -27
  220. package/dist/routes/mcp.js +365 -130
  221. package/dist/routes/oauth-client-metadata.js +15 -0
  222. package/dist/routes/oauth-management.js +45 -107
  223. package/dist/routes/oauth.js +110 -57
  224. package/dist/routes/operator.js +3 -3
  225. package/dist/routes/shared.d.ts +25 -64
  226. package/dist/routes/shared.js +160 -80
  227. package/dist/routes/skills.js +30 -0
  228. package/dist/routes/ui-config.js +225 -0
  229. package/dist/routes/ui-data.js +40 -35
  230. package/dist/routes/ui.js +66 -56
  231. package/dist/runtime/admission.js +4 -2
  232. package/dist/runtime/call-admission.js +3 -1
  233. package/dist/runtime/connector-scope.js +1 -4
  234. package/dist/runtime/run.js +10 -39
  235. package/dist/runtime/services.js +12 -117
  236. package/dist/runtime/shared-read.js +1 -3
  237. package/dist/runtime/storage.js +8 -1
  238. package/dist/sent-secrets.d.ts +93 -0
  239. package/dist/sent-secrets.js +1013 -0
  240. package/dist/server.js +28 -71
  241. package/dist/skills.js +416 -174
  242. package/dist/sqlite.d.ts +48 -0
  243. package/dist/sqlite.js +181 -0
  244. package/dist/storage/keys.js +443 -0
  245. package/dist/storage/memory.js +16 -4
  246. package/dist/storage/sql.d.ts +66 -0
  247. package/dist/storage/sql.js +543 -0
  248. package/dist/timeout.js +4 -2
  249. package/dist/tool-name.js +17 -0
  250. package/dist/tool-safety.d.ts +9 -35
  251. package/dist/tool-safety.js +20 -40
  252. package/dist/types.d.ts +336 -56
  253. package/dist/types.js +0 -1
  254. package/dist/ui.d.ts +7 -19
  255. package/dist/ui.js +76 -90
  256. package/dist/url-safety.js +3 -6
  257. package/dist/usage-guide.js +146 -0
  258. package/dist/validate.d.ts +7 -0
  259. package/dist/validate.js +309 -47
  260. package/dist/version.d.ts +1 -7
  261. package/dist/version.js +3 -7
  262. package/dist/worker.d.ts +11 -0
  263. package/dist/worker.js +435 -0
  264. package/documentation/README.md +63 -0
  265. package/documentation/architecture.md +696 -231
  266. package/documentation/auth.md +1069 -546
  267. package/documentation/code-mode.md +662 -356
  268. package/documentation/deploying.md +146 -0
  269. package/documentation/integrating.md +159 -0
  270. package/documentation/meta-tools.md +570 -216
  271. package/documentation/operating.md +135 -0
  272. package/documentation/operator-tests.md +97 -0
  273. package/documentation/operator-ui.md +132 -0
  274. package/documentation/provider-migration-0.29.md +65 -0
  275. package/examples/worker/AGENTS.md +34 -12
  276. package/examples/worker/README.md +293 -255
  277. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  278. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  279. package/examples/worker/src/connecta.config.ts +151 -0
  280. package/examples/worker/src/index.ts +14 -197
  281. package/examples/worker/wrangler.jsonc +15 -35
  282. package/package.json +126 -54
  283. package/templates/node/.dockerignore +1 -2
  284. package/templates/node/.env.example +20 -21
  285. package/templates/node/AGENTS.md +21 -17
  286. package/templates/node/Dockerfile +3 -5
  287. package/templates/node/README.md +140 -58
  288. package/templates/node/docker-compose.yml +11 -12
  289. package/templates/node/package.json +4 -2
  290. package/templates/node/src/connecta.config.ts +119 -0
  291. package/templates/node/src/index.ts +9 -154
  292. package/templates/node/src/provision-token.ts +17 -0
  293. package/AGENTS.md +0 -170
  294. package/dist/artifacts/connector.d.ts +0 -58
  295. package/dist/artifacts/connector.js +0 -730
  296. package/dist/artifacts/document.js +0 -86
  297. package/dist/artifacts/guide.js +0 -140
  298. package/dist/artifacts/html-scan.js +0 -329
  299. package/dist/artifacts/json.js +0 -78
  300. package/dist/artifacts/kv-store.d.ts +0 -20
  301. package/dist/artifacts/kv-store.js +0 -140
  302. package/dist/artifacts/markdown.js +0 -478
  303. package/dist/artifacts/module.d.ts +0 -37
  304. package/dist/artifacts/module.js +0 -66
  305. package/dist/artifacts/operations.d.ts +0 -197
  306. package/dist/artifacts/operations.js +0 -1002
  307. package/dist/artifacts/refresh.d.ts +0 -55
  308. package/dist/artifacts/refresh.js +0 -236
  309. package/dist/artifacts/routes.js +0 -206
  310. package/dist/artifacts/types.d.ts +0 -214
  311. package/dist/artifacts/types.js +0 -22
  312. package/dist/artifacts/validate.d.ts +0 -62
  313. package/dist/artifacts/validate.js +0 -651
  314. package/dist/artifacts.d.ts +0 -6
  315. package/dist/artifacts.js +0 -9
  316. package/dist/auth/bearer.d.ts +0 -11
  317. package/dist/auth/bearer.js +0 -50
  318. package/dist/providers/basecamp.js +0 -445
  319. package/dist/providers/linear.js +0 -217
  320. package/dist/providers/mixpanel.js +0 -247
  321. package/dist/providers/revenuecat.js +0 -317
  322. package/dist/providers/stripe.js +0 -253
  323. package/dist/providers/vercel.js +0 -1253
  324. package/dist/storage/file.d.ts +0 -15
  325. package/dist/storage/file.js +0 -455
  326. package/ethos.md +0 -93
  327. package/examples/worker/src/cloudflare-kv.ts +0 -43
  328. package/examples/worker/src/d1-activity-row.ts +0 -155
  329. package/examples/worker/src/d1-activity.ts +0 -147
  330. package/examples/worker/src/d1-storage.ts +0 -138
  331. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  332. package/templates/node/src/file-activity.ts +0 -182
package/dist/ui.js CHANGED
@@ -1,59 +1,50 @@
1
+ import { assertKnownOptions, keys, optionsOf } from "./config-schema.js";
1
2
  import { routeUi } from "./routes/ui.js";
2
3
  import { routeCredentials } from "./routes/credentials.js";
3
4
  import { routeOAuthManagement } from "./routes/oauth-management.js";
4
5
  import { uiData } from "./routes/ui-data.js";
5
6
  import { runEdge } from "./runtime/run.js";
6
- import { isExplicitlyReadOnly } from "./tool-safety.js";
7
- import { OPERATOR_UI_CSS, OPERATOR_UI_SCRIPT, } from "./operator-ui/generated.js";
7
+ import { OPERATOR_UI_STYLE_PATH, OPERATOR_UI_SCRIPT_PATH, OPERATOR_UI_NOTICES_PATH } from "./operator-ui/generated.js";
8
8
  export { filterUiConnectors, } from "./operator-ui/model.js";
9
- import { escapeHtml, isSafeHttpsUrl, notFoundResponse, renderPage, resolveBranding, } from "./branding.js";
9
+ import { escapeHtml, isSafeHttpsUrl, renderPage, resolveBranding } from "./branding.js";
10
10
  export { CONNECTA_FAVICON_SVG, resolveBranding, isSafeHttpUrl, isSafeHttpsUrl, isSafeIconHref } from "./branding.js";
11
- /**
12
- * A JS string literal safe to inline in a script element. Escaping `/` keeps
13
- * an operator-supplied `</script>` from terminating the element early.
14
- */
15
- function stringForInlineScript(value) {
16
- return JSON.stringify(value).replace(/\//g, "\\/");
17
- }
18
11
  const OPERATOR_PAGE_LABELS = {
19
- connections: "Connections",
12
+ overview: "Overview",
13
+ connections: "Connectors",
14
+ connector: "Connector",
15
+ tools: "Tools",
16
+ access: "Access",
17
+ config: "Config",
20
18
  tokens: "Access tokens",
21
19
  activity: "Activity",
22
- artifacts: "Artifacts",
23
- artifact: "Artifact",
24
20
  };
25
- /** `/artifacts/<id>` and its snapshots, `/artifacts/<id>/v/<version>`. */
26
- const ARTIFACT_PAGE = /^\/artifacts\/[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?(?:\/v\/\d{1,9})?$/;
27
21
  export function operatorPageForPath(path) {
28
- if (path === "/tokens")
29
- return "tokens";
30
22
  if (path === "/")
23
+ return "overview";
24
+ if (path === "/connectors")
31
25
  return "connections";
26
+ if (/^\/connectors\/[a-z0-9_-]+$/.test(path))
27
+ return "connector";
28
+ if (path === "/tools")
29
+ return "tools";
30
+ if (path === "/access")
31
+ return "access";
32
+ if (path === "/config")
33
+ return "config";
34
+ if (path === "/tokens")
35
+ return "tokens";
32
36
  if (path === "/activity")
33
37
  return "activity";
34
- if (path === "/artifacts")
35
- return "artifacts";
36
- if (ARTIFACT_PAGE.test(path))
37
- return "artifact";
38
38
  return undefined;
39
39
  }
40
- /** Everything artifact pages own: shells, their API, and the frame. */
41
- function isArtifactPath(path) {
42
- return path === "/artifacts" || path.startsWith("/artifacts/");
43
- }
44
40
  export function operatorPageTitle(page, configuredTitle) {
45
41
  return `${OPERATOR_PAGE_LABELS[page]} — ${configuredTitle}`;
46
42
  }
47
- /**
48
- * The badge a tool earns, from the predicates core enforces with. Computed
49
- * here so the page reports the rule rather than restating it. `exempt` is the
50
- * config exemption (#566), which `isApprovalExempt` already refuses to grant
51
- * a read-only tool.
52
- */
53
- export function uiToolSafety(definition, exempt = false) {
54
- if (isExplicitlyReadOnly(definition))
43
+ /** Report the verdict the registry stored, independent of pool trust. */
44
+ export function uiToolSafety(definition) {
45
+ if (definition.classification === "read")
55
46
  return "runs_in_programs";
56
- return exempt ? "exempt" : "needs_approval";
47
+ return "needs_approval";
57
48
  }
58
49
  /**
59
50
  * The fix-prompt key for a connector that is not usable. Chosen from the
@@ -63,6 +54,8 @@ export function uiToolSafety(definition, exempt = false) {
63
54
  export function uiProblemFor(connector, status, observed) {
64
55
  if (observed.credentialDrift)
65
56
  return "credential_mismatch";
57
+ if (status === "credential_required")
58
+ return "credential_required";
66
59
  if (status === "error")
67
60
  return "connector_unavailable";
68
61
  if (status === "auth_required") {
@@ -86,9 +79,7 @@ export function credentialManagementCapability(input) {
86
79
  * isolated: they surface status "error" with an empty tool list rather than
87
80
  * failing the whole payload.
88
81
  */
89
- export async function buildUiData(registry, baseUrl, serverInfo, credentialVault, activityEnabled = false, credentialManagement = credentialVault
90
- ? "available"
91
- : "requires_operator", defer, oauthManagement = false, discoveryConcurrency, personalCredentialOwner, detailOptions = {}) {
82
+ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault, activityEnabled = false, credentialManagement = credentialVault ? "available" : "requires_operator", defer, oauthManagement = false, discoveryConcurrency, personalCredentialOwner, detailOptions = {}) {
92
83
  return runEdge(uiData(registry, baseUrl, {
93
84
  serverInfo,
94
85
  credentialVault,
@@ -102,27 +93,24 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
102
93
  }));
103
94
  }
104
95
  function jsonForInlineScript(value) {
105
- return JSON.stringify(value)
106
- .replaceAll("<", "\\u003c")
107
- .replaceAll(">", "\\u003e")
108
- .replaceAll("&", "\\u0026");
96
+ return JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll(">", "\\u003e").replaceAll("&", "\\u0026");
109
97
  }
110
98
  /**
111
99
  * Every operator page serves this same data-free shell. Connector, credential,
112
100
  * and activity data arrives only through the authenticated `/ui/*` APIs.
113
101
  */
114
- export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "connections", options = {}) {
102
+ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding,
103
+ /** @deprecated Scripts load from same-origin assets; ignored. */
104
+ _nonce, page = "overview") {
115
105
  const clerk = uiAuth?.kind === "clerk" ? uiAuth : undefined;
116
106
  // The Clerk loader's origin. A value that fails the gate is dropped rather
117
107
  // than escaped into the page: the loader tag is simply not emitted, the gate
118
108
  // reports that Clerk could not load, and the rest of the shell still renders —
119
109
  // the same fallback-and-warn posture the branding URLs take, with the drop
120
110
  // named in a startup warning (see `droppedUiAuthUrls`).
121
- const clerkScriptOrigin = clerk && isSafeHttpsUrl(clerk.frontendApiUrl)
122
- ? clerk.frontendApiUrl
123
- : undefined;
111
+ const clerkScriptOrigin = clerk && isSafeHttpsUrl(clerk.frontendApiUrl) ? clerk.frontendApiUrl : undefined;
124
112
  // Enumerated field by field, because this object is serialized into the page's
125
- // inline script: a rejected frontendApiUrl must not reach the document through
113
+ // JSON config: a rejected frontendApiUrl must not reach the document through
126
114
  // `AUTH` after being kept out of the `<script src>`, and a rejected
127
115
  // signInUrl/signUpUrl — which `AUTH` is the only path into the page for — must
128
116
  // not reach it at all. Dropping one leaves the key absent, so `Clerk.load`
@@ -132,20 +120,13 @@ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "c
132
120
  kind: clerk.kind,
133
121
  publishableKey: clerk.publishableKey,
134
122
  ...(clerkScriptOrigin ? { frontendApiUrl: clerkScriptOrigin } : {}),
135
- ...(isSafeHttpsUrl(clerk.signInUrl)
136
- ? { signInUrl: clerk.signInUrl }
137
- : {}),
138
- ...(isSafeHttpsUrl(clerk.signUpUrl)
139
- ? { signUpUrl: clerk.signUpUrl }
140
- : {}),
123
+ ...(isSafeHttpsUrl(clerk.signInUrl) ? { signInUrl: clerk.signInUrl } : {}),
124
+ ...(isSafeHttpsUrl(clerk.signUpUrl) ? { signUpUrl: clerk.signUpUrl } : {}),
141
125
  }
142
126
  : (uiAuth ?? { kind: "bearer" });
143
127
  const brand = resolveBranding(branding);
144
- // When an operator shell ships a nonce-based CSP, every script it emits must
145
- // carry that nonce to run; without a nonce the markup is unchanged.
146
- const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
147
128
  const clerkScript = clerk && clerkScriptOrigin
148
- ? `<script${nonceAttr} crossorigin="anonymous" data-clerk-publishable-key="${escapeHtml(clerk.publishableKey)}" src="${escapeHtml(clerkScriptOrigin)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
129
+ ? `<script crossorigin="anonymous" data-clerk-publishable-key="${escapeHtml(clerk.publishableKey)}" src="${escapeHtml(clerkScriptOrigin)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
149
130
  : "";
150
131
  // The shared layout owns the head, scheme, theme, and masthead; the shell
151
132
  // adds its own bundle (which already imports the shared tokens), the nav
@@ -153,9 +134,10 @@ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "c
153
134
  return renderPage(branding, {
154
135
  title: operatorPageTitle(page, brand.pageTitle),
155
136
  uiMounted: true,
156
- iconOrigin: options.iconOrigin,
157
- styles: OPERATOR_UI_CSS,
158
- head: clerkScript,
137
+ operatorShell: true,
138
+ styles: "",
139
+ head: `<link rel="stylesheet" href="${OPERATOR_UI_STYLE_PATH}"><link rel="license" href="${OPERATOR_UI_NOTICES_PATH}">` +
140
+ clerkScript,
159
141
  skipTo: { id: "operatorContent", label: "Skip to operator page" },
160
142
  mastheadEnd: '<div id="operatorNav"></div>',
161
143
  body: `
@@ -170,28 +152,45 @@ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce, page = "c
170
152
  </div>
171
153
  </main>
172
154
  `,
173
- tail: `<script${nonceAttr}>
174
- const AUTH = ${jsonForInlineScript(auth)};
175
- const MCP_URL = ${jsonForInlineScript(mcpUrl)};
176
- const INITIAL_PAGE = ${jsonForInlineScript(page)};
177
- const HOME_URL = ${jsonForInlineScript(options.homeUrl ?? "/")};
178
- const TITLE_SUFFIX = ${jsonForInlineScript(brand.pageTitle)};
179
- const PRODUCT_NAME = ${stringForInlineScript(brand.productName)};
180
- const PRODUCT_DESCRIPTION = ${stringForInlineScript(brand.description)};
181
- const PRODUCT_OPERATOR_LABEL = ${stringForInlineScript(brand.productName + " operator")};
182
- ${OPERATOR_UI_SCRIPT}</script>`,
155
+ tail: `<script id="operatorConfig" type="application/json">${jsonForInlineScript({
156
+ auth,
157
+ mcpUrl,
158
+ initialPage: page,
159
+ titleSuffix: brand.pageTitle,
160
+ productName: brand.productName,
161
+ productDescription: brand.description,
162
+ productOperatorLabel: brand.productName + " operator",
163
+ })}</script><script defer src="${OPERATOR_UI_SCRIPT_PATH}"></script>`,
183
164
  });
184
165
  }
185
166
  function ownsOperatorPath(reserved, path) {
186
167
  if (path === "/activity" || path === "/tokens")
187
168
  return true;
188
- return reserved.some((pattern) => pattern.endsWith("/*")
189
- ? path.startsWith(pattern.slice(0, -1))
190
- : path === pattern);
169
+ return reserved.some((pattern) => pattern.endsWith("/*") ? path.startsWith(pattern.slice(0, -1)) : path === pattern);
191
170
  }
171
+ /** The closed options operatorUi() accepts; see `assertKnownOptions`. */
172
+ const OPERATOR_UI_OPTIONS = optionsOf()({
173
+ branding: optionsOf()({
174
+ ...keys("productName", "productUrl", "ownerName", "ownerUrl", "description", "pageTitle", "themeColor"),
175
+ favicon: optionsOf()(keys("svg", "ico", "href")),
176
+ theme: optionsOf()(keys("accent", "radius", "fontFamily", "monoFamily", "colorScheme")),
177
+ }),
178
+ });
192
179
  /** Mount the connection UI without enabling any storage or activity module. */
193
180
  export function operatorUi(options = {}) {
194
- const reservedPaths = ["/", "/ui", "/ui/*", "/favicon.svg", "/favicon.ico"];
181
+ options = assertKnownOptions(options, "operatorUi()", OPERATOR_UI_OPTIONS);
182
+ const reservedPaths = [
183
+ "/",
184
+ "/connectors",
185
+ "/connectors/*",
186
+ "/tools",
187
+ "/access",
188
+ "/config",
189
+ "/ui",
190
+ "/ui/*",
191
+ "/favicon.svg",
192
+ "/favicon.ico",
193
+ ];
195
194
  return {
196
195
  ...options,
197
196
  reservedPaths,
@@ -203,25 +202,12 @@ export function operatorUi(options = {}) {
203
202
  // route here, and no activity module, runs for a path this surface does
204
203
  // not own. The Activity page is the one addition; the server reserves
205
204
  // it only when history is readable, and routeUi checks the same thing.
206
- // Artifact pages are the other: they exist only beside the module.
207
- const artifacts = context.opts.artifactsModule;
208
- const artifactPath = Boolean(artifacts) && isArtifactPath(context.path);
209
- if (!artifactPath && !ownsOperatorPath(reservedPaths, context.path))
205
+ if (!ownsOperatorPath(reservedPaths, context.path))
210
206
  return null;
211
- if (artifactPath && !operatorPageForPath(context.path)) {
212
- // The pages' JSON API and the sandboxed frame; the module owns both,
213
- // and this bundle imports none of it.
214
- return (await artifacts?.handle(context)) ??
215
- notFoundResponse(context.request, context.opts);
216
- }
217
- const tokenResponse = await context.opts.accessTokens?.handle(context);
207
+ const tokenResponse = await context.opts.config.accessTokens?.handle(context);
218
208
  if (tokenResponse)
219
209
  return tokenResponse;
220
- const routes = [
221
- ...(context.opts.credentialVault ? [routeCredentials] : []),
222
- routeOAuthManagement,
223
- routeUi,
224
- ];
210
+ const routes = [...(context.opts.config.vault ? [routeCredentials] : []), routeOAuthManagement, routeUi];
225
211
  for (const route of routes) {
226
212
  const response = await route(context);
227
213
  if (response) {
@@ -234,7 +220,7 @@ export function operatorUi(options = {}) {
234
220
  return response;
235
221
  }
236
222
  }
237
- return context.opts.activityModule?.handle(context) ?? null;
223
+ return context.opts.config.activity?.handle(context) ?? null;
238
224
  },
239
225
  };
240
226
  }
@@ -48,7 +48,7 @@ function ipv6Pieces(host) {
48
48
  const pieces = (part) => {
49
49
  if (!part)
50
50
  return [];
51
- const parsed = part.split(":").map((piece) => HEXTET.test(piece) ? Number.parseInt(piece, 16) : Number.NaN);
51
+ const parsed = part.split(":").map((piece) => (HEXTET.test(piece) ? Number.parseInt(piece, 16) : Number.NaN));
52
52
  return parsed.some(Number.isNaN) ? undefined : parsed;
53
53
  };
54
54
  const head = pieces(halves[0]);
@@ -89,9 +89,7 @@ function classifyIpv6(pieces) {
89
89
  function canonicalHost(hostname) {
90
90
  if (/[/\\?#@]/.test(hostname))
91
91
  return undefined;
92
- const bracketed = hostname.includes(":") && !hostname.startsWith("[")
93
- ? `[${hostname}]`
94
- : hostname;
92
+ const bracketed = hostname.includes(":") && !hostname.startsWith("[") ? `[${hostname}]` : hostname;
95
93
  try {
96
94
  return new URL(`http://${bracketed}/`).hostname;
97
95
  }
@@ -144,8 +142,7 @@ export function learnedUrlRefusal(configured, target) {
144
142
  if (target.origin === configured.origin)
145
143
  return undefined;
146
144
  const targetClass = classifyHost(target.hostname);
147
- if (targetClass === "loopback" &&
148
- classifyHost(configured.hostname) === "loopback") {
145
+ if (targetClass === "loopback" && classifyHost(configured.hostname) === "loopback") {
149
146
  return undefined;
150
147
  }
151
148
  if (targetClass !== "public") {
@@ -0,0 +1,146 @@
1
+ /** Self-contained content for the usage skill; independent of skill registration. */
2
+ export const USAGE_SKILL = `---
3
+ name: usage
4
+ description: How to route work between one execute_code program and Connecta's explicit call, authorization, and result tools.
5
+ ---
6
+
7
+ # Connecta usage
8
+
9
+ ## Choose the route
10
+
11
+ | Work | Tool |
12
+ | --- | --- |
13
+ | One known-address read | call_tool |
14
+ | One known-address write | call_destructive_tool |
15
+ | Discovery, multiple or dependent calls, paging, reduction | execute_code |
16
+
17
+ Read one account with call_tool: \`{ "address": "crm.get_account", "args": { "id": "acct_42" } }\`.
18
+ Discover and call together when schemas suffice. search_tools covers catalog
19
+ inspection and write discovery in read-only pools. Programs may
20
+ write only in trusted pools; the tool description states this endpoint's trust.
21
+ Unannotated tools fail closed to writes.
22
+
23
+ ## The global and the envelopes
24
+
25
+ Pass JavaScript \`async () => { ... }\`, no arguments. Do not shadow \`connecta\`.
26
+ No imports, require, filesystem, fetch, or timers.
27
+
28
+ Use \`connecta.call(address, args, { timeoutMs })\` or
29
+ \`connecta.call({ address, args, timeoutMs })\`; both return \`{ data, format }\`.
30
+ \`format: "json"\` carries JSON; \`format: "text"\` carries text. Parse text only
31
+ when documented as JSON. Verify collection roots.
32
+
33
+ \`connecta.read("resource://id/"+encodeURIComponent(uri))\` returns \`{ contents }\` for advertised URIs.
34
+
35
+ Search/describe return \`{ tools }\`, never an array. Search also returns
36
+ \`catalogErrors\`, pagination, and possible \`absence\`; check failures before
37
+ selecting a match. JSON schemas are the program default. Compact schemas have
38
+ \`schemaFormat: "text"\`; do not read their .properties. Select the exact tool and
39
+ account/environment, inspect its schema, and supply its required keys and types.
40
+
41
+ Discover an unfamiliar read and reduce its result:
42
+
43
+ \`\`\`js
44
+ async () => {
45
+ const page = await connecta.search({ connector: "ci", query: "get_run" });
46
+ if (page.catalogErrors.length || page.absence) return page;
47
+ const tool = page.tools.find(t => t.name === "get_run");
48
+ if (!tool) return { gap: "get_run unavailable" };
49
+ const result = await connecta.call(tool.address, { runId: 42 });
50
+ return result.format === "json"
51
+ ? { status: result.data.status, jobId: result.data.failedJobId } : result;
52
+ }
53
+ \`\`\`
54
+
55
+ ## Keep partial successes
56
+
57
+ Use Promise.allSettled to keep independent successes. Rejections retain \`code\`,
58
+ \`message\`, \`retryable\`, and \`details\`. Branch on fields, not prose. Repair
59
+ arguments before batching. Runs report hostCalls separately.
60
+
61
+ Read JSON and text after verifying addresses and schemas:
62
+
63
+ \`\`\`js
64
+ async () => {
65
+ const settled = await Promise.allSettled([
66
+ connecta.call("ci.get_run", { runId: 42 }),
67
+ connecta.call("ci.get_job_logs", { jobId: "job_7" }),
68
+ connecta.call("ci.get_run", { runId: "wrong-type" })
69
+ ]);
70
+ return settled.map(s => s.status === "rejected"
71
+ ? { error: s.reason.code, retryable: s.reason.retryable }
72
+ : s.value.format === "text"
73
+ ? { text: s.value.data.split("\\n").filter(line => line.startsWith("ERROR")) }
74
+ : { status: s.value.data.status });
75
+ }
76
+ \`\`\`
77
+
78
+ Host-call budget exhaustion ends the run with budget_exceeded. Catch and
79
+ allSettled cannot recover it or return partial output. Bound batches before
80
+ starting them; search, describe, call, result, and skill spend host calls.
81
+ Split remaining reads. Never replay writes for their output.
82
+
83
+ ## Per-connector guides
84
+
85
+ Fetch a listed guide with skills by its exact name, or \`connecta.skill(name)\`
86
+ inside a program; it returns \`{ name, text, format: "text" }\`.
87
+ \`guideRequired: true\` is a hard stop. connector_required and approval_required
88
+ remain after schema expansion; schema_truncated clears after exact describe.
89
+ Otherwise fetch for units, pagination, aliases, or sequences.
90
+
91
+ ## Large results and recovery
92
+
93
+ Large direct results and program returns carry resultId when paging is available. Follow
94
+ nextOffset in UTF-8 bytes, reassemble and reduce inside one program. Use the
95
+ returned id in place of result-id below; this example assumes documented JSON.
96
+
97
+ Calls reconstruct up to 1 MiB through stash pages. Larger values return
98
+ \`{ format: "paged", valueFormat, resultId?, totalBytes, hint }\`. Pass the handle
99
+ to \`connecta.result\`. No resultId means no paging. IDs expire after
100
+ 15 minutes; only explicit paging spends extra host calls.
101
+ Use zero-based \`{ page: 0 }\` or UTF-8 \`{ offset }\`, never both. Keep maxBytes
102
+ fixed across pages; connector caps and the 32 KiB bridge ceiling apply.
103
+
104
+ \`\`\`js
105
+ async () => {
106
+ let text = "", offset = 0, page;
107
+ do {
108
+ page = await connecta.result("result-id", { offset, maxBytes: 8000 });
109
+ text += page.text;
110
+ offset = page.nextOffset;
111
+ } while (page.hasMore);
112
+ return JSON.parse(text).items.map(item => item.id);
113
+ }
114
+ \`\`\`
115
+
116
+ Oversized returns lead with nextAction. Never rerun writes for output. write_outcome_unknown forbids retry, even if caught.
117
+ Recovery args may be partial (argsRedacted) or absent (argsOmitted). Reconcile
118
+ before retrying with the original args or repeating a program that sent writes.
119
+ Non-retryable errors need repair.
120
+ host_auth_required needs host connection repair; provider_permission_denied needs
121
+ resource-owner access. Auth failures carry the handoff in error.data. When an auth
122
+ error includes a handoff, give it to the user or operator.
123
+
124
+ ## Output
125
+
126
+ Use \`connecta.emit(await connecta.call(address, args))\` for MCP rich output,
127
+ or \`for (const b of x.content ?? []) connecta.emit(b)\`. Both forward originals,
128
+ including resources and metadata; edits do not change them. Page oversized
129
+ native content with \`connecta.result(x.contentResult)\`.
130
+ Authored blocks accept text or base64 image/audio; images require canonical
131
+ base64 and PNG/JPEG/GIF/WebP MIME. Emit works without await, delivers on success,
132
+ and obeys byte/block budgets; text shares the return cap. Return reduced JSON.
133
+
134
+ `;
135
+ /** Kept in sync with GuestApi by the declaration test. These are type notation, not runnable JS. */
136
+ export const GUEST_API_DECLARATION = `search(args?: CatalogSearchArgs): Promise<CatalogSearchResult>;
137
+ describe(args?: CatalogDescribeArgs): Promise<{ tools: CatalogDescription[] }>;
138
+ call(address: string, args?: unknown, options?: { timeoutMs?: number }): Promise<GuestResult>;
139
+ call(request: { address: string; args?: unknown; timeoutMs?: number }): Promise<GuestResult>;
140
+ read(uri: string): Promise<GuestResourceResult>;
141
+ result(
142
+ id: string | GuestResultHandle,
143
+ options?: { page?: number; offset?: number; maxBytes?: number },
144
+ ): Promise<GuestResultPage>;
145
+ skill(name: string): Promise<{ name: string; text: string; format: "text" }>;
146
+ emit(block: GuestNativeBlock | GuestResult): PromiseLike<void>;`;
@@ -1,4 +1,5 @@
1
1
  import { ConnectorCallError } from "./errors.js";
2
+ import { type FailureSubject } from "./operator-record.js";
2
3
  import type { JsonSchema, Logger } from "./types.js";
3
4
  export interface ValidateToolInputOptions {
4
5
  /**
@@ -54,6 +55,12 @@ export interface CompileValidatorOptions {
54
55
  * the `Connector` interface directly.
55
56
  */
56
57
  export declare function validateToolInput(schema: JsonSchema, args: unknown, opts: ValidateToolInputOptions): ConnectorCallError | null;
58
+ /**
59
+ * `validateToolInput` for a tool connecta resolved in a catalog. `subject`
60
+ * names it in the log record when the schema is unusable; a caller's own
61
+ * address never reaches the log.
62
+ */
63
+ export declare function validateCatalogToolInput(schema: JsonSchema, args: unknown, opts: ValidateToolInputOptions, subject: FailureSubject): ConnectorCallError | null;
57
64
  /**
58
65
  * Eagerly compile and cache a tool's inputSchema, throwing when the validator
59
66
  * cannot use it. Reuses the same module-level cache `validateToolInput` reads,