@zackbart/connecta 0.1.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 (116) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +249 -0
  4. package/SECURITY.md +25 -0
  5. package/assets/connecta-clay-hero.png +0 -0
  6. package/dist/activity.d.ts +82 -0
  7. package/dist/activity.d.ts.map +1 -0
  8. package/dist/activity.js +51 -0
  9. package/dist/activity.js.map +1 -0
  10. package/dist/auth/bearer.d.ts +10 -0
  11. package/dist/auth/bearer.d.ts.map +1 -0
  12. package/dist/auth/bearer.js +47 -0
  13. package/dist/auth/bearer.js.map +1 -0
  14. package/dist/auth/clerk.d.ts +20 -0
  15. package/dist/auth/clerk.d.ts.map +1 -0
  16. package/dist/auth/clerk.js +167 -0
  17. package/dist/auth/clerk.js.map +1 -0
  18. package/dist/auth/downstream-oauth.d.ts +78 -0
  19. package/dist/auth/downstream-oauth.d.ts.map +1 -0
  20. package/dist/auth/downstream-oauth.js +180 -0
  21. package/dist/auth/downstream-oauth.js.map +1 -0
  22. package/dist/catalog.d.ts +11 -0
  23. package/dist/catalog.d.ts.map +1 -0
  24. package/dist/catalog.js +144 -0
  25. package/dist/catalog.js.map +1 -0
  26. package/dist/connectors/api.d.ts +34 -0
  27. package/dist/connectors/api.d.ts.map +1 -0
  28. package/dist/connectors/api.js +36 -0
  29. package/dist/connectors/api.js.map +1 -0
  30. package/dist/connectors/remote-mcp.d.ts +33 -0
  31. package/dist/connectors/remote-mcp.d.ts.map +1 -0
  32. package/dist/connectors/remote-mcp.js +255 -0
  33. package/dist/connectors/remote-mcp.js.map +1 -0
  34. package/dist/credentials.d.ts +33 -0
  35. package/dist/credentials.d.ts.map +1 -0
  36. package/dist/credentials.js +177 -0
  37. package/dist/credentials.js.map +1 -0
  38. package/dist/execute.d.ts +40 -0
  39. package/dist/execute.d.ts.map +1 -0
  40. package/dist/execute.js +424 -0
  41. package/dist/execute.js.map +1 -0
  42. package/dist/executors/quickjs.d.ts +26 -0
  43. package/dist/executors/quickjs.d.ts.map +1 -0
  44. package/dist/executors/quickjs.js +267 -0
  45. package/dist/executors/quickjs.js.map +1 -0
  46. package/dist/favicon.d.ts +2 -0
  47. package/dist/favicon.d.ts.map +1 -0
  48. package/dist/favicon.js +49 -0
  49. package/dist/favicon.js.map +1 -0
  50. package/dist/index.d.ts +85 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +73 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/mcp-result.d.ts +9 -0
  55. package/dist/mcp-result.d.ts.map +1 -0
  56. package/dist/mcp-result.js +35 -0
  57. package/dist/mcp-result.js.map +1 -0
  58. package/dist/meta-tools.d.ts +100 -0
  59. package/dist/meta-tools.d.ts.map +1 -0
  60. package/dist/meta-tools.js +797 -0
  61. package/dist/meta-tools.js.map +1 -0
  62. package/dist/node.d.ts +9 -0
  63. package/dist/node.d.ts.map +1 -0
  64. package/dist/node.js +57 -0
  65. package/dist/node.js.map +1 -0
  66. package/dist/registry.d.ts +78 -0
  67. package/dist/registry.d.ts.map +1 -0
  68. package/dist/registry.js +299 -0
  69. package/dist/registry.js.map +1 -0
  70. package/dist/server.d.ts +31 -0
  71. package/dist/server.d.ts.map +1 -0
  72. package/dist/server.js +543 -0
  73. package/dist/server.js.map +1 -0
  74. package/dist/skills.d.ts +8 -0
  75. package/dist/skills.d.ts.map +1 -0
  76. package/dist/skills.js +60 -0
  77. package/dist/skills.js.map +1 -0
  78. package/dist/storage/file.d.ts +8 -0
  79. package/dist/storage/file.d.ts.map +1 -0
  80. package/dist/storage/file.js +53 -0
  81. package/dist/storage/file.js.map +1 -0
  82. package/dist/storage/memory.d.ts +4 -0
  83. package/dist/storage/memory.d.ts.map +1 -0
  84. package/dist/storage/memory.js +29 -0
  85. package/dist/storage/memory.js.map +1 -0
  86. package/dist/types.d.ts +219 -0
  87. package/dist/types.d.ts.map +1 -0
  88. package/dist/types.js +3 -0
  89. package/dist/types.js.map +1 -0
  90. package/dist/ui.d.ts +91 -0
  91. package/dist/ui.d.ts.map +1 -0
  92. package/dist/ui.js +1031 -0
  93. package/dist/ui.js.map +1 -0
  94. package/package.json +96 -0
  95. package/src/activity.ts +144 -0
  96. package/src/auth/bearer.ts +54 -0
  97. package/src/auth/clerk.ts +226 -0
  98. package/src/auth/downstream-oauth.ts +202 -0
  99. package/src/catalog.ts +166 -0
  100. package/src/connectors/api.ts +80 -0
  101. package/src/connectors/remote-mcp.ts +324 -0
  102. package/src/credentials.ts +259 -0
  103. package/src/execute.ts +550 -0
  104. package/src/executors/quickjs.ts +321 -0
  105. package/src/favicon.ts +53 -0
  106. package/src/index.ts +197 -0
  107. package/src/mcp-result.ts +43 -0
  108. package/src/meta-tools.ts +1120 -0
  109. package/src/node.ts +62 -0
  110. package/src/registry.ts +400 -0
  111. package/src/server.ts +758 -0
  112. package/src/skills.ts +63 -0
  113. package/src/storage/file.ts +62 -0
  114. package/src/storage/memory.ts +34 -0
  115. package/src/types.ts +254 -0
  116. package/src/ui.ts +1121 -0
