@zackbart/connecta 0.8.0 → 0.9.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 (151) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +31 -55
  3. package/SECURITY.md +5 -11
  4. package/dist/activity.d.ts +0 -8
  5. package/dist/activity.d.ts.map +1 -1
  6. package/dist/activity.js +0 -1
  7. package/dist/activity.js.map +1 -1
  8. package/dist/auth/bearer.d.ts +1 -7
  9. package/dist/auth/bearer.d.ts.map +1 -1
  10. package/dist/auth/bearer.js +2 -23
  11. package/dist/auth/bearer.js.map +1 -1
  12. package/dist/auth/clerk.d.ts +2 -11
  13. package/dist/auth/clerk.d.ts.map +1 -1
  14. package/dist/auth/clerk.js +31 -46
  15. package/dist/auth/clerk.js.map +1 -1
  16. package/dist/auth/downstream-oauth.d.ts +15 -6
  17. package/dist/auth/downstream-oauth.d.ts.map +1 -1
  18. package/dist/auth/downstream-oauth.js +60 -11
  19. package/dist/auth/downstream-oauth.js.map +1 -1
  20. package/dist/call-admission.d.ts.map +1 -1
  21. package/dist/call-admission.js +0 -1
  22. package/dist/call-admission.js.map +1 -1
  23. package/dist/catalog-service.d.ts +1 -2
  24. package/dist/catalog-service.d.ts.map +1 -1
  25. package/dist/catalog-service.js +12 -18
  26. package/dist/catalog-service.js.map +1 -1
  27. package/dist/catalog.d.ts +6 -0
  28. package/dist/catalog.d.ts.map +1 -1
  29. package/dist/catalog.js +51 -0
  30. package/dist/catalog.js.map +1 -1
  31. package/dist/connectors/remote-mcp.d.ts +1 -1
  32. package/dist/connectors/remote-mcp.d.ts.map +1 -1
  33. package/dist/connectors/remote-mcp.js +73 -71
  34. package/dist/connectors/remote-mcp.js.map +1 -1
  35. package/dist/errors.d.ts +19 -1
  36. package/dist/errors.d.ts.map +1 -1
  37. package/dist/errors.js +1 -0
  38. package/dist/errors.js.map +1 -1
  39. package/dist/execute.d.ts +4 -9
  40. package/dist/execute.d.ts.map +1 -1
  41. package/dist/execute.js +41 -20
  42. package/dist/execute.js.map +1 -1
  43. package/dist/executor-result.js +1 -1
  44. package/dist/executor-result.js.map +1 -1
  45. package/dist/index.d.ts +3 -82
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +17 -90
  48. package/dist/index.js.map +1 -1
  49. package/dist/invocation.d.ts +5 -0
  50. package/dist/invocation.d.ts.map +1 -1
  51. package/dist/invocation.js +46 -10
  52. package/dist/invocation.js.map +1 -1
  53. package/dist/meta-tools.d.ts +4 -12
  54. package/dist/meta-tools.d.ts.map +1 -1
  55. package/dist/meta-tools.js +119 -169
  56. package/dist/meta-tools.js.map +1 -1
  57. package/dist/operator-ui/generated.d.ts +2 -2
  58. package/dist/operator-ui/generated.d.ts.map +1 -1
  59. package/dist/operator-ui/generated.js +2 -2
  60. package/dist/operator-ui/generated.js.map +1 -1
  61. package/dist/operator-ui/model.d.ts +1 -22
  62. package/dist/operator-ui/model.d.ts.map +1 -1
  63. package/dist/operator-ui/model.js.map +1 -1
  64. package/dist/registry.d.ts +11 -178
  65. package/dist/registry.d.ts.map +1 -1
  66. package/dist/registry.js +52 -330
  67. package/dist/registry.js.map +1 -1
  68. package/dist/retired-toolkits.d.ts +3 -0
  69. package/dist/retired-toolkits.d.ts.map +1 -0
  70. package/dist/retired-toolkits.js +9 -0
  71. package/dist/retired-toolkits.js.map +1 -0
  72. package/dist/routes/activity.d.ts.map +1 -1
  73. package/dist/routes/activity.js +2 -5
  74. package/dist/routes/activity.js.map +1 -1
  75. package/dist/routes/credentials.d.ts.map +1 -1
  76. package/dist/routes/credentials.js +1 -12
  77. package/dist/routes/credentials.js.map +1 -1
  78. package/dist/routes/mcp.d.ts.map +1 -1
  79. package/dist/routes/mcp.js +94 -188
  80. package/dist/routes/mcp.js.map +1 -1
  81. package/dist/routes/oauth.d.ts.map +1 -1
  82. package/dist/routes/oauth.js +3 -8
  83. package/dist/routes/oauth.js.map +1 -1
  84. package/dist/routes/shared.d.ts +9 -30
  85. package/dist/routes/shared.d.ts.map +1 -1
  86. package/dist/routes/shared.js +10 -69
  87. package/dist/routes/shared.js.map +1 -1
  88. package/dist/routes/ui.d.ts.map +1 -1
  89. package/dist/routes/ui.js +4 -10
  90. package/dist/routes/ui.js.map +1 -1
  91. package/dist/server.d.ts.map +1 -1
  92. package/dist/server.js +1 -31
  93. package/dist/server.js.map +1 -1
  94. package/dist/skills.d.ts +3 -6
  95. package/dist/skills.d.ts.map +1 -1
  96. package/dist/skills.js +9 -38
  97. package/dist/skills.js.map +1 -1
  98. package/dist/timeout.d.ts.map +1 -1
  99. package/dist/timeout.js +2 -4
  100. package/dist/timeout.js.map +1 -1
  101. package/dist/types.d.ts +8 -61
  102. package/dist/types.d.ts.map +1 -1
  103. package/dist/ui.d.ts +1 -2
  104. package/dist/ui.d.ts.map +1 -1
  105. package/dist/ui.js +5 -35
  106. package/dist/ui.js.map +1 -1
  107. package/dist/version.d.ts +1 -1
  108. package/dist/version.js +1 -1
  109. package/package.json +3 -2
  110. package/src/activity.ts +0 -9
  111. package/src/auth/bearer.ts +3 -32
  112. package/src/auth/clerk.ts +35 -48
  113. package/src/auth/downstream-oauth.ts +106 -24
  114. package/src/call-admission.ts +0 -1
  115. package/src/catalog-service.ts +23 -23
  116. package/src/catalog.ts +55 -0
  117. package/src/connectors/remote-mcp.ts +106 -86
  118. package/src/errors.ts +22 -0
  119. package/src/execute.ts +43 -24
  120. package/src/executor-result.ts +1 -1
  121. package/src/index.ts +24 -208
  122. package/src/invocation.ts +63 -10
  123. package/src/meta-tools.ts +139 -187
  124. package/src/operator-ui/browser.css +1 -54
  125. package/src/operator-ui/browser.ts +16 -150
  126. package/src/operator-ui/generated.ts +2 -2
  127. package/src/operator-ui/model.ts +1 -23
  128. package/src/registry.ts +59 -429
  129. package/src/retired-toolkits.ts +14 -0
  130. package/src/routes/activity.ts +1 -6
  131. package/src/routes/credentials.ts +0 -12
  132. package/src/routes/mcp.ts +110 -230
  133. package/src/routes/oauth.ts +2 -8
  134. package/src/routes/shared.ts +10 -100
  135. package/src/routes/ui.ts +2 -12
  136. package/src/server.ts +1 -31
  137. package/src/skills.ts +9 -38
  138. package/src/timeout.ts +2 -4
  139. package/src/types.ts +11 -62
  140. package/src/ui.ts +9 -46
  141. package/src/version.ts +1 -1
  142. package/dist/credential-health.d.ts +0 -205
  143. package/dist/credential-health.d.ts.map +0 -1
  144. package/dist/credential-health.js +0 -610
  145. package/dist/credential-health.js.map +0 -1
  146. package/dist/toolkits.d.ts +0 -124
  147. package/dist/toolkits.d.ts.map +0 -1
  148. package/dist/toolkits.js +0 -322
  149. package/dist/toolkits.js.map +0 -1
  150. package/src/credential-health.ts +0 -835
  151. package/src/toolkits.ts +0 -461
