@zackbart/connecta 0.24.3 → 0.25.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 (145) hide show
  1. package/AGENTS.md +58 -30
  2. package/CHANGELOG.md +317 -1
  3. package/README.md +5 -6
  4. package/dist/advertised-schema.js +53 -0
  5. package/dist/auth/downstream-oauth.js +490 -106
  6. package/dist/branding.d.ts +31 -2
  7. package/dist/branding.js +116 -8
  8. package/dist/call-admission.js +139 -113
  9. package/dist/catalog-service.js +141 -105
  10. package/dist/catalog.js +258 -33
  11. package/dist/concurrency.js +24 -23
  12. package/dist/connector-scope.js +6 -37
  13. package/dist/connectors/api.d.ts +3 -3
  14. package/dist/connectors/api.js +10 -2
  15. package/dist/connectors/guarded-fetch.js +34 -4
  16. package/dist/connectors/remote-mcp.d.ts +6 -5
  17. package/dist/connectors/remote-mcp.js +325 -245
  18. package/dist/credential-contract.d.ts +9 -0
  19. package/dist/credential-rules.js +2 -2
  20. package/dist/credentials.d.ts +6 -1
  21. package/dist/credentials.js +45 -3
  22. package/dist/errors.d.ts +11 -3
  23. package/dist/errors.js +2 -1
  24. package/dist/execute.js +363 -257
  25. package/dist/executor-admission.js +67 -50
  26. package/dist/executors/quickjs-runtime.js +4 -2
  27. package/dist/executors/quickjs.js +251 -210
  28. package/dist/fix-prompt.js +37 -0
  29. package/dist/index.d.ts +11 -2
  30. package/dist/index.js +63 -21
  31. package/dist/invocation.js +324 -285
  32. package/dist/meta-tools.js +304 -114
  33. package/dist/oauth-callback-outcome.js +94 -0
  34. package/dist/oauth-sealing.js +29 -0
  35. package/dist/operator-ui/fix-prompts.js +87 -0
  36. package/dist/operator-ui/generated.js +2 -2
  37. package/dist/operator-ui/model.d.ts +46 -1
  38. package/dist/operator-ui/setup-commands.js +53 -0
  39. package/dist/operator-ui/view.js +109 -2
  40. package/dist/program-source.js +205 -0
  41. package/dist/providers/cloudflare.d.ts +14 -2
  42. package/dist/providers/cloudflare.js +107 -16
  43. package/dist/providers/linear.d.ts +26 -4
  44. package/dist/providers/linear.js +19 -4
  45. package/dist/providers/mixpanel.d.ts +16 -3
  46. package/dist/providers/mixpanel.js +13 -2
  47. package/dist/providers/notion.d.ts +8 -1
  48. package/dist/providers/notion.js +85 -12
  49. package/dist/providers/revenuecat.d.ts +30 -4
  50. package/dist/providers/revenuecat.js +70 -5
  51. package/dist/providers/stripe.d.ts +10 -3
  52. package/dist/providers/stripe.js +30 -4
  53. package/dist/providers/vercel.js +12 -2
  54. package/dist/registry.d.ts +13 -5
  55. package/dist/registry.js +317 -258
  56. package/dist/routes/activity.js +120 -137
  57. package/dist/routes/credentials.js +174 -163
  58. package/dist/routes/mcp.js +129 -70
  59. package/dist/routes/oauth-management.js +60 -71
  60. package/dist/routes/oauth.js +44 -16
  61. package/dist/routes/operator.js +82 -0
  62. package/dist/routes/shared.d.ts +2 -0
  63. package/dist/routes/ui-data.js +210 -0
  64. package/dist/routes/ui.js +64 -32
  65. package/dist/runtime/admission.js +43 -0
  66. package/dist/runtime/call-admission.js +67 -0
  67. package/dist/runtime/connector-scope.js +72 -0
  68. package/dist/runtime/run.js +174 -0
  69. package/dist/runtime/services.js +130 -0
  70. package/dist/runtime/storage.js +66 -0
  71. package/dist/server.js +111 -89
  72. package/dist/skills.js +1 -1
  73. package/dist/storage/file.js +39 -2
  74. package/dist/storage/memory.js +28 -16
  75. package/dist/timeout.js +17 -47
  76. package/dist/types.d.ts +64 -7
  77. package/dist/ui.d.ts +16 -2
  78. package/dist/ui.js +68 -176
  79. package/dist/url-safety.js +157 -0
  80. package/dist/version.d.ts +1 -1
  81. package/dist/version.js +1 -1
  82. package/documentation/architecture.md +368 -182
  83. package/documentation/auth.md +277 -176
  84. package/documentation/code-mode.md +479 -329
  85. package/documentation/meta-tools.md +494 -417
  86. package/ethos.md +44 -47
  87. package/examples/worker/AGENTS.md +2 -1
  88. package/examples/worker/README.md +62 -14
  89. package/examples/worker/src/d1-storage.ts +116 -0
  90. package/examples/worker/src/index.ts +18 -21
  91. package/examples/worker/wrangler.jsonc +16 -4
  92. package/package.json +11 -4
  93. package/templates/node/.env.example +3 -3
  94. package/templates/node/AGENTS.md +5 -4
  95. package/templates/node/README.md +2 -1
  96. package/templates/node/package.json +1 -1
  97. package/templates/node/src/index.ts +23 -22
  98. package/dist/auth/downstream-oauth.d.ts +0 -200
  99. package/dist/catalog-fingerprint.d.ts +0 -19
  100. package/dist/catalog-limits.d.ts +0 -9
  101. package/dist/catalog-service.d.ts +0 -263
  102. package/dist/catalog.d.ts +0 -87
  103. package/dist/concurrency.d.ts +0 -3
  104. package/dist/connectors/guarded-fetch.d.ts +0 -92
  105. package/dist/credential-rules.d.ts +0 -85
  106. package/dist/execute.d.ts +0 -154
  107. package/dist/executor-result.d.ts +0 -10
  108. package/dist/executors/quickjs-child.d.ts +0 -1
  109. package/dist/executors/quickjs-protocol.d.ts +0 -67
  110. package/dist/favicon.d.ts +0 -1
  111. package/dist/identity.d.ts +0 -4
  112. package/dist/invocation.d.ts +0 -74
  113. package/dist/mcp-result.d.ts +0 -8
  114. package/dist/meta-tools.d.ts +0 -129
  115. package/dist/operator-ui/generated.d.ts +0 -2
  116. package/dist/operator-ui/view.d.ts +0 -151
  117. package/dist/result-shapes.d.ts +0 -13
  118. package/dist/retired-toolkits.d.ts +0 -2
  119. package/dist/routes/activity.d.ts +0 -2
  120. package/dist/routes/credentials.d.ts +0 -2
  121. package/dist/routes/mcp.d.ts +0 -10
  122. package/dist/routes/oauth-management.d.ts +0 -2
  123. package/dist/routes/oauth.d.ts +0 -2
  124. package/dist/routes/ui.d.ts +0 -2
  125. package/dist/server.d.ts +0 -9
  126. package/dist/skills.d.ts +0 -44
  127. package/dist/timeout.d.ts +0 -21
  128. package/dist/tool-safety.d.ts +0 -9
  129. package/documentation/call-admission.md +0 -158
  130. package/documentation/cloudflare.md +0 -471
  131. package/documentation/connector-guides.md +0 -176
  132. package/documentation/connectors.md +0 -431
  133. package/documentation/linear.md +0 -193
  134. package/documentation/mixpanel.md +0 -160
  135. package/documentation/notion.md +0 -308
  136. package/documentation/operations.md +0 -359
  137. package/documentation/operator-ui.md +0 -135
  138. package/documentation/optional-modules-upgrade.md +0 -243
  139. package/documentation/provider-conventions.md +0 -729
  140. package/documentation/request-admission.md +0 -204
  141. package/documentation/revenuecat.md +0 -305
  142. package/documentation/storage-and-credentials.md +0 -254
  143. package/documentation/stripe.md +0 -262
  144. package/documentation/upgrading.md +0 -768
  145. package/documentation/vercel.md +0 -241
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The frame every copyable fix prompt shares: the operator page's failure
3
+ * notices and the OAuth callback page both hand an operator text to paste into
4
+ * a coding agent working in the deployment's repository.
5
+ *
6
+ * A prompt is assembled from fixed catalogue text and, at most, a configured
7
+ * connector id. It has no parameter an error message, a downstream body, a
8
+ * token, or a URL could arrive through — which is the whole guarantee: the
9
+ * text leaves the page on a clipboard, into a tool the deployment does not
10
+ * control, so it must be something the deployment would be content to publish.
11
+ * The agent finds the underlying cause where it belongs, in the deployment's
12
+ * own logs and a local reproduction.
13
+ */
14
+ const CONNECTOR_ID_RE = /^[a-z0-9_-]+$/;
15
+ /**
16
+ * Render one prompt. A connector id is the only variable part, and only when
17
+ * it has the shape configuration enforces; anything else is dropped rather
18
+ * than escaped, since a value that is not a connector id is not config.
19
+ */
20
+ export function renderFixPrompt(spec, connectorId) {
21
+ const id = connectorId !== undefined && CONNECTOR_ID_RE.test(connectorId)
22
+ ? connectorId
23
+ : undefined;
24
+ return [
25
+ "Diagnose and fix a problem in this connecta deployment (the @zackbart/connecta package). " +
26
+ "It is configured as code: connectors, credential slots, OAuth clients, pools, and inbound auth " +
27
+ "are declared in the deployment's source and environment, so the fix belongs there and not in the operator page.",
28
+ `Problem: ${spec.problem}` + (id ? `\nConnector id: ${id}` : ""),
29
+ `Where to look:\n${spec.steps.map((step) => `- ${step}`).join("\n")}`,
30
+ "This prompt deliberately carries no error text, tokens, or URLs. Find the underlying cause in the deployment's " +
31
+ "own logs (lines prefixed [connecta]) or by reproducing the failure locally. Never put a secret in source, " +
32
+ "a log line, or your reply: secrets belong in the deployment's environment or its credential vault.",
33
+ "Make the smallest change that fixes it, then verify it by redeploying and refreshing the connection on the " +
34
+ "operator Connections page. If the fix needs something you cannot do yourself — a provider console setting, " +
35
+ "a secret only the operator holds — name that exact step instead.",
36
+ ].join("\n\n");
37
+ }
package/dist/index.d.ts CHANGED
@@ -47,7 +47,7 @@ export interface ConnectaCallsConfig {
47
47
  /**
48
48
  * Max inline result size (bytes) before truncation and `get_result` paging.
49
49
  * Must be a finite whole number >= 1; invalid values warn and fall back to
50
- * 50_000. Connectors may override it individually.
50
+ * 24_000. Connectors may override it individually.
51
51
  */
52
52
  maxResultBytes?: number;
53
53
  }
