@zackbart/connecta 0.24.4 → 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 (107) hide show
  1. package/AGENTS.md +40 -10
  2. package/CHANGELOG.md +253 -0
  3. package/dist/advertised-schema.js +53 -0
  4. package/dist/auth/downstream-oauth.js +490 -106
  5. package/dist/call-admission.js +139 -113
  6. package/dist/catalog-service.js +141 -105
  7. package/dist/catalog.js +258 -33
  8. package/dist/concurrency.js +24 -23
  9. package/dist/connector-scope.js +6 -37
  10. package/dist/connectors/api.d.ts +2 -2
  11. package/dist/connectors/remote-mcp.d.ts +6 -5
  12. package/dist/connectors/remote-mcp.js +317 -241
  13. package/dist/credential-contract.d.ts +9 -0
  14. package/dist/credential-rules.js +2 -2
  15. package/dist/credentials.d.ts +6 -1
  16. package/dist/credentials.js +45 -3
  17. package/dist/execute.js +363 -257
  18. package/dist/executor-admission.js +67 -50
  19. package/dist/executors/quickjs-runtime.js +4 -2
  20. package/dist/executors/quickjs.js +251 -210
  21. package/dist/fix-prompt.js +37 -0
  22. package/dist/index.d.ts +10 -1
  23. package/dist/index.js +51 -20
  24. package/dist/invocation.js +324 -285
  25. package/dist/meta-tools.js +206 -92
  26. package/dist/oauth-callback-outcome.js +94 -0
  27. package/dist/oauth-sealing.js +29 -0
  28. package/dist/operator-ui/fix-prompts.js +87 -0
  29. package/dist/operator-ui/generated.js +2 -2
  30. package/dist/operator-ui/model.d.ts +46 -1
  31. package/dist/operator-ui/setup-commands.js +53 -0
  32. package/dist/operator-ui/view.js +38 -2
  33. package/dist/program-source.js +205 -0
  34. package/dist/providers/notion.js +2 -2
  35. package/dist/providers/revenuecat.js +32 -5
  36. package/dist/providers/stripe.d.ts +3 -2
  37. package/dist/providers/vercel.js +1 -1
  38. package/dist/registry.d.ts +1 -1
  39. package/dist/registry.js +305 -260
  40. package/dist/routes/activity.js +120 -137
  41. package/dist/routes/credentials.js +174 -163
  42. package/dist/routes/mcp.js +129 -70
  43. package/dist/routes/oauth-management.js +60 -71
  44. package/dist/routes/oauth.js +44 -16
  45. package/dist/routes/operator.js +82 -0
  46. package/dist/routes/shared.d.ts +2 -0
  47. package/dist/routes/ui-data.js +210 -0
  48. package/dist/routes/ui.js +64 -32
  49. package/dist/runtime/admission.js +43 -0
  50. package/dist/runtime/call-admission.js +67 -0
  51. package/dist/runtime/connector-scope.js +72 -0
  52. package/dist/runtime/run.js +174 -0
  53. package/dist/runtime/services.js +130 -0
  54. package/dist/runtime/storage.js +66 -0
  55. package/dist/server.js +111 -89
  56. package/dist/skills.js +1 -1
  57. package/dist/storage/file.js +39 -2
  58. package/dist/storage/memory.js +28 -16
  59. package/dist/timeout.js +17 -47
  60. package/dist/types.d.ts +27 -7
  61. package/dist/ui.d.ts +16 -2
  62. package/dist/ui.js +50 -166
  63. package/dist/url-safety.js +157 -0
  64. package/dist/version.d.ts +1 -1
  65. package/dist/version.js +1 -1
  66. package/documentation/architecture.md +184 -10
  67. package/documentation/auth.md +80 -0
  68. package/documentation/code-mode.md +69 -24
  69. package/documentation/meta-tools.md +164 -27
  70. package/ethos.md +44 -47
  71. package/examples/worker/README.md +51 -5
  72. package/examples/worker/src/d1-storage.ts +116 -0
  73. package/examples/worker/src/index.ts +6 -6
  74. package/examples/worker/wrangler.jsonc +16 -4
  75. package/package.json +11 -3
  76. package/templates/node/package.json +1 -1
  77. package/dist/auth/downstream-oauth.d.ts +0 -200
  78. package/dist/catalog-fingerprint.d.ts +0 -19
  79. package/dist/catalog-limits.d.ts +0 -9
  80. package/dist/catalog-service.d.ts +0 -263
  81. package/dist/catalog.d.ts +0 -87
  82. package/dist/concurrency.d.ts +0 -3
  83. package/dist/connectors/guarded-fetch.d.ts +0 -96
  84. package/dist/credential-rules.d.ts +0 -85
  85. package/dist/execute.d.ts +0 -154
  86. package/dist/executor-result.d.ts +0 -10
  87. package/dist/executors/quickjs-child.d.ts +0 -1
  88. package/dist/executors/quickjs-protocol.d.ts +0 -67
  89. package/dist/favicon.d.ts +0 -1
  90. package/dist/identity.d.ts +0 -4
  91. package/dist/invocation.d.ts +0 -74
  92. package/dist/mcp-result.d.ts +0 -8
  93. package/dist/meta-tools.d.ts +0 -129
  94. package/dist/operator-ui/generated.d.ts +0 -2
  95. package/dist/operator-ui/view.d.ts +0 -188
  96. package/dist/result-shapes.d.ts +0 -13
  97. package/dist/retired-toolkits.d.ts +0 -2
  98. package/dist/routes/activity.d.ts +0 -2
  99. package/dist/routes/credentials.d.ts +0 -2
  100. package/dist/routes/mcp.d.ts +0 -10
  101. package/dist/routes/oauth-management.d.ts +0 -2
  102. package/dist/routes/oauth.d.ts +0 -2
  103. package/dist/routes/ui.d.ts +0 -2
  104. package/dist/server.d.ts +0 -9
  105. package/dist/skills.d.ts +0 -44
  106. package/dist/timeout.d.ts +0 -21
  107. package/dist/tool-safety.d.ts +0 -9
