@zackbart/connecta 0.8.0 → 0.8.1
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.
- package/CHANGELOG.md +87 -0
- package/README.md +31 -55
- package/dist/activity.d.ts +0 -8
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +0 -1
- package/dist/activity.js.map +1 -1
- package/dist/auth/bearer.d.ts +1 -7
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +2 -23
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/clerk.d.ts +2 -11
- package/dist/auth/clerk.d.ts.map +1 -1
- package/dist/auth/clerk.js +31 -46
- package/dist/auth/clerk.js.map +1 -1
- package/dist/call-admission.d.ts.map +1 -1
- package/dist/call-admission.js +0 -1
- package/dist/call-admission.js.map +1 -1
- package/dist/catalog-service.d.ts +1 -2
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/catalog-service.js +12 -18
- package/dist/catalog-service.js.map +1 -1
- package/dist/catalog.d.ts +6 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +51 -0
- package/dist/catalog.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +9 -22
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +1 -8
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +3 -13
- package/dist/execute.js.map +1 -1
- package/dist/executor-result.js +1 -1
- package/dist/executor-result.js.map +1 -1
- package/dist/index.d.ts +3 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -90
- package/dist/index.js.map +1 -1
- package/dist/invocation.d.ts +5 -0
- package/dist/invocation.d.ts.map +1 -1
- package/dist/invocation.js +46 -10
- package/dist/invocation.js.map +1 -1
- package/dist/meta-tools.d.ts +3 -11
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +105 -157
- package/dist/meta-tools.js.map +1 -1
- package/dist/operator-ui/generated.d.ts +2 -2
- package/dist/operator-ui/generated.d.ts.map +1 -1
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/generated.js.map +1 -1
- package/dist/operator-ui/model.d.ts +1 -22
- package/dist/operator-ui/model.d.ts.map +1 -1
- package/dist/operator-ui/model.js.map +1 -1
- package/dist/registry.d.ts +11 -178
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +52 -330
- package/dist/registry.js.map +1 -1
- package/dist/retired-toolkits.d.ts +3 -0
- package/dist/retired-toolkits.d.ts.map +1 -0
- package/dist/retired-toolkits.js +9 -0
- package/dist/retired-toolkits.js.map +1 -0
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/activity.js +2 -5
- package/dist/routes/activity.js.map +1 -1
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/credentials.js +1 -12
- package/dist/routes/credentials.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +32 -145
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/oauth.d.ts.map +1 -1
- package/dist/routes/oauth.js +2 -7
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/shared.d.ts +7 -28
- package/dist/routes/shared.d.ts.map +1 -1
- package/dist/routes/shared.js +10 -69
- package/dist/routes/shared.js.map +1 -1
- package/dist/routes/ui.d.ts.map +1 -1
- package/dist/routes/ui.js +4 -10
- package/dist/routes/ui.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -31
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +3 -6
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +9 -38
- package/dist/skills.js.map +1 -1
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +2 -4
- package/dist/timeout.js.map +1 -1
- package/dist/types.d.ts +2 -59
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +1 -2
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +5 -35
- package/dist/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/activity.ts +0 -9
- package/src/auth/bearer.ts +3 -32
- package/src/auth/clerk.ts +35 -48
- package/src/call-admission.ts +0 -1
- package/src/catalog-service.ts +23 -23
- package/src/catalog.ts +55 -0
- package/src/connectors/remote-mcp.ts +11 -23
- package/src/errors.ts +20 -0
- package/src/execute.ts +3 -18
- package/src/executor-result.ts +1 -1
- package/src/index.ts +24 -208
- package/src/invocation.ts +63 -10
- package/src/meta-tools.ts +122 -174
- package/src/operator-ui/browser.css +1 -54
- package/src/operator-ui/browser.ts +16 -150
- package/src/operator-ui/generated.ts +2 -2
- package/src/operator-ui/model.ts +1 -23
- package/src/registry.ts +59 -429
- package/src/retired-toolkits.ts +14 -0
- package/src/routes/activity.ts +1 -6
- package/src/routes/credentials.ts +0 -12
- package/src/routes/mcp.ts +40 -186
- package/src/routes/oauth.ts +1 -7
- package/src/routes/shared.ts +8 -98
- package/src/routes/ui.ts +2 -12
- package/src/server.ts +1 -31
- package/src/skills.ts +9 -38
- package/src/timeout.ts +2 -4
- package/src/types.ts +1 -60
- package/src/ui.ts +9 -46
- package/src/version.ts +1 -1
- package/dist/credential-health.d.ts +0 -205
- package/dist/credential-health.d.ts.map +0 -1
- package/dist/credential-health.js +0 -610
- package/dist/credential-health.js.map +0 -1
- package/dist/toolkits.d.ts +0 -124
- package/dist/toolkits.d.ts.map +0 -1
- package/dist/toolkits.js +0 -322
- package/dist/toolkits.js.map +0 -1
- package/src/credential-health.ts +0 -835
- package/src/toolkits.ts +0 -461
package/src/skills.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Connector } from "./types.js";
|
|
2
2
|
|
|
3
3
|
export const CONNECTA_INSTRUCTIONS =
|
|
4
|
-
'Connecta exposes many integrations behind meta-tools. When an address is unknown, start with search_tools and includeSchemas="compact"; use describe_tools only when that schema is insufficient. Use call_tool for one explicitly read-only call, batch_call for 2–10 independent explicitly read-only calls, and execute_code (when available) only for dependent read-only steps, loops, joins, or reducing large results. Unannotated, write-capable, and destructive tools must use call_destructive_tool individually. Use authorize_connector only after auth_required and get_result only for truncated results.
|
|
4
|
+
'Connecta exposes many integrations behind meta-tools. When an address is unknown, start with search_tools and includeSchemas="compact"; use describe_tools only when that schema is insufficient. Use call_tool for one explicitly read-only call, batch_call for 2–10 independent explicitly read-only calls, and execute_code (when available) only for dependent read-only steps, loops, joins, or reducing large results. Unannotated, write-capable, and destructive tools must use call_destructive_tool individually. Use authorize_connector only after auth_required and get_result only for truncated results. Fetch skills({ name: "usage" }) when this routing workflow is unfamiliar.';
|
|
5
5
|
|
|
6
6
|
export const USAGE_SKILL = `# Connecta usage
|
|
7
7
|
|
|
8
8
|
## Choose the smallest execution tool
|
|
9
9
|
|
|
10
|
+
Use exact addresses returned by discovery; never invent one.
|
|
11
|
+
|
|
10
12
|
- Unknown address: \`search_tools({ query, includeSchemas: "compact" })\`.
|
|
11
13
|
- Schema still unclear: \`describe_tools({ addresses: [...] })\`.
|
|
12
14
|
- One explicitly read-only call: \`call_tool\`.
|
|
@@ -14,45 +16,17 @@ export const USAGE_SKILL = `# Connecta usage
|
|
|
14
16
|
- Dependent read-only calls, loops, joins, branching, or large-result reduction: \`execute_code\` when available.
|
|
15
17
|
- Any unannotated, write-capable, or destructive call: \`call_destructive_tool\`, individually and only after reviewing its schema and consequences.
|
|
16
18
|
- Truncated result: retry with \`fields\` when possible; otherwise page it with \`get_result\`.
|
|
17
|
-
- \`auth_required\`: use \`authorize_connector\`,
|
|
19
|
+
- \`auth_required\`: use \`authorize_connector\`, give its recovery handoff to the operator, then retry the original call.
|
|
18
20
|
|
|
19
|
-
Use \`list_connectors({ probe: false })\` for a fast inventory. Use \`probe: true\` only when diagnosing live health or authorization.
|
|
21
|
+
Use \`list_connectors({ probe: false })\` for a fast inventory based on recent call observations and local credential-shape drift. Use \`probe: true\` only when diagnosing live health or authorization.
|
|
20
22
|
|
|
21
23
|
## Code mode
|
|
22
24
|
|
|
23
|
-
Use code mode when
|
|
24
|
-
|
|
25
|
-
Do not use code mode for one straightforward call, for independent calls already handled by \`batch_call\`, or for any tool not explicitly annotated \`readOnlyHint: true\`. Code mode has a bounded host-call budget and per-call deadline. Return only the reduced value the agent needs; do not return a large upstream payload unchanged.
|
|
26
|
-
|
|
27
|
-
## Examples
|
|
28
|
-
|
|
29
|
-
These addresses are illustrative; always use the exact address returned by \`search_tools\`.
|
|
25
|
+
Use code mode when calls depend on earlier results, when joining connectors, or when sandbox filtering or aggregation will substantially shrink the response. Use \`Promise.all\` or \`connecta.batch\` for independent calls inside one execution.
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
\`\`\`json
|
|
33
|
-
{ "address": "crm.get_account", "args": { "id": "acct_123" }, "resultMode": "value" }
|
|
34
|
-
\`\`\`
|
|
27
|
+
Connector namespace calls and \`connecta.call\` use the same read-only gate and throw on downstream errors. Catch only failures the workflow can handle; let authorization failures return to the agent for recovery.
|
|
35
28
|
|
|
36
|
-
|
|
37
|
-
\`\`\`json
|
|
38
|
-
{ "calls": [
|
|
39
|
-
{ "address": "crm.get_account", "args": { "id": "acct_123" } },
|
|
40
|
-
{ "address": "billing.list_invoices", "args": { "status": "open" } }
|
|
41
|
-
] }
|
|
42
|
-
\`\`\`
|
|
43
|
-
|
|
44
|
-
Dependent code with reduction:
|
|
45
|
-
\`\`\`js
|
|
46
|
-
async () => {
|
|
47
|
-
const accounts = await crm.search_accounts({ query: "renewal" });
|
|
48
|
-
const details = await Promise.all(
|
|
49
|
-
accounts.results.slice(0, 5).map((account) =>
|
|
50
|
-
crm.get_account({ id: account.id })
|
|
51
|
-
)
|
|
52
|
-
);
|
|
53
|
-
return details.map(({ id, name, status }) => ({ id, name, status }));
|
|
54
|
-
}
|
|
55
|
-
\`\`\`
|
|
29
|
+
Do not use code mode for one call, independent calls already handled by \`batch_call\`, or any tool lacking \`readOnlyHint: true\`. Host calls and time are bounded. Return only the reduced value the agent needs.
|
|
56
30
|
`;
|
|
57
31
|
|
|
58
32
|
/**
|
|
@@ -173,10 +147,7 @@ export interface SkillListing {
|
|
|
173
147
|
/**
|
|
174
148
|
* Every fetchable skill: the built-in guides plus one entry per connector that
|
|
175
149
|
* carries a usage guide. Derived from the connector list passed in — the single
|
|
176
|
-
* place guide visibility is decided.
|
|
177
|
-
* connection's `registry.listConnectors()`, so a toolkit-scoped session lists
|
|
178
|
-
* only in-scope guides, and `resolveSkill` below reports an out-of-scope
|
|
179
|
-
* `connector:<id>` exactly as it reports an unknown connector.
|
|
150
|
+
* place guide visibility is decided.
|
|
180
151
|
*/
|
|
181
152
|
export function listSkills(connectors: readonly Connector[]): SkillListing[] {
|
|
182
153
|
const listing: SkillListing[] = AVAILABLE_SKILLS.map((skill) => ({
|
package/src/timeout.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
// The deadline vocabulary shared by
|
|
2
|
-
//
|
|
3
|
-
// liveness checks (src/credential-health.ts). One definition so a "probe" means
|
|
4
|
-
// the same thing, and is bounded the same way, wherever one is issued.
|
|
1
|
+
// The deadline vocabulary shared by downstream discovery probes and tool calls.
|
|
2
|
+
// One definition keeps those waits bounded consistently.
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* Generous default bound for a single downstream probe/catalog call. High enough
|
package/src/types.ts
CHANGED
|
@@ -174,7 +174,7 @@ export interface ConnectorContext {
|
|
|
174
174
|
timeoutMs?: number;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
type ConnectorStatusState = "ok" | "auth_required" | "error";
|
|
178
178
|
|
|
179
179
|
export interface ConnectorStatus {
|
|
180
180
|
state: ConnectorStatusState;
|
|
@@ -226,19 +226,6 @@ export interface Connector {
|
|
|
226
226
|
values: ConnectorCredentialValues,
|
|
227
227
|
ctx: ConnectorContext,
|
|
228
228
|
): Promise<CredentialTestResult>;
|
|
229
|
-
/**
|
|
230
|
-
* Optional: whether this connector currently holds a stored downstream
|
|
231
|
-
* credential — an OAuth grant it persisted, typically. Read only by the
|
|
232
|
-
* credential liveness checks: a connector with nothing stored has no
|
|
233
|
-
* credential whose liveness could be in question, and probing it anyway would
|
|
234
|
-
* start an authorization flow nobody asked for.
|
|
235
|
-
*
|
|
236
|
-
* Implement it on connectors that manage their own credential storage (the
|
|
237
|
-
* shipped `remoteMcp` does, for `auth: { type: "oauth" }`). Connectors whose
|
|
238
|
-
* credential lives in connecta's vault (`credential` above) need not: the
|
|
239
|
-
* vault answers for them. Must not perform downstream I/O.
|
|
240
|
-
*/
|
|
241
|
-
hasStoredCredential?(ctx: ConnectorContext): Promise<boolean>;
|
|
242
229
|
/**
|
|
243
230
|
* Statically-known tool defs, exposed by in-code connectors (`api()`) for
|
|
244
231
|
* startup convention checks. Remote connectors omit this — their tools are
|
|
@@ -384,50 +371,12 @@ export interface ExecutorLease {
|
|
|
384
371
|
release(): void;
|
|
385
372
|
}
|
|
386
373
|
|
|
387
|
-
/**
|
|
388
|
-
* Which toolkits one inbound identity may open — the membership half of the
|
|
389
|
-
* deployment=org / toolkit=team framing (documentation/toolkits.md). A mapping, never a
|
|
390
|
-
* policy engine: one identity → the toolkit names it may select, plus whether
|
|
391
|
-
* it may connect with no `?toolkit=` at all.
|
|
392
|
-
*
|
|
393
|
-
* An identity with NO binding is unbound and keeps the pre-binding behavior:
|
|
394
|
-
* any declared toolkit, or the full registry. A binding is enforced at connect
|
|
395
|
-
* time, before any scoped registry is constructed.
|
|
396
|
-
*/
|
|
397
|
-
export interface ToolkitBinding {
|
|
398
|
-
/** Toolkit names this identity may select with `?toolkit=<name>`. */
|
|
399
|
-
readonly toolkits: readonly string[];
|
|
400
|
-
/**
|
|
401
|
-
* Whether this identity may also connect with no `?toolkit=` and see the full
|
|
402
|
-
* registry (and read the deployment-wide operator surfaces). Defaults to
|
|
403
|
-
* false: binding a credential to a toolkit means binding it.
|
|
404
|
-
*/
|
|
405
|
-
readonly unscoped?: boolean;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
374
|
/** Result of an inbound-auth check. */
|
|
409
375
|
export type AuthResult =
|
|
410
376
|
| {
|
|
411
377
|
ok: true;
|
|
412
378
|
userId?: string;
|
|
413
379
|
subjectId?: string;
|
|
414
|
-
/**
|
|
415
|
-
* Toolkit binding resolved for THIS identity — the seam for an adapter
|
|
416
|
-
* that maps its own users (or an IdP claim) to views. Omit to inherit the
|
|
417
|
-
* provider's `toolkitBinding`.
|
|
418
|
-
*
|
|
419
|
-
* When the provider also declares one, the declaration is a **CEILING**,
|
|
420
|
-
* not a default: connecta intersects the two, and grants `unscoped` only
|
|
421
|
-
* if both do. A per-identity binding can therefore narrow the credential's
|
|
422
|
-
* view but never widen it — otherwise an adapter reading a user-writable
|
|
423
|
-
* claim would let the user name their own toolkits. When the provider
|
|
424
|
-
* declares nothing, this binding is used as given.
|
|
425
|
-
*
|
|
426
|
-
* Validated on arrival (a malformed one refuses the request with 403
|
|
427
|
-
* rather than being ignored), but never checked against the configured
|
|
428
|
-
* toolkits, which is only possible for the static declaration at startup.
|
|
429
|
-
*/
|
|
430
|
-
toolkitBinding?: ToolkitBinding;
|
|
431
380
|
}
|
|
432
381
|
| { ok: false; response: Response };
|
|
433
382
|
|
|
@@ -527,14 +476,6 @@ export interface InboundAuth {
|
|
|
527
476
|
* provider instead of asking the operator to paste a static bearer secret.
|
|
528
477
|
*/
|
|
529
478
|
uiAuth?: UiAuthConfig;
|
|
530
|
-
/**
|
|
531
|
-
* Optional toolkit binding for every identity this provider admits
|
|
532
|
-
* (documentation/toolkits.md). Declared statically so `createConnecta` can validate the
|
|
533
|
-
* names against `ConnectaConfig.toolkits` and throw on a typo — a binding
|
|
534
|
-
* nobody wrote is not one an operator can reason about. An `authorize` result
|
|
535
|
-
* may narrow it per identity with its own `toolkitBinding`.
|
|
536
|
-
*/
|
|
537
|
-
toolkitBinding?: ToolkitBinding;
|
|
538
479
|
/** Serve/short-circuit .well-known + OPTIONS. Return null when not handled. */
|
|
539
480
|
handleMetadata?(
|
|
540
481
|
request: Request,
|
package/src/ui.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
type CredentialManagementCapability,
|
|
17
17
|
type UiConnector,
|
|
18
18
|
type UiData,
|
|
19
|
-
type UiToolkit,
|
|
20
19
|
type UiTool,
|
|
21
20
|
} from "./operator-ui/model.js";
|
|
22
21
|
import {
|
|
@@ -24,8 +23,11 @@ import {
|
|
|
24
23
|
OPERATOR_UI_SCRIPT,
|
|
25
24
|
} from "./operator-ui/generated.js";
|
|
26
25
|
import type { Registry } from "./registry.js";
|
|
27
|
-
import type {
|
|
28
|
-
|
|
26
|
+
import type {
|
|
27
|
+
ConnectaBranding,
|
|
28
|
+
ConnectorStatus,
|
|
29
|
+
UiAuthConfig,
|
|
30
|
+
} from "./types.js";
|
|
29
31
|
import { CONNECTA_VERSION } from "./version.js";
|
|
30
32
|
|
|
31
33
|
export {
|
|
@@ -310,7 +312,6 @@ export async function buildUiData(
|
|
|
310
312
|
credentialManagement: CredentialManagementCapability = credentialVault
|
|
311
313
|
? "available"
|
|
312
314
|
: "requires_clerk",
|
|
313
|
-
toolkits?: ReadonlyMap<string, Toolkit>,
|
|
314
315
|
defer?: DeferredWork,
|
|
315
316
|
oauthManagement = false,
|
|
316
317
|
discoveryConcurrency?: number,
|
|
@@ -322,8 +323,10 @@ export async function buildUiData(
|
|
|
322
323
|
connectorSet,
|
|
323
324
|
concurrency,
|
|
324
325
|
async (c): Promise<UiConnector> => {
|
|
325
|
-
const
|
|
326
|
-
const
|
|
326
|
+
const drift = await registry.credentialDriftFor(c.id);
|
|
327
|
+
const status: ConnectorStatus = drift
|
|
328
|
+
? { state: "auth_required", message: drift }
|
|
329
|
+
: await registry.statusFor(c.id, baseUrl, requestScope);
|
|
327
330
|
let tools: UiTool[] = [];
|
|
328
331
|
// `status()` on an unauthenticated remote connector starts OAuth and
|
|
329
332
|
// stores its state + PKCE verifier. Probing listTools immediately
|
|
@@ -447,17 +450,6 @@ export async function buildUiData(
|
|
|
447
450
|
toolCount: tools.length,
|
|
448
451
|
tools,
|
|
449
452
|
...(c.disconnectAuth && c.startAuth ? { oauth: true } : {}),
|
|
450
|
-
...(credentialCheck
|
|
451
|
-
? {
|
|
452
|
-
credentialCheck: {
|
|
453
|
-
state: credentialCheck.state,
|
|
454
|
-
checkedAt: credentialCheck.checkedAt,
|
|
455
|
-
...(credentialCheck.message
|
|
456
|
-
? { message: credentialCheck.message }
|
|
457
|
-
: {}),
|
|
458
|
-
},
|
|
459
|
-
}
|
|
460
|
-
: {}),
|
|
461
453
|
...(credential ? { credential } : {}),
|
|
462
454
|
};
|
|
463
455
|
},
|
|
@@ -476,29 +468,10 @@ export async function buildUiData(
|
|
|
476
468
|
if (result.status === "rejected") throw result.reason;
|
|
477
469
|
return result.value;
|
|
478
470
|
});
|
|
479
|
-
const toolkitData: UiToolkit[] = [...(toolkits?.values() ?? [])].map(
|
|
480
|
-
(toolkit) => ({
|
|
481
|
-
name: toolkit.name,
|
|
482
|
-
connectors: [...toolkit.connectors],
|
|
483
|
-
includeTools: [...toolkit.includeTools],
|
|
484
|
-
excludeTools: [...toolkit.excludeTools],
|
|
485
|
-
toolCount: connectors.reduce(
|
|
486
|
-
(count, connector) =>
|
|
487
|
-
count +
|
|
488
|
-
(toolkit.hasConnector(connector.id)
|
|
489
|
-
? connector.tools.filter((tool) =>
|
|
490
|
-
toolkit.hasTool(connector.id, tool.name),
|
|
491
|
-
).length
|
|
492
|
-
: 0),
|
|
493
|
-
0,
|
|
494
|
-
),
|
|
495
|
-
}),
|
|
496
|
-
);
|
|
497
471
|
return {
|
|
498
472
|
serverInfo,
|
|
499
473
|
connectaVersion: CONNECTA_VERSION,
|
|
500
474
|
connectors,
|
|
501
|
-
toolkits: toolkitData,
|
|
502
475
|
activityEnabled,
|
|
503
476
|
credentialManagement,
|
|
504
477
|
oauthManagement,
|
|
@@ -668,16 +641,6 @@ ${clerkScript}
|
|
|
668
641
|
<div id="list" class="connector-tools" aria-busy="false"></div>
|
|
669
642
|
</div>
|
|
670
643
|
</section>
|
|
671
|
-
<section class="section pgrid" aria-labelledby="toolkitLedgerHeading">
|
|
672
|
-
<h2 class="pcap" id="toolkitLedgerHeading">Toolkits</h2>
|
|
673
|
-
<div class="pbody">
|
|
674
|
-
<p class="toolkit-copy meta">
|
|
675
|
-
Read-only views from deployment config. Change the config and redeploy
|
|
676
|
-
to update them.
|
|
677
|
-
</p>
|
|
678
|
-
<div id="toolkitList" class="toolkit-ledger"></div>
|
|
679
|
-
</div>
|
|
680
|
-
</section>
|
|
681
644
|
</section>
|
|
682
645
|
|
|
683
646
|
<section id="credentialsView"${page === "credentials" ? "" : ' class="hidden"'}>
|
package/src/version.ts
CHANGED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import type { CredentialVault } from "./credentials.js";
|
|
2
|
-
import { type DeferredWork } from "./connector-scope.js";
|
|
3
|
-
import type { Connector, ConnectorContext, ConnectorStatusState, KVStorage, Logger } from "./types.js";
|
|
4
|
-
/** Verdict of one liveness check. Same vocabulary as `ConnectorStatus.state`. */
|
|
5
|
-
export type CredentialCheckState = ConnectorStatusState;
|
|
6
|
-
/** The stored verdict of the most recent liveness check of one connector. */
|
|
7
|
-
export interface CredentialHealthRecord {
|
|
8
|
-
state: CredentialCheckState;
|
|
9
|
-
/** ISO timestamp of the check that produced this record. */
|
|
10
|
-
checkedAt: string;
|
|
11
|
-
/** Why, for a non-ok state — the connector's own reason, verbatim. */
|
|
12
|
-
message?: string;
|
|
13
|
-
/** Consent URL to open, when the connector reported one. */
|
|
14
|
-
authorizationUrl?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Why a connector was not checked.
|
|
18
|
-
*
|
|
19
|
-
* - `not_found` — no connector with that id is registered. Only reachable
|
|
20
|
-
* through an explicit `ids` request, and reported rather than dropped so a
|
|
21
|
-
* typo in a scheduled check is visible instead of silent.
|
|
22
|
-
* - `not_checkable` — it stores no credential connecta manages, or exposes no
|
|
23
|
-
* usable way to ask: neither `status()` nor a credential test hook the
|
|
24
|
-
* declared credential shape can use (`credentialTestRule`), against a value
|
|
25
|
-
* actually stored under it.
|
|
26
|
-
* - `no_credential` — checkable, but nothing is stored yet: there is no
|
|
27
|
-
* credential whose liveness could be in question, and probing would start an
|
|
28
|
-
* OAuth flow nobody asked for.
|
|
29
|
-
* - `fresh` — checked less than `intervalSeconds` ago (by any isolate — the
|
|
30
|
-
* record is persisted), so this is the rate limit doing its job.
|
|
31
|
-
* - `in_flight` — another check of this connector is already running.
|
|
32
|
-
*/
|
|
33
|
-
export type CredentialCheckSkip = "not_found" | "not_checkable" | "no_credential" | "fresh" | "in_flight";
|
|
34
|
-
/** One connector's outcome in a sweep. */
|
|
35
|
-
export interface CredentialCheckResult {
|
|
36
|
-
connectorId: string;
|
|
37
|
-
/**
|
|
38
|
-
* The record now in force. Present for a completed check, and for a `fresh`
|
|
39
|
-
* skip (where the still-valid record is what the skip deferred to).
|
|
40
|
-
*/
|
|
41
|
-
record?: CredentialHealthRecord;
|
|
42
|
-
/** Set when no check ran; `record` is then whatever was already stored. */
|
|
43
|
-
skipped?: CredentialCheckSkip;
|
|
44
|
-
/**
|
|
45
|
-
* The check ran, but its verdict was thrown away: the credential it judged
|
|
46
|
-
* was replaced or removed while it was in flight (see `clear`). `record` is
|
|
47
|
-
* what the check saw, not what is stored — nothing is.
|
|
48
|
-
*/
|
|
49
|
-
discarded?: true;
|
|
50
|
-
/** How long the check took, when one ran. */
|
|
51
|
-
latencyMs?: number;
|
|
52
|
-
}
|
|
53
|
-
/** Deployment-wide tuning for credential liveness checks. */
|
|
54
|
-
export interface CredentialHealthConfig {
|
|
55
|
-
/**
|
|
56
|
-
* Minimum seconds between checks of the same connector, across isolates (the
|
|
57
|
-
* verdict is persisted, so a Worker cron isolate and a request isolate share
|
|
58
|
-
* one clock). Default 900 (15 minutes). This is the bound on downstream cost:
|
|
59
|
-
* repeated status reads never each trigger a check.
|
|
60
|
-
*/
|
|
61
|
-
intervalSeconds?: number;
|
|
62
|
-
/** Max checks in flight at once during one sweep. Default 4. */
|
|
63
|
-
concurrency?: number;
|
|
64
|
-
/** Per-check deadline. Default 30 000, the probe default. */
|
|
65
|
-
timeoutMs?: number;
|
|
66
|
-
/**
|
|
67
|
-
* Let inbound authenticated `/mcp` and `/ui/data` traffic trigger a *due*
|
|
68
|
-
* sweep in the background (`ctx.waitUntil` where the runtime has it). Default
|
|
69
|
-
* true — it is the trigger that makes stale-credential detection work with no
|
|
70
|
-
* scheduler wired at all, and it cannot slow a request down or change a
|
|
71
|
-
* result. Set false to check only from `Connecta.checkCredentials()`.
|
|
72
|
-
*/
|
|
73
|
-
onRequest?: boolean;
|
|
74
|
-
}
|
|
75
|
-
/** What the checker needs from the registry, without depending on it. */
|
|
76
|
-
export interface CredentialHealthDeps {
|
|
77
|
-
listConnectors(): Connector[];
|
|
78
|
-
getConnector(id: string): Connector | undefined;
|
|
79
|
-
contextFor(id: string, baseUrl: string, requestScope?: object): ConnectorContext;
|
|
80
|
-
storage: KVStorage;
|
|
81
|
-
logger: Logger;
|
|
82
|
-
credentialVault?: CredentialVault;
|
|
83
|
-
}
|
|
84
|
-
export interface CredentialCheckOptions {
|
|
85
|
-
/** Check even connectors whose verdict is still fresh. */
|
|
86
|
-
force?: boolean;
|
|
87
|
-
/** Restrict the sweep to these connector ids. Default: every connector. */
|
|
88
|
-
ids?: string[];
|
|
89
|
-
/**
|
|
90
|
-
* @deprecated Ignored. Credential checks always create and close their own
|
|
91
|
-
* probe scope; no core path supplies an existing request scope.
|
|
92
|
-
*/
|
|
93
|
-
requestScope?: object;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Runs and caches credential liveness checks. One instance per `Registry`.
|
|
97
|
-
*
|
|
98
|
-
* Cost is bounded four ways, because a status surface an agent polls must never
|
|
99
|
-
* become a way to hammer a downstream auth endpoint:
|
|
100
|
-
*
|
|
101
|
-
* 1. **Eligibility** — only connectors holding a credential of ours are probed
|
|
102
|
-
* at all (`isCheckableConnector`), and only when something is actually stored.
|
|
103
|
-
* 2. **Freshness (cross-isolate)** — a persisted verdict younger than
|
|
104
|
-
* `intervalSeconds` short-circuits the check, so every isolate and every
|
|
105
|
-
* trigger share one budget.
|
|
106
|
-
* 3. **Sweep gate (per isolate)** — `sweepIfDue` runs at most one traffic-
|
|
107
|
-
* triggered sweep per interval per isolate, and never two at once, so a burst
|
|
108
|
-
* of requests costs one sweep, not one per request.
|
|
109
|
-
* 4. **Deadline + fan-out bound** — each check is bounded by `timeoutMs` and at
|
|
110
|
-
* most `concurrency` run together (the same shape as the
|
|
111
|
-
* `discovery.probeTimeoutMs` bound on the discovery fan-out, issue #19).
|
|
112
|
-
*/
|
|
113
|
-
export declare class CredentialHealthChecker {
|
|
114
|
-
private readonly deps;
|
|
115
|
-
private readonly store;
|
|
116
|
-
private readonly intervalMs;
|
|
117
|
-
private readonly concurrency;
|
|
118
|
-
private readonly timeoutMs;
|
|
119
|
-
private readonly onRequest;
|
|
120
|
-
/** Per-connector checks in flight in THIS isolate. */
|
|
121
|
-
private readonly inFlight;
|
|
122
|
-
/** Earliest a traffic-triggered sweep may run again in this isolate. */
|
|
123
|
-
private nextSweepAt;
|
|
124
|
-
private sweeping;
|
|
125
|
-
constructor(deps: CredentialHealthDeps, config?: CredentialHealthConfig);
|
|
126
|
-
/**
|
|
127
|
-
* The stored verdict, if any. No downstream I/O — and no storage read at all
|
|
128
|
-
* for a connector that stores no credential of ours, since only a checkable
|
|
129
|
-
* connector can ever have had a verdict written. That keeps
|
|
130
|
-
* `list_connectors({ probe: false })` exactly as cheap as it was for the
|
|
131
|
-
* deployments this feature does not apply to.
|
|
132
|
-
*/
|
|
133
|
-
healthFor(connectorId: string): Promise<CredentialHealthRecord | undefined>;
|
|
134
|
-
/**
|
|
135
|
-
* Record a liveness verdict observed elsewhere — today, the live status a
|
|
136
|
-
* `list_connectors({ probe: true })` just performed. Filtered by the same
|
|
137
|
-
* eligibility rule as a check, so this stays a record of *credential* health
|
|
138
|
-
* rather than a general status cache, and so it also counts against the
|
|
139
|
-
* freshness budget: an operator who just probed live does not get swept again
|
|
140
|
-
* moments later.
|
|
141
|
-
*/
|
|
142
|
-
record(connectorId: string, record: CredentialHealthRecord): Promise<void>;
|
|
143
|
-
/** Forget a connector's verdict — its credential just changed under us. */
|
|
144
|
-
clear(connectorId: string): Promise<void>;
|
|
145
|
-
/** Whether any connector in this deployment could be checked at all. */
|
|
146
|
-
hasCheckableConnectors(): boolean;
|
|
147
|
-
/**
|
|
148
|
-
* Check every (or the named) connector's stored credential and return one
|
|
149
|
-
* outcome per connector considered. Never rejects: a connector that throws,
|
|
150
|
-
* hangs past `timeoutMs`, or cannot be persisted becomes an `error` verdict.
|
|
151
|
-
*/
|
|
152
|
-
check(baseUrl: string, opts?: CredentialCheckOptions, defer?: DeferredWork): Promise<CredentialCheckResult[]>;
|
|
153
|
-
/**
|
|
154
|
-
* The traffic-triggered sweep: a promise to hand to `ctx.waitUntil`, or
|
|
155
|
-
* `undefined` when nothing is due (the common case, and free — no I/O). The
|
|
156
|
-
* gate is armed BEFORE the sweep starts, so a burst of concurrent requests
|
|
157
|
-
* produces one sweep.
|
|
158
|
-
*/
|
|
159
|
-
sweepIfDue(baseUrl: string, defer?: DeferredWork): Promise<CredentialCheckResult[]> | undefined;
|
|
160
|
-
private checkOne;
|
|
161
|
-
private recordOrNothing;
|
|
162
|
-
private runCheck;
|
|
163
|
-
/**
|
|
164
|
-
* `isCheckableConnector` re-asked against what is actually stored: the hook
|
|
165
|
-
* the declared shape selects, bound to a value that fits it (see
|
|
166
|
-
* {@link testHookFor}), or a `status()` to fall back on. Neither ⇒ there is no
|
|
167
|
-
* honest question to put to this connector.
|
|
168
|
-
*/
|
|
169
|
-
private canAsk;
|
|
170
|
-
/**
|
|
171
|
-
* Ask the connector whether the credential it holds still works — with no
|
|
172
|
-
* downstream mutation and no tool call. A credential test is preferred for a
|
|
173
|
-
* vault credential because it validates the stored value itself; `status()` is
|
|
174
|
-
* the downstream-OAuth answer (it refreshes the grant, which is the liveness
|
|
175
|
-
* question for a token).
|
|
176
|
-
*/
|
|
177
|
-
private probe;
|
|
178
|
-
private settle;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Whether a liveness verdict may DECIDE a connector's cached status.
|
|
182
|
-
*
|
|
183
|
-
* Only `auth_required` ever does, and only while nothing better has happened
|
|
184
|
-
* since. Two separate judgements:
|
|
185
|
-
*
|
|
186
|
-
* 1. **`error` is not credential evidence.** A check that timed out, threw, or
|
|
187
|
-
* got a 502 from the provider's status endpoint failed to *complete* — it
|
|
188
|
-
* learned nothing about the credential. Letting it set the status would flip
|
|
189
|
-
* a connector whose calls are fine to `error` for a whole interval on a DNS
|
|
190
|
-
* blip. Error verdicts stay visible in `credentialCheck` (an operator wants
|
|
191
|
-
* to know checks are failing) but the status keeps coming from observed real
|
|
192
|
-
* calls, which is evidence.
|
|
193
|
-
* 2. **A successful real call retires the verdict, except static shape drift.**
|
|
194
|
-
* Traffic beats a background probe, so a `lastSuccessAt` at or after
|
|
195
|
-
* `checkedAt` normally means the credential demonstrably works. Stored-shape
|
|
196
|
-
* drift is different: a credential-independent tool can succeed without
|
|
197
|
-
* making a missing declared field appear, so only replacement/removal clears
|
|
198
|
-
* that verdict.
|
|
199
|
-
*
|
|
200
|
-
* `auth_required` deliberately outranks an observed real-call *failure*: both
|
|
201
|
-
* say something is wrong, and only one of them carries the URL that fixes it.
|
|
202
|
-
* The failure stays visible as `lastError`.
|
|
203
|
-
*/
|
|
204
|
-
export declare function credentialVerdictApplies(record: CredentialHealthRecord | undefined, lastSuccessAt: string | undefined): boolean;
|
|
205
|
-
//# sourceMappingURL=credential-health.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credential-health.d.ts","sourceRoot":"","sources":["../src/credential-health.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAEhB,oBAAoB,EAEpB,SAAS,EACT,MAAM,EACP,MAAM,YAAY,CAAC;AAEpB,iFAAiF;AACjF,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAExD,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,eAAe,GACf,eAAe,GACf,OAAO,GACP,WAAW,CAAC;AAEhB,0CAA0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,6DAA6D;AAC7D,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA0MD,yEAAyE;AACzE,MAAM,WAAW,oBAAoB;IACnC,cAAc,IAAI,SAAS,EAAE,CAAC;IAC9B,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAChD,UAAU,CACR,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,gBAAgB,CAAC;IACpB,OAAO,EAAE,SAAS,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAiGD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAuB;IAahC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,sDAAsD;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAChE,wEAAwE;IACxE,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAA+C;gBAG5C,IAAI,EAAE,oBAAoB,EAC3C,MAAM,GAAE,sBAA2B;IAsBrC;;;;;;OAMG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAQ3E;;;;;;;OAOG;IACG,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,IAAI,CAAC;IAMhB,2EAA2E;IAC3E,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,wEAAwE;IACxE,sBAAsB,IAAI,OAAO;IAIjC;;;;OAIG;IACG,KAAK,CACT,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,sBAA2B,EACjC,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAcnC;;;;;OAKG;IACH,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,qBAAqB,EAAE,CAAC,GAAG,SAAS;YAajC,QAAQ;YAiCR,eAAe;YAOf,QAAQ;IAiHtB;;;;;OAKG;IACH,OAAO,CAAC,MAAM;IAOd;;;;;;OAMG;YACW,KAAK;YA+BL,MAAM;CAcrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,sBAAsB,GAAG,SAAS,EAC1C,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,OAAO,CAMT"}
|