@@ -81,6 +81,15 @@ export interface ConnectaExecuteConfig {
81
81
  * values fall back to the default.
82
82
  */
83
83
  hostCallTimeoutMs?: number;
84
+ /**
85
+ * Hard ceiling on one execution, in milliseconds, enforced outside the
86
+ * sandbox. Default 120_000, above both executors' own deadlines (QuickJS
87
+ * 30_000, the Dynamic Worker 60_000), so it only ends a run whose executor
88
+ * never settled; that run fails as unresponsive and its admission slot is
89
+ * released. Keep it above any raised executor deadline. Invalid values
90
+ * fall back to the default.
91
+ */
92
+ watchdogMs?: number;
84
93
  }
85
94
  export interface AdmissionPoolConfig {
86
95
  /** Simultaneous work admitted to this pool. */
@@ -222,5 +231,5 @@ export { CONNECTA_VERSION } from "./version.js";
222
231
  export type { Registry } from "./registry.js";
223
232
  export type { RemoteMcpOptions, RemoteMcpAuth, RemoteMcpRedirectPolicy, } from "./connectors/remote-mcp.js";
224
233
  export type { ApiOptions, ApiTool } from "./connectors/api.js";
225
- export type { CatalogDriftCounts, CatalogDriftReport, ConnectaBranding, Connector, ConnectorCallAdmissionInput, ConnectorCallAdmissionPolicy, ConnectorCallAdmissionRule, ConnectorRollingWindowBudget, ConnectorCredentialAccess, ConnectorCredentialConfig, ConnectorCredentialFieldConfig, ConnectorCredentialValues, ConnectorContext, ConnectorUsageGuide, ConnectorStatus, CredentialTestResult, AdmittingExecutor, AdmissionSnapshot, ExecuteResult, Executor, ExecutorLease, ExecutorProvider, InboundAuth, InboundAuthRuntimeContext, UiAuthConfig, AuthResult, AuthenticatedIdentity, IdentityReference, JsonSchema, KVStorage, Logger, ToolDef, ToolAnnotations, } from "./types.js";
234
+ export type { CatalogDriftCounts, CatalogDriftReport, ConnectaBranding, ConnectaTheme, Connector, ConnectorCallAdmissionInput, ConnectorCallAdmissionPolicy, ConnectorCallAdmissionRule, ConnectorRollingWindowBudget, ConnectorCredentialAccess, ConnectorCredentialConfig, ConnectorCredentialFieldConfig, ConnectorCredentialValues, ConnectorContext, ConnectorUsageGuide, ConnectorStatus, CredentialTestResult, AdmittingExecutor, AdmissionSnapshot, ExecuteResult, Executor, ExecutorLease, ExecutorProvider, InboundAuth, InboundAuthRuntimeContext, UiAuthConfig, AuthResult, AuthenticatedIdentity, IdentityReference, JsonSchema, KVStorage, Logger, ToolDef, ToolAnnotations, } from "./types.js";
226
235
  export type { ActivityActor, ActivityCallSource, ActivityOutcome, ActivityPage, ActivityReadActor, ActivityReadEvent, ActivityReader, ActivityReadGate, ActivityReadPage, ActivitySink, ActivityStore, AgentFriction, CatalogDriftActivityEvent, ToolCallActivityEvent, } from "./activity.js";
package/dist/index.js CHANGED
@@ -2,10 +2,12 @@ import { credentialTestRule, describeCredentialTestMismatch, } from "./credentia
2
2
  import { Registry } from "./registry.js";
3
3
  import { parseConnectorAccess, POOL_NAME_RE } from "./connector-access.js";
4
4
  import { createFetchHandler } from "./server.js";
5
- import { droppedBrandingUrls, droppedUiAuthUrls } from "./branding.js";
5
+ import { droppedBrandingUrls, droppedThemeTokens, droppedUiAuthUrls, } from "./branding.js";
6
6
  import { memoryStorage } from "./storage/memory.js";
7
7
  import { CONNECTA_VERSION } from "./version.js";
8
8
  import { AdmissionController, executorName, isAdmittingExecutor, withExecutorAdmission, } from "./executor-admission.js";
9
+ import { disposeEdgeRuntime } from "./runtime/run.js";
10
+ import { createCoreRuntime, resolveLogger } from "./runtime/services.js";
9
11
  const REQUEST_ADMISSION_DEFAULTS = {
10
12
  concurrency: 16,
11
13
  maxQueueSize: 32,
@@ -26,14 +28,6 @@ function admissionController(options, defaults) {
26
28
  retryAfterMs: options?.retryAfterMs ?? defaults.retryAfterMs,
27
29
  });
28
30
  }