@@ -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. */
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import { droppedBrandingUrls, droppedThemeTokens, droppedUiAuthUrls, } from "./b
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(...)");
@@ -296,18 +291,31 @@ function warnInsecureConfig(config, inboundAuth, logger) {
296
291
  }
297
292
  // A credential test hook that cannot test the declared credential shape.
298
293
  // The shape picks the hook (see `credentialTestRule`) and the other one is
299
- // never substituted, so the connector is simply not testable: /credentials offers no
300
- // Test action and the route answers 400. Without this line the only way to
301
- // 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.
302
297
  for (const connector of config.connectors) {
303
298
  const { mismatch } = credentialTestRule(connector);
304
299
  if (!mismatch)
305
300
  continue;
306
301
  logger.warn(`[connecta] connector "${connector.id}" cannot test its credential: ` +
307
- `${describeCredentialTestMismatch(mismatch)}. /credentials offers no Test ` +
302
+ `${describeCredentialTestMismatch(mismatch)}. The connection in the operator UI offers no Test ` +
308
303
  `action and POST /ui/credentials/${connector.id}/test answers 400 ` +
309
304
  "until the matching hook is implemented.");
310
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
+ }
311
319
  // OAuth connectors whose callback cannot perform a state/CSRF check. The
312
320
  // public route refuses every callback for these connectors rather than hand
313
321
  // an unverified code to finishAuth, so this warning explains why auth cannot
@@ -331,9 +339,7 @@ export function createConnecta(config) {
331
339
  '"@cloudflare/codemode" on Workers.');
332
340
  }
333
341
  const storage = config.storage ?? memoryStorage();
334
- const logger = config.logger === "silent"
335
- ? { debug() { }, info() { }, warn() { }, error() { } }
336
- : config.logger ?? defaultLogger();
342
+ const logger = resolveLogger(config.logger);
337
343
  const credentialVault = config.vault;
338
344
  const configuredAuth = normalizeAuth(config.auth);
339
345
  const serverInfo = {
@@ -381,6 +387,22 @@ export function createConnecta(config) {
381
387
  "implements acquire() and owns its admission pool; configure that " +
382
388
  "executor's concurrency and queue options instead.");
383
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
+ });
384
406
  const handler = createFetchHandler({
385
407
  registry,
386
408
  auth: inboundAuth,
@@ -404,6 +426,7 @@ export function createConnecta(config) {
404
426
  maxEmittedBlocks: config.execute?.maxEmittedBlocks,
405
427
  maxHostCalls: config.execute?.maxHostCalls,
406
428
  hostCallTimeoutMs: config.execute?.hostCallTimeoutMs,
429
+ watchdogMs: config.execute?.watchdogMs,
407
430
  credentialVault,
408
431
  ui: config.ui,
409
432
  deploymentInfo: config.deploymentInfo,
@@ -417,10 +440,18 @@ export function createConnecta(config) {
417
440
  registry,
418
441
  close: async () => {
419
442
  closePromise ??= Promise.resolve().then(async () => {
420
- requestAdmission.close();
421
- codeAdmission?.close();
422
- registry.closeCallAdmission();
423
- 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
+ }
424
455
  });
425
456
  await closePromise;
426
457
  },