@@ -0,0 +1,167 @@
1
+ // Clerk as the OAuth 2.1 authorization server; connecta is the resource server.
2
+ // Single tenant, no tenant-tag requirement, optional gate() with ~60s identity
3
+ // caching.
4
+ import { createClerkClient } from "@clerk/backend";
5
+ const CORS_HEADERS = {
6
+ "Access-Control-Allow-Origin": "*",
7
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
8
+ "Access-Control-Allow-Headers": "Content-Type, Authorization, mcp-protocol-version",
9
+ };
10
+ /** Coarse bearer shape for diagnostics — never the token itself. */
11
+ function tokenShape(request) {
12
+ const header = request.headers.get("authorization");
13
+ if (!header)
14
+ return "none";
15
+ const token = header.replace(/^Bearer\s+/i, "");
16
+ if (token.startsWith("oat_"))
17
+ return "oauth-opaque";
18
+ if (token.startsWith("eyJ"))
19
+ return "jwt";
20
+ return "other";
21
+ }
22
+ /** Clerk Frontend API origin, derived from pk_(test|live)_<b64 domain>. */
23
+ function fapiUrl(publishableKey) {
24
+ const key = publishableKey.replace(/^pk_(test|live)_/, "");
25
+ const decoded = atob(key).replace(/\$$/, "");
26
+ return `https://${decoded}`;
27
+ }
28
+ const GATE_ALLOWED_TTL_MS = 60 * 1000;
29
+ const GATE_FORBIDDEN_TTL_MS = 30 * 1000;
30
+ export function clerkAuth(opts) {
31
+ const clerk = createClerkClient({
32
+ secretKey: opts.secretKey,
33
+ publishableKey: opts.publishableKey,
34
+ });
35
+ const scopes = opts.scopes ?? ["openid", "profile", "email"];
36
+ const gateCache = new Map();
37
+ const resolveBase = (baseUrl) => opts.publicUrl ?? baseUrl;
38
+ const unauthorized = (baseUrl, tokenPresent) => {
39
+ const error = tokenPresent ? `error="invalid_token", ` : "";
40
+ const meta = `${resolveBase(baseUrl)}/.well-known/oauth-protected-resource`;
41
+ return new Response(JSON.stringify({ error: "unauthorized" }), {
42
+ status: 401,
43
+ headers: {
44
+ "Content-Type": "application/json",
45
+ "WWW-Authenticate": `Bearer ${error}resource_metadata="${meta}"`,
46
+ },
47
+ });
48
+ };
49
+ const forbidden = () => new Response(JSON.stringify({ error: "forbidden" }), { status: 403, headers: { "Content-Type": "application/json" } });
50
+ const checkGate = async (userId) => {
51
+ if (!opts.gate)
52
+ return true;
53
+ const hit = gateCache.get(userId);
54
+ if (hit && Date.now() < hit.exp)
55
+ return hit.allowed;
56
+ let allowed = false;
57
+ try {
58
+ allowed = await opts.gate(userId, clerk);
59
+ }
60
+ catch {
61
+ allowed = false;
62
+ }
63
+ gateCache.set(userId, {
64
+ allowed,
65
+ exp: Date.now() +
66
+ (allowed ? GATE_ALLOWED_TTL_MS : GATE_FORBIDDEN_TTL_MS),
67
+ });
68
+ return allowed;
69
+ };
70
+ return {
71
+ kind: "clerk",
72
+ uiAuth: {
73
+ kind: "clerk",
74
+ publishableKey: opts.publishableKey,
75
+ frontendApiUrl: fapiUrl(opts.publishableKey),
76
+ ...(opts.signInUrl ? { signInUrl: opts.signInUrl } : {}),
77
+ ...(opts.signUpUrl ? { signUpUrl: opts.signUpUrl } : {}),
78
+ },
79
+ async handleMetadata(request, baseUrl) {
80
+ const { pathname } = new URL(request.url);
81
+ if (!pathname.startsWith("/.well-known/"))
82
+ return null;
83
+ if (request.method === "OPTIONS") {
84
+ return new Response(null, { status: 204, headers: CORS_HEADERS });
85
+ }
86
+ const base = resolveBase(baseUrl);
87
+ if (pathname === "/.well-known/oauth-protected-resource" ||
88
+ pathname === "/.well-known/oauth-protected-resource/mcp") {
89
+ return Response.json({
90
+ resource: `${base}/mcp`,
91
+ authorization_servers: [fapiUrl(opts.publishableKey)],
92
+ bearer_methods_supported: ["header"],
93
+ scopes_supported: scopes,
94
+ }, { headers: CORS_HEADERS });
95
+ }
96
+ if (pathname === "/.well-known/oauth-authorization-server") {
97
+ try {
98
+ const upstream = await fetch(`${fapiUrl(opts.publishableKey)}/.well-known/oauth-authorization-server`);
99
+ if (!upstream.ok) {
100
+ return Response.json({ error: "upstream authorization server metadata unavailable" }, { status: 502, headers: CORS_HEADERS });
101
+ }
102
+ return Response.json(await upstream.json(), { headers: CORS_HEADERS });
103
+ }
104
+ catch {
105
+ return Response.json({ error: "upstream authorization server metadata unavailable" }, { status: 502, headers: CORS_HEADERS });
106
+ }
107
+ }
108
+ return null;
109
+ },
110
+ async authorize(request, baseUrl) {
111
+ const tokenPresent = Boolean(request.headers.get("authorization"));
112
+ let userId;
113
+ try {
114
+ const state = await clerk.authenticateRequest(request, {
115
+ // MCP clients use Clerk OAuth access tokens; the browser dashboard
116
+ // uses the signed-in operator's short-lived Clerk session token.
117
+ // authorizedParties must NOT be passed here: OAuth access tokens may
118
+ // be JWTs without an azp claim, and Clerk rejects azp=undefined when
119
+ // that option is set. The sibling-subdomain pin it provided is
120
+ // enforced below, only for session tokens that actually carry azp.
121
+ acceptsToken: ["oauth_token", "session_token"],
122
+ });
123
+ const auth = state.toAuth();
124
+ if (!auth?.isAuthenticated) {
125
+ // Reason (not the token) in the logs: bearer rejections are
126
+ // otherwise indistinguishable 401s in `wrangler tail`.
127
+ const detail = state;
128
+ console.warn(`[connecta] clerk rejected request: status=${state.status}` +
129
+ ` reason=${detail.reason ?? "?"} message=${detail.message ?? ""}` +
130
+ ` tokenShape=${tokenShape(request)}`);
131
+ return {
132
+ ok: false,
133
+ response: unauthorized(baseUrl, tokenPresent),
134
+ };
135
+ }
136
+ // Session JWTs carry `azp` (the origin they were minted for); pin it
137
+ // to this connecta deployment so a sibling subdomain's cookie/token
138
+ // cannot be replayed here. OAuth access tokens may have no azp.
139
+ const typed = auth;
140
+ if (typed.tokenType === "session_token") {
141
+ const azp = typed.sessionClaims?.azp;
142
+ const origin = new URL(resolveBase(baseUrl)).origin;
143
+ if (azp && azp !== origin) {
144
+ console.warn(`[connecta] session token azp mismatch: azp=${azp} expected=${origin}`);
145
+ return {
146
+ ok: false,
147
+ response: unauthorized(baseUrl, tokenPresent),
148
+ };
149
+ }
150
+ }
151
+ userId = typed.userId ?? undefined;
152
+ }
153
+ catch (error) {
154
+ console.warn(`[connecta] clerk authenticateRequest threw: ${error instanceof Error ? error.message : String(error)} tokenShape=${tokenShape(request)}`);
155
+ return { ok: false, response: unauthorized(baseUrl, true) };
156
+ }
157
+ if (!userId) {
158
+ return { ok: false, response: unauthorized(baseUrl, true) };
159
+ }
160
+ if (!(await checkGate(userId))) {
161
+ return { ok: false, response: forbidden() };
162
+ }
163
+ return { ok: true, userId };
164
+ },
165
+ };
166
+ }
167
+ //# sourceMappingURL=clerk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clerk.js","sourceRoot":"","sources":["../../src/auth/clerk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
2
+ import type { OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
3
+ import type { KVStorage } from "../types.js";
4
+ /**
5
+ * OAuthClientProvider implemented over KVStorage for a single downstream
6
+ * connector. Keys live in the connector's namespace as `oauth:<field>`.
7
+ *
8
+ * Headless twist: redirectToAuthorization() cannot navigate a user agent, so it
9
+ * STORES the authorization URL; the registry surfaces it as status
10
+ * "auth_required" and the operator opens it. The /oauth/callback/<id> route then
11
+ * drives transport.finishAuth(code).
12
+ */
13
+ export declare class KvOAuthProvider implements OAuthClientProvider {
14
+ private readonly connectorId;
15
+ private readonly storage;
16
+ private readonly redirectUri;
17
+ /**
18
+ * The force-reauth generation this provider's flow started under, captured by
19
+ * the connector before it drives a connect. `null` until stamped — a provider
20
+ * used outside a connect flow (or in isolation) writes unconditionally.
21
+ */
22
+ private capturedGeneration;
23
+ constructor(connectorId: string, storage: KVStorage, redirectUri: string);
24
+ /**
25
+ * Stamp the force-reauth generation the current connect flow started under.
26
+ * Subsequent saveTokens/saveClientInformation writes are dropped if KV's
27
+ * generation has since advanced (a concurrent force wiped credentials) — see
28
+ * isStale(). Called by the connector once per connect, before c.connect().
29
+ */
30
+ captureGeneration(gen: number): void;
31
+ /**
32
+ * Whether a concurrent force re-auth advanced the KV generation past the one
33
+ * this flow captured. When true, this isolate's SDK is mid-flight against
34
+ * credentials that were just wiped — persisting them would resurrect the
35
+ * wiped-and-reauthorized connector for later isolates. Fails open (writes)
36
+ * when no generation was captured.
37
+ */
38
+ private isStale;
39
+ get redirectUrl(): string;
40
+ get clientMetadata(): OAuthClientMetadata;
41
+ clientInformation(): Promise<OAuthClientInformationMixed | undefined>;
42
+ saveClientInformation(info: OAuthClientInformationMixed): Promise<void>;
43
+ tokens(): Promise<OAuthTokens | undefined>;
44
+ saveTokens(tokens: OAuthTokens): Promise<void>;
45
+ /**
46
+ * OAuth `state`. The SDK calls this (when present) and appends the value to
47
+ * the authorization URL. We generate a fresh random value and persist it so
48
+ * the public /oauth/callback route can prove the callback belongs to a flow
49
+ * WE started. Without it, anyone holding a pending authorization URL could
50
+ * complete consent with their own account (login CSRF) — PKCE does not help,
51
+ * since the verifier belongs to connecta, not the attacker. Cleared in
52
+ * clearPending() once the flow completes.
53
+ */
54
+ state(): Promise<string>;
55
+ /**
56
+ * Constant-time check of a callback's `state` against the stored one-shot
57
+ * value. Absent stored state or absent candidate → false (fail closed).
58
+ */
59
+ verifyState(candidate: string | null): Promise<boolean>;
60
+ saveCodeVerifier(verifier: string): Promise<void>;
61
+ codeVerifier(): Promise<string>;
62
+ redirectToAuthorization(authorizationUrl: URL): Promise<void>;
63
+ /** The stored authorization URL, if a flow is pending. */
64
+ pendingAuthorizationUrl(): Promise<string | undefined>;
65
+ /** Clear one-shot flow state after the callback completes. */
66
+ clearPending(): Promise<void>;
67
+ /**
68
+ * Force-reauth generation. Monotonic counter shared across isolates via KV;
69
+ * NOT cleared by clearPending/invalidateCredentials — its whole job is to
70
+ * keep advancing so an isolate holding a client from a prior generation can
71
+ * notice it went stale. Defaults to 0 when never bumped.
72
+ */
73
+ generation(): Promise<number>;
74
+ /** Advance the generation (on force re-auth) and return the new value. */
75
+ bumpGeneration(): Promise<number>;
76
+ invalidateCredentials(scope: "all" | "client" | "tokens" | "verifier" | "discovery"): Promise<void>;
77
+ }
78
+ //# sourceMappingURL=downstream-oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downstream-oauth.d.ts","sourceRoot":"","sources":["../../src/auth/downstream-oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,KAAK,EACV,2BAA2B,EAC3B,mBAAmB,EACnB,WAAW,EACZ,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAiB7C;;;;;;;;GAQG;AACH,qBAAa,eAAgB,YAAW,mBAAmB;IASvD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAV9B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB,CAAuB;gBAG9B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,MAAM;IAGtC;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIpC;;;;;;OAMG;YACW,OAAO;IAKrB,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,cAAc,IAAI,mBAAmB,CAQxC;IAEK,iBAAiB,IAAI,OAAO,CAAC,2BAA2B,GAAG,SAAS,CAAC;IAKrE,qBAAqB,CACzB,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,IAAI,CAAC;IASV,MAAM,IAAI,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAK1C,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD;;;;;;;;OAQG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAM9B;;;OAGG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAMvD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAM/B,uBAAuB,CAAC,gBAAgB,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAInE,0DAA0D;IACpD,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAI5D,8DAA8D;IACxD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAMnC;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAMnC,0EAA0E;IACpE,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAMjC,qBAAqB,CACzB,KAAK,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAC5D,OAAO,CAAC,IAAI,CAAC;CAWjB"}
@@ -0,0 +1,180 @@
1
+ /** A 256-bit random opaque value, hex-encoded — used for the OAuth `state`. */
2
+ function randomState() {
3
+ const bytes = new Uint8Array(32);
4
+ crypto.getRandomValues(bytes);
5
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
6
+ }
7
+ /** Length-safe, constant-time string compare (no early-exit on first mismatch). */
8
+ function timingSafeEqual(a, b) {
9
+ if (a.length !== b.length)
10
+ return false;
11
+ let diff = 0;
12
+ for (let i = 0; i < a.length; i++)
13
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
14
+ return diff === 0;
15
+ }
16
+ /**
17
+ * OAuthClientProvider implemented over KVStorage for a single downstream
18
+ * connector. Keys live in the connector's namespace as `oauth:<field>`.
19
+ *
20
+ * Headless twist: redirectToAuthorization() cannot navigate a user agent, so it
21
+ * STORES the authorization URL; the registry surfaces it as status
22
+ * "auth_required" and the operator opens it. The /oauth/callback/<id> route then
23
+ * drives transport.finishAuth(code).
24
+ */
25
+ export class KvOAuthProvider {
26
+ connectorId;
27
+ storage;
28
+ redirectUri;
29
+ /**
30
+ * The force-reauth generation this provider's flow started under, captured by
31
+ * the connector before it drives a connect. `null` until stamped — a provider
32
+ * used outside a connect flow (or in isolation) writes unconditionally.
33
+ */
34
+ capturedGeneration = null;
35
+ constructor(connectorId, storage, redirectUri) {
36
+ this.connectorId = connectorId;
37
+ this.storage = storage;
38
+ this.redirectUri = redirectUri;
39
+ }
40
+ /**
41
+ * Stamp the force-reauth generation the current connect flow started under.
42
+ * Subsequent saveTokens/saveClientInformation writes are dropped if KV's
43
+ * generation has since advanced (a concurrent force wiped credentials) — see
44
+ * isStale(). Called by the connector once per connect, before c.connect().
45
+ */
46
+ captureGeneration(gen) {
47
+ this.capturedGeneration = gen;
48
+ }
49
+ /**
50
+ * Whether a concurrent force re-auth advanced the KV generation past the one
51
+ * this flow captured. When true, this isolate's SDK is mid-flight against
52
+ * credentials that were just wiped — persisting them would resurrect the
53
+ * wiped-and-reauthorized connector for later isolates. Fails open (writes)
54
+ * when no generation was captured.
55
+ */
56
+ async isStale() {
57
+ if (this.capturedGeneration === null)
58
+ return false;
59
+ return (await this.generation()) > this.capturedGeneration;
60
+ }
61
+ get redirectUrl() {
62
+ return this.redirectUri;
63
+ }
64
+ get clientMetadata() {
65
+ return {
66
+ redirect_uris: [this.redirectUri],
67
+ client_name: "connecta",
68
+ grant_types: ["authorization_code", "refresh_token"],
69
+ response_types: ["code"],
70
+ token_endpoint_auth_method: "none",
71
+ };
72
+ }
73
+ async clientInformation() {
74
+ const raw = await this.storage.get("oauth:client");
75
+ return raw ? JSON.parse(raw) : undefined;
76
+ }
77
+ async saveClientInformation(info) {
78
+ // A concurrent force wiped credentials and bumped the generation while this
79
+ // flow ran — skip rather than re-register a client under revoked state. The
80
+ // in-memory SDK keeps this info for the current (doomed) connect, which the
81
+ // connector's post-connect fence then discards.
82
+ if (await this.isStale())
83
+ return;
84
+ await this.storage.set("oauth:client", JSON.stringify(info));
85
+ }
86
+ async tokens() {
87
+ const raw = await this.storage.get("oauth:tokens");
88
+ return raw ? JSON.parse(raw) : undefined;
89
+ }
90
+ async saveTokens(tokens) {
91
+ // Generation-guarded write. The dangerous case: an isolate mid-connect (or
92
+ // mid-refresh) whose SDK just minted fresh tokens against a still-valid
93
+ // grant, racing a force re-auth that wiped KV and bumped the generation.
94
+ // Persisting here would resurrect those tokens for later isolates to read.
95
+ // A silent skip (not a throw) is deliberate: throwing propagates out of the
96
+ // SDK's auth() and fails the in-flight request/connect noisily, whereas
97
+ // skipping lets the in-memory client finish its current operation while
98
+ // leaving KV wiped — the connector's generation check drops that client on
99
+ // its next call. Fails open when no generation was captured, so ordinary
100
+ // token refresh (no force) still persists.
101
+ if (await this.isStale())
102
+ return;
103
+ await this.storage.set("oauth:tokens", JSON.stringify(tokens));
104
+ }
105
+ /**
106
+ * OAuth `state`. The SDK calls this (when present) and appends the value to
107
+ * the authorization URL. We generate a fresh random value and persist it so
108
+ * the public /oauth/callback route can prove the callback belongs to a flow
109
+ * WE started. Without it, anyone holding a pending authorization URL could
110
+ * complete consent with their own account (login CSRF) — PKCE does not help,
111
+ * since the verifier belongs to connecta, not the attacker. Cleared in
112
+ * clearPending() once the flow completes.
113
+ */
114
+ async state() {
115
+ const value = randomState();
116
+ await this.storage.set("oauth:state", value);
117
+ return value;
118
+ }
119
+ /**
120
+ * Constant-time check of a callback's `state` against the stored one-shot
121
+ * value. Absent stored state or absent candidate → false (fail closed).
122
+ */
123
+ async verifyState(candidate) {
124
+ const expected = await this.storage.get("oauth:state");
125
+ if (!expected || candidate === null)
126
+ return false;
127
+ return timingSafeEqual(candidate, expected);
128
+ }
129
+ async saveCodeVerifier(verifier) {
130
+ await this.storage.set("oauth:verifier", verifier);
131
+ }
132
+ async codeVerifier() {
133
+ const raw = await this.storage.get("oauth:verifier");
134
+ if (!raw)
135
+ throw new Error(`No PKCE code verifier for "${this.connectorId}"`);
136
+ return raw;
137
+ }
138
+ async redirectToAuthorization(authorizationUrl) {
139
+ await this.storage.set("oauth:pending", authorizationUrl.toString());
140
+ }
141
+ /** The stored authorization URL, if a flow is pending. */
142
+ async pendingAuthorizationUrl() {
143
+ return (await this.storage.get("oauth:pending")) ?? undefined;
144
+ }
145
+ /** Clear one-shot flow state after the callback completes. */
146
+ async clearPending() {
147
+ await this.storage.delete("oauth:pending");
148
+ await this.storage.delete("oauth:verifier");
149
+ await this.storage.delete("oauth:state");
150
+ }
151
+ /**
152
+ * Force-reauth generation. Monotonic counter shared across isolates via KV;
153
+ * NOT cleared by clearPending/invalidateCredentials — its whole job is to
154
+ * keep advancing so an isolate holding a client from a prior generation can
155
+ * notice it went stale. Defaults to 0 when never bumped.
156
+ */
157
+ async generation() {
158
+ const raw = await this.storage.get("oauth:generation");
159
+ const n = raw ? Number(raw) : 0;
160
+ return Number.isFinite(n) ? n : 0;
161
+ }
162
+ /** Advance the generation (on force re-auth) and return the new value. */
163
+ async bumpGeneration() {
164
+ const next = (await this.generation()) + 1;
165
+ await this.storage.set("oauth:generation", String(next));
166
+ return next;
167
+ }
168
+ async invalidateCredentials(scope) {
169
+ if (scope === "all" || scope === "client") {
170
+ await this.storage.delete("oauth:client");
171
+ }
172
+ if (scope === "all" || scope === "tokens") {
173
+ await this.storage.delete("oauth:tokens");
174
+ }
175
+ if (scope === "all" || scope === "verifier") {
176
+ await this.storage.delete("oauth:verifier");
177
+ }
178
+ }
179
+ }
180
+ //# sourceMappingURL=downstream-oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downstream-oauth.js","sourceRoot":"","sources":["../../src/auth/downstream-oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { JsonSchema, ToolDef } from "./types.js";
2
+ export declare function summarizeDescription(text: string | undefined, full: boolean): string | undefined;
3
+ /** Rank a connector's tools while caching its normalized plain-data index. */
4
+ export declare function rankTools(tools: ToolDef[], query: string): Array<{
5
+ tool: ToolDef;
6
+ score: number;
7
+ order: number;
8
+ }>;
9
+ /** Render and cache a compact TypeScript-like representation of JSON Schema. */
10
+ export declare function compactSchema(schema: JsonSchema): string;
11
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAItD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,OAAO,GACZ,MAAM,GAAG,SAAS,CAMpB;AAiDD,8EAA8E;AAC9E,wBAAgB,SAAS,CACvB,KAAK,EAAE,OAAO,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CASxD;AA0ED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAexD"}
@@ -0,0 +1,144 @@
1
+ const DEFAULT_DESCRIPTION_LENGTH = 240;
2
+ export function summarizeDescription(text, full) {
3
+ if (!text)
4
+ return undefined;
5
+ if (full)
6
+ return text;
7
+ const compact = text.replace(/\s+/g, " ").trim();
8
+ if (compact.length <= DEFAULT_DESCRIPTION_LENGTH)
9
+ return compact;
10
+ return `${compact.slice(0, DEFAULT_DESCRIPTION_LENGTH - 1).trimEnd()}…`;
11
+ }
12
+ function normalized(text) {
13
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
14
+ }
15
+ const searchDocuments = new WeakMap();
16
+ function documentsFor(tools) {
17
+ let docs = searchDocuments.get(tools);
18
+ if (!docs) {
19
+ docs = tools.map((tool) => ({
20
+ tool,
21
+ name: normalized(tool.name),
22
+ description: normalized(tool.description ?? ""),
23
+ }));
24
+ searchDocuments.set(tools, docs);
25
+ }
26
+ return docs;
27
+ }
28
+ function scoreDocument(doc, phrase, terms) {
29
+ if (!phrase)
30
+ return 0;
31
+ const haystack = `${doc.name} ${doc.description}`;
32
+ if (!terms.every((term) => haystack.includes(term)))
33
+ return null;
34
+ let score = 0;
35
+ if (doc.name === phrase)
36
+ score += 1_000;
37
+ else if (doc.name.startsWith(phrase))
38
+ score += 800;
39
+ else if (doc.name.includes(phrase))
40
+ score += 600;
41
+ for (const term of terms) {
42
+ if (doc.name === term)
43
+ score += 200;
44
+ else if (doc.name.startsWith(term))
45
+ score += 120;
46
+ else if (doc.name.includes(term))
47
+ score += 80;
48
+ if (doc.description.includes(term))
49
+ score += 10;
50
+ }
51
+ return score;
52
+ }
53
+ /** Rank a connector's tools while caching its normalized plain-data index. */
54
+ export function rankTools(tools, query) {
55
+ const phrase = normalized(query);
56
+ const terms = phrase.split(/\s+/).filter(Boolean);
57
+ const ranked = [];
58
+ documentsFor(tools).forEach((doc, order) => {
59
+ const score = scoreDocument(doc, phrase, terms);
60
+ if (score !== null)
61
+ ranked.push({ tool: doc.tool, score, order });
62
+ });
63
+ return ranked;
64
+ }
65
+ function refName(ref) {
66
+ return ref.split("/").pop() ?? ref;
67
+ }
68
+ function renderSchema(schema, defs, seen, depth) {
69
+ if (depth > 4)
70
+ return "…";
71
+ if (schema === null || typeof schema !== "object") {
72
+ return JSON.stringify(schema);
73
+ }
74
+ const s = schema;
75
+ if (typeof s.$ref === "string") {
76
+ const name = refName(s.$ref);
77
+ if (seen.has(name))
78
+ return name;
79
+ const target = defs[name];
80
+ if (target === undefined)
81
+ return name;
82
+ seen.add(name);
83
+ const rendered = renderSchema(target, defs, seen, depth);
84
+ seen.delete(name);
85
+ return rendered;
86
+ }
87
+ const union = (s.oneOf ?? s.anyOf);
88
+ if (Array.isArray(union)) {
89
+ return (union.map((u) => renderSchema(u, defs, seen, depth + 1)).join(" | ") ||
90
+ "unknown");
91
+ }
92
+ if (Array.isArray(s.enum)) {
93
+ return s.enum.map((value) => JSON.stringify(value)).join(" | ");
94
+ }
95
+ const type = s.type;
96
+ if (type === "array" || s.items) {
97
+ const items = s.items
98
+ ? renderSchema(s.items, defs, seen, depth + 1)
99
+ : "unknown";
100
+ return `${items}[]`;
101
+ }
102
+ if (type === "object" || s.properties) {
103
+ const props = (s.properties ?? {});
104
+ const required = new Set((Array.isArray(s.required) ? s.required : []));
105
+ const keys = Object.keys(props);
106
+ if (keys.length === 0)
107
+ return "{}";
108
+ return `{ ${keys
109
+ .map((key) => {
110
+ const optional = required.has(key) ? "" : "?";
111
+ const rendered = renderSchema(props[key], defs, seen, depth + 1);
112
+ const description = props[key]?.description;
113
+ const comment = typeof description === "string" ? ` // ${description}` : "";
114
+ return `${key}${optional}: ${rendered}${comment}`;
115
+ })
116
+ .join(", ")} }`;
117
+ }
118
+ if (typeof type === "string")
119
+ return type;
120
+ if (Array.isArray(type))
121
+ return type.join(" | ");
122
+ return JSON.stringify(schema);
123
+ }
124
+ const compactSchemas = new WeakMap();
125
+ /** Render and cache a compact TypeScript-like representation of JSON Schema. */
126
+ export function compactSchema(schema) {
127
+ const cached = compactSchemas.get(schema);
128
+ if (cached)
129
+ return cached;
130
+ const defs = {
131
+ ...(schema.$defs ?? {}),
132
+ ...(schema.definitions ?? {}),
133
+ };
134
+ let rendered;
135
+ try {
136
+ rendered = renderSchema(schema, defs, new Set(), 0);
137
+ }
138
+ catch {
139
+ rendered = JSON.stringify(schema);
140
+ }
141
+ compactSchemas.set(schema, rendered);
142
+ return rendered;
143
+ }
144
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}