29
- function defaultLogger() {
30
- return {
31
- debug: (...a) => console.debug("[connecta]", ...a),
32
- info: (...a) => console.info("[connecta]", ...a),
33
- warn: (...a) => console.warn("[connecta]", ...a),
34
- error: (...a) => console.error("[connecta]", ...a),
35
- };
36
- }
37
31
  /** Bearer providers are checked before Clerk (per spec). */
38
32
  function normalizeAuth(auth) {
39
33
  const list = auth ? (Array.isArray(auth) ? auth : [auth]) : [];
@@ -84,6 +78,7 @@ const CONFIG_SCHEMA = {
84
78
  maxEmittedBlocks: null,
85
79
  maxHostCalls: null,
86
80
  hostCallTimeoutMs: null,
81
+ watchdogMs: null,
87
82
  },
88
83
  admission: {
89
84
  requests: admissionPoolSchema,
@@ -151,7 +146,7 @@ function assertKnownConfig(config) {
151
146
  }
152
147
  }
153
148
  }
154
- if (config.vault && ["get", "getAll", "set", "setAll", "metadata", "delete"].some(key => typeof config.vault[key] !== "function"))
149
+ if (config.vault && (["get", "getAll", "set", "setAll", "metadata", "delete"].some(key => typeof config.vault[key] !== "function") || ["seal", "open"].some(key => !["undefined", "function"].includes(typeof config.vault[key]))))
155
150
  throw new Error("ConnectaConfig.vault must implement CredentialVault");
