@zackbart/connecta 0.28.1 → 0.29.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.
- package/CHANGELOG.md +788 -5
- package/README.md +62 -42
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1584 -1580
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +93 -71
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +59 -7
- package/dist/connectors/api-connector.js +106 -19
- package/dist/connectors/api.js +4 -1
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +84 -35
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1774 -320
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +24 -256
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
- package/dist/providers/{breeze.js → breeze/index.js} +184 -140
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
- package/dist/providers/{ccb.js → ccb/index.js} +271 -116
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
- package/dist/providers/{overflow.js → overflow/index.js} +74 -111
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
- package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
- package/dist/providers/{tithely.js → tithely/index.js} +60 -91
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +110 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +664 -231
- package/documentation/auth.md +936 -546
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +120 -54
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/basecamp.js +0 -445
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- package/templates/node/src/file-activity.ts +0 -182
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { type Field } from "./config-schema.js";
|
|
2
|
+
import type { Connector, ConnectorCallAdmissionPolicy, ConnectorUsageGuide, ToolClassification } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* How a provider reaches its vendor: the vendor's hosted MCP server, an
|
|
5
|
+
* `api()` connector over the vendor's HTTP API, or one connector composing
|
|
6
|
+
* both with an explicit operation ownership map.
|
|
7
|
+
*/
|
|
8
|
+
export type ProviderKind = "mcp" | "api" | "composed";
|
|
9
|
+
/** Options every provider accepts, validated before `create` runs. */
|
|
10
|
+
export interface ProviderOptions {
|
|
11
|
+
/** What this connection is for and which decisions it answers. Required. */
|
|
12
|
+
purpose: string;
|
|
13
|
+
/** Display name; each provider chooses its own default. */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Deployment conventions, appended after the maintained guide. */
|
|
16
|
+
instructions?: string;
|
|
17
|
+
/** Downstream auth ownership. Defaults to one shared deployment grant. */
|
|
18
|
+
authScope?: "shared" | "personal";
|
|
19
|
+
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
20
|
+
maxResultBytes?: number;
|
|
21
|
+
/** Optional per-runtime downstream call-admission policy. */
|
|
22
|
+
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
23
|
+
}
|
|
24
|
+
export declare const CALL_ADMISSION: Field<unknown, unknown, true> & {
|
|
25
|
+
readonly shape: {
|
|
26
|
+
rules: Field<unknown[], readonly unknown[] | undefined, true>;
|
|
27
|
+
maxPartitions: Field<unknown, unknown, true>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** Options every maintained provider shares. */
|
|
31
|
+
export declare const PROVIDER_COMMON: {
|
|
32
|
+
callAdmission: Field<unknown, unknown, true> & {
|
|
33
|
+
readonly shape: {
|
|
34
|
+
rules: Field<unknown[], readonly unknown[] | undefined, true>;
|
|
35
|
+
maxPartitions: Field<unknown, unknown, true>;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
title: Field<unknown, unknown, true>;
|
|
39
|
+
authScope: Field<unknown, unknown, true>;
|
|
40
|
+
maxResultBytes: Field<unknown, unknown, true>;
|
|
41
|
+
purpose: Field<unknown, unknown, true>;
|
|
42
|
+
instructions: Field<unknown, unknown, true>;
|
|
43
|
+
};
|
|
44
|
+
/** The provider's maintained, connection-independent usage guide. */
|
|
45
|
+
export interface ProviderSkill {
|
|
46
|
+
/**
|
|
47
|
+
* Markdown rendered after the heading and the connection context. It states
|
|
48
|
+
* conventions the vendor's schemas cannot carry, and never varies by
|
|
49
|
+
* deployment.
|
|
50
|
+
*/
|
|
51
|
+
content: string;
|
|
52
|
+
/** Heading for deployment instructions, such as `"Workspace instructions"`. */
|
|
53
|
+
instructionsHeading: string;
|
|
54
|
+
/** Exact generated guide fragments used by existing renderers. */
|
|
55
|
+
fragments?: Readonly<Record<string, string>>;
|
|
56
|
+
}
|
|
57
|
+
/** Connection facts a provider renders around its maintained guide. */
|
|
58
|
+
export interface ProviderGuideInput {
|
|
59
|
+
/**
|
|
60
|
+
* Paragraphs that lead the guide: access mode, purpose, region, account
|
|
61
|
+
* scope. The first is what discovery summarizes when `summary` is omitted.
|
|
62
|
+
*/
|
|
63
|
+
context: readonly string[];
|
|
64
|
+
/** Replaces the default `<title> usage` heading. */
|
|
65
|
+
heading?: string;
|
|
66
|
+
/** Discovery summary; see `ConnectorUsageGuide.summary`. */
|
|
67
|
+
summary?: string;
|
|
68
|
+
/** See `ConnectorUsageGuide.required`. */
|
|
69
|
+
required?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** What `defineProvider()` hands `create` after validating its options. */
|
|
72
|
+
export interface ProviderContext {
|
|
73
|
+
/** The definition's reviewed classification, for `remoteMcp({ classify })`. */
|
|
74
|
+
readonly classify?: ToolClassification;
|
|
75
|
+
/**
|
|
76
|
+
* Common connector options the deployment set, and only those, ready to
|
|
77
|
+
* spread into `remoteMcp()` or `api()` options.
|
|
78
|
+
*/
|
|
79
|
+
readonly connectorOptions: Readonly<Pick<ProviderOptions, "authScope" | "maxResultBytes" | "callAdmission">>;
|
|
80
|
+
/**
|
|
81
|
+
* Render the maintained skill with this connection's context and the
|
|
82
|
+
* deployment's instructions. Instructions are appended; they never replace
|
|
83
|
+
* the maintained text.
|
|
84
|
+
*/
|
|
85
|
+
usageGuide(input: ProviderGuideInput): ConnectorUsageGuide;
|
|
86
|
+
}
|
|
87
|
+
export interface ProviderDefinition<O extends ProviderOptions> {
|
|
88
|
+
/**
|
|
89
|
+
* Stable lowercase name: the `providers/<name>` subpath and the default
|
|
90
|
+
* factory name in construction errors.
|
|
91
|
+
*/
|
|
92
|
+
name: string;
|
|
93
|
+
/** Vendor display name, and the default guide heading. */
|
|
94
|
+
title: string;
|
|
95
|
+
kind: ProviderKind;
|
|
96
|
+
skill: ProviderSkill;
|
|
97
|
+
/** Maintained display name in the generated README inventory. */
|
|
98
|
+
readme?: string;
|
|
99
|
+
/** Reviewed build facts; generation never raises an observed cap. */
|
|
100
|
+
bundle?: {
|
|
101
|
+
baselineGzip: number;
|
|
102
|
+
maxGzip: number;
|
|
103
|
+
note?: string;
|
|
104
|
+
};
|
|
105
|
+
/** Closed factory options, declared with `optionsOf<O>()` for key parity. */
|
|
106
|
+
options: Field<unknown, unknown>;
|
|
107
|
+
/**
|
|
108
|
+
* Reviewed classification of the vendor's hosted MCP catalog. Hosted and
|
|
109
|
+
* composed providers pass it to `remoteMcp({ classify })` through
|
|
110
|
+
* `ProviderContext.classify`, so the verdicts that classify live tools and
|
|
111
|
+
* the record a drift check reads cannot disagree. `api()` providers annotate
|
|
112
|
+
* each tool they author and must omit it.
|
|
113
|
+
*/
|
|
114
|
+
classify?: ToolClassification;
|
|
115
|
+
/**
|
|
116
|
+
* Build the connector. Runs synchronously at construction after its closed
|
|
117
|
+
* options and common values are validated. Throw here for provider-specific
|
|
118
|
+
* mistakes.
|
|
119
|
+
*/
|
|
120
|
+
create(id: string, options: Readonly<O>, provider: ProviderContext): Connector;
|
|
121
|
+
}
|
|
122
|
+
/** A provider's factory, carrying the definition build and check tools read. */
|
|
123
|
+
export interface ProviderFactory<O extends ProviderOptions> {
|
|
124
|
+
(id: string, options: O): Connector;
|
|
125
|
+
readonly definition: Readonly<ProviderDefinition<O>>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Define a maintained provider: one validated description and one factory.
|
|
129
|
+
*
|
|
130
|
+
* Definition mistakes throw when the provider module loads. Closed-option
|
|
131
|
+
* and common-value mistakes throw before `create` runs,
|
|
132
|
+
* so a deployment never boots in the wrong shape (INV-11).
|
|
133
|
+
*/
|
|
134
|
+
export declare function defineProvider<O extends ProviderOptions>(definition: ProviderDefinition<O>): ProviderFactory<O>;
|
|
135
|
+
/**
|
|
136
|
+
* Attach the same validated definition to an existing provider constructor.
|
|
137
|
+
* Its option policy stays in its builder during the mechanical folder move;
|
|
138
|
+
* adopting defineProvider's common-value policy is a separate conversion.
|
|
139
|
+
*/
|
|
140
|
+
export declare function asProviderFactory<O extends ProviderOptions>(definition: Omit<ProviderDefinition<O>, "create"> & {
|
|
141
|
+
create(id: string, options: Readonly<O>): Connector;
|
|
142
|
+
}): ProviderFactory<O>;
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// The one shape every maintained provider takes. Deliberately transport-free:
|
|
2
|
+
// it imports neither `remoteMcp()` nor `api()`, so an `api()` provider does not
|
|
3
|
+
// acquire the MCP client, OAuth, or Effect graph by using it, and a hosted
|
|
4
|
+
// provider does not acquire `api()`. Its runtime imports validate plain
|
|
5
|
+
// options and reviewed classification, with no I/O.
|
|
6
|
+
import { array, assertKnownOptions, keys, optionsOf } from "./config-schema.js";
|
|
7
|
+
import { reviewedClassification } from "./catalog-drift.js";
|
|
8
|
+
const budget = optionsOf()(keys("kind", "maxCalls", "windowMs"));
|
|
9
|
+
const admissionRule = optionsOf()({
|
|
10
|
+
...keys("maxConcurrency", "maxQueueSize", "queueTimeoutMs", "retryAfterMs", "partitionKey"),
|
|
11
|
+
budget,
|
|
12
|
+
});
|
|
13
|
+
export const CALL_ADMISSION = optionsOf()({
|
|
14
|
+
...keys("maxPartitions"),
|
|
15
|
+
rules: array(admissionRule),
|
|
16
|
+
});
|
|
17
|
+
/** Options every maintained provider shares. */
|
|
18
|
+
export const PROVIDER_COMMON = {
|
|
19
|
+
...keys("title", "authScope", "purpose", "instructions", "maxResultBytes"),
|
|
20
|
+
callAdmission: CALL_ADMISSION,
|
|
21
|
+
};
|
|
22
|
+
const PROVIDER_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
23
|
+
const KINDS = new Set(["mcp", "api", "composed"]);
|
|
24
|
+
function nonEmpty(value) {
|
|
25
|
+
return typeof value === "string" && value.trim() !== "";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Define a maintained provider: one validated description and one factory.
|
|
29
|
+
*
|
|
30
|
+
* Definition mistakes throw when the provider module loads. Closed-option
|
|
31
|
+
* and common-value mistakes throw before `create` runs,
|
|
32
|
+
* so a deployment never boots in the wrong shape (INV-11).
|
|
33
|
+
*/
|
|
34
|
+
export function defineProvider(definition) {
|
|
35
|
+
const label = `defineProvider("${String(definition?.name)}")`;
|
|
36
|
+
if (!nonEmpty(definition?.name) || !PROVIDER_NAME.test(definition.name)) {
|
|
37
|
+
throw new Error(`${label} name must be lowercase words joined by hyphens.`);
|
|
38
|
+
}
|
|
39
|
+
if (!nonEmpty(definition.title)) {
|
|
40
|
+
throw new Error(`${label} requires a non-empty title.`);
|
|
41
|
+
}
|
|
42
|
+
if (!KINDS.has(definition.kind)) {
|
|
43
|
+
throw new Error(`${label} kind must be "mcp", "api", or "composed".`);
|
|
44
|
+
}
|
|
45
|
+
if (!nonEmpty(definition.skill?.content) || !nonEmpty(definition.skill.instructionsHeading)) {
|
|
46
|
+
throw new Error(`${label} skill requires non-empty content and instructionsHeading.`);
|
|
47
|
+
}
|
|
48
|
+
if (definition.classify !== undefined && definition.kind === "api") {
|
|
49
|
+
throw new Error(`${label} is an api() provider; annotate each authored tool instead of classifying a hosted catalog.`);
|
|
50
|
+
}
|
|
51
|
+
if (!definition.options?.shape && !definition.options?.variants) {
|
|
52
|
+
throw new Error(`${label} requires a closed options shape from optionsOf().`);
|
|
53
|
+
}
|
|
54
|
+
// A validated, deep-frozen copy. The definition is what build and check
|
|
55
|
+
// tools read and what every later connector classifies with, so nothing
|
|
56
|
+
// reachable from it may change a verdict after review: neither the
|
|
57
|
+
// caller's original object nor a write through `factory.definition`.
|
|
58
|
+
const classify = definition.classify === undefined ? undefined : reviewedClassification(definition.classify, label);
|
|
59
|
+
if (typeof definition.create !== "function") {
|
|
60
|
+
throw new Error(`${label} requires a create function.`);
|
|
61
|
+
}
|
|
62
|
+
const frozen = Object.freeze({
|
|
63
|
+
...definition,
|
|
64
|
+
skill: Object.freeze({
|
|
65
|
+
...definition.skill,
|
|
66
|
+
...(definition.skill.fragments ? { fragments: Object.freeze(definition.skill.fragments) } : {}),
|
|
67
|
+
}),
|
|
68
|
+
...(definition.bundle ? { bundle: Object.freeze({ ...definition.bundle }) } : {}),
|
|
69
|
+
...(classify !== undefined ? { classify } : {}),
|
|
70
|
+
});
|
|
71
|
+
const factoryName = `${frozen.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())}()`;
|
|
72
|
+
const factory = (id, options) => asProvider(frozen.name, frozen.options, id, options, (id, options) => {
|
|
73
|
+
const at = `${factoryName.slice(0, -2)}(${JSON.stringify(id)})`;
|
|
74
|
+
if (typeof options !== "object" || options === null) {
|
|
75
|
+
throw new Error(`${factoryName} requires an options object.`);
|
|
76
|
+
}
|
|
77
|
+
if (!nonEmpty(options.purpose)) {
|
|
78
|
+
throw new Error(`${at} requires a non-empty purpose: what this connection is for and which decisions it answers.`);
|
|
79
|
+
}
|
|
80
|
+
if (options.title !== undefined && !nonEmpty(options.title)) {
|
|
81
|
+
throw new Error(`${at} title must be a non-empty string when set.`);
|
|
82
|
+
}
|
|
83
|
+
if (options.instructions !== undefined && typeof options.instructions !== "string") {
|
|
84
|
+
throw new Error(`${at} instructions must be a string when set.`);
|
|
85
|
+
}
|
|
86
|
+
if (options.authScope !== undefined && options.authScope !== "shared" && options.authScope !== "personal") {
|
|
87
|
+
throw new Error(`${at} authScope must be "shared" or "personal".`);
|
|
88
|
+
}
|
|
89
|
+
const resolved = { ...options, purpose: options.purpose.trim() };
|
|
90
|
+
const instructions = options.instructions?.trim();
|
|
91
|
+
const connector = frozen.create(id, resolved, {
|
|
92
|
+
...(frozen.classify !== undefined ? { classify: frozen.classify } : {}),
|
|
93
|
+
connectorOptions: {
|
|
94
|
+
...(options.authScope !== undefined ? { authScope: options.authScope } : {}),
|
|
95
|
+
...(options.maxResultBytes !== undefined ? { maxResultBytes: options.maxResultBytes } : {}),
|
|
96
|
+
...(options.callAdmission !== undefined ? { callAdmission: options.callAdmission } : {}),
|
|
97
|
+
},
|
|
98
|
+
usageGuide(input) {
|
|
99
|
+
const heading = input.heading ?? `${frozen.title} usage`;
|
|
100
|
+
const body = [...input.context, frozen.skill.content.trim()].join("\n\n");
|
|
101
|
+
return {
|
|
102
|
+
content: `# ${heading}\n\n${body}\n` +
|
|
103
|
+
(instructions ? `\n## ${frozen.skill.instructionsHeading}\n\n${instructions}\n` : ""),
|
|
104
|
+
...(input.summary !== undefined ? { summary: input.summary } : {}),
|
|
105
|
+
...(input.required === true ? { required: true } : {}),
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
if (connector?.id !== id) {
|
|
110
|
+
throw new Error(`${at} create() must return a connector with id "${id}".`);
|
|
111
|
+
}
|
|
112
|
+
return connector;
|
|
113
|
+
});
|
|
114
|
+
// Frozen whole: build and check tools read `definition` to learn what the
|
|
115
|
+
// factory classifies, so neither replacing nor deleting it may take effect.
|
|
116
|
+
return Object.freeze(Object.assign(factory, { definition: frozen }));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Attach the same validated definition to an existing provider constructor.
|
|
120
|
+
* Its option policy stays in its builder during the mechanical folder move;
|
|
121
|
+
* adopting defineProvider's common-value policy is a separate conversion.
|
|
122
|
+
*/
|
|
123
|
+
export function asProviderFactory(definition) {
|
|
124
|
+
const frozen = defineProvider(definition).definition;
|
|
125
|
+
const create = definition.create;
|
|
126
|
+
const factory = (id, options) => asProvider(frozen.name, frozen.options, id, options, create);
|
|
127
|
+
Object.defineProperty(factory, "name", {
|
|
128
|
+
value: frozen.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()),
|
|
129
|
+
});
|
|
130
|
+
return Object.freeze(Object.assign(factory, { definition: frozen }));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Build a maintained provider's connector: refuse unknown options and
|
|
134
|
+
* accessors by path before the builder reads any of them, then stamp the
|
|
135
|
+
* provider onto its description, so the operator surface can say "Linear"
|
|
136
|
+
* rather than "remote MCP". This is the same construction path defineProvider
|
|
137
|
+
* uses; existing constructors retain their provider-specific option policy.
|
|
138
|
+
*/
|
|
139
|
+
function asProvider(provider, shape, id, options, build) {
|
|
140
|
+
// The factory a deployment called: "planning-center" is planningCenter().
|
|
141
|
+
const factory = provider.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
142
|
+
let connector;
|
|
143
|
+
try {
|
|
144
|
+
options = assertKnownOptions(options, `${factory}(${JSON.stringify(id)})`, shape);
|
|
145
|
+
connector = build(id, options);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw providerConstructionError(factory, id, error);
|
|
149
|
+
}
|
|
150
|
+
const describe = connector.describe?.bind(connector);
|
|
151
|
+
const ownDescribe = Object.getOwnPropertyDescriptor(connector, "describe");
|
|
152
|
+
// Keep the receiver of prototype methods (including private fields). When
|
|
153
|
+
// the description cannot be replaced, inherit the whole connector instead
|
|
154
|
+
// of copying it: decorators may inherit their id, methods, and review.
|
|
155
|
+
const stamped = Object.isExtensible(connector) && (!ownDescribe || ownDescribe.configurable)
|
|
156
|
+
? connector
|
|
157
|
+
: Object.create(connector);
|
|
158
|
+
Object.defineProperty(stamped, "describe", {
|
|
159
|
+
configurable: true,
|
|
160
|
+
enumerable: false,
|
|
161
|
+
value: () => {
|
|
162
|
+
const base = describe?.() ?? { source: { kind: "custom" } };
|
|
163
|
+
return { ...base, source: { ...base.source, provider } };
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
return stamped;
|
|
167
|
+
}
|
|
168
|
+
/** Keep vendor-specific refusals in one construction-error format. */
|
|
169
|
+
function providerConstructionError(provider, id, error) {
|
|
170
|
+
if (!(error instanceof Error))
|
|
171
|
+
return error;
|
|
172
|
+
const at = `${provider}(${JSON.stringify(id)})`;
|
|
173
|
+
// The same helper handles legacy provider validation and shared config
|
|
174
|
+
// validation. Retain the original error class and the actionable detail.
|
|
175
|
+
const prefix = error.message.startsWith(at) ? at : error.message.startsWith(`${provider}()`) ? `${provider}()` : "";
|
|
176
|
+
const detail = error.message
|
|
177
|
+
.slice(prefix.length)
|
|
178
|
+
.trimStart()
|
|
179
|
+
.replace(/^\./, "")
|
|
180
|
+
.replace(/^requires\s+/, "")
|
|
181
|
+
.replace(/^must be /, "")
|
|
182
|
+
.replace(/^(.+) must be /, "$1 to be ")
|
|
183
|
+
.replace(/^declares /, "a consistent declaration of ")
|
|
184
|
+
.replace(/^with headers or credential auth requires /, "headers or credential auth to declare ");
|
|
185
|
+
error.message = `${at} requires ${detail}`;
|
|
186
|
+
return error;
|
|
187
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How big one tool result may be, measured the way it is delivered: as UTF-8
|
|
3
|
+
* JSON. Every Workspace read must reach the agent both ways it can be called.
|
|
4
|
+
* Inside `execute_code` the QuickJS bridge refuses any single host result over
|
|
5
|
+
* 256 KiB (`MAX_HOST_RESULT_BYTES` in `src/executors/quickjs-runtime.ts`);
|
|
6
|
+
* through `call_tool` a large result is stashed and paged, up to the
|
|
7
|
+
* deployment's stash size (8 MiB by default). A result built to stay under
|
|
8
|
+
* {@link RESULT_BUDGET_BYTES} clears both, so a product never has to know which
|
|
9
|
+
* route called it. Shared by the Workspace products; never its own export.
|
|
10
|
+
*
|
|
11
|
+
* The budget leaves 64 KiB of the bridge's 256 for the envelope a tool adds
|
|
12
|
+
* around its rows — page objects, counts, ids — so a product sizes its rows
|
|
13
|
+
* against this and its envelope never needs measuring.
|
|
14
|
+
*/
|
|
15
|
+
/** The size one Workspace result is built to stay under, in UTF-8 JSON bytes. */
|
|
16
|
+
export const RESULT_BUDGET_BYTES = 192 * 1024;
|
|
17
|
+
const encoder = new TextEncoder();
|
|
18
|
+
/** The UTF-8 length of a value's JSON — what both delivery routes measure. */
|
|
19
|
+
export function jsonBytes(value) {
|
|
20
|
+
const json = JSON.stringify(value);
|
|
21
|
+
return json === undefined ? 0 : encoder.encode(json).length;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* `text` cut so that its JSON string encoding — quotes and escapes included —
|
|
25
|
+
* takes at most `maxBytes` UTF-8 bytes, with `marker(dropped)` appended when
|
|
26
|
+
* anything was cut. The cut never splits a surrogate pair, and the marker is
|
|
27
|
+
* counted inside the bound, so the result always fits.
|
|
28
|
+
*/
|
|
29
|
+
export function clampText(text, maxBytes, marker) {
|
|
30
|
+
if (jsonBytes(text) <= maxBytes)
|
|
31
|
+
return text;
|
|
32
|
+
const fits = (length) => {
|
|
33
|
+
const cut = safePrefix(text, length);
|
|
34
|
+
return jsonBytes(cut + marker(text.length - cut.length)) <= maxBytes;
|
|
35
|
+
};
|
|
36
|
+
// The largest prefix that fits, by binary search over its length.
|
|
37
|
+
let low = 0;
|
|
38
|
+
let high = text.length;
|
|
39
|
+
while (low < high) {
|
|
40
|
+
const middle = Math.ceil((low + high) / 2);
|
|
41
|
+
if (fits(middle))
|
|
42
|
+
low = middle;
|
|
43
|
+
else
|
|
44
|
+
high = middle - 1;
|
|
45
|
+
}
|
|
46
|
+
const cut = safePrefix(text, low);
|
|
47
|
+
const clamped = cut + marker(text.length - cut.length);
|
|
48
|
+
// A marker longer than the bound itself leaves nothing honest to return
|
|
49
|
+
// but the cut alone, which is empty here.
|
|
50
|
+
return jsonBytes(clamped) <= maxBytes ? clamped : "";
|
|
51
|
+
}
|
|
52
|
+
/** The first `length` UTF-16 units, minus a dangling high surrogate. */
|
|
53
|
+
function safePrefix(text, length) {
|
|
54
|
+
const cut = text.slice(0, length);
|
|
55
|
+
const last = cut.charCodeAt(cut.length - 1);
|
|
56
|
+
return last >= 0xd800 && last <= 0xdbff ? cut.slice(0, -1) : cut;
|
|
57
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ConnectorContext } from "../../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The two fields of a Google service-account JSON key that delegation needs.
|
|
4
|
+
* Copy them from the downloaded key file, or pass the file's whole JSON text
|
|
5
|
+
* instead and let the provider read it.
|
|
6
|
+
*/
|
|
7
|
+
export interface GoogleServiceAccount {
|
|
8
|
+
/** The key's `client_email`, `name@project.iam.gserviceaccount.com`. */
|
|
9
|
+
clientEmail: string;
|
|
10
|
+
/**
|
|
11
|
+
* The key's `private_key`: a PKCS#8 PEM, `-----BEGIN PRIVATE KEY-----`. An
|
|
12
|
+
* environment variable that kept the JSON's `\n` escapes literally is
|
|
13
|
+
* accepted as written.
|
|
14
|
+
*/
|
|
15
|
+
privateKey: string;
|
|
16
|
+
/**
|
|
17
|
+
* The key's `client_id`, the number the Admin console's domain-wide
|
|
18
|
+
* delegation page asks for. Optional; when known, a refused grant names it.
|
|
19
|
+
*/
|
|
20
|
+
clientId?: string;
|
|
21
|
+
}
|
|
22
|
+
/** A parsed, construction-checked key. Its fields never reach a message. */
|
|
23
|
+
export interface ServiceAccountKey {
|
|
24
|
+
readonly clientEmail: string;
|
|
25
|
+
readonly clientId: string | undefined;
|
|
26
|
+
/** PKCS#8 DER of an RSA key, decoded and checked once at construction. */
|
|
27
|
+
readonly der: Uint8Array<ArrayBuffer>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check a service account at construction: the object form, or the JSON key
|
|
31
|
+
* file's text. Structural mistakes throw here, so a deployment never boots into
|
|
32
|
+
* a connector whose every call would fail.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseServiceAccount(owner: string, input: unknown): ServiceAccountKey;
|
|
35
|
+
/** One delegated token request: whose account, acting as whom, for what. */
|
|
36
|
+
export interface DelegatedTokenRequest {
|
|
37
|
+
account: ServiceAccountKey;
|
|
38
|
+
subject: string;
|
|
39
|
+
scopes: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* An access token for this subject and scope set: the cached one while it has
|
|
43
|
+
* more than a minute left, otherwise one fresh mint shared by every concurrent
|
|
44
|
+
* caller asking for the same key.
|
|
45
|
+
*
|
|
46
|
+
* Followers share only a scalar outcome and a deadline. The owner records
|
|
47
|
+
* what happened inside its own request — a token, Google's refusal, or
|
|
48
|
+
* `abandoned` for anything else — and a follower that hears `abandoned`, or
|
|
49
|
+
* reaches the deadline first, mints for itself. A refusal is rebuilt for each
|
|
50
|
+
* follower from its code and message, never handed over as the owner's object.
|
|
51
|
+
*/
|
|
52
|
+
export declare function delegatedToken(request: DelegatedTokenRequest, ctx: ConnectorContext): Promise<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Drop a token the API has just rejected, so the next request mints anew —
|
|
55
|
+
* but only that token. A 401 that arrives after a newer token replaced it
|
|
56
|
+
* leaves the newer one alone.
|
|
57
|
+
*/
|
|
58
|
+
export declare function forgetDelegatedToken(request: DelegatedTokenRequest, rejected: string): Promise<void>;
|