@@ -1,610 +0,0 @@
1
- // Proactive liveness checks for the credentials connecta itself stores —
2
- // downstream-OAuth tokens and operator-managed vault credentials (issue #24).
3
- //
4
- // The problem this solves: a connector's auth state used to flip only when
5
- // something *observed* a failure, so an expired or revoked token surfaced
6
- // mid-task as a failed agent call. A liveness check asks the connector whether
7
- // the credential it holds still works, records the verdict, and lets the cached
8
- // status surfaces (`list_connectors({ probe: false })`, operator pages) report
9
- // `auth_required` BEFORE a real call discovers it.
10
- //
11
- // Runtime-agnostic on purpose: nothing here schedules itself. The core exposes a
12
- // due-gated sweep (piggybacked on inbound traffic by the server) and an awaited
13
- // entry point (`Connecta.checkCredentials()`) an operator wires to whatever
14
- // scheduler their runtime has — a Worker cron trigger, a Node `setInterval`.
15
- // There is no background daemon and no long-lived timer, so Workers and Node run
16
- // the same code.
17
- import { credentialTestRule, STORED_CREDENTIAL_SHAPE_MISMATCH_ERROR, storedCredentialShape, } from "./credentials.js";
18
- import { closeConnectorScope, } from "./connector-scope.js";
19
- import { DEFAULT_PROBE_TIMEOUT_MS, normalizeTimeoutMs, withTimeout } from "./timeout.js";
20
- const DEFAULT_CREDENTIAL_CHECK_INTERVAL_SECONDS = 900;
21
- const DEFAULT_CREDENTIAL_CHECK_CONCURRENCY = 4;
22
- /**
23
- * How long a read of one connector's record is served from memory before going
24
- * back to storage. Short: it exists so a burst of `list_connectors` calls costs
25
- * one storage read rather than one per call, not to cache a verdict.
26
- */
27
- const MIRROR_TTL_MS = 5_000;
28
- /**
29
- * Minimum gap between storage WRITES of an unchanged verdict. A liveness
30
- * observation arrives from more than one place (the sweep, and every
31
- * `list_connectors({ probe: true })`), and re-persisting "still ok" on each one
32
- * would spend a KV write per probe for no new information. A verdict whose state
33
- * or message CHANGED is always written immediately.
34
- */
35
- const MIN_WRITE_GAP_MS = 60_000;
36
- function msg(err) {
37
- return err instanceof Error ? err.message : String(err);
38
- }
39
- function storageKey(connectorId) {
40
- return `credhealth:${connectorId}`;
41
- }
42
- /**
43
- * Generation counter key. Connector ids are `[a-z0-9_-]+`, so the extra colon
44
- * puts this outside the space `storageKey` can produce — no id can collide with
45
- * another id's counter.
46
- */
47
- function generationKey(connectorId) {
48
- return `credhealth:gen:${connectorId}`;
49
- }
50
- function validRecord(raw) {
51
- if (!raw)
52
- return null;
53
- try {
54
- const value = JSON.parse(raw);
55
- if ((value.state !== "ok" &&
56
- value.state !== "auth_required" &&
57
- value.state !== "error") ||
58
- typeof value.checkedAt !== "string" ||
59
- Number.isNaN(Date.parse(value.checkedAt))) {
60
- return null;
61
- }
62
- const stamped = Date.parse(value.checkedAt);
63
- const now = Date.now();
64
- return {
65
- state: value.state,
66
- // A verdict from the future is a clock-skewed isolate, and left alone it
67
- // would be permanently fresh (never re-checked) AND permanently newer than
68
- // any real-call success (never retired) — a wrong answer that cannot age
69
- // out. Clamping to now costs at most one early re-check.
70
- checkedAt: stamped > now ? new Date(now).toISOString() : value.checkedAt,
71
- ...(typeof value.message === "string" ? { message: value.message } : {}),
72
- ...(typeof value.authorizationUrl === "string"
73
- ? { authorizationUrl: value.authorizationUrl }
74
- : {}),
75
- };
76
- }
77
- catch {
78
- return null;
79
- }
80
- }
81
- /**
82
- * The stored verdicts, in the deployment's own KVStorage under
83
- * `credhealth:<connectorId>`.
84
- *
85
- * Persisted rather than held in memory because the two runtimes disagree about
86
- * what "in memory" means: a Cloudflare cron trigger runs in a different isolate
87
- * from the fetch handlers, so a verdict only reaches `list_connectors` if it
88
- * goes through storage. The in-memory mirror is a read cache over that, not the
89
- * source of truth.
90
- *
91
- * Never throws: a check that cannot be persisted (or read back) must degrade to
92
- * "no verdict" rather than break the status surface that reads it.
93
- */
94
- class CredentialHealthStore {
95
- storage;
96
- logger;
97
- mirror = new Map();
98
- constructor(storage, logger) {
99
- this.storage = storage;
100
- this.logger = logger;
101
- }
102
- async get(connectorId) {
103
- const cached = this.mirror.get(connectorId);
104
- if (cached && Date.now() - cached.readAt < MIRROR_TTL_MS) {
105
- return cached.record ?? undefined;
106
- }
107
- let record = null;
108
- try {
109
- record = validRecord(await this.storage.get(storageKey(connectorId)));
110
- }
111
- catch (err) {
112
- this.logger.warn(`[connecta] connector "${connectorId}" credential-health read failed: ${msg(err)}`);
113
- return cached?.record ?? undefined;
114
- }
115
- this.mirror.set(connectorId, { record, readAt: Date.now() });
116
- return record ?? undefined;
117
- }
118
- /**
119
- * Monotonic per-connector counter, advanced by {@link clear}. Read straight
120
- * from storage, never from the mirror: its whole job is to notice a change
121
- * another isolate made, which a read cache would hide.
122
- *
123
- * A read failure answers 0. Paired with the fence in `put`, that fails
124
- * *closed* — a mismatched generation drops the verdict — because losing one
125
- * verdict costs a re-check, while resurrecting one costs an operator a
126
- * connector that reports dead after they just fixed it.
127
- */
128
- async generation(connectorId) {
129
- try {
130
- const raw = await this.storage.get(generationKey(connectorId));
131
- const value = raw ? Number(raw) : 0;
132
- return Number.isFinite(value) ? value : 0;
133
- }
134
- catch {
135
- return 0;
136
- }
137
- }
138
- /**
139
- * Write a verdict. `expectedGeneration` fences the write against a `clear`
140
- * that landed while the check was in flight: pass the generation captured
141
- * before the check started, and the write is dropped if it has since advanced.
142
- * Omit it for a verdict observed synchronously (a live probe, an operator's
143
- * Test), where there is no window to race.
144
- *
145
- * Returns whether the verdict was actually stored.
146
- */
147
- async put(connectorId, record, expectedGeneration) {
148
- if (expectedGeneration !== undefined &&
149
- (await this.generation(connectorId)) !== expectedGeneration) {
150
- // The credential this verdict judged was replaced or removed mid-check.
151
- // Drop the mirror too: this isolate's idea of the verdict is as stale as
152
- // the write it just declined to make.
153
- this.mirror.delete(connectorId);
154
- return false;
155
- }
156
- const current = await this.get(connectorId);
157
- const unchanged = current !== undefined &&
158
- current.state === record.state &&
159
- current.message === record.message &&
160
- current.authorizationUrl === record.authorizationUrl;
161
- if (unchanged &&
162
- Date.parse(record.checkedAt) - Date.parse(current.checkedAt) <
163
- MIN_WRITE_GAP_MS) {
164
- return true;
165
- }
166
- this.mirror.set(connectorId, { record, readAt: Date.now() });
167
- try {
168
- await this.storage.set(storageKey(connectorId), JSON.stringify(record));
169
- }
170
- catch (err) {
171
- this.logger.warn(`[connecta] connector "${connectorId}" credential-health persistence failed: ${msg(err)}`);
172
- }
173
- return true;
174
- }
175
- /**
176
- * Forget a connector's verdict, and advance its generation so a check already
177
- * in flight — in this isolate or any other — cannot write the verdict it
178
- * formed about the credential that was just replaced.
179
- *
180
- * Bump BEFORE the delete, the same ordering the OAuth force path uses: a
181
- * racing writer must see the advance rather than land between the two writes.
182
- */
183
- async clear(connectorId) {
184
- this.mirror.delete(connectorId);
185
- try {
186
- const next = (await this.generation(connectorId)) + 1;
187
- await this.storage.set(generationKey(connectorId), String(next));
188
- await this.storage.delete(storageKey(connectorId));
189
- }
190
- catch (err) {
191
- this.logger.warn(`[connecta] connector "${connectorId}" credential-health reset failed: ${msg(err)}`);
192
- }
193
- }
194
- }
195
- /**
196
- * Whether a connector holds a credential connecta stores AND exposes a way to
197
- * ask whether it still works.
198
- *
199
- * Deliberately narrow. `listTools`/`callTool` are NOT liveness probes here: a
200
- * tool call may mutate downstream state, and the catalog path is already covered
201
- * by the existing probe. So a connector is checkable only through the two hooks
202
- * that exist to answer exactly this question — `testCredential(s)` (what the
203
- * Test button runs) and `status()` — and only when it has a credential of ours
204
- * to be asked about: an operator-managed `credential`, or a stored downstream
205
- * grant it reports via `hasStoredCredential`. A static-token connector stores
206
- * nothing here and is never probed on a timer.
207
- *
208
- * Whether a test hook counts is `credentialTestRule`'s call, not this function's
209
- * Credentials Test button and the credential API read (issue #55), so
210
- * a credential the operator cannot test by hand is not one a sweep tests behind
211
- * their back. A connector whose only hook cannot test its declared shape is
212
- * checkable only if it also implements `status()`.
213
- */
214
- function isCheckableConnector(connector) {
215
- const hasCredentialStore = Boolean(connector.credential || connector.hasStoredCredential);
216
- const canAsk = Boolean(credentialTestRule(connector).mode !== null || connector.status);
217
- return hasCredentialStore && canAsk;
218
- }
219
- /**
220
- * The credential test the connector's DECLARED shape selects, bound to what is
221
- * actually stored — or undefined when there is no honest question to put.
222
- *
223
- * `isCheckableConnector` answers the static question ("could this connector be
224
- * asked at all"); this answers it against the vault. The hook itself is picked
225
- * by `credentialTestRule` (src/credentials.ts, issue #55), the one rule the UI's
226
- * `testable` flag and `POST /ui/credentials/<id>/test` also read: named
227
- * `credential.fields` are tested as a set by `testCredentials`, a single-value
228
- * `credential` by `testCredential` on the vault's reserved `value` field, and
229
- * the other hook is never substituted. Substituting it is what a sweep must not
230
- * do quietly — handing `testCredential` a `values.value` that named fields never
231
- * wrote would test the empty string and record a confident `auth_required` about
232
- * a credential nothing examined, and handing `testCredentials` the reserved
233
- * `{ value }` map would call a hook with a shape its connector never declared.
234
- * Either way the connector is skipped (`not_checkable`) rather than given an
235
- * invented verdict, and `createConnecta` already warned about the mismatch at
236
- * construction.
237
- */
238
- function testHookFor(connector, values) {
239
- if (!connector.credential ||
240
- storedCredentialShape(connector.credential, values).state !== "valid") {
241
- return undefined;
242
- }
243
- const storedValues = values;
244
- const { mode } = credentialTestRule(connector);
245
- if (mode === "multiple") {
246
- return (ctx) => connector.testCredentials(storedValues, ctx);
247
- }
248
- // A single-value shape with nothing under the reserved field is still nothing
249
- // to test, so the stored value gets the last word even when the rule fits.
250
- const storedValue = storedValues.value;
251
- if (mode === "single" && typeof storedValue === "string") {
252
- return (ctx) => connector.testCredential(storedValue, ctx);
253
- }
254
- return undefined;
255
- }
256
- /** Run `fn` over `items` with at most `limit` in flight, preserving order. */
257
- async function mapWithConcurrency(items, limit, fn) {
258
- const out = Array(items.length);
259
- const remaining = items.entries();
260
- const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
261
- for (;;) {
262
- const next = remaining.next();
263
- if (next.done)
264
- return;
265
- const [index, item] = next.value;
266
- out[index] = await fn(item);
267
- }
268
- });
269
- await Promise.all(workers);
270
- return out;
271
- }
272
- /**
273
- * Runs and caches credential liveness checks. One instance per `Registry`.
274
- *
275
- * Cost is bounded four ways, because a status surface an agent polls must never
276
- * become a way to hammer a downstream auth endpoint:
277
- *
278
- * 1. **Eligibility** — only connectors holding a credential of ours are probed
279
- * at all (`isCheckableConnector`), and only when something is actually stored.
280
- * 2. **Freshness (cross-isolate)** — a persisted verdict younger than
281
- * `intervalSeconds` short-circuits the check, so every isolate and every
282
- * trigger share one budget.
283
- * 3. **Sweep gate (per isolate)** — `sweepIfDue` runs at most one traffic-
284
- * triggered sweep per interval per isolate, and never two at once, so a burst
285
- * of requests costs one sweep, not one per request.
286
- * 4. **Deadline + fan-out bound** — each check is bounded by `timeoutMs` and at
287
- * most `concurrency` run together (the same shape as the
288
- * `discovery.probeTimeoutMs` bound on the discovery fan-out, issue #19).
289
- */
290
- export class CredentialHealthChecker {
291
- deps;
292
- store;
293
- intervalMs;
294
- concurrency;
295
- timeoutMs;
296
- onRequest;
297
- /** Per-connector checks in flight in THIS isolate. */
298
- inFlight = new Map();
299
- /** Earliest a traffic-triggered sweep may run again in this isolate. */
300
- nextSweepAt = 0;
301
- sweeping;
302
- constructor(deps, config = {}) {
303
- this.deps = deps;
304
- this.store = new CredentialHealthStore(deps.storage, deps.logger);
305
- // Out-of-range tuning falls back to the default rather than being coerced:
306
- // a zero or negative interval would turn the rate limit off, which is the
307
- // one thing this class is for.
308
- const seconds = config.intervalSeconds;
309
- this.intervalMs =
310
- seconds !== undefined && Number.isFinite(seconds) && seconds > 0
311
- ? seconds * 1000
312
- : DEFAULT_CREDENTIAL_CHECK_INTERVAL_SECONDS * 1000;
313
- this.concurrency =
314
- config.concurrency !== undefined &&
315
- Number.isInteger(config.concurrency) &&
316
- config.concurrency > 0
317
- ? config.concurrency
318
- : DEFAULT_CREDENTIAL_CHECK_CONCURRENCY;
319
- this.timeoutMs =
320
- normalizeTimeoutMs(config.timeoutMs) ?? DEFAULT_PROBE_TIMEOUT_MS;
321
- this.onRequest = config.onRequest ?? true;
322
- }
323
- /**
324
- * The stored verdict, if any. No downstream I/O — and no storage read at all
325
- * for a connector that stores no credential of ours, since only a checkable
326
- * connector can ever have had a verdict written. That keeps
327
- * `list_connectors({ probe: false })` exactly as cheap as it was for the
328
- * deployments this feature does not apply to.
329
- */
330
- healthFor(connectorId) {
331
- const connector = this.deps.getConnector(connectorId);
332
- if (!connector || !isCheckableConnector(connector)) {
333
- return Promise.resolve(undefined);
334
- }
335
- return this.store.get(connectorId);
336
- }
337
- /**
338
- * Record a liveness verdict observed elsewhere — today, the live status a
339
- * `list_connectors({ probe: true })` just performed. Filtered by the same
340
- * eligibility rule as a check, so this stays a record of *credential* health
341
- * rather than a general status cache, and so it also counts against the
342
- * freshness budget: an operator who just probed live does not get swept again
343
- * moments later.
344
- */
345
- async record(connectorId, record) {
346
- const connector = this.deps.getConnector(connectorId);
347
- if (!connector || !isCheckableConnector(connector))
348
- return;
349
- await this.store.put(connectorId, record);
350
- }
351
- /** Forget a connector's verdict — its credential just changed under us. */
352
- clear(connectorId) {
353
- return this.store.clear(connectorId);
354
- }
355
- /** Whether any connector in this deployment could be checked at all. */
356
- hasCheckableConnectors() {
357
- return this.deps.listConnectors().some(isCheckableConnector);
358
- }
359
- /**
360
- * Check every (or the named) connector's stored credential and return one
361
- * outcome per connector considered. Never rejects: a connector that throws,
362
- * hangs past `timeoutMs`, or cannot be persisted becomes an `error` verdict.
363
- */
364
- async check(baseUrl, opts = {}, defer) {
365
- // An id naming no connector is reported, not dropped: a typo in a scheduled
366
- // check would otherwise return an empty list that looks exactly like a
367
- // deployment with nothing to check.
368
- const targets = opts.ids
369
- ? opts.ids.map((id) => this.deps.getConnector(id) ?? id)
370
- : this.deps.listConnectors();
371
- return mapWithConcurrency(targets, this.concurrency, (target) => typeof target === "string"
372
- ? Promise.resolve({ connectorId: target, skipped: "not_found" })
373
- : this.checkOne(target, baseUrl, opts, defer));
374
- }
375
- /**
376
- * The traffic-triggered sweep: a promise to hand to `ctx.waitUntil`, or
377
- * `undefined` when nothing is due (the common case, and free — no I/O). The
378
- * gate is armed BEFORE the sweep starts, so a burst of concurrent requests
379
- * produces one sweep.
380
- */
381
- sweepIfDue(baseUrl, defer) {
382
- if (!this.onRequest || this.sweeping)
383
- return undefined;
384
- const now = Date.now();
385
- if (now < this.nextSweepAt)
386
- return undefined;
387
- if (!this.hasCheckableConnectors())
388
- return undefined;
389
- this.nextSweepAt = now + this.intervalMs;
390
- const sweep = this.check(baseUrl, {}, defer).finally(() => {
391
- this.sweeping = undefined;
392
- });
393
- this.sweeping = sweep;
394
- return sweep;
395
- }
396
- async checkOne(connector, baseUrl, opts, defer) {
397
- const connectorId = connector.id;
398
- if (!isCheckableConnector(connector)) {
399
- return { connectorId, skipped: "not_checkable" };
400
- }
401
- if (this.inFlight.has(connectorId)) {
402
- // Report rather than join: the caller wants to know a check happened, not
403
- // to be blocked behind one someone else already pays for.
404
- return {
405
- connectorId,
406
- skipped: "in_flight",
407
- ...(await this.recordOrNothing(connectorId)),
408
- };
409
- }
410
- const run = this.runCheck(connector, baseUrl, opts.force ?? false, defer);
411
- this.inFlight.set(connectorId, run);
412
- try {
413
- return await run;
414
- }
415
- finally {
416
- this.inFlight.delete(connectorId);
417
- }
418
- }
419
- async recordOrNothing(connectorId) {
420
- const record = await this.store.get(connectorId);
421
- return record ? { record } : {};
422
- }
423
- async runCheck(connector, baseUrl, force, defer) {
424
- const connectorId = connector.id;
425
- const started = Date.now();
426
- // Captured BEFORE anything downstream happens — including the vault read
427
- // below: everything after this point is a window in which the operator may
428
- // replace the very credential being judged, and `settle` fences the write
429
- // against exactly that. It cannot move later to save a read on the fresh
430
- // path; a generation sampled AFTER the values would miss a `clear` that
431
- // landed between the two and let a verdict about the replaced credential
432
- // through the fence.
433
- const generation = await this.store.generation(connectorId);
434
- let values = null;
435
- let credentialReadError;
436
- if (connector.credential && this.deps.credentialVault) {
437
- try {
438
- values = await this.deps.credentialVault.getAll(connectorId);
439
- }
440
- catch (err) {
441
- credentialReadError = err;
442
- }
443
- const shape = storedCredentialShape(connector.credential, values);
444
- if (shape.state === "mismatch") {
445
- // Drift is a persistent operator-reconfiguration state, not an event:
446
- // outside the freshness gate it would spend a write on every sweep in
447
- // every isolate, forever, against exactly the deployments this feature
448
- // is meant to help (and on Cloudflare KV those writes are metered).
449
- // Once the SAME drift verdict is already stored and still fresh, this
450
- // is the rate limit doing its job, same as a fresh `ok`. A stored
451
- // verdict that says anything else — a pre-redeploy `ok`, a different
452
- // message — is still replaced immediately, which is the whole point of
453
- // checking the shape before the gate.
454
- if (!force) {
455
- const current = await this.store.get(connectorId);
456
- if (current &&
457
- current.state === "auth_required" &&
458
- current.message === shape.message &&
459
- Date.now() - Date.parse(current.checkedAt) < this.intervalMs) {
460
- return { connectorId, skipped: "fresh", record: current };
461
- }
462
- }
463
- return this.settle(connectorId, started, generation, {
464
- // Unlike a failed check, this is a completed static classification:
465
- // the current declaration cannot consume what the vault holds.
466
- state: "auth_required",
467
- checkedAt: new Date().toISOString(),
468
- message: shape.message,
469
- });
470
- }
471
- }
472
- // Shape drift is checked before this shortcut so an old, still-fresh `ok`
473
- // cannot survive a redeploy that changed the credential declaration.
474
- if (!force) {
475
- const current = await this.store.get(connectorId);
476
- if (current &&
477
- Date.now() - Date.parse(current.checkedAt) < this.intervalMs) {
478
- return { connectorId, skipped: "fresh", record: current };
479
- }
480
- }
481
- // Credential checks are always probe owners. No caller may lend them an
482
- // ordinary request scope and thereby suppress the teardown below.
483
- const scope = {};
484
- const ctx = this.deps.contextFor(connectorId, baseUrl, scope);
485
- try {
486
- if (credentialReadError) {
487
- // A stored credential that cannot be decrypted (rotated key, corrupt
488
- // envelope) is exactly the kind of dead credential this feature
489
- // exists to surface early, so it is a verdict rather than a skip.
490
- return this.settle(connectorId, started, generation, {
491
- state: "auth_required",
492
- checkedAt: new Date().toISOString(),
493
- message: msg(credentialReadError),
494
- });
495
- }
496
- const stored = connector.hasStoredCredential
497
- ? await connector
498
- .hasStoredCredential(ctx)
499
- .catch(() => values !== null)
500
- : values !== null;
501
- if (!stored)
502
- return { connectorId, skipped: "no_credential" };
503
- if (!this.canAsk(connector, values)) {
504
- return { connectorId, skipped: "not_checkable" };
505
- }
506
- try {
507
- const verdict = await withTimeout(this.probe(connector, ctx, values), this.timeoutMs, `credential check of "${connectorId}"`);
508
- return await this.settle(connectorId, started, generation, {
509
- ...verdict,
510
- checkedAt: new Date().toISOString(),
511
- });
512
- }
513
- catch (err) {
514
- return await this.settle(connectorId, started, generation, {
515
- state: "error",
516
- checkedAt: new Date().toISOString(),
517
- message: msg(err),
518
- });
519
- }
520
- }
521
- finally {
522
- await closeConnectorScope(connector, ctx, defer);
523
- }
524
- }
525
- /**
526
- * `isCheckableConnector` re-asked against what is actually stored: the hook
527
- * the declared shape selects, bound to a value that fits it (see
528
- * {@link testHookFor}), or a `status()` to fall back on. Neither ⇒ there is no
529
- * honest question to put to this connector.
530
- */
531
- canAsk(connector, values) {
532
- return Boolean(testHookFor(connector, values) || connector.status);
533
- }
534
- /**
535
- * Ask the connector whether the credential it holds still works — with no
536
- * downstream mutation and no tool call. A credential test is preferred for a
537
- * vault credential because it validates the stored value itself; `status()` is
538
- * the downstream-OAuth answer (it refreshes the grant, which is the liveness
539
- * question for a token).
540
- */
541
- async probe(connector, ctx, values) {
542
- const test = testHookFor(connector, values);
543
- if (test) {
544
- const result = await test(ctx);
545
- if (result.ok) {
546
- return { state: "ok", ...(result.message ? { message: result.message } : {}) };
547
- }
548
- // A rejected stored credential needs an operator, not a retry — the same
549
- // actionable state a revoked OAuth grant reports. There is no consent URL
550
- // for a vault credential; /credentials is where it is replaced.
551
- return {
552
- state: "auth_required",
553
- message: result.message ??
554
- "Stored credential was rejected by the connector — replace it in /credentials.",
555
- };
556
- }
557
- const status = await connector.status(ctx);
558
- return {
559
- state: status.state,
560
- ...(status.message ? { message: status.message } : {}),
561
- ...(status.authorizationUrl
562
- ? { authorizationUrl: status.authorizationUrl }
563
- : {}),
564
- };
565
- }
566
- async settle(connectorId, started, generation, record) {
567
- const stored = await this.store.put(connectorId, record, generation);
568
- return {
569
- connectorId,
570
- record,
571
- ...(stored ? {} : { discarded: true }),
572
- latencyMs: Date.now() - started,
573
- };
574
- }
575
- }
576
- /**
577
- * Whether a liveness verdict may DECIDE a connector's cached status.
578
- *
579
- * Only `auth_required` ever does, and only while nothing better has happened
580
- * since. Two separate judgements:
581
- *
582
- * 1. **`error` is not credential evidence.** A check that timed out, threw, or
583
- * got a 502 from the provider's status endpoint failed to *complete* — it
584
- * learned nothing about the credential. Letting it set the status would flip
585
- * a connector whose calls are fine to `error` for a whole interval on a DNS
586
- * blip. Error verdicts stay visible in `credentialCheck` (an operator wants
587
- * to know checks are failing) but the status keeps coming from observed real
588
- * calls, which is evidence.
589
- * 2. **A successful real call retires the verdict, except static shape drift.**
590
- * Traffic beats a background probe, so a `lastSuccessAt` at or after
591
- * `checkedAt` normally means the credential demonstrably works. Stored-shape
592
- * drift is different: a credential-independent tool can succeed without
593
- * making a missing declared field appear, so only replacement/removal clears
594
- * that verdict.
595
- *
596
- * `auth_required` deliberately outranks an observed real-call *failure*: both
597
- * say something is wrong, and only one of them carries the URL that fixes it.
598
- * The failure stays visible as `lastError`.
599
- */
600
- export function credentialVerdictApplies(record, lastSuccessAt) {
601
- if (!record || record.state !== "auth_required")
602
- return false;
603
- if (record.message === STORED_CREDENTIAL_SHAPE_MISMATCH_ERROR)
604
- return true;
605
- if (!lastSuccessAt)
606
- return true;
607
- const success = Date.parse(lastSuccessAt);
608
- return Number.isNaN(success) || success < Date.parse(record.checkedAt);
609
- }
610
- //# sourceMappingURL=credential-health.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"credential-health.js","sourceRoot":"","sources":["../src/credential-health.ts"],"names":[],"mappings":"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"}