156
151
  if (config.ui && (typeof config.ui.handle !== "function" || typeof config.ui.credentialHandoffUrl !== "function" || !Array.isArray(config.ui.reservedPaths)))
157
152
  throw new Error("ConnectaConfig.ui must be created with operatorUi(...)");
@@ -262,6 +257,17 @@ function warnInsecureConfig(config, inboundAuth, logger) {
262
257
  "used as an href, so it must be an absolute http(s) URL (favicon.href " +
263
258
  "may also be a root-relative path). The default is rendered instead.");
264
259
  }
260
+ // Theme tokens are written into a `:root` block, so each is gated
261
+ // syntactically and a rejected value takes the stylesheet's default. Same
262
+ // reason as the branding URLs above: the page still renders, so without this
263
+ // line the only evidence is that the operator's color never showed up.
264
+ const droppedTheme = droppedThemeTokens(config.ui?.branding?.theme);
265
+ if (droppedTheme.length > 0) {
266
+ logger.warn(`[connecta] branding ${droppedTheme.join(", ")} dropped: accent must be ` +
267
+ "a hex color, radius a CSS length, the font families a plain " +
268
+ "font-family list, and colorScheme one of system/light/dark. The " +
269
+ "default is rendered instead.");
270
+ }
265
271
  // Operator shells render exactly one provider's browser sign-in config — the
266
272
  // first that offers one, matching the server route's `find` — and that
267
273
  // provider's URLs reach the browser: frontendApiUrl as the loader's
@@ -285,18 +291,31 @@ function warnInsecureConfig(config, inboundAuth, logger) {
285
291
  }
286
292
  // A credential test hook that cannot test the declared credential shape.
287
293
  // The shape picks the hook (see `credentialTestRule`) and the other one is
288
- // never substituted, so the connector is simply not testable: /credentials offers no
289
- // Test action and the route answers 400. Without this line the only way to
290
- // discover the mistake is to click a button that isn't there.
294
+ // never substituted, so the connection offers no Test action in the operator
295
+ // UI and the route answers 400. Without this line the only way to discover
296
+ // the mistake is to click a button that isn't there.
291
297
  for (const connector of config.connectors) {
292
298
  const { mismatch } = credentialTestRule(connector);
293
299
  if (!mismatch)
294
300
  continue;
295
301
  logger.warn(`[connecta] connector "${connector.id}" cannot test its credential: ` +
296
- `${describeCredentialTestMismatch(mismatch)}. /credentials offers no Test ` +
302
+ `${describeCredentialTestMismatch(mismatch)}. The connection in the operator UI offers no Test ` +
297
303
  `action and POST /ui/credentials/${connector.id}/test answers 400 ` +
298
304
  "until the matching hook is implemented.");
299
305
  }
306
+ // A vault written before seal/open existed encrypts credentials but cannot
307
+ // seal downstream OAuth state, so tokens stay plaintext at rest beside them.
308
+ const vault = config.vault;
309
+ if (vault && (typeof vault.seal !== "function" || typeof vault.open !== "function")) {
310
+ for (const connector of config.connectors) {
311
+ if (!connector.startAuth)
312
+ continue;
313
+ logger.warn(`[connecta] connector "${connector.id}" stores its downstream OAuth ` +
314
+ "tokens, client registration, and PKCE verifier as plaintext: the " +
315
+ "configured vault implements no `seal`/`open`. Use " +
316
+ "encryptedCredentialVault(...) or add both methods to seal them.");
317
+ }
318
+ }
300
319
  // OAuth connectors whose callback cannot perform a state/CSRF check. The
301
320
  // public route refuses every callback for these connectors rather than hand
302
321
  // an unverified code to finishAuth, so this warning explains why auth cannot
@@ -320,9 +339,7 @@ export function createConnecta(config) {
320
339
  '"@cloudflare/codemode" on Workers.');
321
340
  }
322
341
  const storage = config.storage ?? memoryStorage();
323
- const logger = config.logger === "silent"
324
- ? { debug() { }, info() { }, warn() { }, error() { } }
325
- : config.logger ?? defaultLogger();
342
+ const logger = resolveLogger(config.logger);
326
343
  const credentialVault = config.vault;
327
344
  const configuredAuth = normalizeAuth(config.auth);
328
345
  const serverInfo = {
@@ -370,6 +387,22 @@ export function createConnecta(config) {
370
387
  "implements acquire() and owns its admission pool; configure that " +
371
388
  "executor's concurrency and queue options instead.");
372
389
  }
390
+ // Every structural check has passed, so this is the configuration the
391
+ // deployment runs with. Creating the runtime builds nothing — a Worker may
392
+ // construct its Connecta at global scope, where no fiber may start.
393
+ const runtime = createCoreRuntime(registry, {
394
+ storage,
395
+ logger,
396
+ vault: credentialVault,
397
+ activity: config.activity,
398
+ config: {
399
+ config,
400
+ serverInfo,
401
+ auth: inboundAuth,
402
+ pools,
403
+ executorName: configuredExecutorName,
404
+ },
405
+ });
373
406
  const handler = createFetchHandler({
374
407
  registry,
375
408
  auth: inboundAuth,
@@ -393,6 +426,7 @@ export function createConnecta(config) {
393
426
  maxEmittedBlocks: config.execute?.maxEmittedBlocks,
394
427
  maxHostCalls: config.execute?.maxHostCalls,
395
428
  hostCallTimeoutMs: config.execute?.hostCallTimeoutMs,
429
+ watchdogMs: config.execute?.watchdogMs,
396
430
  credentialVault,
397
431
  ui: config.ui,
398
432
  deploymentInfo: config.deploymentInfo,
@@ -406,10 +440,18 @@ export function createConnecta(config) {
406
440
  registry,
407
441
  close: async () => {
408
442
  closePromise ??= Promise.resolve().then(async () => {
409
- requestAdmission.close();
410
- codeAdmission?.close();
411
- registry.closeCallAdmission();
412
- await config.executor?.close?.();
443
+ try {
444
+ requestAdmission.close();
445
+ codeAdmission?.close();
446
+ registry.closeCallAdmission();
447
+ await config.executor?.close?.();
448
+ }
449
+ finally {
450
+ // Last, and whether or not the executor closed cleanly. A fiber
451
+ // already running keeps the services it was given; only a run
452
+ // started after this is refused.
453
+ await disposeEdgeRuntime(runtime);
454
+ }
413
455
  });
414
456
  await closePromise;
415
457
  },