@codemem/mcp 0.31.4 → 0.32.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/dist/audit.d.ts +54 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/content.d.ts +13 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/http.d.ts +38 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +1049 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +2 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -602
- package/dist/memory-access.d.ts.map +1 -1
- package/dist/memory-kinds.d.ts +2 -0
- package/dist/memory-kinds.d.ts.map +1 -0
- package/dist/oauth.d.ts +176 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oidc.d.ts +35 -0
- package/dist/oidc.d.ts.map +1 -0
- package/dist/project-scope.d.ts +4 -0
- package/dist/project-scope.d.ts.map +1 -1
- package/dist/provider.d.ts +51 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/schemas.d.ts +36 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/server-Djvf0Yik.js +488 -0
- package/dist/server-Djvf0Yik.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/stdio.d.ts +9 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +69 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tool-context.d.ts +12 -0
- package/dist/tool-context.d.ts.map +1 -0
- package/dist/tools/items.d.ts +4 -0
- package/dist/tools/items.d.ts.map +1 -0
- package/dist/tools/learn.d.ts +3 -0
- package/dist/tools/learn.d.ts.map +1 -0
- package/dist/tools/schema.d.ts +3 -0
- package/dist/tools/schema.d.ts.map +1 -0
- package/dist/tools/search.d.ts +4 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/timeline.d.ts +4 -0
- package/dist/tools/timeline.d.ts.map +1 -0
- package/package.json +19 -7
- package/dist/index.js.map +0 -1
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","names":["#clients","#codes","#deleteExpiredCodes","#tokensByHash","#tokenHashKey","#deleteInactiveTokens","#grantsById","#grantIdsByRefreshHash","#refreshHashesByGrantId","#deleteInactiveGrants","#deleteGrantRefreshHashIndexEntries","#records","#deleteExpired","#clientsStore","#codeStore","#tokenStore","#refreshTokenStore","#publicMcpUrl","#resourceServerUrl","#oidc","#now"],"sources":["../src/audit.ts","../src/oauth.ts","../src/oidc.ts","../src/provider.ts","../src/http.ts"],"sourcesContent":["/**\n * @codemem/mcp — privacy-safe OAuth audit events.\n *\n * Phase 1 single-user remote MCP needs operator-visible security events\n * (registration, authorize, OIDC callback, token issuance, revocation, bearer\n * verification) without ever logging bearer tokens, authorization codes,\n * client secrets, OIDC ID tokens, or private memory contents.\n */\n\nexport type OAuthAuditKind =\n\t| \"registration\"\n\t| \"authorize\"\n\t| \"oidc_callback\"\n\t| \"token\"\n\t| \"refresh\"\n\t| \"rotation\"\n\t| \"revocation\"\n\t| \"bearer\";\n\nexport type OAuthAuditOutcome = \"success\" | \"denied\" | \"error\";\n\nexport type BearerDenyReason =\n\t| \"missing_authorization_header\"\n\t| \"malformed_authorization_header\"\n\t| \"unknown_token\"\n\t| \"expired_token\"\n\t| \"revoked_token\";\n\nexport interface OAuthAuditEventBase {\n\ttimestamp: string;\n\toutcome: OAuthAuditOutcome;\n\treason?: string;\n\tclientId?: string;\n\tremoteAddress?: string;\n}\n\nexport type OAuthAuditEvent = OAuthAuditEventBase & { kind: OAuthAuditKind };\n\nexport type OAuthAuditEmitter = (event: OAuthAuditEvent) => void;\n\n// Canonicalized denylist — keys are compared after lowercasing and stripping\n// non-alphanumeric separators, so `access_token`, `accessToken`, `ACCESS-TOKEN`\n// and `access token` all reduce to `accesstoken` and are blocked uniformly.\n// This codebase commonly uses camelCase identifiers, so a snake_case-only\n// denylist would miss real leak vectors.\nconst FORBIDDEN_KEY_TOKENS = new Set([\n\t\"token\",\n\t\"accesstoken\",\n\t\"refreshtoken\",\n\t\"idtoken\",\n\t\"code\",\n\t\"codeverifier\",\n\t\"codechallenge\",\n\t\"clientsecret\",\n\t\"authorization\",\n\t\"password\",\n\t\"secret\",\n]);\n\nfunction canonicalizeAuditKey(key: string): string {\n\treturn key.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n}\n\n/**\n * Build an audit event with redaction safety. Throws if a forbidden field\n * leaks in; callers should never need to pass token/code material here.\n */\nexport function buildOAuthAuditEvent(\n\tkind: OAuthAuditKind,\n\tfields: Omit<OAuthAuditEventBase, \"timestamp\"> & { now?: number },\n): OAuthAuditEvent {\n\tfor (const key of Object.keys(fields)) {\n\t\tif (FORBIDDEN_KEY_TOKENS.has(canonicalizeAuditKey(key))) {\n\t\t\tthrow new Error(`OAuth audit event must not carry forbidden field: ${key}`);\n\t\t}\n\t}\n\tconst { now, ...rest } = fields;\n\treturn {\n\t\tkind,\n\t\ttimestamp: new Date(now ?? Date.now()).toISOString(),\n\t\t...rest,\n\t};\n}\n\n/**\n * Wrap an audit emitter so any synchronous throw or stream-write failure stays\n * contained. Audit logging is best-effort observability and must never break\n * the OAuth/MCP request flow it observes. Failures are reported once per\n * process to `console.error` so they remain visible without flooding output.\n */\nexport function wrapAuditEmitterBestEffort(emit: OAuthAuditEmitter): OAuthAuditEmitter {\n\tlet reportedFailure = false;\n\treturn (event) => {\n\t\ttry {\n\t\t\temit(event);\n\t\t} catch (err) {\n\t\t\tif (!reportedFailure) {\n\t\t\t\treportedFailure = true;\n\t\t\t\tconsole.error(\"codemem MCP OAuth audit emitter failed (further failures suppressed):\", err);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Default audit emitter writes one JSON object per line to stderr. This keeps\n * MCP HTTP stdout reserved for protocol traffic and is greppable by operators\n * via `journalctl` or a redirected log file.\n */\nexport function createDefaultOAuthAuditEmitter(\n\tstream: NodeJS.WriteStream = process.stderr,\n): OAuthAuditEmitter {\n\treturn (event) => {\n\t\tstream.write(`${JSON.stringify({ source: \"codemem-mcp-oauth-audit\", ...event })}\\n`);\n\t};\n}\n\n/**\n * No-op emitter, used when CODEMEM_MCP_AUDIT=0 or when callers explicitly\n * want to suppress audit output (for example, embedded tests).\n */\nexport function createSilentOAuthAuditEmitter(): OAuthAuditEmitter {\n\treturn () => {};\n}\n\n/**\n * Resolve an emitter from environment configuration. `CODEMEM_MCP_AUDIT=0`,\n * `false`, or `no` disables audit logging; anything else (including unset)\n * uses the default stderr emitter.\n */\nexport function resolveOAuthAuditEmitterFromEnv(\n\tvalue = process.env.CODEMEM_MCP_AUDIT,\n): OAuthAuditEmitter {\n\tconst normalized = value?.trim().toLowerCase();\n\tif (normalized === \"0\" || normalized === \"false\" || normalized === \"no\") {\n\t\treturn createSilentOAuthAuditEmitter();\n\t}\n\treturn createDefaultOAuthAuditEmitter();\n}\n","import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from \"node:crypto\";\nimport type { OAuthRegisteredClientsStore } from \"@modelcontextprotocol/sdk/server/auth/clients.js\";\nimport { InvalidClientMetadataError } from \"@modelcontextprotocol/sdk/server/auth/errors.js\";\nimport type { OAuthServerProvider } from \"@modelcontextprotocol/sdk/server/auth/provider.js\";\nimport { createOAuthMetadata } from \"@modelcontextprotocol/sdk/server/auth/router.js\";\nimport {\n\ttype OAuthClientInformationFull,\n\ttype OAuthClientMetadata,\n\tOAuthClientMetadataSchema,\n\ttype OAuthErrorResponse,\n\ttype OAuthMetadata,\n\ttype OAuthProtectedResourceMetadata,\n\tOAuthProtectedResourceMetadataSchema,\n\ttype OAuthTokens,\n\tOAuthTokensSchema,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\nexport const MCP_OAUTH_PUBLIC_URL_ENV = \"CODEMEM_MCP_HTTP_PUBLIC_URL\";\nexport const MCP_OAUTH_RESOURCE_NAME = \"codemem MCP\";\nexport const MCP_OAUTH_SCOPES_SUPPORTED = [\"memory:read\", \"memory:write\"];\nexport const MCP_OAUTH_SERVICE_DOCUMENTATION_URL = \"https://github.com/kunickiaj/codemem#readme\";\n\nconst CLAUDE_HOSTED_CALLBACK = \"https://claude.ai/api/mcp/auth_callback\";\nconst LOCAL_CALLBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst SUPPORTED_GRANT_TYPES = new Set([\"authorization_code\", \"refresh_token\"]);\nconst SUPPORTED_RESPONSE_TYPES = new Set([\"code\"]);\nconst AUTHORIZATION_CODE_TTL_MS = 5 * 60 * 1000;\nconst ACCESS_TOKEN_TTL_SECONDS = 60 * 60;\nconst REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;\nconst MAX_AUTHORIZATION_CODES = 100;\nconst MAX_ACCESS_TOKENS = 100;\nconst MAX_REFRESH_GRANTS = 100;\nconst ACCESS_TOKEN_BYTES = 32;\nconst ACCESS_TOKEN_BASE64URL_LENGTH = 43;\nconst ACCESS_TOKEN_BASE64URL = /^[A-Za-z0-9_-]{43}$/;\nconst PKCE_S256_CHALLENGE = /^[A-Za-z0-9_-]{43}$/;\nconst PKCE_VERIFIER = /^[A-Za-z0-9._~-]{43,128}$/;\n\nexport interface McpOAuthMetadataOptions {\n\tmcpUrl: string;\n\tclientsStore: OAuthRegisteredClientsStore;\n}\n\nexport interface RegisteredMcpOAuthClient {\n\tstatus: 201;\n\tbody: OAuthClientInformationFull;\n}\n\nexport interface McpOAuthRegistrationError {\n\tstatus: 400;\n\tbody: { error: \"invalid_client_metadata\"; error_description: string };\n}\n\nexport interface AuthorizationCodeRecord {\n\tclientId: string;\n\tredirectUri: string;\n\tcodeChallenge: string;\n\tscopes: string[];\n\tresource?: string;\n\texpiresAt: number;\n\tused: boolean;\n}\n\nexport interface OAuthAuthorizationCodeStore {\n\tissueCode(record: Omit<AuthorizationCodeRecord, \"used\">, now?: number): string | undefined;\n\tpeekCode(code: string): AuthorizationCodeRecord | undefined;\n\tconsumeCode(code: string): AuthorizationCodeRecord | undefined;\n}\n\nexport interface AccessTokenRecord {\n\tclientId: string;\n\tgrantId?: string;\n\tscopes: string[];\n\tresource?: string;\n\ttokenHash: string;\n\tissuedAt: number;\n\texpiresAt: number;\n\tlastUsedAt: number | null;\n\trevokedAt: number | null;\n}\n\nexport type AccessTokenVerificationResult =\n\t| { ok: true; record: AccessTokenRecord }\n\t| { ok: false; reason: \"unknown_token\" | \"expired_token\" | \"revoked_token\" };\n\nexport interface OAuthAccessTokenStore {\n\tissueToken(\n\t\tclientId: string,\n\t\tnow?: number,\n\t\tresource?: string,\n\t\tgrantId?: string,\n\t\tscopes?: string[],\n\t): { token: string; expiresIn: number } | undefined;\n\tverifyToken(token: string, now?: number): AccessTokenVerificationResult;\n\trevokeToken(token: string, now?: number): boolean;\n\trevokeTokensForGrant?(grantId: string, now?: number): number;\n}\n\nexport interface RefreshTokenGrantRecord {\n\tgrantId: string;\n\tclientId: string;\n\tscopes: string[];\n\tresource?: string;\n\tcurrentRefreshTokenHash: string;\n\tpreviousRefreshTokenHash: string | null;\n\tissuedAt: number;\n\texpiresAt: number;\n\trotatedAt: number | null;\n\trevokedAt: number | null;\n}\n\nexport type RefreshTokenRotationResult =\n\t| { ok: true; grant: RefreshTokenGrantRecord; refreshToken: string; expiresIn: number }\n\t| {\n\t\t\tok: false;\n\t\t\treason:\n\t\t\t\t| \"unknown_refresh_token\"\n\t\t\t\t| \"expired_refresh_token\"\n\t\t\t\t| \"revoked_refresh_token\"\n\t\t\t\t| \"client_mismatch\"\n\t\t\t\t| \"scope_mismatch\"\n\t\t\t\t| \"resource_mismatch\"\n\t\t\t\t| \"refresh_token_replay\";\n\t\t\tgrantId?: string;\n\t };\n\nexport interface OAuthRefreshTokenStore {\n\tissueGrant(\n\t\trecord: { clientId: string; scopes?: string[]; resource?: string },\n\t\tnow?: number,\n\t): { grant: RefreshTokenGrantRecord; refreshToken: string; expiresIn: number } | undefined;\n\trotateRefreshToken(\n\t\tclientId: string,\n\t\trefreshToken: string,\n\t\toptions?: { scopes?: string[]; resource?: string },\n\t\tnow?: number,\n\t): RefreshTokenRotationResult;\n\trevokeRefreshToken(token: string, now?: number): string | undefined;\n\trevokeGrant(grantId: string, now?: number): boolean;\n}\n\nexport interface McpOAuthRedirectResult {\n\tstatus: 302;\n\tlocation: string;\n}\n\nexport interface McpOAuthErrorResult {\n\tstatus: 400;\n\tbody: OAuthErrorResponse;\n}\n\nexport interface McpOAuthTokenResult {\n\tstatus: 200;\n\tbody: OAuthTokens;\n}\n\nexport interface PreparedMcpOAuthAuthorizationRequest {\n\tclientId: string;\n\tredirectUri: string;\n\tcodeChallenge: string;\n\tscopes: string[];\n\tresource: string | null;\n\tstate: string | null;\n}\n\nexport class InMemoryOAuthClientsStore implements OAuthRegisteredClientsStore {\n\treadonly #clients = new Map<string, OAuthClientInformationFull>();\n\n\tgetClient(clientId: string): OAuthClientInformationFull | undefined {\n\t\treturn this.#clients.get(clientId);\n\t}\n\n\tregisterClient(\n\t\tclient: Omit<OAuthClientInformationFull, \"client_id\" | \"client_id_issued_at\">,\n\t): OAuthClientInformationFull {\n\t\tconst redirectError = validateRedirectUris(client.redirect_uris);\n\t\tif (redirectError) throw new InvalidClientMetadataError(redirectError);\n\t\tif (client.client_secret || client.token_endpoint_auth_method !== \"none\") {\n\t\t\tthrow new InvalidClientMetadataError(\n\t\t\t\t\"Only public clients with token_endpoint_auth_method=none are supported\",\n\t\t\t);\n\t\t}\n\t\tif (client.grant_types && !isSupportedList(client.grant_types, SUPPORTED_GRANT_TYPES)) {\n\t\t\tthrow new InvalidClientMetadataError(\n\t\t\t\t\"Only authorization_code and refresh_token grant_types are supported\",\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tclient.response_types &&\n\t\t\t!isSupportedList(client.response_types, SUPPORTED_RESPONSE_TYPES)\n\t\t) {\n\t\t\tthrow new InvalidClientMetadataError(\"Only code response_type is supported\");\n\t\t}\n\t\tif (this.#clients.size >= 100) {\n\t\t\tconst oldestClientId = this.#clients.keys().next().value;\n\t\t\tif (oldestClientId) this.#clients.delete(oldestClientId);\n\t\t}\n\t\tconst registered = {\n\t\t\t...client,\n\t\t\ttoken_endpoint_auth_method: \"none\" as const,\n\t\t\tgrant_types: client.grant_types ?? [\"authorization_code\", \"refresh_token\"],\n\t\t\tresponse_types: client.response_types ?? [\"code\"],\n\t\t\tclient_id: randomUUID(),\n\t\t\tclient_id_issued_at: Math.floor(Date.now() / 1000),\n\t\t};\n\t\tthis.#clients.set(registered.client_id, registered);\n\t\treturn registered;\n\t}\n}\n\nexport class InMemoryOAuthAuthorizationCodeStore implements OAuthAuthorizationCodeStore {\n\treadonly #codes = new Map<string, AuthorizationCodeRecord>();\n\n\tissueCode(record: Omit<AuthorizationCodeRecord, \"used\">, now = Date.now()): string | undefined {\n\t\tthis.#deleteExpiredCodes(now);\n\t\tif (this.#codes.size >= MAX_AUTHORIZATION_CODES) return undefined;\n\t\tconst code = randomUUID();\n\t\tthis.#codes.set(code, { ...record, used: false });\n\t\treturn code;\n\t}\n\n\tpeekCode(code: string): AuthorizationCodeRecord | undefined {\n\t\tconst record = this.#codes.get(code);\n\t\tif (!record || record.used) return undefined;\n\t\treturn record;\n\t}\n\n\tconsumeCode(code: string): AuthorizationCodeRecord | undefined {\n\t\tconst record = this.#codes.get(code);\n\t\tif (!record || record.used) return undefined;\n\t\trecord.used = true;\n\t\treturn record;\n\t}\n\n\t#deleteExpiredCodes(now: number): void {\n\t\tfor (const [code, record] of this.#codes) {\n\t\t\tif (record.expiresAt <= now) this.#codes.delete(code);\n\t\t}\n\t}\n}\n\nexport class InMemoryOAuthAccessTokenStore implements OAuthAccessTokenStore {\n\treadonly #tokensByHash = new Map<string, AccessTokenRecord>();\n\treadonly #tokenHashKey = randomBytes(32);\n\n\tissueToken(\n\t\tclientId: string,\n\t\tnow = Date.now(),\n\t\tresource?: string,\n\t\tgrantId?: string,\n\t\tscopes: string[] = [],\n\t): { token: string; expiresIn: number } | undefined {\n\t\tthis.#deleteInactiveTokens(now);\n\t\tif (this.#tokensByHash.size >= MAX_ACCESS_TOKENS) return undefined;\n\t\tconst tokenBytes = randomBytes(ACCESS_TOKEN_BYTES);\n\t\tconst tokenHash = signOAuthAccessTokenBytes(tokenBytes, this.#tokenHashKey);\n\t\tthis.#tokensByHash.set(tokenHash, {\n\t\t\tclientId,\n\t\t\tgrantId,\n\t\t\tscopes: [...scopes],\n\t\t\tresource,\n\t\t\ttokenHash,\n\t\t\tissuedAt: now,\n\t\t\texpiresAt: now + ACCESS_TOKEN_TTL_SECONDS * 1000,\n\t\t\tlastUsedAt: null,\n\t\t\trevokedAt: null,\n\t\t});\n\t\treturn {\n\t\t\ttoken: tokenBytes.toString(\"base64url\"),\n\t\t\texpiresIn: ACCESS_TOKEN_TTL_SECONDS,\n\t\t};\n\t}\n\n\tverifyToken(token: string, now = Date.now()): AccessTokenVerificationResult {\n\t\tconst tokenBytes = decodeOAuthAccessToken(token);\n\t\tif (!tokenBytes) return { ok: false, reason: \"unknown_token\" };\n\t\tconst tokenHash = signOAuthAccessTokenBytes(tokenBytes, this.#tokenHashKey);\n\t\tconst record = this.#tokensByHash.get(tokenHash);\n\t\tif (!record || !isSameTokenHash(record.tokenHash, tokenHash)) {\n\t\t\treturn { ok: false, reason: \"unknown_token\" };\n\t\t}\n\t\tif (record.revokedAt !== null) return { ok: false, reason: \"revoked_token\" };\n\t\tif (record.expiresAt <= now) return { ok: false, reason: \"expired_token\" };\n\t\trecord.lastUsedAt = now;\n\t\treturn { ok: true, record: { ...record } };\n\t}\n\n\trevokeToken(token: string, now = Date.now()): boolean {\n\t\tconst tokenBytes = decodeOAuthAccessToken(token);\n\t\tif (!tokenBytes) return false;\n\t\tconst tokenHash = signOAuthAccessTokenBytes(tokenBytes, this.#tokenHashKey);\n\t\tconst record = this.#tokensByHash.get(tokenHash);\n\t\tif (!record || !isSameTokenHash(record.tokenHash, tokenHash)) return false;\n\t\tif (record.revokedAt !== null) return true;\n\t\trecord.revokedAt = now;\n\t\treturn true;\n\t}\n\n\trevokeTokensForGrant(grantId: string, now = Date.now()): number {\n\t\tlet revoked = 0;\n\t\tfor (const record of this.#tokensByHash.values()) {\n\t\t\tif (record.grantId !== grantId || record.revokedAt !== null) continue;\n\t\t\trecord.revokedAt = now;\n\t\t\trevoked += 1;\n\t\t}\n\t\treturn revoked;\n\t}\n\n\t#deleteInactiveTokens(now: number): void {\n\t\tfor (const [tokenHash, record] of this.#tokensByHash) {\n\t\t\tif (record.expiresAt <= now || record.revokedAt !== null)\n\t\t\t\tthis.#tokensByHash.delete(tokenHash);\n\t\t}\n\t}\n}\n\nexport class InMemoryOAuthRefreshTokenStore implements OAuthRefreshTokenStore {\n\treadonly #grantsById = new Map<string, RefreshTokenGrantRecord>();\n\treadonly #grantIdsByRefreshHash = new Map<string, string>();\n\treadonly #refreshHashesByGrantId = new Map<string, Set<string>>();\n\treadonly #tokenHashKey = randomBytes(32);\n\n\tissueGrant(\n\t\trecord: { clientId: string; scopes?: string[]; resource?: string },\n\t\tnow = Date.now(),\n\t): { grant: RefreshTokenGrantRecord; refreshToken: string; expiresIn: number } | undefined {\n\t\tthis.#deleteInactiveGrants(now);\n\t\tif (this.#grantsById.size >= MAX_REFRESH_GRANTS) return undefined;\n\t\tconst refreshToken = randomToken();\n\t\tconst refreshTokenHash = hashSerializedOAuthToken(refreshToken, this.#tokenHashKey);\n\t\tif (!refreshTokenHash) return undefined;\n\t\tconst grant: RefreshTokenGrantRecord = {\n\t\t\tgrantId: randomUUID(),\n\t\t\tclientId: record.clientId,\n\t\t\tscopes: [...(record.scopes ?? [])],\n\t\t\tresource: record.resource,\n\t\t\tcurrentRefreshTokenHash: refreshTokenHash,\n\t\t\tpreviousRefreshTokenHash: null,\n\t\t\tissuedAt: now,\n\t\t\texpiresAt: now + REFRESH_TOKEN_TTL_SECONDS * 1000,\n\t\t\trotatedAt: null,\n\t\t\trevokedAt: null,\n\t\t};\n\t\tthis.#grantsById.set(grant.grantId, grant);\n\t\tthis.#grantIdsByRefreshHash.set(refreshTokenHash, grant.grantId);\n\t\tthis.#refreshHashesByGrantId.set(grant.grantId, new Set([refreshTokenHash]));\n\t\treturn {\n\t\t\tgrant: { ...grant, scopes: [...grant.scopes] },\n\t\t\trefreshToken,\n\t\t\texpiresIn: REFRESH_TOKEN_TTL_SECONDS,\n\t\t};\n\t}\n\n\trotateRefreshToken(\n\t\tclientId: string,\n\t\trefreshToken: string,\n\t\toptions: { scopes?: string[]; resource?: string } = {},\n\t\tnow = Date.now(),\n\t): RefreshTokenRotationResult {\n\t\tconst tokenHash = hashSerializedOAuthToken(refreshToken, this.#tokenHashKey);\n\t\tif (!tokenHash) return { ok: false, reason: \"unknown_refresh_token\" };\n\t\tconst grantId = this.#grantIdsByRefreshHash.get(tokenHash);\n\t\tif (!grantId) return { ok: false, reason: \"unknown_refresh_token\" };\n\t\tconst grant = this.#grantsById.get(grantId);\n\t\tif (!grant) return { ok: false, reason: \"unknown_refresh_token\" };\n\t\tif (grant.revokedAt !== null) return { ok: false, reason: \"revoked_refresh_token\", grantId };\n\t\tif (grant.expiresAt <= now) {\n\t\t\tthis.revokeGrant(grantId, now);\n\t\t\treturn { ok: false, reason: \"expired_refresh_token\", grantId };\n\t\t}\n\t\tif (grant.clientId !== clientId) return { ok: false, reason: \"client_mismatch\", grantId };\n\t\tif (!isScopeSubset(options.scopes ?? grant.scopes, grant.scopes)) {\n\t\t\treturn { ok: false, reason: \"scope_mismatch\", grantId };\n\t\t}\n\t\tif ((grant.resource ?? null) !== (options.resource ?? grant.resource ?? null)) {\n\t\t\treturn { ok: false, reason: \"resource_mismatch\", grantId };\n\t\t}\n\n\t\tconst matchesCurrent = isSameTokenHash(grant.currentRefreshTokenHash, tokenHash);\n\t\tconst matchesPrevious =\n\t\t\tgrant.previousRefreshTokenHash !== null &&\n\t\t\tisSameTokenHash(grant.previousRefreshTokenHash, tokenHash);\n\t\tif (!matchesCurrent && !matchesPrevious) {\n\t\t\tthis.revokeGrant(grantId, now);\n\t\t\treturn { ok: false, reason: \"refresh_token_replay\", grantId };\n\t\t}\n\n\t\tconst nextRefreshToken = randomToken();\n\t\tconst nextRefreshTokenHash = hashSerializedOAuthToken(nextRefreshToken, this.#tokenHashKey);\n\t\tif (!nextRefreshTokenHash) return { ok: false, reason: \"unknown_refresh_token\" };\n\t\tgrant.scopes = [...(options.scopes ?? grant.scopes)];\n\t\tgrant.previousRefreshTokenHash = matchesCurrent ? grant.currentRefreshTokenHash : null;\n\t\tgrant.currentRefreshTokenHash = nextRefreshTokenHash;\n\t\tgrant.rotatedAt = now;\n\t\tthis.#grantIdsByRefreshHash.set(nextRefreshTokenHash, grantId);\n\t\tthis.#refreshHashesByGrantId.get(grantId)?.add(nextRefreshTokenHash);\n\t\treturn {\n\t\t\tok: true,\n\t\t\tgrant: { ...grant, scopes: [...grant.scopes] },\n\t\t\trefreshToken: nextRefreshToken,\n\t\t\texpiresIn: Math.max(0, Math.floor((grant.expiresAt - now) / 1000)),\n\t\t};\n\t}\n\n\trevokeRefreshToken(token: string, now = Date.now()): string | undefined {\n\t\tconst tokenHash = hashSerializedOAuthToken(token, this.#tokenHashKey);\n\t\tif (!tokenHash) return undefined;\n\t\tconst grantId = this.#grantIdsByRefreshHash.get(tokenHash);\n\t\tif (!grantId) return undefined;\n\t\tthis.revokeGrant(grantId, now);\n\t\treturn grantId;\n\t}\n\n\trevokeGrant(grantId: string, now = Date.now()): boolean {\n\t\tconst grant = this.#grantsById.get(grantId);\n\t\tif (!grant) return false;\n\t\tif (grant.revokedAt !== null) return true;\n\t\tgrant.revokedAt = now;\n\t\tthis.#deleteGrantRefreshHashIndexEntries(grant);\n\t\treturn true;\n\t}\n\n\t#deleteInactiveGrants(now: number): void {\n\t\tfor (const [grantId, grant] of this.#grantsById) {\n\t\t\tif (grant.expiresAt > now && grant.revokedAt === null) continue;\n\t\t\tthis.#grantsById.delete(grantId);\n\t\t\tthis.#deleteGrantRefreshHashIndexEntries(grant);\n\t\t}\n\t}\n\n\t#deleteGrantRefreshHashIndexEntries(grant: RefreshTokenGrantRecord): void {\n\t\tconst refreshHashes = this.#refreshHashesByGrantId.get(grant.grantId);\n\t\tif (refreshHashes) {\n\t\t\tfor (const refreshHash of refreshHashes) this.#grantIdsByRefreshHash.delete(refreshHash);\n\t\t\tthis.#refreshHashesByGrantId.delete(grant.grantId);\n\t\t\treturn;\n\t\t}\n\t\tthis.#grantIdsByRefreshHash.delete(grant.currentRefreshTokenHash);\n\t\tif (grant.previousRefreshTokenHash)\n\t\t\tthis.#grantIdsByRefreshHash.delete(grant.previousRefreshTokenHash);\n\t}\n}\n\nexport function createInMemoryOAuthClientsStore(): OAuthRegisteredClientsStore {\n\treturn new InMemoryOAuthClientsStore();\n}\n\nexport function createInMemoryOAuthAuthorizationCodeStore(): OAuthAuthorizationCodeStore {\n\treturn new InMemoryOAuthAuthorizationCodeStore();\n}\n\nexport function createInMemoryOAuthAccessTokenStore(): OAuthAccessTokenStore {\n\treturn new InMemoryOAuthAccessTokenStore();\n}\n\nexport function createInMemoryOAuthRefreshTokenStore(): OAuthRefreshTokenStore {\n\treturn new InMemoryOAuthRefreshTokenStore();\n}\n\nexport function createMcpOAuthMetadata(options: McpOAuthMetadataOptions): OAuthMetadata {\n\tconst mcpUrl = normalizeMcpPublicUrl(options.mcpUrl);\n\tconst issuerUrl = getOriginUrl(mcpUrl);\n\tconst provider = createMetadataOnlyProvider(options.clientsStore);\n\treturn {\n\t\t...createOAuthMetadata({\n\t\t\tprovider,\n\t\t\tissuerUrl,\n\t\t\tbaseUrl: issuerUrl,\n\t\t}),\n\t\t// Phase 1 treats claude.ai and local callback clients as public clients.\n\t\t// Do not issue or advertise client secrets until we have a concrete need.\n\t\tgrant_types_supported: [\"authorization_code\", \"refresh_token\"],\n\t\ttoken_endpoint_auth_methods_supported: [\"none\"],\n\t\tscopes_supported: MCP_OAUTH_SCOPES_SUPPORTED,\n\t\tservice_documentation: MCP_OAUTH_SERVICE_DOCUMENTATION_URL,\n\t\trevocation_endpoint: new URL(\"/revoke\", issuerUrl).href,\n\t\trevocation_endpoint_auth_methods_supported: [\"none\"],\n\t\tclient_id_metadata_document_supported: false,\n\t};\n}\n\nexport function createMcpProtectedResourceMetadata(mcpUrl: string): OAuthProtectedResourceMetadata {\n\tconst normalizedMcpUrl = normalizeMcpPublicUrl(mcpUrl);\n\tconst metadata = {\n\t\tresource: normalizedMcpUrl.href,\n\t\tauthorization_servers: [getOriginUrl(normalizedMcpUrl).href],\n\t\tscopes_supported: MCP_OAUTH_SCOPES_SUPPORTED,\n\t\tbearer_methods_supported: [\"header\"],\n\t\tresource_name: MCP_OAUTH_RESOURCE_NAME,\n\t\tresource_documentation: MCP_OAUTH_SERVICE_DOCUMENTATION_URL,\n\t};\n\treturn OAuthProtectedResourceMetadataSchema.parse(metadata);\n}\n\nexport function registerMcpOAuthClient(\n\trequestBody: unknown,\n\tclientsStore: OAuthRegisteredClientsStore,\n): RegisteredMcpOAuthClient | McpOAuthRegistrationError {\n\tif (!clientsStore.registerClient) {\n\t\treturn invalidClientMetadata(\"Dynamic client registration is not enabled\");\n\t}\n\n\tconst parseResult = OAuthClientMetadataSchema.safeParse(requestBody);\n\tif (!parseResult.success) {\n\t\treturn invalidClientMetadata(parseResult.error.message);\n\t}\n\n\tconst clientMetadata = parseResult.data;\n\tconst redirectError = validateRedirectUris(clientMetadata.redirect_uris);\n\tif (redirectError) return invalidClientMetadata(redirectError);\n\n\tif (!isSupportedTokenEndpointAuthMethod(clientMetadata.token_endpoint_auth_method)) {\n\t\treturn invalidClientMetadata(\n\t\t\t\"Only public clients with token_endpoint_auth_method=none are supported\",\n\t\t);\n\t}\n\n\tif (\n\t\tclientMetadata.grant_types &&\n\t\t!isSupportedList(clientMetadata.grant_types, SUPPORTED_GRANT_TYPES)\n\t) {\n\t\treturn invalidClientMetadata(\n\t\t\t\"Only authorization_code and refresh_token grant_types are supported\",\n\t\t);\n\t}\n\n\tif (\n\t\tclientMetadata.response_types &&\n\t\t!isSupportedList(clientMetadata.response_types, SUPPORTED_RESPONSE_TYPES)\n\t) {\n\t\treturn invalidClientMetadata(\"Only code response_type is supported\");\n\t}\n\n\tconst registered = clientsStore.registerClient({\n\t\t...clientMetadata,\n\t\ttoken_endpoint_auth_method: \"none\",\n\t\tgrant_types: clientMetadata.grant_types ?? [\"authorization_code\", \"refresh_token\"],\n\t\tresponse_types: clientMetadata.response_types ?? [\"code\"],\n\t});\n\n\tif (registered instanceof Promise) {\n\t\tthrow new Error(\n\t\t\t\"Asynchronous OAuth client stores are not supported by this HTTP bootstrap yet\",\n\t\t);\n\t}\n\n\treturn { status: 201, body: registered };\n}\n\nexport function authorizeMcpOAuthClient(\n\tparams: URLSearchParams,\n\tclientsStore: OAuthRegisteredClientsStore,\n\tcodeStore: OAuthAuthorizationCodeStore,\n\tnow = Date.now(),\n): McpOAuthRedirectResult | McpOAuthErrorResult {\n\tconst prepared = prepareMcpOAuthAuthorizationRequest(params, clientsStore);\n\tif (\"status\" in prepared) return prepared;\n\n\tconst code = codeStore.issueCode(\n\t\t{\n\t\t\tclientId: prepared.clientId,\n\t\t\tredirectUri: prepared.redirectUri,\n\t\t\tcodeChallenge: prepared.codeChallenge,\n\t\t\tscopes: prepared.scopes,\n\t\t\t...(prepared.resource ? { resource: prepared.resource } : {}),\n\t\t\texpiresAt: now + AUTHORIZATION_CODE_TTL_MS,\n\t\t},\n\t\tnow,\n\t);\n\tif (!code)\n\t\treturn invalidOAuthRequest(\"temporarily_unavailable\", \"Too many active authorization codes\");\n\tconst redirect = new URL(prepared.redirectUri);\n\tredirect.searchParams.set(\"code\", code);\n\tif (prepared.state !== null) redirect.searchParams.set(\"state\", prepared.state);\n\treturn { status: 302, location: redirect.href };\n}\n\nexport function prepareMcpOAuthAuthorizationRequest(\n\tparams: URLSearchParams,\n\tclientsStore: OAuthRegisteredClientsStore,\n): PreparedMcpOAuthAuthorizationRequest | McpOAuthErrorResult {\n\tconst clientId = params.get(\"client_id\") ?? \"\";\n\tconst redirectUri = params.get(\"redirect_uri\") ?? \"\";\n\tconst responseType = params.get(\"response_type\") ?? \"\";\n\tconst codeChallenge = params.get(\"code_challenge\") ?? \"\";\n\tconst codeChallengeMethod = params.get(\"code_challenge_method\") ?? \"\";\n\tconst resource = params.get(\"resource\");\n\tconst scopes = parseScopeList(params.get(\"scope\"));\n\tconst state = params.get(\"state\");\n\n\tif (responseType !== \"code\") return invalidOAuthRequest(\"unsupported_response_type\");\n\tif (codeChallengeMethod !== \"S256\") {\n\t\treturn invalidOAuthRequest(\"invalid_request\", \"PKCE code_challenge_method=S256 is required\");\n\t}\n\tif (!PKCE_S256_CHALLENGE.test(codeChallenge)) {\n\t\treturn invalidOAuthRequest(\"invalid_request\", \"Valid PKCE S256 code_challenge is required\");\n\t}\n\n\tconst client = getRegisteredClient(clientsStore, clientId);\n\tif (!client) return invalidOAuthRequest(\"invalid_client\", \"Unknown OAuth client_id\");\n\tif (!client.redirect_uris.includes(redirectUri)) {\n\t\treturn invalidOAuthRequest(\"invalid_request\", \"redirect_uri is not registered for this client\");\n\t}\n\n\treturn { clientId, redirectUri, codeChallenge, scopes, resource, state };\n}\n\nexport function exchangeMcpOAuthAuthorizationCode(\n\tparams: URLSearchParams,\n\tclientsStore: OAuthRegisteredClientsStore,\n\tcodeStore: OAuthAuthorizationCodeStore,\n\ttokenStore: OAuthAccessTokenStore,\n\tnow = Date.now(),\n): McpOAuthTokenResult | McpOAuthErrorResult {\n\tif ((params.get(\"grant_type\") ?? \"\") !== \"authorization_code\") {\n\t\treturn invalidOAuthRequest(\"unsupported_grant_type\", \"Only authorization_code is supported\");\n\t}\n\n\tconst clientId = params.get(\"client_id\") ?? \"\";\n\tconst code = params.get(\"code\") ?? \"\";\n\tconst redirectUri = params.get(\"redirect_uri\") ?? \"\";\n\tconst codeVerifier = params.get(\"code_verifier\") ?? \"\";\n\tconst resource = params.get(\"resource\");\n\tconst client = getRegisteredClient(clientsStore, clientId);\n\tif (!client) return invalidOAuthRequest(\"invalid_client\", \"Unknown OAuth client_id\");\n\tif (!PKCE_VERIFIER.test(codeVerifier)) {\n\t\treturn invalidOAuthRequest(\"invalid_grant\", \"Invalid PKCE code_verifier\");\n\t}\n\n\t// Peek the authorization code without consuming it so a transient token-\n\t// store overflow (`temporarily_unavailable`) leaves the code reusable; only\n\t// permanent grant failures (expiry, client/redirect mismatch, PKCE) consume\n\t// the code. The code is finally consumed atomically with token issuance.\n\tconst record = codeStore.peekCode(code);\n\tif (!record) return invalidOAuthRequest(\"invalid_grant\", \"Invalid or already used code\");\n\tif (record.expiresAt <= now) {\n\t\tcodeStore.consumeCode(code);\n\t\treturn invalidOAuthRequest(\"invalid_grant\", \"Expired code\");\n\t}\n\tif (record.clientId !== clientId || record.redirectUri !== redirectUri) {\n\t\tcodeStore.consumeCode(code);\n\t\treturn invalidOAuthRequest(\"invalid_grant\", \"Code does not match client or redirect_uri\");\n\t}\n\tif (pkceS256(codeVerifier) !== record.codeChallenge) {\n\t\tcodeStore.consumeCode(code);\n\t\treturn invalidOAuthRequest(\"invalid_grant\", \"PKCE verification failed\");\n\t}\n\tif ((record.resource ?? null) !== (resource || null)) {\n\t\tcodeStore.consumeCode(code);\n\t\treturn invalidOAuthRequest(\"invalid_grant\", \"Code does not match resource\");\n\t}\n\n\tconst issued = tokenStore.issueToken(clientId, now, record.resource, undefined, record.scopes);\n\tif (!issued) {\n\t\t// Token-store overload is transient: leave the auth code unused so the\n\t\t// client can retry token exchange without restarting the OAuth flow.\n\t\treturn invalidOAuthRequest(\"temporarily_unavailable\", \"Too many active access tokens\");\n\t}\n\n\tconst consumed = codeStore.consumeCode(code);\n\tif (!consumed) {\n\t\ttokenStore.revokeToken(issued.token, now);\n\t\t// Lost a race with another request that consumed the code in the gap\n\t\t// between peek and consume. Treat as invalid_grant rather than issuing\n\t\t// a duplicate usable token, since the code is no longer single-use atomic.\n\t\treturn invalidOAuthRequest(\"invalid_grant\", \"Authorization code already used\");\n\t}\n\n\tconst tokens = OAuthTokensSchema.parse({\n\t\taccess_token: issued.token,\n\t\ttoken_type: \"Bearer\",\n\t\texpires_in: issued.expiresIn,\n\t});\n\treturn { status: 200, body: tokens };\n}\n\nexport function revokeMcpOAuthAccessToken(\n\tparams: URLSearchParams,\n\ttokenStore: OAuthAccessTokenStore,\n): { status: 200; body: Record<string, never> } {\n\tconst token = params.get(\"token\") ?? \"\";\n\ttokenStore.revokeToken(token);\n\treturn { status: 200, body: {} };\n}\n\nexport function normalizeMcpPublicUrl(value: string): URL {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\tthrow new Error(\"Invalid MCP HTTP public URL; expected an HTTPS /mcp URL\");\n\t}\n\tif (url.protocol !== \"https:\" && !isLoopbackUrl(url)) {\n\t\tthrow new Error(\"Invalid MCP HTTP public URL; use HTTPS for non-loopback URLs\");\n\t}\n\tif (url.username || url.password || url.search || url.hash) {\n\t\tthrow new Error(\n\t\t\t\"Invalid MCP HTTP public URL; credentials, query strings, and fragments are not allowed\",\n\t\t);\n\t}\n\tif (url.pathname === \"/\") url.pathname = \"/mcp\";\n\tif (url.pathname !== \"/mcp\") {\n\t\tthrow new Error(\"Invalid MCP HTTP public URL; expected /mcp path\");\n\t}\n\treturn url;\n}\n\nfunction getOriginUrl(url: URL): URL {\n\treturn new URL(url.origin);\n}\n\nfunction validateRedirectUris(redirectUris: OAuthClientMetadata[\"redirect_uris\"]): string | null {\n\tfor (const redirectUri of redirectUris) {\n\t\tconst url = new URL(redirectUri);\n\t\tif (url.username || url.password || url.hash) return `Invalid redirect URI: ${redirectUri}`;\n\t\tif (url.href === CLAUDE_HOSTED_CALLBACK) continue;\n\t\tif (isLoopbackCallbackUrl(url)) continue;\n\t\treturn `Unsupported redirect URI: ${redirectUri}`;\n\t}\n\treturn null;\n}\n\nfunction isSupportedTokenEndpointAuthMethod(method: string | undefined): boolean {\n\treturn method === undefined || method === \"none\";\n}\n\nfunction isSupportedList(values: string[], supported: Set<string>): boolean {\n\treturn values.length > 0 && values.every((value) => supported.has(value));\n}\n\nfunction isLoopbackCallbackUrl(url: URL): boolean {\n\treturn (\n\t\turl.protocol === \"http:\" &&\n\t\tLOCAL_CALLBACK_HOSTS.has(normalizeHostname(url.hostname)) &&\n\t\turl.pathname === \"/callback\" &&\n\t\turl.search === \"\"\n\t);\n}\n\nfunction isLoopbackUrl(url: URL): boolean {\n\tconst hostname = normalizeHostname(url.hostname);\n\treturn url.protocol === \"http:\" && LOCAL_CALLBACK_HOSTS.has(hostname);\n}\n\nfunction normalizeHostname(hostname: string): string {\n\treturn hostname.replace(/^\\[(.*)]$/, \"$1\").toLowerCase();\n}\n\nfunction parseScopeList(scope: string | null | undefined): string[] {\n\treturn (\n\t\tscope\n\t\t\t?.split(/\\s+/)\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean) ?? []\n\t);\n}\n\nfunction invalidClientMetadata(description: string): McpOAuthRegistrationError {\n\treturn {\n\t\tstatus: 400,\n\t\tbody: { error: \"invalid_client_metadata\", error_description: description },\n\t};\n}\n\nfunction invalidOAuthRequest(error: string, description?: string): McpOAuthErrorResult {\n\treturn {\n\t\tstatus: 400,\n\t\tbody: description ? { error, error_description: description } : { error },\n\t};\n}\n\nfunction getRegisteredClient(\n\tclientsStore: OAuthRegisteredClientsStore,\n\tclientId: string,\n): OAuthClientInformationFull | undefined {\n\tconst client = clientsStore.getClient(clientId);\n\tif (client instanceof Promise) {\n\t\tthrow new Error(\n\t\t\t\"Asynchronous OAuth client stores are not supported by this HTTP bootstrap yet\",\n\t\t);\n\t}\n\treturn client;\n}\n\nfunction pkceS256(codeVerifier: string): string {\n\treturn createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n}\n\n// Decode an externally-supplied access-token string into the fixed-length\n// random Buffer it represents. Rejects anything that is not a base64url-encoded\n// ACCESS_TOKEN_BYTES-length value, so invalid input never reaches the HMAC\n// signer. Returns null on any decode/length mismatch.\nfunction decodeOAuthAccessToken(serialized: string): Buffer | null {\n\tif (typeof serialized !== \"string\") return null;\n\tif (serialized.length !== ACCESS_TOKEN_BASE64URL_LENGTH) return null;\n\tif (!ACCESS_TOKEN_BASE64URL.test(serialized)) return null;\n\tconst bytes = Buffer.from(serialized, \"base64url\");\n\tif (bytes.length !== ACCESS_TOKEN_BYTES) return null;\n\treturn bytes;\n}\n\nfunction randomToken(): string {\n\treturn randomBytes(ACCESS_TOKEN_BYTES).toString(\"base64url\");\n}\n\nfunction hashSerializedOAuthToken(serialized: string, key: Buffer): string | null {\n\tconst tokenBytes = decodeOAuthAccessToken(serialized);\n\tif (!tokenBytes) return null;\n\treturn signOAuthToken(tokenBytes, key);\n}\n\n// Compute the HMAC-SHA256 signature of the binary access-token material using\n// the per-store random key. This is an integrity signature over a random\n// 256-bit value, not password hashing; tokens are validated by re-signing the\n// presented bytes and comparing the digest to the stored signature.\nfunction signOAuthAccessTokenBytes(material: Buffer, key: Buffer): string {\n\treturn signOAuthToken(material, key);\n}\n\nfunction signOAuthToken(material: Buffer | string, key: Buffer): string {\n\treturn createHmac(\"sha256\", key).update(material).digest(\"base64url\");\n}\n\nfunction isSameTokenHash(left: string, right: string): boolean {\n\tconst leftBuffer = Buffer.from(left);\n\tconst rightBuffer = Buffer.from(right);\n\treturn leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer);\n}\n\nfunction isScopeSubset(requested: string[], granted: string[]): boolean {\n\tconst grantedScopes = new Set(granted);\n\treturn requested.every((scope) => grantedScopes.has(scope));\n}\n\nfunction createMetadataOnlyProvider(\n\tclientsStore: OAuthRegisteredClientsStore,\n): OAuthServerProvider {\n\treturn {\n\t\tclientsStore,\n\t\tasync authorize() {\n\t\t\tthrow new Error(\"OAuth authorize is not implemented yet\");\n\t\t},\n\t\tasync challengeForAuthorizationCode() {\n\t\t\tthrow new Error(\"OAuth token exchange is not implemented yet\");\n\t\t},\n\t\tasync exchangeAuthorizationCode() {\n\t\t\tthrow new Error(\"OAuth token exchange is not implemented yet\");\n\t\t},\n\t\tasync exchangeRefreshToken() {\n\t\t\tthrow new Error(\"OAuth refresh is not implemented yet\");\n\t\t},\n\t\tasync verifyAccessToken() {\n\t\t\tthrow new Error(\"OAuth bearer verification is not implemented yet\");\n\t\t},\n\t};\n}\n","import { createPublicKey, randomUUID, verify } from \"node:crypto\";\nimport type { OAuthRegisteredClientsStore } from \"@modelcontextprotocol/sdk/server/auth/clients.js\";\nimport {\n\ttype McpOAuthErrorResult,\n\ttype McpOAuthRedirectResult,\n\tprepareMcpOAuthAuthorizationRequest,\n} from \"./oauth.js\";\n\nexport const MCP_OIDC_ISSUER_URL_ENV = \"CODEMEM_MCP_OIDC_ISSUER_URL\";\nexport const MCP_OIDC_CLIENT_ID_ENV = \"CODEMEM_MCP_OIDC_CLIENT_ID\";\nexport const MCP_OIDC_CLIENT_SECRET_ENV = \"CODEMEM_MCP_OIDC_CLIENT_SECRET\";\nexport const MCP_OAUTH_ALLOWED_SUBJECT_ENV = \"CODEMEM_MCP_OAUTH_ALLOWED_SUBJECT\";\nexport const MCP_OAUTH_ALLOWED_EMAIL_ENV = \"CODEMEM_MCP_OAUTH_ALLOWED_EMAIL\";\n\nconst PENDING_AUTH_TTL_MS = 5 * 60 * 1000;\nconst MAX_PENDING_AUTH = 100;\nconst ID_TOKEN_CLOCK_SKEW_MS = 5 * 60 * 1000;\n\ntype JsonWebKey = Record<string, unknown>;\n\nexport interface OidcConfig {\n\tissuerUrl: string;\n\tclientId: string;\n\tclientSecret: string;\n\tallowedSubject?: string;\n\tallowedEmail?: string;\n}\n\nexport interface PendingOidcAuthorization {\n\toauthParams: string;\n\tnonce: string;\n\texpiresAt: number;\n}\n\nexport interface OidcPendingAuthorizationStore {\n\tissue(record: Omit<PendingOidcAuthorization, \"expiresAt\">, now?: number): string | undefined;\n\tconsume(state: string): PendingOidcAuthorization | undefined;\n}\n\ninterface OidcDiscoveryMetadata {\n\tissuer: string;\n\tauthorization_endpoint: string;\n\ttoken_endpoint: string;\n\tjwks_uri: string;\n}\n\ninterface OidcTokensResponse {\n\tid_token?: string;\n\terror?: string;\n\terror_description?: string;\n}\n\ninterface JwtHeader {\n\talg?: string;\n\tkid?: string;\n}\n\ninterface JwtClaims {\n\tiss?: string;\n\taud?: string | string[];\n\tazp?: string;\n\texp?: number;\n\tiat?: number;\n\tnonce?: string;\n\tsub?: string;\n\temail?: string;\n\temail_verified?: boolean;\n}\n\ninterface JsonWebKeySet {\n\tkeys?: JsonWebKey[];\n}\n\nexport class InMemoryOidcPendingAuthorizationStore implements OidcPendingAuthorizationStore {\n\treadonly #records = new Map<string, PendingOidcAuthorization>();\n\n\tissue(record: Omit<PendingOidcAuthorization, \"expiresAt\">, now = Date.now()): string | undefined {\n\t\tthis.#deleteExpired(now);\n\t\tif (this.#records.size >= MAX_PENDING_AUTH) return undefined;\n\t\tconst state = randomUUID();\n\t\tthis.#records.set(state, { ...record, expiresAt: now + PENDING_AUTH_TTL_MS });\n\t\treturn state;\n\t}\n\n\tconsume(state: string): PendingOidcAuthorization | undefined {\n\t\tconst record = this.#records.get(state);\n\t\tthis.#records.delete(state);\n\t\treturn record;\n\t}\n\n\t#deleteExpired(now: number): void {\n\t\tfor (const [state, record] of this.#records) {\n\t\t\tif (record.expiresAt <= now) this.#records.delete(state);\n\t\t}\n\t}\n}\n\nexport function createInMemoryOidcPendingAuthorizationStore(): OidcPendingAuthorizationStore {\n\treturn new InMemoryOidcPendingAuthorizationStore();\n}\n\nexport function resolveOidcConfig(env: NodeJS.ProcessEnv = process.env): OidcConfig | undefined {\n\tconst issuerUrl = env[MCP_OIDC_ISSUER_URL_ENV]?.trim();\n\tconst clientId = env[MCP_OIDC_CLIENT_ID_ENV]?.trim();\n\tconst clientSecret = env[MCP_OIDC_CLIENT_SECRET_ENV]?.trim();\n\tconst allowedSubject = env[MCP_OAUTH_ALLOWED_SUBJECT_ENV]?.trim();\n\tconst allowedEmail = env[MCP_OAUTH_ALLOWED_EMAIL_ENV]?.trim().toLowerCase();\n\tif (!issuerUrl && !clientId && !clientSecret && !allowedSubject && !allowedEmail)\n\t\treturn undefined;\n\tif (!issuerUrl || !clientId || !clientSecret) {\n\t\tthrow new Error(\"Incomplete MCP OIDC configuration\");\n\t}\n\tvalidateOidcUrl(issuerUrl, \"issuer\", { allowLoopbackHttp: true });\n\tif (!allowedSubject && !allowedEmail) {\n\t\tthrow new Error(\n\t\t\t\"MCP OIDC requires CODEMEM_MCP_OAUTH_ALLOWED_SUBJECT or CODEMEM_MCP_OAUTH_ALLOWED_EMAIL\",\n\t\t);\n\t}\n\treturn { issuerUrl, clientId, clientSecret, allowedSubject, allowedEmail };\n}\n\nexport async function beginOidcAuthorization(\n\tparams: URLSearchParams,\n\tclientsStore: OAuthRegisteredClientsStore,\n\tpendingStore: OidcPendingAuthorizationStore,\n\tconfig: OidcConfig | undefined,\n\tpublicMcpUrl: string,\n\tnow = Date.now(),\n): Promise<McpOAuthRedirectResult | McpOAuthErrorResult> {\n\tif (!config) return invalidOAuthRequest(\"temporarily_unavailable\", \"OIDC is not configured\");\n\tconst prepared = prepareMcpOAuthAuthorizationRequest(params, clientsStore);\n\tif (\"status\" in prepared) return prepared;\n\n\tconst discovery = await fetchOidcDiscovery(config.issuerUrl);\n\tconst nonce = randomUUID();\n\tconst upstreamState = pendingStore.issue({ oauthParams: params.toString(), nonce }, now);\n\tif (!upstreamState)\n\t\treturn invalidOAuthRequest(\"temporarily_unavailable\", \"Too many pending OIDC authorizations\");\n\n\tconst redirectUri = getCallbackUrl(publicMcpUrl);\n\tconst upstream = new URL(discovery.authorization_endpoint);\n\tupstream.searchParams.set(\"response_type\", \"code\");\n\tupstream.searchParams.set(\"client_id\", config.clientId);\n\tupstream.searchParams.set(\"redirect_uri\", redirectUri);\n\tupstream.searchParams.set(\"scope\", \"openid email\");\n\tupstream.searchParams.set(\"state\", upstreamState);\n\tupstream.searchParams.set(\"nonce\", nonce);\n\treturn { status: 302, location: upstream.href };\n}\n\nexport async function completeOidcAuthorization(\n\tcallbackParams: URLSearchParams,\n\tpendingStore: OidcPendingAuthorizationStore,\n\tconfig: OidcConfig,\n\tpublicMcpUrl: string,\n\tnow = Date.now(),\n): Promise<{ oauthParams: URLSearchParams } | McpOAuthErrorResult> {\n\ttry {\n\t\tconst code = callbackParams.get(\"code\") ?? \"\";\n\t\tconst state = callbackParams.get(\"state\") ?? \"\";\n\t\tif (!state) return invalidOAuthRequest(\"invalid_request\", \"Missing OIDC state\");\n\t\tconst pending = pendingStore.consume(state);\n\t\tif (!pending || pending.expiresAt <= now)\n\t\t\treturn invalidOAuthRequest(\"invalid_request\", \"Expired or unknown OIDC state\");\n\t\tif (!code) return invalidOAuthRequest(\"invalid_request\", \"Missing OIDC code\");\n\n\t\tconst discovery = await fetchOidcDiscovery(config.issuerUrl);\n\t\tconst tokens = await exchangeOidcCode(\n\t\t\tdiscovery.token_endpoint,\n\t\t\tconfig,\n\t\t\tgetCallbackUrl(publicMcpUrl),\n\t\t\tcode,\n\t\t);\n\t\tif (!tokens.id_token)\n\t\t\treturn invalidOAuthRequest(\"invalid_request\", \"OIDC token exchange failed\");\n\t\tconst claims = await validateIdToken(\n\t\t\ttokens.id_token,\n\t\t\tdiscovery,\n\t\t\tconfig.clientId,\n\t\t\tpending.nonce,\n\t\t\tnow,\n\t\t);\n\t\tif (!isAllowedIdentity(claims, config))\n\t\t\treturn invalidOAuthRequest(\"access_denied\", \"OIDC identity is not allowed\");\n\n\t\treturn { oauthParams: new URLSearchParams(pending.oauthParams) };\n\t} catch {\n\t\treturn invalidOAuthRequest(\"access_denied\", \"OIDC identity verification failed\");\n\t}\n}\n\nfunction getCallbackUrl(publicMcpUrl: string): string {\n\tconst url = new URL(publicMcpUrl);\n\treturn new URL(\"/oauth/callback\", url.origin).href;\n}\n\nasync function fetchOidcDiscovery(issuerUrl: string): Promise<OidcDiscoveryMetadata> {\n\tconst issuer = new URL(issuerUrl);\n\tif (issuer.search || issuer.hash)\n\t\tthrow new Error(\"OIDC issuer must not include query or fragment\");\n\tif (issuer.username || issuer.password)\n\t\tthrow new Error(\"OIDC issuer must not include credentials\");\n\tconst discoveryUrl = new URL(\n\t\t`${issuer.pathname.replace(/\\/$/, \"\")}/.well-known/openid-configuration`,\n\t\tissuer.origin,\n\t);\n\tconst response = await fetch(discoveryUrl);\n\tif (!response.ok) throw new Error(\"OIDC discovery request failed\");\n\tconst metadata = (await response.json()) as Partial<OidcDiscoveryMetadata>;\n\tif (metadata.issuer !== issuer.href && metadata.issuer !== issuer.href.replace(/\\/$/, \"\")) {\n\t\tthrow new Error(\"OIDC discovery issuer mismatch\");\n\t}\n\tif (!metadata.authorization_endpoint || !metadata.token_endpoint || !metadata.jwks_uri) {\n\t\tthrow new Error(\"OIDC discovery metadata is incomplete\");\n\t}\n\tconst allowLoopbackHttp = issuer.protocol === \"http:\" && isLoopbackHostname(issuer.hostname);\n\tvalidateOidcUrl(metadata.authorization_endpoint, \"authorization endpoint\", { allowLoopbackHttp });\n\tvalidateOidcUrl(metadata.token_endpoint, \"token endpoint\", { allowLoopbackHttp });\n\tvalidateOidcUrl(metadata.jwks_uri, \"JWKS URI\", { allowLoopbackHttp });\n\treturn metadata as OidcDiscoveryMetadata;\n}\n\nasync function exchangeOidcCode(\n\ttokenEndpoint: string,\n\tconfig: OidcConfig,\n\tredirectUri: string,\n\tcode: string,\n): Promise<OidcTokensResponse> {\n\tconst body = new URLSearchParams({\n\t\tgrant_type: \"authorization_code\",\n\t\tcode,\n\t\tredirect_uri: redirectUri,\n\t\tclient_id: config.clientId,\n\t\tclient_secret: config.clientSecret,\n\t});\n\tconst response = await fetch(tokenEndpoint, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"content-type\": \"application/x-www-form-urlencoded\" },\n\t\tbody,\n\t});\n\tif (!response.ok) throw new Error(\"OIDC token request failed\");\n\treturn (await response.json()) as OidcTokensResponse;\n}\n\nasync function validateIdToken(\n\tidToken: string,\n\tdiscovery: OidcDiscoveryMetadata,\n\tclientId: string,\n\tnonce: string,\n\tnow: number,\n): Promise<JwtClaims> {\n\tconst [encodedHeader, encodedClaims, signature] = idToken.split(\".\");\n\tif (!encodedHeader || !encodedClaims || !signature) throw new Error(\"Invalid OIDC id_token\");\n\tconst header = parseJwtPart<JwtHeader>(encodedHeader);\n\tconst claims = parseJwtPart<JwtClaims>(encodedClaims);\n\tif (header.alg !== \"RS256\" || !header.kid) throw new Error(\"Unsupported OIDC id_token signature\");\n\tif (claims.iss !== discovery.issuer) throw new Error(\"OIDC id_token issuer mismatch\");\n\tif (!audienceIncludes(claims.aud, clientId)) throw new Error(\"OIDC id_token audience mismatch\");\n\tif (!isValidAuthorizedParty(claims.aud, claims.azp, clientId))\n\t\tthrow new Error(\"OIDC id_token authorized party mismatch\");\n\tif (typeof claims.exp !== \"number\" || !Number.isFinite(claims.exp) || claims.exp * 1000 <= now)\n\t\tthrow new Error(\"OIDC id_token is expired\");\n\tif (typeof claims.iat !== \"number\" || !Number.isFinite(claims.iat))\n\t\tthrow new Error(\"OIDC id_token issued-at time is missing\");\n\tif (claims.iat * 1000 > now + ID_TOKEN_CLOCK_SKEW_MS)\n\t\tthrow new Error(\"OIDC id_token issued-at time is in the future\");\n\tif (claims.nonce !== nonce) throw new Error(\"OIDC id_token nonce mismatch\");\n\tif (!claims.sub) throw new Error(\"OIDC id_token subject is missing\");\n\n\tconst jwksResponse = await fetch(discovery.jwks_uri);\n\tif (!jwksResponse.ok) throw new Error(\"OIDC JWKS request failed\");\n\tconst jwks = (await jwksResponse.json()) as JsonWebKeySet;\n\tconst jwk = jwks.keys?.find((key) => key.kid === header.kid && key.kty === \"RSA\");\n\tif (!jwk) throw new Error(\"OIDC signing key not found\");\n\tconst key = createPublicKey({ key: jwk, format: \"jwk\" });\n\tconst signed = `${encodedHeader}.${encodedClaims}`;\n\tconst ok = verify(\"RSA-SHA256\", Buffer.from(signed), key, Buffer.from(signature, \"base64url\"));\n\tif (!ok) throw new Error(\"OIDC id_token signature verification failed\");\n\treturn claims;\n}\n\nfunction parseJwtPart<T>(encoded: string): T {\n\treturn JSON.parse(Buffer.from(encoded, \"base64url\").toString(\"utf8\")) as T;\n}\n\nfunction audienceIncludes(audience: string | string[] | undefined, clientId: string): boolean {\n\treturn Array.isArray(audience) ? audience.includes(clientId) : audience === clientId;\n}\n\nfunction isValidAuthorizedParty(\n\taudience: string | string[] | undefined,\n\tauthorizedParty: string | undefined,\n\tclientId: string,\n): boolean {\n\tif (authorizedParty && authorizedParty !== clientId) return false;\n\treturn !Array.isArray(audience) || audience.length <= 1 || authorizedParty === clientId;\n}\n\nfunction isAllowedIdentity(claims: JwtClaims, config: OidcConfig): boolean {\n\tif (config.allowedSubject && claims.sub === config.allowedSubject) return true;\n\tif (!config.allowedEmail) return false;\n\treturn claims.email_verified === true && claims.email?.toLowerCase() === config.allowedEmail;\n}\n\nfunction validateOidcUrl(\n\tvalue: string,\n\tlabel: string,\n\toptions: { allowLoopbackHttp?: boolean } = {},\n): void {\n\tconst url = new URL(value);\n\tif (url.username || url.password) throw new Error(`OIDC ${label} must not include credentials`);\n\tif (url.protocol === \"https:\") return;\n\tif (options.allowLoopbackHttp && url.protocol === \"http:\" && isLoopbackHostname(url.hostname))\n\t\treturn;\n\tthrow new Error(`OIDC ${label} must use HTTPS`);\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n\tconst normalized = hostname.replace(/^\\[(.*)]$/, \"$1\").toLowerCase();\n\treturn normalized === \"localhost\" || normalized === \"127.0.0.1\" || normalized === \"::1\";\n}\n\nfunction invalidOAuthRequest(error: string, description?: string): McpOAuthErrorResult {\n\treturn {\n\t\tstatus: 400,\n\t\tbody: description ? { error, error_description: description } : { error },\n\t};\n}\n","import type { OAuthRegisteredClientsStore } from \"@modelcontextprotocol/sdk/server/auth/clients.js\";\nimport {\n\tAccessDeniedError,\n\tInvalidClientError,\n\tInvalidGrantError,\n\tInvalidRequestError,\n\tInvalidTokenError,\n\tServerError,\n\tTemporarilyUnavailableError,\n\tUnsupportedResponseTypeError,\n} from \"@modelcontextprotocol/sdk/server/auth/errors.js\";\nimport type {\n\tAuthorizationParams,\n\tOAuthServerProvider,\n} from \"@modelcontextprotocol/sdk/server/auth/provider.js\";\nimport type { AuthInfo } from \"@modelcontextprotocol/sdk/server/auth/types.js\";\nimport {\n\ttype OAuthClientInformationFull,\n\ttype OAuthErrorResponse,\n\ttype OAuthTokenRevocationRequest,\n\ttype OAuthTokens,\n\tOAuthTokensSchema,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport type { Response } from \"express\";\nimport {\n\tcreateInMemoryOAuthRefreshTokenStore,\n\ttype OAuthAccessTokenStore,\n\ttype OAuthAuthorizationCodeStore,\n\ttype OAuthRefreshTokenStore,\n} from \"./oauth.js\";\nimport {\n\tbeginOidcAuthorization,\n\ttype OidcConfig,\n\ttype OidcPendingAuthorizationStore,\n} from \"./oidc.js\";\n\nexport interface MemoryOAuthServerProviderOptions {\n\tclientsStore: OAuthRegisteredClientsStore;\n\tcodeStore: OAuthAuthorizationCodeStore;\n\ttokenStore: OAuthAccessTokenStore;\n\trefreshTokenStore?: OAuthRefreshTokenStore;\n\tpublicMcpUrl: string;\n\toidc?: { config: OidcConfig; pendingStore: OidcPendingAuthorizationStore };\n\tnow?: () => number;\n}\n\n/**\n * MemoryOAuthServerProvider adapts codemem's existing in-memory stores to the\n * `@modelcontextprotocol/sdk` `OAuthServerProvider` contract so the SDK's\n * `mcpAuthRouter` and `requireBearerAuth` middleware can drive the OAuth\n * lifecycle. This class is the boundary between SDK-handled HTTP and\n * codemem-owned storage / upstream-OIDC bridging.\n *\n * Conventions encoded here:\n * - `authorize()` does NOT mint the MCP authorization code itself. It\n * persists pending state and redirects the user-agent to the upstream OIDC\n * provider. The MCP code is issued later by codemem's own /oauth/callback\n * route (wired in codemem-b20m.3) after the upstream identity is validated.\n * - PKCE is validated by the SDK token handler against the challenge returned\n * from `challengeForAuthorizationCode`. We do not re-verify here.\n * - `expiresAt` returned in `AuthInfo` is epoch SECONDS per the SDK's\n * `requireBearerAuth` expectations; the underlying store records ms.\n * - Refresh-token grants use dual-token rotation: the current token and one\n * previous token are accepted to tolerate client retry races, while older\n * replayed tokens revoke the whole grant.\n */\nexport class MemoryOAuthServerProvider implements OAuthServerProvider {\n\treadonly #clientsStore: OAuthRegisteredClientsStore;\n\treadonly #codeStore: OAuthAuthorizationCodeStore;\n\treadonly #tokenStore: OAuthAccessTokenStore;\n\treadonly #refreshTokenStore: OAuthRefreshTokenStore;\n\treadonly #publicMcpUrl: string;\n\treadonly #resourceServerUrl: string;\n\treadonly #oidc?: { config: OidcConfig; pendingStore: OidcPendingAuthorizationStore };\n\treadonly #now: () => number;\n\n\tconstructor(options: MemoryOAuthServerProviderOptions) {\n\t\tthis.#clientsStore = options.clientsStore;\n\t\tthis.#codeStore = options.codeStore;\n\t\tthis.#tokenStore = options.tokenStore;\n\t\tthis.#refreshTokenStore = options.refreshTokenStore ?? createInMemoryOAuthRefreshTokenStore();\n\t\tthis.#publicMcpUrl = options.publicMcpUrl;\n\t\tthis.#resourceServerUrl = new URL(options.publicMcpUrl).href;\n\t\tthis.#oidc = options.oidc;\n\t\tthis.#now = options.now ?? Date.now;\n\t}\n\n\tget clientsStore(): OAuthRegisteredClientsStore {\n\t\treturn this.#clientsStore;\n\t}\n\n\tasync authorize(\n\t\tclient: OAuthClientInformationFull,\n\t\tparams: AuthorizationParams,\n\t\tres: Response,\n\t): Promise<void> {\n\t\tif (!this.#oidc) {\n\t\t\tthrow new TemporarilyUnavailableError(\"OIDC is not configured\");\n\t\t}\n\t\tconst upstreamParams = buildUpstreamAuthorizationParams(client, params);\n\t\tconst result = await beginOidcAuthorization(\n\t\t\tupstreamParams,\n\t\t\tthis.#clientsStore,\n\t\t\tthis.#oidc.pendingStore,\n\t\t\tthis.#oidc.config,\n\t\t\tthis.#publicMcpUrl,\n\t\t\tthis.#now(),\n\t\t);\n\t\tif (result.status === 302) {\n\t\t\tres.redirect(302, result.location);\n\t\t\treturn;\n\t\t}\n\t\tthrow mapOAuthErrorBody(result.body);\n\t}\n\n\tasync challengeForAuthorizationCode(\n\t\t_client: OAuthClientInformationFull,\n\t\tauthorizationCode: string,\n\t): Promise<string> {\n\t\tconst record = this.#codeStore.peekCode(authorizationCode);\n\t\tif (!record) throw new InvalidGrantError(\"Invalid or already used code\");\n\t\tif (record.clientId !== _client.client_id) {\n\t\t\tthrow new InvalidGrantError(\"Code does not match client\");\n\t\t}\n\t\tif (record.expiresAt <= this.#now()) {\n\t\t\tthis.#codeStore.consumeCode(authorizationCode);\n\t\t\tthrow new InvalidGrantError(\"Expired code\");\n\t\t}\n\t\treturn record.codeChallenge;\n\t}\n\n\tasync exchangeAuthorizationCode(\n\t\tclient: OAuthClientInformationFull,\n\t\tauthorizationCode: string,\n\t\t_codeVerifier?: string,\n\t\tredirectUri?: string,\n\t\tresource?: URL,\n\t): Promise<OAuthTokens> {\n\t\tconst now = this.#now();\n\t\tconst record = this.#codeStore.peekCode(authorizationCode);\n\t\tif (!record) throw new InvalidGrantError(\"Invalid or already used code\");\n\t\tif (record.expiresAt <= now) {\n\t\t\tthis.#codeStore.consumeCode(authorizationCode);\n\t\t\tthrow new InvalidGrantError(\"Expired code\");\n\t\t}\n\t\tif (record.clientId !== client.client_id) {\n\t\t\tthis.#codeStore.consumeCode(authorizationCode);\n\t\t\tthrow new InvalidGrantError(\"Code does not match client\");\n\t\t}\n\t\t// RFC 6749 §4.1.3: if the authorization request included redirect_uri,\n\t\t// the token request MUST include the same value. codemem always binds a\n\t\t// redirect_uri at /authorize, so a missing value at /token is always a\n\t\t// grant mismatch, not an optional check.\n\t\tif (redirectUri === undefined || record.redirectUri !== redirectUri) {\n\t\t\tthis.#codeStore.consumeCode(authorizationCode);\n\t\t\tthrow new InvalidGrantError(\"Code does not match redirect_uri\");\n\t\t}\n\t\tif ((record.resource ?? null) !== (resource?.href ?? null)) {\n\t\t\tthis.#codeStore.consumeCode(authorizationCode);\n\t\t\tthrow new InvalidGrantError(\"Code does not match resource\");\n\t\t}\n\n\t\tconst scopes = normalizeClientScopes(record.scopes) ?? [];\n\t\tconst grant = this.#refreshTokenStore.issueGrant(\n\t\t\t{ clientId: client.client_id, scopes, resource: record.resource },\n\t\t\tnow,\n\t\t);\n\t\tif (!grant) {\n\t\t\tthrow new TemporarilyUnavailableError(\"Too many active refresh grants\");\n\t\t}\n\t\tconst issued = this.#tokenStore.issueToken(\n\t\t\tclient.client_id,\n\t\t\tnow,\n\t\t\trecord.resource,\n\t\t\tgrant.grant.grantId,\n\t\t\tscopes,\n\t\t);\n\t\tif (!issued) {\n\t\t\t// Token-store overload is transient: leave the auth code unused so the\n\t\t\t// client can retry token exchange without restarting the OAuth flow.\n\t\t\tthis.#refreshTokenStore.revokeGrant(grant.grant.grantId, now);\n\t\t\tthrow new TemporarilyUnavailableError(\"Too many active access tokens\");\n\t\t}\n\n\t\tconst consumed = this.#codeStore.consumeCode(authorizationCode);\n\t\tif (!consumed) {\n\t\t\tthis.#tokenStore.revokeToken(issued.token, now);\n\t\t\tthis.#refreshTokenStore.revokeGrant(grant.grant.grantId, now);\n\t\t\t// Lost a race with a concurrent exchange that consumed the code in\n\t\t\t// the gap between peek and consume. Reject rather than issuing a\n\t\t\t// duplicate usable token.\n\t\t\tthrow new InvalidGrantError(\"Authorization code already used\");\n\t\t}\n\n\t\treturn OAuthTokensSchema.parse({\n\t\t\taccess_token: issued.token,\n\t\t\ttoken_type: \"Bearer\",\n\t\t\texpires_in: issued.expiresIn,\n\t\t\trefresh_token: grant.refreshToken,\n\t\t});\n\t}\n\n\tasync exchangeRefreshToken(\n\t\tclient: OAuthClientInformationFull,\n\t\trefreshToken: string,\n\t\tscopes?: string[],\n\t\tresource?: URL,\n\t): Promise<OAuthTokens> {\n\t\tconst now = this.#now();\n\t\tconst rotation = this.#refreshTokenStore.rotateRefreshToken(\n\t\t\tclient.client_id,\n\t\t\trefreshToken,\n\t\t\t{ scopes: normalizeClientScopes(scopes), ...(resource ? { resource: resource.href } : {}) },\n\t\t\tnow,\n\t\t);\n\t\tif (!rotation.ok) {\n\t\t\tif (rotation.reason === \"refresh_token_replay\" && rotation.grantId) {\n\t\t\t\tthis.#tokenStore.revokeTokensForGrant?.(rotation.grantId, now);\n\t\t\t}\n\t\t\tthrow new InvalidGrantError(refreshTokenErrorDescription(rotation.reason));\n\t\t}\n\t\tconst issued = this.#tokenStore.issueToken(\n\t\t\tclient.client_id,\n\t\t\tnow,\n\t\t\trotation.grant.resource,\n\t\t\trotation.grant.grantId,\n\t\t\trotation.grant.scopes,\n\t\t);\n\t\tif (!issued) throw new TemporarilyUnavailableError(\"Too many active access tokens\");\n\t\treturn OAuthTokensSchema.parse({\n\t\t\taccess_token: issued.token,\n\t\t\ttoken_type: \"Bearer\",\n\t\t\texpires_in: issued.expiresIn,\n\t\t\trefresh_token: rotation.refreshToken,\n\t\t});\n\t}\n\n\tasync verifyAccessToken(token: string): Promise<AuthInfo> {\n\t\tconst result = this.#tokenStore.verifyToken(token, this.#now());\n\t\tif (!result.ok) {\n\t\t\tconst reason =\n\t\t\t\tresult.reason === \"expired_token\"\n\t\t\t\t\t? \"Token has expired\"\n\t\t\t\t\t: result.reason === \"revoked_token\"\n\t\t\t\t\t\t? \"Token has been revoked\"\n\t\t\t\t\t\t: \"Token is invalid\";\n\t\t\tthrow new InvalidTokenError(reason);\n\t\t}\n\t\tconst authInfo: AuthInfo = {\n\t\t\ttoken,\n\t\t\tclientId: result.record.clientId,\n\t\t\tscopes: result.record.scopes,\n\t\t\texpiresAt: Math.floor(result.record.expiresAt / 1000),\n\t\t};\n\t\tif (result.record.resource) {\n\t\t\tif (new URL(result.record.resource).href !== this.#resourceServerUrl) {\n\t\t\t\tthrow new InvalidTokenError(\"Token audience does not match this resource server\");\n\t\t\t}\n\t\t\tauthInfo.resource = new URL(result.record.resource);\n\t\t}\n\t\treturn authInfo;\n\t}\n\n\tasync revokeToken(\n\t\t_client: OAuthClientInformationFull,\n\t\trequest: OAuthTokenRevocationRequest,\n\t): Promise<void> {\n\t\tconst now = this.#now();\n\t\tthis.#tokenStore.revokeToken(request.token, now);\n\t\tconst grantId = this.#refreshTokenStore.revokeRefreshToken(request.token, now);\n\t\tif (grantId) this.#tokenStore.revokeTokensForGrant?.(grantId, now);\n\t}\n}\n\nfunction buildUpstreamAuthorizationParams(\n\tclient: OAuthClientInformationFull,\n\tparams: AuthorizationParams,\n): URLSearchParams {\n\tconst upstream = new URLSearchParams();\n\tupstream.set(\"client_id\", client.client_id);\n\tupstream.set(\"redirect_uri\", params.redirectUri);\n\tupstream.set(\"response_type\", \"code\");\n\tupstream.set(\"code_challenge\", params.codeChallenge);\n\tupstream.set(\"code_challenge_method\", \"S256\");\n\tif (params.state) upstream.set(\"state\", params.state);\n\tif (params.scopes?.length) upstream.set(\"scope\", params.scopes.join(\" \"));\n\tif (params.resource) upstream.set(\"resource\", params.resource.href);\n\treturn upstream;\n}\n\nfunction mapOAuthErrorBody(body: OAuthErrorResponse): Error {\n\tconst description = body.error_description ?? body.error;\n\tswitch (body.error) {\n\t\tcase \"temporarily_unavailable\":\n\t\t\treturn new TemporarilyUnavailableError(description);\n\t\tcase \"invalid_request\":\n\t\t\treturn new InvalidRequestError(description);\n\t\tcase \"invalid_client\":\n\t\t\treturn new InvalidClientError(description);\n\t\tcase \"access_denied\":\n\t\t\treturn new AccessDeniedError(description);\n\t\tcase \"unsupported_response_type\":\n\t\t\treturn new UnsupportedResponseTypeError(description);\n\t\tdefault:\n\t\t\treturn new ServerError(description);\n\t}\n}\n\nfunction refreshTokenErrorDescription(reason: string): string {\n\tswitch (reason) {\n\t\tcase \"expired_refresh_token\":\n\t\t\treturn \"Refresh token has expired\";\n\t\tcase \"revoked_refresh_token\":\n\t\t\treturn \"Refresh token has been revoked\";\n\t\tcase \"client_mismatch\":\n\t\t\treturn \"Refresh token does not match client\";\n\t\tcase \"scope_mismatch\":\n\t\t\treturn \"Refresh token scope cannot be expanded\";\n\t\tcase \"resource_mismatch\":\n\t\t\treturn \"Refresh token does not match resource\";\n\t\tcase \"refresh_token_replay\":\n\t\t\treturn \"Refresh token replay detected\";\n\t\tdefault:\n\t\t\treturn \"Invalid refresh token\";\n\t}\n}\n\nfunction normalizeClientScopes(scopes: string[] | undefined): string[] | undefined {\n\tconst normalized = scopes\n\t\t?.map((scope) => scope.trim())\n\t\t.filter(Boolean)\n\t\t.map((scope) => {\n\t\t\tif (scope === \"memory:read_access\") return \"memory:read\";\n\t\t\tif (scope === \"memory:write_access\") return \"memory:write\";\n\t\t\treturn scope;\n\t\t});\n\treturn normalized && normalized.length > 0 ? normalized : undefined;\n}\n","#!/usr/bin/env node\n\n/**\n * @codemem/mcp — MCP Streamable HTTP server bootstrap.\n *\n * Local-first HTTP transport for MCP clients. OAuth metadata and Dynamic Client\n * Registration are exposed for remote MCP setup; public MCP requests require\n * OAuth bearer tokens.\n */\n\nimport { createServer, type IncomingMessage, type Server } from \"node:http\";\nimport { isIP } from \"node:net\";\nimport { pathToFileURL } from \"node:url\";\nimport { MemoryStore, resolveDbPath } from \"@codemem/core\";\nimport { requireBearerAuth } from \"@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js\";\nimport {\n\tgetOAuthProtectedResourceMetadataUrl,\n\tmcpAuthRouter,\n} from \"@modelcontextprotocol/sdk/server/auth/router.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport express, { type NextFunction, type Request, type Response } from \"express\";\nimport {\n\ttype BearerDenyReason,\n\tbuildOAuthAuditEvent,\n\ttype OAuthAuditEmitter,\n\tresolveOAuthAuditEmitterFromEnv,\n\twrapAuditEmitterBestEffort,\n} from \"./audit.js\";\nimport {\n\tcreateInMemoryOAuthAccessTokenStore,\n\tcreateInMemoryOAuthAuthorizationCodeStore,\n\tcreateInMemoryOAuthClientsStore,\n\tcreateMcpOAuthMetadata,\n\tcreateMcpProtectedResourceMetadata,\n\tMCP_OAUTH_PUBLIC_URL_ENV,\n\tMCP_OAUTH_RESOURCE_NAME,\n\tMCP_OAUTH_SCOPES_SUPPORTED,\n\tMCP_OAUTH_SERVICE_DOCUMENTATION_URL,\n\tnormalizeMcpPublicUrl,\n\ttype OAuthAccessTokenStore,\n} from \"./oauth.js\";\nimport {\n\tcompleteOidcAuthorization,\n\tcreateInMemoryOidcPendingAuthorizationStore,\n\tresolveOidcConfig,\n} from \"./oidc.js\";\nimport { MemoryOAuthServerProvider } from \"./provider.js\";\nimport { createCodememMcpServer } from \"./server.js\";\n\nexport const DEFAULT_MCP_HTTP_HOST = \"127.0.0.1\";\nexport const DEFAULT_MCP_HTTP_PORT = 38889;\nconst HTTP_DEFAULT_PORT = 80;\n\nconst VALID_HOSTNAME = /^[a-zA-Z0-9.-]+$/;\nconst LOOPBACK_HOSTS = new Set([\"127.0.0.1\", \"::1\", \"localhost\"]);\n\ninterface ActiveRequest {\n\tmcpServer: McpServer;\n}\n\nexport interface CodememMcpHttpOptions {\n\thost?: string;\n\tport?: number | string;\n\tdbPath?: string;\n\tallowUnsafePublic?: boolean;\n\tpublicUrl?: string;\n\toauthAccessTokenStore?: OAuthAccessTokenStore;\n\tauditEmitter?: OAuthAuditEmitter;\n}\n\nexport interface CodememMcpHttpServer {\n\tserver: Server;\n\tstore: MemoryStore;\n\turl: string;\n\tclose: () => Promise<void>;\n}\n\nexport function validateMcpHttpHost(host: string | undefined, allowUnsafePublic = false): string {\n\tconst value = host?.trim() || DEFAULT_MCP_HTTP_HOST;\n\tif (value.includes(\"://\") || value.includes(\"/\") || value.includes(\"\\\\\")) {\n\t\tthrow new Error(`Invalid MCP HTTP host: ${host}`);\n\t}\n\tif (!(isIP(value) || value === \"localhost\" || VALID_HOSTNAME.test(value))) {\n\t\tthrow new Error(`Invalid MCP HTTP host: ${host}`);\n\t}\n\tif (!allowUnsafePublic && !isLoopbackHost(value)) {\n\t\tthrow new Error(\n\t\t\t`Refusing unsafe MCP HTTP host ${value}; set CODEMEM_MCP_HTTP_UNSAFE_PUBLIC=1 to allow non-loopback binding`,\n\t\t);\n\t}\n\treturn value;\n}\n\nexport function isLoopbackHost(host: string): boolean {\n\treturn LOOPBACK_HOSTS.has(normalizeHost(host));\n}\n\nexport function isUnsafePublicBindAllowed(\n\tvalue = process.env.CODEMEM_MCP_HTTP_UNSAFE_PUBLIC,\n): boolean {\n\treturn value === \"1\" || value?.toLowerCase() === \"true\" || value?.toLowerCase() === \"yes\";\n}\n\nexport function isAllowedMcpHttpRequestHost(\n\theader: string | undefined,\n\texpectedPort: number,\n): boolean {\n\tconst parsed = parseHostHeader(header);\n\tif (!parsed) return false;\n\tif (!isLoopbackHost(parsed.host)) return false;\n\tif (parsed.port === null) {\n\t\t// No `:port` in the Host header means the client is using the protocol\n\t\t// default. This server is plain HTTP, so the implied port is 80. Only\n\t\t// accept the bare-host form when the bound port is also 80.\n\t\treturn expectedPort === HTTP_DEFAULT_PORT;\n\t}\n\treturn parsed.port === expectedPort;\n}\n\nexport function isAllowedMcpHttpRequestOrigin(\n\theader: string | undefined,\n\texpectedPort: number,\n): boolean {\n\tif (!header) return true;\n\ttry {\n\t\tconst url = new URL(header);\n\t\tconst port = url.port ? Number(url.port) : url.protocol === \"https:\" ? 443 : 80;\n\t\treturn (\n\t\t\t(url.protocol === \"http:\" || url.protocol === \"https:\") &&\n\t\t\turl.username === \"\" &&\n\t\t\turl.password === \"\" &&\n\t\t\turl.pathname === \"/\" &&\n\t\t\turl.search === \"\" &&\n\t\t\turl.hash === \"\" &&\n\t\t\tisLoopbackHost(url.hostname) &&\n\t\t\tport === expectedPort\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function isAllowedMcpHttpRequestRemoteAddress(address: string | undefined): boolean {\n\tif (!address) return false;\n\treturn isLoopbackHost(address.replace(/^::ffff:/, \"\"));\n}\n\nfunction normalizeHost(host: string): string {\n\treturn host.replace(/^\\[(.*)]$/, \"$1\").toLowerCase();\n}\n\nfunction parseHostHeader(header: string | undefined): { host: string; port: number | null } | null {\n\tif (!header) return null;\n\tif (header.startsWith(\"[\")) {\n\t\t// Bracketed IPv6: `[::1]` or `[::1]:8080`.\n\t\tconst withPort = /^\\[([^\\]]+)]:(\\d+)$/.exec(header);\n\t\tif (withPort?.[1] && withPort[2]) {\n\t\t\treturn { host: withPort[1], port: Number(withPort[2]) };\n\t\t}\n\t\tconst bareIpv6 = /^\\[([^\\]]+)]$/.exec(header);\n\t\tif (bareIpv6?.[1]) return { host: bareIpv6[1], port: null };\n\t\treturn null;\n\t}\n\tconst lastColon = header.lastIndexOf(\":\");\n\tif (lastColon < 0) {\n\t\t// RFC-compliant clients may omit `:port` on default-port requests. Treat\n\t\t// the implied port as \"use the protocol default\" and let the caller\n\t\t// reconcile against the actually bound port.\n\t\treturn { host: header, port: null };\n\t}\n\tconst host = header.slice(0, lastColon);\n\tconst port = Number(header.slice(lastColon + 1));\n\tif (!Number.isInteger(port)) return null;\n\treturn { host, port };\n}\n\nexport function parseMcpHttpPort(port: number | string | undefined): number {\n\tif (port === undefined || port === \"\") return DEFAULT_MCP_HTTP_PORT;\n\tconst value = typeof port === \"number\" ? port : Number(port);\n\tif (!Number.isInteger(value) || value < 0 || value > 65_535) {\n\t\tthrow new Error(`Invalid MCP HTTP port: ${port}`);\n\t}\n\treturn value;\n}\n\nexport async function startCodememMcpHttpServer(\n\toptions: CodememMcpHttpOptions = {},\n): Promise<CodememMcpHttpServer> {\n\tconst host = validateMcpHttpHost(\n\t\toptions.host ?? process.env.CODEMEM_MCP_HTTP_HOST,\n\t\toptions.allowUnsafePublic ?? isUnsafePublicBindAllowed(),\n\t);\n\tconst port = parseMcpHttpPort(options.port ?? process.env.CODEMEM_MCP_HTTP_PORT);\n\tconst configuredPublicUrl = options.publicUrl ?? process.env[MCP_OAUTH_PUBLIC_URL_ENV];\n\tconst configuredPublicMcpUrl = configuredPublicUrl\n\t\t? normalizeMcpPublicUrl(configuredPublicUrl)\n\t\t: undefined;\n\tconst store = new MemoryStore(options.dbPath ?? resolveDbPath());\n\tconst clientsStore = createInMemoryOAuthClientsStore();\n\tconst codeStore = createInMemoryOAuthAuthorizationCodeStore();\n\tconst tokenStore = options.oauthAccessTokenStore ?? createInMemoryOAuthAccessTokenStore();\n\tconst oidcConfig = resolveOidcConfig();\n\tconst oidcPendingStore = createInMemoryOidcPendingAuthorizationStore();\n\tconst shouldRequireMcpBearer = configuredPublicMcpUrl !== undefined || oidcConfig !== undefined;\n\tconst auditEmit = wrapAuditEmitterBestEffort(\n\t\toptions.auditEmitter ?? resolveOAuthAuditEmitterFromEnv(),\n\t);\n\tconst activeRequests = new Set<ActiveRequest>();\n\tlet closePromise: Promise<void> | null = null;\n\n\tconst app = express();\n\tapp.disable(\"x-powered-by\");\n\t// Tailscale Funnel and similar local ingress proxies forward from loopback and\n\t// add X-Forwarded-For. Trust only loopback proxy hops so SDK rate limiters can\n\t// key on the real client IP without making public clients spoofable.\n\tapp.set(\"trust proxy\", \"loopback\");\n\tconst server = createServer(app);\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen({ host, port }, () => {\n\t\t\tserver.off(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\n\tconst publicMcpUrl = configuredPublicMcpUrl?.href ?? getServerUrl(server, host);\n\tconst publicMcpUrlObject = new URL(publicMcpUrl);\n\tconst issuerUrl = getSdkIssuerUrl(publicMcpUrlObject, getBoundPort(server));\n\tconst provider = new MemoryOAuthServerProvider({\n\t\tclientsStore,\n\t\tcodeStore,\n\t\ttokenStore,\n\t\tpublicMcpUrl,\n\t\t...(oidcConfig ? { oidc: { config: oidcConfig, pendingStore: oidcPendingStore } } : {}),\n\t});\n\tconst resourceMetadataUrl = getOAuthProtectedResourceMetadataUrl(publicMcpUrlObject);\n\n\tapp.use((req, res, next) => {\n\t\tconst boundPort = getBoundPort(server);\n\t\tconst pathname = normalizeRoutePath(req.path);\n\t\tif (pathname === \"/mcp\") {\n\t\t\tif (isAllowedMcpHttpRequest(req, boundPort, configuredPublicMcpUrl)) return next();\n\t\t\tres.status(403).type(\"text/plain\").send(\"Forbidden\");\n\t\t\treturn;\n\t\t}\n\t\tif (isOAuthOrMetadataPath(pathname)) {\n\t\t\tif (isAllowedOAuthHttpRequest(req, boundPort, configuredPublicMcpUrl)) return next();\n\t\t\tres.status(403).type(\"text/plain\").send(\"Forbidden\");\n\t\t\treturn;\n\t\t}\n\t\tnext();\n\t});\n\n\tapp.use(auditOAuthRouteResponses(auditEmit));\n\n\tapp.get(\"/oauth/callback\", async (req, res) => {\n\t\tconst remoteAddress = req.socket.remoteAddress ?? undefined;\n\t\tif (!oidcConfig) {\n\t\t\tauditEmit(\n\t\t\t\tbuildOAuthAuditEvent(\"oidc_callback\", {\n\t\t\t\t\toutcome: \"denied\",\n\t\t\t\t\treason: \"oidc_not_configured\",\n\t\t\t\t\tremoteAddress,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tres.status(400).json({\n\t\t\t\terror: \"temporarily_unavailable\",\n\t\t\t\terror_description: \"OIDC is not configured\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst completed = await completeOidcAuthorization(\n\t\t\tnew URLSearchParams(req.query as Record<string, string>),\n\t\t\toidcPendingStore,\n\t\t\toidcConfig,\n\t\t\tpublicMcpUrl,\n\t\t);\n\t\tif (\"status\" in completed) {\n\t\t\tauditEmit(\n\t\t\t\tbuildOAuthAuditEvent(\"oidc_callback\", {\n\t\t\t\t\toutcome: \"denied\",\n\t\t\t\t\treason: completed.body.error,\n\t\t\t\t\tremoteAddress,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tres.status(completed.status).json(completed.body);\n\t\t\treturn;\n\t\t}\n\t\tconst oauthClientId = completed.oauthParams.get(\"client_id\") ?? undefined;\n\t\tconst code = codeStore.issueCode({\n\t\t\tclientId: completed.oauthParams.get(\"client_id\") ?? \"\",\n\t\t\tredirectUri: completed.oauthParams.get(\"redirect_uri\") ?? \"\",\n\t\t\tcodeChallenge: completed.oauthParams.get(\"code_challenge\") ?? \"\",\n\t\t\tscopes: parseScopeList(completed.oauthParams.get(\"scope\")),\n\t\t\t...(completed.oauthParams.get(\"resource\")\n\t\t\t\t? { resource: completed.oauthParams.get(\"resource\") ?? undefined }\n\t\t\t\t: {}),\n\t\t\texpiresAt: Date.now() + 5 * 60 * 1000,\n\t\t});\n\t\tif (!code) {\n\t\t\tauditEmit(\n\t\t\t\tbuildOAuthAuditEvent(\"oidc_callback\", {\n\t\t\t\t\toutcome: \"denied\",\n\t\t\t\t\treason: \"temporarily_unavailable\",\n\t\t\t\t\tclientId: oauthClientId,\n\t\t\t\t\tremoteAddress,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tres.status(400).json({\n\t\t\t\terror: \"temporarily_unavailable\",\n\t\t\t\terror_description: \"Too many active authorization codes\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tconst redirect = new URL(completed.oauthParams.get(\"redirect_uri\") ?? \"\");\n\t\tredirect.searchParams.set(\"code\", code);\n\t\tconst state = completed.oauthParams.get(\"state\");\n\t\tif (state) redirect.searchParams.set(\"state\", state);\n\t\tauditEmit(\n\t\t\tbuildOAuthAuditEvent(\"oidc_callback\", {\n\t\t\t\toutcome: \"success\",\n\t\t\t\treason: \"code_issued\",\n\t\t\t\tclientId: oauthClientId,\n\t\t\t\tremoteAddress,\n\t\t\t}),\n\t\t);\n\t\tres.redirect(302, redirect.href);\n\t});\n\n\tapp.get(\"/.well-known/oauth-authorization-server\", (_req, res) => {\n\t\tres.status(200).json(createMcpOAuthMetadata({ mcpUrl: publicMcpUrl, clientsStore }));\n\t});\n\tapp.get(\"/.well-known/oauth-protected-resource/mcp\", (_req, res) => {\n\t\tres.status(200).json(createMcpProtectedResourceMetadata(publicMcpUrl));\n\t});\n\n\tapp.use(\n\t\tmcpAuthRouter({\n\t\t\tprovider,\n\t\t\tissuerUrl,\n\t\t\tbaseUrl: issuerUrl,\n\t\t\tresourceServerUrl: publicMcpUrlObject,\n\t\t\tresourceName: MCP_OAUTH_RESOURCE_NAME,\n\t\t\tscopesSupported: MCP_OAUTH_SCOPES_SUPPORTED,\n\t\t\tserviceDocumentationUrl: new URL(MCP_OAUTH_SERVICE_DOCUMENTATION_URL),\n\t\t\tclientRegistrationOptions: { clientSecretExpirySeconds: 0 },\n\t\t}),\n\t);\n\n\tconst bearerMiddleware = shouldRequireMcpBearer\n\t\t? requireBearerAuth({ verifier: provider, resourceMetadataUrl })\n\t\t: (_req: Request, _res: Response, next: NextFunction) => next();\n\tconst bearerAuditMiddleware = shouldRequireMcpBearer\n\t\t? auditBearerPreflight(auditEmit, tokenStore)\n\t\t: (_req: Request, _res: Response, next: NextFunction) => next();\n\tapp.post(\"/mcp\", bearerAuditMiddleware, bearerMiddleware, async (req, res) => {\n\t\tif (req.auth) {\n\t\t\tauditEmit(\n\t\t\t\tbuildOAuthAuditEvent(\"bearer\", {\n\t\t\t\t\toutcome: \"success\",\n\t\t\t\t\tclientId: req.auth.clientId,\n\t\t\t\t\tremoteAddress: req.socket.remoteAddress ?? undefined,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tconst mcpServer = createCodememMcpServer(store);\n\t\tconst transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });\n\t\tconst activeRequest = { mcpServer };\n\t\tactiveRequests.add(activeRequest);\n\t\ttry {\n\t\t\tawait mcpServer.connect(transport);\n\t\t\tawait transport.handleRequest(req, res);\n\t\t} finally {\n\t\t\tactiveRequests.delete(activeRequest);\n\t\t\tawait mcpServer.close();\n\t\t}\n\t});\n\tapp.all(\"/mcp\", (_req, res) => {\n\t\tres.setHeader(\"Allow\", \"POST\");\n\t\tres.status(405).type(\"text/plain\").send(\"Method not allowed\");\n\t});\n\tapp.use((_req, res) => {\n\t\tres.status(404).type(\"text/plain\").send(\"Not found\");\n\t});\n\n\tconst close = () => {\n\t\tclosePromise ??= (async () => {\n\t\t\tawait Promise.allSettled([...activeRequests].map(({ mcpServer }) => mcpServer.close()));\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tserver.close((err) => {\n\t\t\t\t\tif (!err || (err as NodeJS.ErrnoException).code === \"ERR_SERVER_NOT_RUNNING\") {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t\t\tserver.closeAllConnections();\n\t\t\t});\n\t\t\tstore.close();\n\t\t})();\n\t\treturn closePromise;\n\t};\n\n\treturn {\n\t\tserver,\n\t\tstore,\n\t\turl: `http://${formatHostForUrl(host)}:${getBoundPort(server)}/mcp`,\n\t\tclose,\n\t};\n}\n\nfunction isAllowedMcpHttpRequest(\n\treq: IncomingMessage,\n\texpectedPort: number,\n\tpublicMcpUrl: URL | undefined,\n): boolean {\n\tif (isAllowedLocalMcpHttpRequest(req, expectedPort)) return true;\n\tif (!publicMcpUrl) return false;\n\treturn (\n\t\tisAllowedPublicRequestHost(req.headers.host, publicMcpUrl) &&\n\t\tisAllowedPublicOrigin(req.headers.origin, publicMcpUrl)\n\t);\n}\n\nfunction isAllowedLocalMcpHttpRequest(req: IncomingMessage, expectedPort: number): boolean {\n\treturn (\n\t\tisAllowedMcpHttpRequestRemoteAddress(req.socket.remoteAddress) &&\n\t\tisAllowedMcpHttpRequestHost(req.headers.host, expectedPort) &&\n\t\tisAllowedMcpHttpRequestOrigin(req.headers.origin, expectedPort)\n\t);\n}\n\nfunction isAllowedOAuthHttpRequest(\n\treq: IncomingMessage,\n\texpectedPort: number,\n\tpublicMcpUrl: URL | undefined,\n): boolean {\n\tif (isAllowedLocalMcpHttpRequest(req, expectedPort)) return true;\n\tif (!publicMcpUrl) return false;\n\treturn (\n\t\tisAllowedPublicRequestHost(req.headers.host, publicMcpUrl) &&\n\t\tisAllowedPublicOrigin(req.headers.origin, publicMcpUrl)\n\t);\n}\n\nfunction isAllowedPublicOrigin(header: string | undefined, publicMcpUrl: URL): boolean {\n\tif (!header) return true;\n\ttry {\n\t\tconst origin = new URL(header);\n\t\treturn origin.origin === publicMcpUrl.origin;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isAllowedPublicRequestHost(header: string | undefined, publicMcpUrl: URL): boolean {\n\tconst parsed = parseHostHeader(header);\n\tif (!parsed) return false;\n\tconst expectedPort = publicMcpUrl.port\n\t\t? Number(publicMcpUrl.port)\n\t\t: publicMcpUrl.protocol === \"https:\"\n\t\t\t? 443\n\t\t\t: 80;\n\tconst actualPort = parsed.port ?? expectedPort;\n\treturn (\n\t\tnormalizeHost(parsed.host) === normalizeHost(publicMcpUrl.hostname) &&\n\t\tactualPort === expectedPort\n\t);\n}\n\nfunction getBoundPort(server: Server): number {\n\tconst address = server.address();\n\tif (!address || typeof address === \"string\") return DEFAULT_MCP_HTTP_PORT;\n\treturn address.port;\n}\n\nfunction formatHostForUrl(host: string): string {\n\treturn isIP(host) === 6 ? `[${host}]` : host;\n}\n\nfunction getServerUrl(server: Server, host: string): string {\n\treturn `http://${formatHostForUrl(host)}:${getBoundPort(server)}/mcp`;\n}\n\nfunction getSdkIssuerUrl(publicMcpUrl: URL, port: number): URL {\n\tif (publicMcpUrl.protocol === \"https:\") return new URL(publicMcpUrl.origin);\n\tif (isLoopbackHost(publicMcpUrl.hostname) && normalizeHost(publicMcpUrl.hostname) !== \"::1\") {\n\t\treturn new URL(publicMcpUrl.origin);\n\t}\n\treturn new URL(`http://localhost:${port}/`);\n}\n\nfunction isOAuthOrMetadataPath(pathname: string): boolean {\n\treturn (\n\t\tpathname === \"/register\" ||\n\t\tpathname === \"/authorize\" ||\n\t\tpathname === \"/token\" ||\n\t\tpathname === \"/revoke\" ||\n\t\tpathname === \"/oauth/callback\" ||\n\t\tpathname === \"/.well-known/oauth-authorization-server\" ||\n\t\tpathname === \"/.well-known/oauth-protected-resource/mcp\"\n\t);\n}\n\nfunction normalizeRoutePath(pathname: string): string {\n\treturn pathname.length > 1 ? pathname.replace(/\\/+$/, \"\") : pathname;\n}\n\nfunction auditOAuthRouteResponses(\n\tauditEmit: (event: ReturnType<typeof buildOAuthAuditEvent>) => void,\n) {\n\treturn (req: Request, res: Response, next: NextFunction) => {\n\t\tconst pathname = req.path;\n\t\tif (!isAuditedOAuthPath(pathname)) return next();\n\t\tlet responseBody: unknown;\n\t\tconst originalJson = res.json.bind(res);\n\t\tres.json = ((body: unknown) => {\n\t\t\tresponseBody = body;\n\t\t\treturn originalJson(body);\n\t\t}) as typeof res.json;\n\t\tres.on(\"finish\", () => {\n\t\t\tif (pathname === \"/oauth/callback\") return;\n\t\t\tconst kind = oauthAuditKindForRequest(pathname, req);\n\t\t\tif (!kind) return;\n\t\t\tconst error = getOAuthResponseError(res, responseBody);\n\t\t\tconst clientId = getRequestClientId(req, responseBody);\n\t\t\tauditEmit(\n\t\t\t\tbuildOAuthAuditEvent(kind, {\n\t\t\t\t\toutcome: error ? \"denied\" : \"success\",\n\t\t\t\t\t...(error ? { reason: error } : {}),\n\t\t\t\t\t...(clientId ? { clientId } : {}),\n\t\t\t\t\tremoteAddress: req.socket.remoteAddress ?? undefined,\n\t\t\t\t}),\n\t\t\t);\n\t\t});\n\t\tnext();\n\t};\n}\n\nfunction isAuditedOAuthPath(pathname: string): boolean {\n\treturn oauthAuditKindForPath(pathname) !== null;\n}\n\nfunction oauthAuditKindForRequest(\n\tpathname: string,\n\treq: Request,\n): \"registration\" | \"authorize\" | \"token\" | \"refresh\" | \"revocation\" | null {\n\tif (pathname === \"/token\" && req.body?.grant_type === \"refresh_token\") return \"refresh\";\n\treturn oauthAuditKindForPath(pathname);\n}\n\nfunction oauthAuditKindForPath(\n\tpathname: string,\n): \"registration\" | \"authorize\" | \"token\" | \"revocation\" | null {\n\tif (pathname === \"/register\") return \"registration\";\n\tif (pathname === \"/authorize\") return \"authorize\";\n\tif (pathname === \"/token\") return \"token\";\n\tif (pathname === \"/revoke\") return \"revocation\";\n\treturn null;\n}\n\nfunction getOAuthResponseError(res: Response, body: unknown): string | undefined {\n\tconst bodyError = getOAuthBodyError(body);\n\tif (bodyError) return bodyError;\n\tconst location = res.getHeader(\"location\");\n\tif (typeof location !== \"string\") return undefined;\n\ttry {\n\t\tconst redirect = new URL(location);\n\t\treturn redirect.searchParams.get(\"error\") ?? undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction getOAuthBodyError(body: unknown): string | undefined {\n\tif (\n\t\ttypeof body === \"object\" &&\n\t\tbody !== null &&\n\t\t\"error\" in body &&\n\t\ttypeof body.error === \"string\"\n\t) {\n\t\treturn body.error;\n\t}\n\treturn undefined;\n}\n\nfunction getRequestClientId(req: Request, body: unknown): string | undefined {\n\tif (\n\t\ttypeof body === \"object\" &&\n\t\tbody !== null &&\n\t\t\"client_id\" in body &&\n\t\ttypeof body.client_id === \"string\"\n\t) {\n\t\treturn body.client_id;\n\t}\n\tconst bodyClientId = typeof req.body?.client_id === \"string\" ? req.body.client_id : undefined;\n\tif (bodyClientId) return bodyClientId;\n\tconst queryClientId = typeof req.query.client_id === \"string\" ? req.query.client_id : undefined;\n\treturn queryClientId;\n}\n\nfunction auditBearerPreflight(\n\tauditEmit: (event: ReturnType<typeof buildOAuthAuditEvent>) => void,\n\ttokenStore: OAuthAccessTokenStore,\n) {\n\treturn (req: Request, _res: Response, next: NextFunction) => {\n\t\tconst reason = getBearerPreflightDenyReason(req.headers.authorization, tokenStore);\n\t\tif (reason) {\n\t\t\tauditEmit(\n\t\t\t\tbuildOAuthAuditEvent(\"bearer\", {\n\t\t\t\t\toutcome: \"denied\",\n\t\t\t\t\treason,\n\t\t\t\t\tremoteAddress: req.socket.remoteAddress ?? undefined,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tnext();\n\t};\n}\n\nfunction getBearerPreflightDenyReason(\n\theader: string | undefined,\n\ttokenStore: OAuthAccessTokenStore,\n): BearerDenyReason | null {\n\tif (!header) return \"missing_authorization_header\";\n\tconst [scheme, token, extra] = header.trim().split(/\\s+/);\n\tif (!scheme || !token || extra || scheme.toLowerCase() !== \"bearer\") {\n\t\treturn \"malformed_authorization_header\";\n\t}\n\tconst verification = tokenStore.verifyToken(token);\n\tif (!verification.ok) return verification.reason;\n\treturn null;\n}\n\nfunction parseScopeList(scope: string | null | undefined): string[] {\n\treturn (\n\t\tscope\n\t\t\t?.split(/\\s+/)\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean) ?? []\n\t);\n}\n\nfunction isEntrypoint(): boolean {\n\tconst script = process.argv[1];\n\treturn script ? import.meta.url === pathToFileURL(script).href : false;\n}\n\nasync function main() {\n\tconst httpServer = await startCodememMcpHttpServer();\n\tconsole.error(`codemem MCP HTTP server listening at ${httpServer.url}`);\n\n\tconst shutdown = async () => {\n\t\ttry {\n\t\t\tawait httpServer.close();\n\t\t} catch {\n\t\t\t// Best effort — process is exiting.\n\t\t}\n\t\tprocess.exit(0);\n\t};\n\n\tprocess.on(\"SIGINT\", shutdown);\n\tprocess.on(\"SIGTERM\", shutdown);\n}\n\nif (isEntrypoint()) {\n\tmain().catch((err) => {\n\t\tconsole.error(\"codemem MCP HTTP server failed to start:\", err);\n\t\tprocess.exit(1);\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;AA6CA,IAAM,IAAuB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,CAAC;AAEF,SAAS,GAAqB,GAAqB;AAClD,QAAO,EAAI,aAAa,CAAC,QAAQ,cAAc,GAAG;;AAOnD,SAAgB,EACf,GACA,GACkB;AAClB,MAAK,IAAM,KAAO,OAAO,KAAK,EAAO,CACpC,KAAI,EAAqB,IAAI,GAAqB,EAAI,CAAC,CACtD,OAAU,MAAM,qDAAqD,IAAM;CAG7E,IAAM,EAAE,QAAK,GAAG,MAAS;AACzB,QAAO;EACN;EACA,WAAW,IAAI,KAAK,KAAO,KAAK,KAAK,CAAC,CAAC,aAAa;EACpD,GAAG;EACH;;AASF,SAAgB,GAA2B,GAA4C;CACtF,IAAI,IAAkB;AACtB,SAAQ,MAAU;AACjB,MAAI;AACH,KAAK,EAAM;WACH,GAAK;AACb,GAAK,MACJ,IAAkB,IAClB,QAAQ,MAAM,yEAAyE,EAAI;;;;AAW/F,SAAgB,GACf,IAA6B,QAAQ,QACjB;AACpB,SAAQ,MAAU;AACjB,IAAO,MAAM,GAAG,KAAK,UAAU;GAAE,QAAQ;GAA2B,GAAG;GAAO,CAAC,CAAC,IAAI;;;AAQtF,SAAgB,KAAmD;AAClE,cAAa;;AAQd,SAAgB,GACf,IAAQ,QAAQ,IAAI,mBACA;CACpB,IAAM,IAAa,GAAO,MAAM,CAAC,aAAa;AAI9C,QAHI,MAAe,OAAO,MAAe,WAAW,MAAe,OAC3D,IAA+B,GAEhC,IAAgC;;ACvHxC,IAAa,KAA0B,eAC1B,IAA6B,CAAC,eAAe,eAAe,EAC5D,IAAsC,+CAE7C,KAAyB,2CACzB,IAAuB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAM,CAAC,EACjE,KAAwB,IAAI,IAAI,CAAC,sBAAsB,gBAAgB,CAAC,EACxE,KAA2B,IAAI,IAAI,CAAC,OAAO,CAAC,EAE5C,IAA2B,MAC3B,IAA4B,MAAU,KAAK,IAC3C,KAA0B,KAC1B,KAAoB,KACpB,KAAqB,KACrB,IAAqB,IACrB,KAAgC,IAChC,KAAyB,uBACzB,KAAsB,uBAkIf,KAAb,MAA8E;CAC7E,qBAAoB,IAAI,KAAyC;CAEjE,UAAU,GAA0D;AACnE,SAAO,MAAA,EAAc,IAAI,EAAS;;CAGnC,eACC,GAC6B;EAC7B,IAAM,IAAgB,GAAqB,EAAO,cAAc;AAChE,MAAI,EAAe,OAAM,IAAI,EAA2B,EAAc;AACtE,MAAI,EAAO,iBAAiB,EAAO,+BAA+B,OACjE,OAAM,IAAI,EACT,yEACA;AAEF,MAAI,EAAO,eAAe,CAAC,GAAgB,EAAO,aAAa,GAAsB,CACpF,OAAM,IAAI,EACT,sEACA;AAEF,MACC,EAAO,kBACP,CAAC,GAAgB,EAAO,gBAAgB,GAAyB,CAEjE,OAAM,IAAI,EAA2B,uCAAuC;AAE7E,MAAI,MAAA,EAAc,QAAQ,KAAK;GAC9B,IAAM,IAAiB,MAAA,EAAc,MAAM,CAAC,MAAM,CAAC;AACnD,GAAI,KAAgB,MAAA,EAAc,OAAO,EAAe;;EAEzD,IAAM,IAAa;GAClB,GAAG;GACH,4BAA4B;GAC5B,aAAa,EAAO,eAAe,CAAC,sBAAsB,gBAAgB;GAC1E,gBAAgB,EAAO,kBAAkB,CAAC,OAAO;GACjD,WAAW,GAAY;GACvB,qBAAqB,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;GAClD;AAED,SADA,MAAA,EAAc,IAAI,EAAW,WAAW,EAAW,EAC5C;;GAII,KAAb,MAAwF;CACvF,qBAAkB,IAAI,KAAsC;CAE5D,UAAU,GAA+C,IAAM,KAAK,KAAK,EAAsB;AAE9F,MADA,MAAA,EAAyB,EAAI,EACzB,MAAA,EAAY,QAAQ,GAAyB;EACjD,IAAM,IAAO,GAAY;AAEzB,SADA,MAAA,EAAY,IAAI,GAAM;GAAE,GAAG;GAAQ,MAAM;GAAO,CAAC,EAC1C;;CAGR,SAAS,GAAmD;EAC3D,IAAM,IAAS,MAAA,EAAY,IAAI,EAAK;AAChC,SAAC,KAAU,EAAO,MACtB,QAAO;;CAGR,YAAY,GAAmD;EAC9D,IAAM,IAAS,MAAA,EAAY,IAAI,EAAK;AAChC,SAAC,KAAU,EAAO,MAEtB,QADA,EAAO,OAAO,IACP;;CAGR,GAAoB,GAAmB;AACtC,OAAK,IAAM,CAAC,GAAM,MAAW,MAAA,EAC5B,CAAI,EAAO,aAAa,KAAK,MAAA,EAAY,OAAO,EAAK;;GAK3C,KAAb,MAA4E;CAC3E,qBAAyB,IAAI,KAAgC;CAC7D,KAAyB,EAAY,GAAG;CAExC,WACC,GACA,IAAM,KAAK,KAAK,EAChB,GACA,GACA,IAAmB,EAAE,EAC8B;AAEnD,MADA,MAAA,EAA2B,EAAI,EAC3B,MAAA,EAAmB,QAAQ,GAAmB;EAClD,IAAM,IAAa,EAAY,EAAmB,EAC5C,IAAY,EAA0B,GAAY,MAAA,EAAmB;AAY3E,SAXA,MAAA,EAAmB,IAAI,GAAW;GACjC;GACA;GACA,QAAQ,CAAC,GAAG,EAAO;GACnB;GACA;GACA,UAAU;GACV,WAAW,IAAM,IAA2B;GAC5C,YAAY;GACZ,WAAW;GACX,CAAC,EACK;GACN,OAAO,EAAW,SAAS,YAAY;GACvC,WAAW;GACX;;CAGF,YAAY,GAAe,IAAM,KAAK,KAAK,EAAiC;EAC3E,IAAM,IAAa,EAAuB,EAAM;AAChD,MAAI,CAAC,EAAY,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAiB;EAC9D,IAAM,IAAY,EAA0B,GAAY,MAAA,EAAmB,EACrE,IAAS,MAAA,EAAmB,IAAI,EAAU;AAOhD,SANI,CAAC,KAAU,CAAC,EAAgB,EAAO,WAAW,EAAU,GACpD;GAAE,IAAI;GAAO,QAAQ;GAAiB,GAE1C,EAAO,cAAc,OACrB,EAAO,aAAa,IAAY;GAAE,IAAI;GAAO,QAAQ;GAAiB,IAC1E,EAAO,aAAa,GACb;GAAE,IAAI;GAAM,QAAQ,EAAE,GAAG,GAAQ;GAAE,IAHJ;GAAE,IAAI;GAAO,QAAQ;GAAiB;;CAM7E,YAAY,GAAe,IAAM,KAAK,KAAK,EAAW;EACrD,IAAM,IAAa,EAAuB,EAAM;AAChD,MAAI,CAAC,EAAY,QAAO;EACxB,IAAM,IAAY,EAA0B,GAAY,MAAA,EAAmB,EACrE,IAAS,MAAA,EAAmB,IAAI,EAAU;AAIhD,SAHI,CAAC,KAAU,CAAC,EAAgB,EAAO,WAAW,EAAU,GAAS,MACjE,EAAO,cAAc,SACzB,EAAO,YAAY,IADmB;;CAKvC,qBAAqB,GAAiB,IAAM,KAAK,KAAK,EAAU;EAC/D,IAAI,IAAU;AACd,OAAK,IAAM,KAAU,MAAA,EAAmB,QAAQ,CAC3C,GAAO,YAAY,KAAW,EAAO,cAAc,SACvD,EAAO,YAAY,GACnB,KAAW;AAEZ,SAAO;;CAGR,GAAsB,GAAmB;AACxC,OAAK,IAAM,CAAC,GAAW,MAAW,MAAA,EACjC,EAAI,EAAO,aAAa,KAAO,EAAO,cAAc,SACnD,MAAA,EAAmB,OAAO,EAAU;;GAK3B,KAAb,MAA8E;CAC7E,qBAAuB,IAAI,KAAsC;CACjE,qBAAkC,IAAI,KAAqB;CAC3D,qBAAmC,IAAI,KAA0B;CACjE,KAAyB,EAAY,GAAG;CAExC,WACC,GACA,IAAM,KAAK,KAAK,EAC0E;AAE1F,MADA,MAAA,EAA2B,EAAI,EAC3B,MAAA,EAAiB,QAAQ,GAAoB;EACjD,IAAM,IAAe,IAAa,EAC5B,IAAmB,EAAyB,GAAc,MAAA,EAAmB;AACnF,MAAI,CAAC,EAAkB;EACvB,IAAM,IAAiC;GACtC,SAAS,GAAY;GACrB,UAAU,EAAO;GACjB,QAAQ,CAAC,GAAI,EAAO,UAAU,EAAE,CAAE;GAClC,UAAU,EAAO;GACjB,yBAAyB;GACzB,0BAA0B;GAC1B,UAAU;GACV,WAAW,IAAM,IAA4B;GAC7C,WAAW;GACX,WAAW;GACX;AAID,SAHA,MAAA,EAAiB,IAAI,EAAM,SAAS,EAAM,EAC1C,MAAA,EAA4B,IAAI,GAAkB,EAAM,QAAQ,EAChE,MAAA,EAA6B,IAAI,EAAM,SAAS,IAAI,IAAI,CAAC,EAAiB,CAAC,CAAC,EACrE;GACN,OAAO;IAAE,GAAG;IAAO,QAAQ,CAAC,GAAG,EAAM,OAAO;IAAE;GAC9C;GACA,WAAW;GACX;;CAGF,mBACC,GACA,GACA,IAAoD,EAAE,EACtD,IAAM,KAAK,KAAK,EACa;EAC7B,IAAM,IAAY,EAAyB,GAAc,MAAA,EAAmB;AAC5E,MAAI,CAAC,EAAW,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAyB;EACrE,IAAM,IAAU,MAAA,EAA4B,IAAI,EAAU;AAC1D,MAAI,CAAC,EAAS,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAyB;EACnE,IAAM,IAAQ,MAAA,EAAiB,IAAI,EAAQ;AAC3C,MAAI,CAAC,EAAO,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAyB;AACjE,MAAI,EAAM,cAAc,KAAM,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAyB;GAAS;AAC5F,MAAI,EAAM,aAAa,EAEtB,QADA,KAAK,YAAY,GAAS,EAAI,EACvB;GAAE,IAAI;GAAO,QAAQ;GAAyB;GAAS;AAE/D,MAAI,EAAM,aAAa,EAAU,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAmB;GAAS;AACzF,MAAI,CAAC,GAAc,EAAQ,UAAU,EAAM,QAAQ,EAAM,OAAO,CAC/D,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAkB;GAAS;AAExD,OAAK,EAAM,YAAY,WAAW,EAAQ,YAAY,EAAM,YAAY,MACvE,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAqB;GAAS;EAG3D,IAAM,IAAiB,EAAgB,EAAM,yBAAyB,EAAU,EAC1E,IACL,EAAM,6BAA6B,QACnC,EAAgB,EAAM,0BAA0B,EAAU;AAC3D,MAAI,CAAC,KAAkB,CAAC,EAEvB,QADA,KAAK,YAAY,GAAS,EAAI,EACvB;GAAE,IAAI;GAAO,QAAQ;GAAwB;GAAS;EAG9D,IAAM,IAAmB,IAAa,EAChC,IAAuB,EAAyB,GAAkB,MAAA,EAAmB;AAQ3F,SAPK,KACL,EAAM,SAAS,CAAC,GAAI,EAAQ,UAAU,EAAM,OAAQ,EACpD,EAAM,2BAA2B,IAAiB,EAAM,0BAA0B,MAClF,EAAM,0BAA0B,GAChC,EAAM,YAAY,GAClB,MAAA,EAA4B,IAAI,GAAsB,EAAQ,EAC9D,MAAA,EAA6B,IAAI,EAAQ,EAAE,IAAI,EAAqB,EAC7D;GACN,IAAI;GACJ,OAAO;IAAE,GAAG;IAAO,QAAQ,CAAC,GAAG,EAAM,OAAO;IAAE;GAC9C,cAAc;GACd,WAAW,KAAK,IAAI,GAAG,KAAK,OAAO,EAAM,YAAY,KAAO,IAAK,CAAC;GAClE,IAZiC;GAAE,IAAI;GAAO,QAAQ;GAAyB;;CAejF,mBAAmB,GAAe,IAAM,KAAK,KAAK,EAAsB;EACvE,IAAM,IAAY,EAAyB,GAAO,MAAA,EAAmB;AACrE,MAAI,CAAC,EAAW;EAChB,IAAM,IAAU,MAAA,EAA4B,IAAI,EAAU;AACrD,QAEL,QADA,KAAK,YAAY,GAAS,EAAI,EACvB;;CAGR,YAAY,GAAiB,IAAM,KAAK,KAAK,EAAW;EACvD,IAAM,IAAQ,MAAA,EAAiB,IAAI,EAAQ;AAK3C,SAJK,IACD,EAAM,cAAc,QACxB,EAAM,YAAY,GAClB,MAAA,EAAyC,EAAM,EACxC,MAH8B,KADlB;;CAOpB,GAAsB,GAAmB;AACxC,OAAK,IAAM,CAAC,GAAS,MAAU,MAAA,EAC1B,GAAM,YAAY,KAAO,EAAM,cAAc,SACjD,MAAA,EAAiB,OAAO,EAAQ,EAChC,MAAA,EAAyC,EAAM;;CAIjD,GAAoC,GAAsC;EACzE,IAAM,IAAgB,MAAA,EAA6B,IAAI,EAAM,QAAQ;AACrE,MAAI,GAAe;AAClB,QAAK,IAAM,KAAe,EAAe,OAAA,EAA4B,OAAO,EAAY;AACxF,SAAA,EAA6B,OAAO,EAAM,QAAQ;AAClD;;AAGD,EADA,MAAA,EAA4B,OAAO,EAAM,wBAAwB,EAC7D,EAAM,4BACT,MAAA,EAA4B,OAAO,EAAM,yBAAyB;;;AAIrE,SAAgB,KAA+D;AAC9E,QAAO,IAAI,IAA2B;;AAGvC,SAAgB,KAAyE;AACxF,QAAO,IAAI,IAAqC;;AAGjD,SAAgB,KAA6D;AAC5E,QAAO,IAAI,IAA+B;;AAG3C,SAAgB,KAA+D;AAC9E,QAAO,IAAI,IAAgC;;AAG5C,SAAgB,GAAuB,GAAiD;CAEvF,IAAM,IAAY,GADH,EAAsB,EAAQ,OACd,CAAO;AAEtC,QAAO;EACN,GAAG,EAAoB;GACtB,UAHe,GAA2B,EAAQ,aAGlD;GACA;GACA,SAAS;GACT,CAAC;EAGF,uBAAuB,CAAC,sBAAsB,gBAAgB;EAC9D,uCAAuC,CAAC,OAAO;EAC/C,kBAAkB;EAClB,uBAAuB;EACvB,qBAAqB,IAAI,IAAI,WAAW,EAAU,CAAC;EACnD,4CAA4C,CAAC,OAAO;EACpD,uCAAuC;EACvC;;AAGF,SAAgB,GAAmC,GAAgD;CAClG,IAAM,IAAmB,EAAsB,EAAO,EAChD,IAAW;EAChB,UAAU,EAAiB;EAC3B,uBAAuB,CAAC,GAAa,EAAiB,CAAC,KAAK;EAC5D,kBAAkB;EAClB,0BAA0B,CAAC,SAAS;EACpC,eAAe;EACf,wBAAwB;EACxB;AACD,QAAO,EAAqC,MAAM,EAAS;;AAsF5D,SAAgB,GACf,GACA,GAC6D;CAC7D,IAAM,IAAW,EAAO,IAAI,YAAY,IAAI,IACtC,IAAc,EAAO,IAAI,eAAe,IAAI,IAC5C,IAAe,EAAO,IAAI,gBAAgB,IAAI,IAC9C,IAAgB,EAAO,IAAI,iBAAiB,IAAI,IAChD,IAAsB,EAAO,IAAI,wBAAwB,IAAI,IAC7D,IAAW,EAAO,IAAI,WAAW,EACjC,IAAS,GAAe,EAAO,IAAI,QAAQ,CAAC,EAC5C,IAAQ,EAAO,IAAI,QAAQ;AAEjC,KAAI,MAAiB,OAAQ,QAAO,EAAoB,4BAA4B;AACpF,KAAI,MAAwB,OAC3B,QAAO,EAAoB,mBAAmB,8CAA8C;AAE7F,KAAI,CAAC,GAAoB,KAAK,EAAc,CAC3C,QAAO,EAAoB,mBAAmB,6CAA6C;CAG5F,IAAM,IAAS,GAAoB,GAAc,EAAS;AAM1D,QALK,IACA,EAAO,cAAc,SAAS,EAAY,GAIxC;EAAE;EAAU;EAAa;EAAe;EAAQ;EAAU;EAAO,GAHhE,EAAoB,mBAAmB,iDAAiD,GAF5E,EAAoB,kBAAkB,0BAA0B;;AAsFrF,SAAgB,EAAsB,GAAoB;CACzD,IAAI;AACJ,KAAI;AACH,MAAM,IAAI,IAAI,EAAM;SACb;AACP,QAAU,MAAM,0DAA0D;;AAE3E,KAAI,EAAI,aAAa,YAAY,CAAC,GAAc,EAAI,CACnD,OAAU,MAAM,+DAA+D;AAEhF,KAAI,EAAI,YAAY,EAAI,YAAY,EAAI,UAAU,EAAI,KACrD,OAAU,MACT,yFACA;AAGF,KADI,EAAI,aAAa,QAAK,EAAI,WAAW,SACrC,EAAI,aAAa,OACpB,OAAU,MAAM,kDAAkD;AAEnE,QAAO;;AAGR,SAAS,GAAa,GAAe;AACpC,QAAO,IAAI,IAAI,EAAI,OAAO;;AAG3B,SAAS,GAAqB,GAAmE;AAChG,MAAK,IAAM,KAAe,GAAc;EACvC,IAAM,IAAM,IAAI,IAAI,EAAY;AAChC,MAAI,EAAI,YAAY,EAAI,YAAY,EAAI,KAAM,QAAO,yBAAyB;AAC1E,QAAI,SAAS,MACb,IAAsB,EAAI,CAC9B,QAAO,6BAA6B;;AAErC,QAAO;;AAOR,SAAS,GAAgB,GAAkB,GAAiC;AAC3E,QAAO,EAAO,SAAS,KAAK,EAAO,OAAO,MAAU,EAAU,IAAI,EAAM,CAAC;;AAG1E,SAAS,GAAsB,GAAmB;AACjD,QACC,EAAI,aAAa,WACjB,EAAqB,IAAI,GAAkB,EAAI,SAAS,CAAC,IACzD,EAAI,aAAa,eACjB,EAAI,WAAW;;AAIjB,SAAS,GAAc,GAAmB;CACzC,IAAM,IAAW,GAAkB,EAAI,SAAS;AAChD,QAAO,EAAI,aAAa,WAAW,EAAqB,IAAI,EAAS;;AAGtE,SAAS,GAAkB,GAA0B;AACpD,QAAO,EAAS,QAAQ,aAAa,KAAK,CAAC,aAAa;;AAGzD,SAAS,GAAe,GAA4C;AACnE,QACC,GACG,MAAM,MAAM,CACb,KAAK,MAAS,EAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,IAAI,EAAE;;AAWzB,SAAS,EAAoB,GAAe,GAA2C;AACtF,QAAO;EACN,QAAQ;EACR,MAAM,IAAc;GAAE;GAAO,mBAAmB;GAAa,GAAG,EAAE,UAAO;EACzE;;AAGF,SAAS,GACR,GACA,GACyC;CACzC,IAAM,IAAS,EAAa,UAAU,EAAS;AAC/C,KAAI,aAAkB,QACrB,OAAU,MACT,gFACA;AAEF,QAAO;;AAWR,SAAS,EAAuB,GAAmC;AAGlE,KAFI,OAAO,KAAe,YACtB,EAAW,WAAW,MACtB,CAAC,GAAuB,KAAK,EAAW,CAAE,QAAO;CACrD,IAAM,IAAQ,OAAO,KAAK,GAAY,YAAY;AAElD,QADI,EAAM,WAAW,IACd,IADyC;;AAIjD,SAAS,KAAsB;AAC9B,QAAO,EAAY,EAAmB,CAAC,SAAS,YAAY;;AAG7D,SAAS,EAAyB,GAAoB,GAA4B;CACjF,IAAM,IAAa,EAAuB,EAAW;AAErD,QADK,IACE,EAAe,GAAY,EAAI,GADd;;AAQzB,SAAS,EAA0B,GAAkB,GAAqB;AACzE,QAAO,EAAe,GAAU,EAAI;;AAGrC,SAAS,EAAe,GAA2B,GAAqB;AACvE,QAAO,EAAW,UAAU,EAAI,CAAC,OAAO,EAAS,CAAC,OAAO,YAAY;;AAGtE,SAAS,EAAgB,GAAc,GAAwB;CAC9D,IAAM,IAAa,OAAO,KAAK,EAAK,EAC9B,IAAc,OAAO,KAAK,EAAM;AACtC,QAAO,EAAW,WAAW,EAAY,UAAU,EAAgB,GAAY,EAAY;;AAG5F,SAAS,GAAc,GAAqB,GAA4B;CACvE,IAAM,IAAgB,IAAI,IAAI,EAAQ;AACtC,QAAO,EAAU,OAAO,MAAU,EAAc,IAAI,EAAM,CAAC;;AAG5D,SAAS,GACR,GACsB;AACtB,QAAO;EACN;EACA,MAAM,YAAY;AACjB,SAAU,MAAM,yCAAyC;;EAE1D,MAAM,gCAAgC;AACrC,SAAU,MAAM,8CAA8C;;EAE/D,MAAM,4BAA4B;AACjC,SAAU,MAAM,8CAA8C;;EAE/D,MAAM,uBAAuB;AAC5B,SAAU,MAAM,uCAAuC;;EAExD,MAAM,oBAAoB;AACzB,SAAU,MAAM,mDAAmD;;EAEpE;;;;AC90BF,IAAa,KAA0B,+BAC1B,KAAyB,8BACzB,KAA6B,kCAC7B,KAAgC,qCAChC,KAA8B,mCAErC,KAAsB,MAAS,KAC/B,KAAmB,KACnB,KAAyB,MAAS,KAyD3B,KAAb,MAA4F;CAC3F,qBAAoB,IAAI,KAAuC;CAE/D,MAAM,GAAqD,IAAM,KAAK,KAAK,EAAsB;AAEhG,MADA,MAAA,EAAoB,EAAI,EACpB,MAAA,EAAc,QAAQ,GAAkB;EAC5C,IAAM,IAAQ,GAAY;AAE1B,SADA,MAAA,EAAc,IAAI,GAAO;GAAE,GAAG;GAAQ,WAAW,IAAM;GAAqB,CAAC,EACtE;;CAGR,QAAQ,GAAqD;EAC5D,IAAM,IAAS,MAAA,EAAc,IAAI,EAAM;AAEvC,SADA,MAAA,EAAc,OAAO,EAAM,EACpB;;CAGR,GAAe,GAAmB;AACjC,OAAK,IAAM,CAAC,GAAO,MAAW,MAAA,EAC7B,CAAI,EAAO,aAAa,KAAK,MAAA,EAAc,OAAO,EAAM;;;AAK3D,SAAgB,KAA6E;AAC5F,QAAO,IAAI,IAAuC;;AAGnD,SAAgB,GAAkB,IAAyB,QAAQ,KAA6B;CAC/F,IAAM,IAAY,EAAI,KAA0B,MAAM,EAChD,IAAW,EAAI,KAAyB,MAAM,EAC9C,IAAe,EAAI,KAA6B,MAAM,EACtD,IAAiB,EAAI,KAAgC,MAAM,EAC3D,IAAe,EAAI,KAA8B,MAAM,CAAC,aAAa;AACvE,QAAC,KAAa,CAAC,KAAY,CAAC,KAAgB,CAAC,KAAkB,CAAC,IAEpE;MAAI,CAAC,KAAa,CAAC,KAAY,CAAC,EAC/B,OAAU,MAAM,oCAAoC;AAGrD,MADA,EAAgB,GAAW,UAAU,EAAE,mBAAmB,IAAM,CAAC,EAC7D,CAAC,KAAkB,CAAC,EACvB,OAAU,MACT,yFACA;AAEF,SAAO;GAAE;GAAW;GAAU;GAAc;GAAgB;GAAc;;;AAG3E,eAAsB,GACrB,GACA,GACA,GACA,GACA,GACA,IAAM,KAAK,KAAK,EACwC;AACxD,KAAI,CAAC,EAAQ,QAAO,EAAoB,2BAA2B,yBAAyB;CAC5F,IAAM,IAAW,GAAoC,GAAQ,EAAa;AAC1E,KAAI,YAAY,EAAU,QAAO;CAEjC,IAAM,IAAY,MAAM,EAAmB,EAAO,UAAU,EACtD,IAAQ,GAAY,EACpB,IAAgB,EAAa,MAAM;EAAE,aAAa,EAAO,UAAU;EAAE;EAAO,EAAE,EAAI;AACxF,KAAI,CAAC,EACJ,QAAO,EAAoB,2BAA2B,uCAAuC;CAE9F,IAAM,IAAc,EAAe,EAAa,EAC1C,IAAW,IAAI,IAAI,EAAU,uBAAuB;AAO1D,QANA,EAAS,aAAa,IAAI,iBAAiB,OAAO,EAClD,EAAS,aAAa,IAAI,aAAa,EAAO,SAAS,EACvD,EAAS,aAAa,IAAI,gBAAgB,EAAY,EACtD,EAAS,aAAa,IAAI,SAAS,eAAe,EAClD,EAAS,aAAa,IAAI,SAAS,EAAc,EACjD,EAAS,aAAa,IAAI,SAAS,EAAM,EAClC;EAAE,QAAQ;EAAK,UAAU,EAAS;EAAM;;AAGhD,eAAsB,GACrB,GACA,GACA,GACA,GACA,IAAM,KAAK,KAAK,EACkD;AAClE,KAAI;EACH,IAAM,IAAO,EAAe,IAAI,OAAO,IAAI,IACrC,IAAQ,EAAe,IAAI,QAAQ,IAAI;AAC7C,MAAI,CAAC,EAAO,QAAO,EAAoB,mBAAmB,qBAAqB;EAC/E,IAAM,IAAU,EAAa,QAAQ,EAAM;AAC3C,MAAI,CAAC,KAAW,EAAQ,aAAa,EACpC,QAAO,EAAoB,mBAAmB,gCAAgC;AAC/E,MAAI,CAAC,EAAM,QAAO,EAAoB,mBAAmB,oBAAoB;EAE7E,IAAM,IAAY,MAAM,EAAmB,EAAO,UAAU,EACtD,IAAS,MAAM,GACpB,EAAU,gBACV,GACA,EAAe,EAAa,EAC5B,EACA;AAaD,SAZK,EAAO,WASP,GAAkB,MAPF,GACpB,EAAO,UACP,GACA,EAAO,UACP,EAAQ,OACR,EACA,EAC8B,EAAO,GAG/B,EAAE,aAAa,IAAI,gBAAgB,EAAQ,YAAY,EAAE,GAFxD,EAAoB,iBAAiB,+BAA+B,GATpE,EAAoB,mBAAmB,6BAA6B;SAYrE;AACP,SAAO,EAAoB,iBAAiB,oCAAoC;;;AAIlF,SAAS,EAAe,GAA8B;CACrD,IAAM,IAAM,IAAI,IAAI,EAAa;AACjC,QAAO,IAAI,IAAI,mBAAmB,EAAI,OAAO,CAAC;;AAG/C,eAAe,EAAmB,GAAmD;CACpF,IAAM,IAAS,IAAI,IAAI,EAAU;AACjC,KAAI,EAAO,UAAU,EAAO,KAC3B,OAAU,MAAM,iDAAiD;AAClE,KAAI,EAAO,YAAY,EAAO,SAC7B,OAAU,MAAM,2CAA2C;CAC5D,IAAM,IAAe,IAAI,IACxB,GAAG,EAAO,SAAS,QAAQ,OAAO,GAAG,CAAC,oCACtC,EAAO,OACP,EACK,IAAW,MAAM,MAAM,EAAa;AAC1C,KAAI,CAAC,EAAS,GAAI,OAAU,MAAM,gCAAgC;CAClE,IAAM,IAAY,MAAM,EAAS,MAAM;AACvC,KAAI,EAAS,WAAW,EAAO,QAAQ,EAAS,WAAW,EAAO,KAAK,QAAQ,OAAO,GAAG,CACxF,OAAU,MAAM,iCAAiC;AAElD,KAAI,CAAC,EAAS,0BAA0B,CAAC,EAAS,kBAAkB,CAAC,EAAS,SAC7E,OAAU,MAAM,wCAAwC;CAEzD,IAAM,IAAoB,EAAO,aAAa,WAAW,GAAmB,EAAO,SAAS;AAI5F,QAHA,EAAgB,EAAS,wBAAwB,0BAA0B,EAAE,sBAAmB,CAAC,EACjG,EAAgB,EAAS,gBAAgB,kBAAkB,EAAE,sBAAmB,CAAC,EACjF,EAAgB,EAAS,UAAU,YAAY,EAAE,sBAAmB,CAAC,EAC9D;;AAGR,eAAe,GACd,GACA,GACA,GACA,GAC8B;CAC9B,IAAM,IAAO,IAAI,gBAAgB;EAChC,YAAY;EACZ;EACA,cAAc;EACd,WAAW,EAAO;EAClB,eAAe,EAAO;EACtB,CAAC,EACI,IAAW,MAAM,MAAM,GAAe;EAC3C,QAAQ;EACR,SAAS,EAAE,gBAAgB,qCAAqC;EAChE;EACA,CAAC;AACF,KAAI,CAAC,EAAS,GAAI,OAAU,MAAM,4BAA4B;AAC9D,QAAQ,MAAM,EAAS,MAAM;;AAG9B,eAAe,GACd,GACA,GACA,GACA,GACA,GACqB;CACrB,IAAM,CAAC,GAAe,GAAe,KAAa,EAAQ,MAAM,IAAI;AACpE,KAAI,CAAC,KAAiB,CAAC,KAAiB,CAAC,EAAW,OAAU,MAAM,wBAAwB;CAC5F,IAAM,IAAS,EAAwB,EAAc,EAC/C,IAAS,EAAwB,EAAc;AACrD,KAAI,EAAO,QAAQ,WAAW,CAAC,EAAO,IAAK,OAAU,MAAM,sCAAsC;AACjG,KAAI,EAAO,QAAQ,EAAU,OAAQ,OAAU,MAAM,gCAAgC;AACrF,KAAI,CAAC,GAAiB,EAAO,KAAK,EAAS,CAAE,OAAU,MAAM,kCAAkC;AAC/F,KAAI,CAAC,GAAuB,EAAO,KAAK,EAAO,KAAK,EAAS,CAC5D,OAAU,MAAM,0CAA0C;AAC3D,KAAI,OAAO,EAAO,OAAQ,YAAY,CAAC,OAAO,SAAS,EAAO,IAAI,IAAI,EAAO,MAAM,OAAQ,EAC1F,OAAU,MAAM,2BAA2B;AAC5C,KAAI,OAAO,EAAO,OAAQ,YAAY,CAAC,OAAO,SAAS,EAAO,IAAI,CACjE,OAAU,MAAM,0CAA0C;AAC3D,KAAI,EAAO,MAAM,MAAO,IAAM,GAC7B,OAAU,MAAM,gDAAgD;AACjE,KAAI,EAAO,UAAU,EAAO,OAAU,MAAM,+BAA+B;AAC3E,KAAI,CAAC,EAAO,IAAK,OAAU,MAAM,mCAAmC;CAEpE,IAAM,IAAe,MAAM,MAAM,EAAU,SAAS;AACpD,KAAI,CAAC,EAAa,GAAI,OAAU,MAAM,2BAA2B;CAEjE,IAAM,KAAM,MADQ,EAAa,MAAM,EACtB,MAAM,MAAM,MAAQ,EAAI,QAAQ,EAAO,OAAO,EAAI,QAAQ,MAAM;AACjF,KAAI,CAAC,EAAK,OAAU,MAAM,6BAA6B;CACvD,IAAM,IAAM,EAAgB;EAAE,KAAK;EAAK,QAAQ;EAAO,CAAC,EAClD,IAAS,GAAG,EAAc,GAAG;AAEnC,KAAI,CADO,EAAO,cAAc,OAAO,KAAK,EAAO,EAAE,GAAK,OAAO,KAAK,GAAW,YAAY,CACxF,CAAI,OAAU,MAAM,8CAA8C;AACvE,QAAO;;AAGR,SAAS,EAAgB,GAAoB;AAC5C,QAAO,KAAK,MAAM,OAAO,KAAK,GAAS,YAAY,CAAC,SAAS,OAAO,CAAC;;AAGtE,SAAS,GAAiB,GAAyC,GAA2B;AAC7F,QAAO,MAAM,QAAQ,EAAS,GAAG,EAAS,SAAS,EAAS,GAAG,MAAa;;AAG7E,SAAS,GACR,GACA,GACA,GACU;AAEV,QADI,KAAmB,MAAoB,IAAiB,KACrD,CAAC,MAAM,QAAQ,EAAS,IAAI,EAAS,UAAU,KAAK,MAAoB;;AAGhF,SAAS,GAAkB,GAAmB,GAA6B;AAG1E,QAFI,EAAO,kBAAkB,EAAO,QAAQ,EAAO,iBAAuB,KACrE,EAAO,eACL,EAAO,mBAAmB,MAAQ,EAAO,OAAO,aAAa,KAAK,EAAO,eAD/C;;AAIlC,SAAS,EACR,GACA,GACA,IAA2C,EAAE,EACtC;CACP,IAAM,IAAM,IAAI,IAAI,EAAM;AAC1B,KAAI,EAAI,YAAY,EAAI,SAAU,OAAU,MAAM,QAAQ,EAAM,+BAA+B;AAC3F,OAAI,aAAa,YACjB,IAAQ,qBAAqB,EAAI,aAAa,WAAW,GAAmB,EAAI,SAAS,EAE7F,OAAU,MAAM,QAAQ,EAAM,iBAAiB;;AAGhD,SAAS,GAAmB,GAA2B;CACtD,IAAM,IAAa,EAAS,QAAQ,aAAa,KAAK,CAAC,aAAa;AACpE,QAAO,MAAe,eAAe,MAAe,eAAe,MAAe;;AAGnF,SAAS,EAAoB,GAAe,GAA2C;AACtF,QAAO;EACN,QAAQ;EACR,MAAM,IAAc;GAAE;GAAO,mBAAmB;GAAa,GAAG,EAAE,UAAO;EACzE;;;;ACpQF,IAAa,KAAb,MAAsE;CACrE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,GAA2C;AAQtD,EAPA,MAAA,IAAqB,EAAQ,cAC7B,MAAA,IAAkB,EAAQ,WAC1B,MAAA,IAAmB,EAAQ,YAC3B,MAAA,IAA0B,EAAQ,qBAAqB,IAAsC,EAC7F,MAAA,IAAqB,EAAQ,cAC7B,MAAA,IAA0B,IAAI,IAAI,EAAQ,aAAa,CAAC,MACxD,MAAA,IAAa,EAAQ,MACrB,MAAA,IAAY,EAAQ,OAAO,KAAK;;CAGjC,IAAI,eAA4C;AAC/C,SAAO,MAAA;;CAGR,MAAM,UACL,GACA,GACA,GACgB;AAChB,MAAI,CAAC,MAAA,EACJ,OAAM,IAAI,EAA4B,yBAAyB;EAGhE,IAAM,IAAS,MAAM,GADE,GAAiC,GAAQ,EAE/D,EACA,MAAA,GACA,MAAA,EAAW,cACX,MAAA,EAAW,QACX,MAAA,GACA,MAAA,GAAW,CACX;AACD,MAAI,EAAO,WAAW,KAAK;AAC1B,KAAI,SAAS,KAAK,EAAO,SAAS;AAClC;;AAED,QAAM,GAAkB,EAAO,KAAK;;CAGrC,MAAM,8BACL,GACA,GACkB;EAClB,IAAM,IAAS,MAAA,EAAgB,SAAS,EAAkB;AAC1D,MAAI,CAAC,EAAQ,OAAM,IAAI,EAAkB,+BAA+B;AACxE,MAAI,EAAO,aAAa,EAAQ,UAC/B,OAAM,IAAI,EAAkB,6BAA6B;AAE1D,MAAI,EAAO,aAAa,MAAA,GAAW,CAElC,OADA,MAAA,EAAgB,YAAY,EAAkB,EACxC,IAAI,EAAkB,eAAe;AAE5C,SAAO,EAAO;;CAGf,MAAM,0BACL,GACA,GACA,GACA,GACA,GACuB;EACvB,IAAM,IAAM,MAAA,GAAW,EACjB,IAAS,MAAA,EAAgB,SAAS,EAAkB;AAC1D,MAAI,CAAC,EAAQ,OAAM,IAAI,EAAkB,+BAA+B;AACxE,MAAI,EAAO,aAAa,EAEvB,OADA,MAAA,EAAgB,YAAY,EAAkB,EACxC,IAAI,EAAkB,eAAe;AAE5C,MAAI,EAAO,aAAa,EAAO,UAE9B,OADA,MAAA,EAAgB,YAAY,EAAkB,EACxC,IAAI,EAAkB,6BAA6B;AAM1D,MAAI,MAAgB,KAAA,KAAa,EAAO,gBAAgB,EAEvD,OADA,MAAA,EAAgB,YAAY,EAAkB,EACxC,IAAI,EAAkB,mCAAmC;AAEhE,OAAK,EAAO,YAAY,WAAW,GAAU,QAAQ,MAEpD,OADA,MAAA,EAAgB,YAAY,EAAkB,EACxC,IAAI,EAAkB,+BAA+B;EAG5D,IAAM,IAAS,GAAsB,EAAO,OAAO,IAAI,EAAE,EACnD,IAAQ,MAAA,EAAwB,WACrC;GAAE,UAAU,EAAO;GAAW;GAAQ,UAAU,EAAO;GAAU,EACjE,EACA;AACD,MAAI,CAAC,EACJ,OAAM,IAAI,EAA4B,iCAAiC;EAExE,IAAM,IAAS,MAAA,EAAiB,WAC/B,EAAO,WACP,GACA,EAAO,UACP,EAAM,MAAM,SACZ,EACA;AACD,MAAI,CAAC,EAIJ,OADA,MAAA,EAAwB,YAAY,EAAM,MAAM,SAAS,EAAI,EACvD,IAAI,EAA4B,gCAAgC;AAIvE,MAAI,CADa,MAAA,EAAgB,YAAY,EACxC,CAMJ,OALA,MAAA,EAAiB,YAAY,EAAO,OAAO,EAAI,EAC/C,MAAA,EAAwB,YAAY,EAAM,MAAM,SAAS,EAAI,EAIvD,IAAI,EAAkB,kCAAkC;AAG/D,SAAO,EAAkB,MAAM;GAC9B,cAAc,EAAO;GACrB,YAAY;GACZ,YAAY,EAAO;GACnB,eAAe,EAAM;GACrB,CAAC;;CAGH,MAAM,qBACL,GACA,GACA,GACA,GACuB;EACvB,IAAM,IAAM,MAAA,GAAW,EACjB,IAAW,MAAA,EAAwB,mBACxC,EAAO,WACP,GACA;GAAE,QAAQ,GAAsB,EAAO;GAAE,GAAI,IAAW,EAAE,UAAU,EAAS,MAAM,GAAG,EAAE;GAAG,EAC3F,EACA;AACD,MAAI,CAAC,EAAS,GAIb,OAHI,EAAS,WAAW,0BAA0B,EAAS,WAC1D,MAAA,EAAiB,uBAAuB,EAAS,SAAS,EAAI,EAEzD,IAAI,EAAkB,GAA6B,EAAS,OAAO,CAAC;EAE3E,IAAM,IAAS,MAAA,EAAiB,WAC/B,EAAO,WACP,GACA,EAAS,MAAM,UACf,EAAS,MAAM,SACf,EAAS,MAAM,OACf;AACD,MAAI,CAAC,EAAQ,OAAM,IAAI,EAA4B,gCAAgC;AACnF,SAAO,EAAkB,MAAM;GAC9B,cAAc,EAAO;GACrB,YAAY;GACZ,YAAY,EAAO;GACnB,eAAe,EAAS;GACxB,CAAC;;CAGH,MAAM,kBAAkB,GAAkC;EACzD,IAAM,IAAS,MAAA,EAAiB,YAAY,GAAO,MAAA,GAAW,CAAC;AAC/D,MAAI,CAAC,EAAO,GAOX,OAAM,IAAI,EALT,EAAO,WAAW,kBACf,sBACA,EAAO,WAAW,kBACjB,2BACA,mBAC8B;EAEpC,IAAM,IAAqB;GAC1B;GACA,UAAU,EAAO,OAAO;GACxB,QAAQ,EAAO,OAAO;GACtB,WAAW,KAAK,MAAM,EAAO,OAAO,YAAY,IAAK;GACrD;AACD,MAAI,EAAO,OAAO,UAAU;AAC3B,OAAI,IAAI,IAAI,EAAO,OAAO,SAAS,CAAC,SAAS,MAAA,EAC5C,OAAM,IAAI,EAAkB,qDAAqD;AAElF,KAAS,WAAW,IAAI,IAAI,EAAO,OAAO,SAAS;;AAEpD,SAAO;;CAGR,MAAM,YACL,GACA,GACgB;EAChB,IAAM,IAAM,MAAA,GAAW;AACvB,QAAA,EAAiB,YAAY,EAAQ,OAAO,EAAI;EAChD,IAAM,IAAU,MAAA,EAAwB,mBAAmB,EAAQ,OAAO,EAAI;AAC9E,EAAI,KAAS,MAAA,EAAiB,uBAAuB,GAAS,EAAI;;;AAIpE,SAAS,GACR,GACA,GACkB;CAClB,IAAM,IAAW,IAAI,iBAAiB;AAStC,QARA,EAAS,IAAI,aAAa,EAAO,UAAU,EAC3C,EAAS,IAAI,gBAAgB,EAAO,YAAY,EAChD,EAAS,IAAI,iBAAiB,OAAO,EACrC,EAAS,IAAI,kBAAkB,EAAO,cAAc,EACpD,EAAS,IAAI,yBAAyB,OAAO,EACzC,EAAO,SAAO,EAAS,IAAI,SAAS,EAAO,MAAM,EACjD,EAAO,QAAQ,UAAQ,EAAS,IAAI,SAAS,EAAO,OAAO,KAAK,IAAI,CAAC,EACrE,EAAO,YAAU,EAAS,IAAI,YAAY,EAAO,SAAS,KAAK,EAC5D;;AAGR,SAAS,GAAkB,GAAiC;CAC3D,IAAM,IAAc,EAAK,qBAAqB,EAAK;AACnD,SAAQ,EAAK,OAAb;EACC,KAAK,0BACJ,QAAO,IAAI,EAA4B,EAAY;EACpD,KAAK,kBACJ,QAAO,IAAI,EAAoB,EAAY;EAC5C,KAAK,iBACJ,QAAO,IAAI,EAAmB,EAAY;EAC3C,KAAK,gBACJ,QAAO,IAAI,EAAkB,EAAY;EAC1C,KAAK,4BACJ,QAAO,IAAI,EAA6B,EAAY;EACrD,QACC,QAAO,IAAI,EAAY,EAAY;;;AAItC,SAAS,GAA6B,GAAwB;AAC7D,SAAQ,GAAR;EACC,KAAK,wBACJ,QAAO;EACR,KAAK,wBACJ,QAAO;EACR,KAAK,kBACJ,QAAO;EACR,KAAK,iBACJ,QAAO;EACR,KAAK,oBACJ,QAAO;EACR,KAAK,uBACJ,QAAO;EACR,QACC,QAAO;;;AAIV,SAAS,GAAsB,GAAoD;CAClF,IAAM,IAAa,GAChB,KAAK,MAAU,EAAM,MAAM,CAAC,CAC7B,OAAO,QAAQ,CACf,KAAK,MACD,MAAU,uBAA6B,gBACvC,MAAU,wBAA8B,iBACrC,EACN;AACH,QAAO,KAAc,EAAW,SAAS,IAAI,IAAa,KAAA;;;;AC9R3D,IAAa,KAAwB,aACxB,IAAwB,OAC/B,KAAoB,IAEpB,KAAiB,oBACjB,KAAiB,IAAI,IAAI;CAAC;CAAa;CAAO;CAAY,CAAC;AAuBjE,SAAgB,GAAoB,GAA0B,IAAoB,IAAe;CAChG,IAAM,IAAQ,GAAM,MAAM,IAAA;AAI1B,KAHI,EAAM,SAAS,MAAM,IAAI,EAAM,SAAS,IAAI,IAAI,EAAM,SAAS,KAAK,IAGpE,EAAE,EAAK,EAAM,IAAI,MAAU,eAAe,GAAe,KAAK,EAAM,EACvE,OAAU,MAAM,0BAA0B,IAAO;AAElD,KAAI,CAAC,KAAqB,CAAC,EAAe,EAAM,CAC/C,OAAU,MACT,iCAAiC,EAAM,sEACvC;AAEF,QAAO;;AAGR,SAAgB,EAAe,GAAuB;AACrD,QAAO,GAAe,IAAI,EAAc,EAAK,CAAC;;AAG/C,SAAgB,GACf,IAAQ,QAAQ,IAAI,gCACV;AACV,QAAO,MAAU,OAAO,GAAO,aAAa,KAAK,UAAU,GAAO,aAAa,KAAK;;AAGrF,SAAgB,GACf,GACA,GACU;CACV,IAAM,IAAS,GAAgB,EAAO;AAStC,QARI,CAAC,KACD,CAAC,EAAe,EAAO,KAAK,GAAS,KACrC,EAAO,SAAS,OAIZ,MAAiB,KAElB,EAAO,SAAS;;AAGxB,SAAgB,GACf,GACA,GACU;AACV,KAAI,CAAC,EAAQ,QAAO;AACpB,KAAI;EACH,IAAM,IAAM,IAAI,IAAI,EAAO,EACrB,IAAO,EAAI,OAAO,OAAO,EAAI,KAAK,GAAG,EAAI,aAAa,WAAW,MAAM;AAC7E,UACE,EAAI,aAAa,WAAW,EAAI,aAAa,aAC9C,EAAI,aAAa,MACjB,EAAI,aAAa,MACjB,EAAI,aAAa,OACjB,EAAI,WAAW,MACf,EAAI,SAAS,MACb,EAAe,EAAI,SAAS,IAC5B,MAAS;SAEH;AACP,SAAO;;;AAIT,SAAgB,GAAqC,GAAsC;AAE1F,QADK,IACE,EAAe,EAAQ,QAAQ,YAAY,GAAG,CAAC,GADjC;;AAItB,SAAS,EAAc,GAAsB;AAC5C,QAAO,EAAK,QAAQ,aAAa,KAAK,CAAC,aAAa;;AAGrD,SAAS,GAAgB,GAA0E;AAClG,KAAI,CAAC,EAAQ,QAAO;AACpB,KAAI,EAAO,WAAW,IAAI,EAAE;EAE3B,IAAM,IAAW,sBAAsB,KAAK,EAAO;AACnD,MAAI,IAAW,MAAM,EAAS,GAC7B,QAAO;GAAE,MAAM,EAAS;GAAI,MAAM,OAAO,EAAS,GAAG;GAAE;EAExD,IAAM,IAAW,gBAAgB,KAAK,EAAO;AAE7C,SADI,IAAW,KAAW;GAAE,MAAM,EAAS;GAAI,MAAM;GAAM,GACpD;;CAER,IAAM,IAAY,EAAO,YAAY,IAAI;AACzC,KAAI,IAAY,EAIf,QAAO;EAAE,MAAM;EAAQ,MAAM;EAAM;CAEpC,IAAM,IAAO,EAAO,MAAM,GAAG,EAAU,EACjC,IAAO,OAAO,EAAO,MAAM,IAAY,EAAE,CAAC;AAEhD,QADK,OAAO,UAAU,EAAK,GACpB;EAAE;EAAM;EAAM,GADe;;AAIrC,SAAgB,EAAiB,GAA2C;AAC3E,KAAI,MAAS,KAAA,KAAa,MAAS,GAAI,QAAO;CAC9C,IAAM,IAAQ,OAAO,KAAS,WAAW,IAAO,OAAO,EAAK;AAC5D,KAAI,CAAC,OAAO,UAAU,EAAM,IAAI,IAAQ,KAAK,IAAQ,MACpD,OAAU,MAAM,0BAA0B,IAAO;AAElD,QAAO;;AAGR,eAAsB,GACrB,IAAiC,EAAE,EACH;CAChC,IAAM,IAAO,GACZ,EAAQ,QAAQ,QAAQ,IAAI,uBAC5B,EAAQ,qBAAqB,IAA2B,CACxD,EACK,IAAO,EAAiB,EAAQ,QAAQ,QAAQ,IAAI,sBAAsB,EAC1E,IAAsB,EAAQ,aAAa,QAAQ,IAAA,6BACnD,IAAyB,IAC5B,EAAsB,EAAoB,GAC1C,KAAA,GACG,IAAQ,IAAI,EAAY,EAAQ,UAAU,GAAe,CAAC,EAC1D,IAAe,IAAiC,EAChD,IAAY,IAA2C,EACvD,IAAa,EAAQ,yBAAyB,IAAqC,EACnF,IAAa,IAAmB,EAChC,IAAmB,IAA6C,EAChE,IAAyB,MAA2B,KAAA,KAAa,MAAe,KAAA,GAChF,IAAY,GACjB,EAAQ,gBAAgB,IAAiC,CACzD,EACK,oBAAiB,IAAI,KAAoB,EAC3C,IAAqC,MAEnC,IAAM,GAAS;AAKrB,CAJA,EAAI,QAAQ,eAAe,EAI3B,EAAI,IAAI,eAAe,WAAW;CAClC,IAAM,IAAS,EAAa,EAAI;AAEhC,OAAM,IAAI,SAAe,GAAS,MAAW;AAE5C,EADA,EAAO,KAAK,SAAS,EAAO,EAC5B,EAAO,OAAO;GAAE;GAAM;GAAM,QAAQ;AAEnC,GADA,EAAO,IAAI,SAAS,EAAO,EAC3B,GAAS;IACR;GACD;CAEF,IAAM,IAAe,GAAwB,QAAQ,GAAa,GAAQ,EAAK,EACzE,IAAqB,IAAI,IAAI,EAAa,EAC1C,IAAY,GAAgB,GAAoB,EAAa,EAAO,CAAC,EACrE,IAAW,IAAI,GAA0B;EAC9C;EACA;EACA;EACA;EACA,GAAI,IAAa,EAAE,MAAM;GAAE,QAAQ;GAAY,cAAc;GAAkB,EAAE,GAAG,EAAE;EACtF,CAAC,EACI,KAAsB,EAAqC,EAAmB;AAqGpF,CAnGA,EAAI,KAAK,GAAK,GAAK,MAAS;EAC3B,IAAM,IAAY,EAAa,EAAO,EAChC,IAAW,GAAmB,EAAI,KAAK;AAC7C,MAAI,MAAa,QAAQ;AACxB,OAAI,GAAwB,GAAK,GAAW,EAAuB,CAAE,QAAO,GAAM;AAClF,KAAI,OAAO,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,YAAY;AACpD;;AAED,MAAI,GAAsB,EAAS,EAAE;AACpC,OAAI,GAA0B,GAAK,GAAW,EAAuB,CAAE,QAAO,GAAM;AACpF,KAAI,OAAO,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,YAAY;AACpD;;AAED,KAAM;GACL,EAEF,EAAI,IAAI,GAAyB,EAAU,CAAC,EAE5C,EAAI,IAAI,mBAAmB,OAAO,GAAK,MAAQ;EAC9C,IAAM,IAAgB,EAAI,OAAO,iBAAiB,KAAA;AAClD,MAAI,CAAC,GAAY;AAQhB,GAPA,EACC,EAAqB,iBAAiB;IACrC,SAAS;IACT,QAAQ;IACR;IACA,CAAC,CACF,EACD,EAAI,OAAO,IAAI,CAAC,KAAK;IACpB,OAAO;IACP,mBAAmB;IACnB,CAAC;AACF;;EAED,IAAM,IAAY,MAAM,GACvB,IAAI,gBAAgB,EAAI,MAAgC,EACxD,GACA,GACA,EACA;AACD,MAAI,YAAY,GAAW;AAQ1B,GAPA,EACC,EAAqB,iBAAiB;IACrC,SAAS;IACT,QAAQ,EAAU,KAAK;IACvB;IACA,CAAC,CACF,EACD,EAAI,OAAO,EAAU,OAAO,CAAC,KAAK,EAAU,KAAK;AACjD;;EAED,IAAM,IAAgB,EAAU,YAAY,IAAI,YAAY,IAAI,KAAA,GAC1D,IAAO,EAAU,UAAU;GAChC,UAAU,EAAU,YAAY,IAAI,YAAY,IAAI;GACpD,aAAa,EAAU,YAAY,IAAI,eAAe,IAAI;GAC1D,eAAe,EAAU,YAAY,IAAI,iBAAiB,IAAI;GAC9D,QAAQ,GAAe,EAAU,YAAY,IAAI,QAAQ,CAAC;GAC1D,GAAI,EAAU,YAAY,IAAI,WAAW,GACtC,EAAE,UAAU,EAAU,YAAY,IAAI,WAAW,IAAI,KAAA,GAAW,GAChE,EAAE;GACL,WAAW,KAAK,KAAK,GAAG,MAAS;GACjC,CAAC;AACF,MAAI,CAAC,GAAM;AASV,GARA,EACC,EAAqB,iBAAiB;IACrC,SAAS;IACT,QAAQ;IACR,UAAU;IACV;IACA,CAAC,CACF,EACD,EAAI,OAAO,IAAI,CAAC,KAAK;IACpB,OAAO;IACP,mBAAmB;IACnB,CAAC;AACF;;EAED,IAAM,IAAW,IAAI,IAAI,EAAU,YAAY,IAAI,eAAe,IAAI,GAAG;AACzE,IAAS,aAAa,IAAI,QAAQ,EAAK;EACvC,IAAM,IAAQ,EAAU,YAAY,IAAI,QAAQ;AAUhD,EATI,KAAO,EAAS,aAAa,IAAI,SAAS,EAAM,EACpD,EACC,EAAqB,iBAAiB;GACrC,SAAS;GACT,QAAQ;GACR,UAAU;GACV;GACA,CAAC,CACF,EACD,EAAI,SAAS,KAAK,EAAS,KAAK;GAC/B,EAEF,EAAI,IAAI,4CAA4C,GAAM,MAAQ;AACjE,IAAI,OAAO,IAAI,CAAC,KAAK,GAAuB;GAAE,QAAQ;GAAc;GAAc,CAAC,CAAC;GACnF,EACF,EAAI,IAAI,8CAA8C,GAAM,MAAQ;AACnE,IAAI,OAAO,IAAI,CAAC,KAAK,GAAmC,EAAa,CAAC;GACrE,EAEF,EAAI,IACH,EAAc;EACb;EACA;EACA,SAAS;EACT,mBAAmB;EACnB,cAAc;EACd,iBAAiB;EACjB,yBAAyB,IAAI,IAAI,EAAoC;EACrE,2BAA2B,EAAE,2BAA2B,GAAG;EAC3D,CAAC,CACF;CAED,IAAM,KAAmB,IACtB,EAAkB;EAAE,UAAU;EAAU;EAAqB,CAAC,IAC7D,GAAe,GAAgB,MAAuB,GAAM,EAC1D,KAAwB,IAC3B,GAAqB,GAAW,EAAW,IAC1C,GAAe,GAAgB,MAAuB,GAAM;AAiDhE,QAhDA,EAAI,KAAK,QAAQ,IAAuB,IAAkB,OAAO,GAAK,MAAQ;AAC7E,EAAI,EAAI,QACP,EACC,EAAqB,UAAU;GAC9B,SAAS;GACT,UAAU,EAAI,KAAK;GACnB,eAAe,EAAI,OAAO,iBAAiB,KAAA;GAC3C,CAAC,CACF;EAEF,IAAM,IAAY,EAAuB,EAAM,EACzC,IAAY,IAAI,EAA8B,EAAE,oBAAoB,KAAA,GAAW,CAAC,EAChF,IAAgB,EAAE,cAAW;AACnC,IAAe,IAAI,EAAc;AACjC,MAAI;AAEH,GADA,MAAM,EAAU,QAAQ,EAAU,EAClC,MAAM,EAAU,cAAc,GAAK,EAAI;YAC9B;AAET,GADA,EAAe,OAAO,EAAc,EACpC,MAAM,EAAU,OAAO;;GAEvB,EACF,EAAI,IAAI,SAAS,GAAM,MAAQ;AAE9B,EADA,EAAI,UAAU,SAAS,OAAO,EAC9B,EAAI,OAAO,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,qBAAqB;GAC5D,EACF,EAAI,KAAK,GAAM,MAAQ;AACtB,IAAI,OAAO,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,YAAY;GACnD,EAoBK;EACN;EACA;EACA,KAAK,UAAU,GAAiB,EAAK,CAAC,GAAG,EAAa,EAAO,CAAC;EAC9D,cArBA,OAAkB,YAAY;AAY7B,GAXA,MAAM,QAAQ,WAAW,CAAC,GAAG,EAAe,CAAC,KAAK,EAAE,mBAAgB,EAAU,OAAO,CAAC,CAAC,EACvF,MAAM,IAAI,SAAe,GAAS,MAAW;AAQ5C,IAPA,EAAO,OAAO,MAAQ;AACrB,SAAI,CAAC,KAAQ,EAA8B,SAAS,0BAA0B;AAC7E,SAAS;AACT;;AAED,OAAO,EAAI;MACV,EACF,EAAO,qBAAqB;KAC3B,EACF,EAAM,OAAO;MACV,EACG;EAQP;;AAGF,SAAS,GACR,GACA,GACA,GACU;AAGV,QAFI,GAA6B,GAAK,EAAa,GAAS,KACvD,IAEJ,GAA2B,EAAI,QAAQ,MAAM,EAAa,IAC1D,GAAsB,EAAI,QAAQ,QAAQ,EAAa,GAH9B;;AAO3B,SAAS,GAA6B,GAAsB,GAA+B;AAC1F,QACC,GAAqC,EAAI,OAAO,cAAc,IAC9D,GAA4B,EAAI,QAAQ,MAAM,EAAa,IAC3D,GAA8B,EAAI,QAAQ,QAAQ,EAAa;;AAIjE,SAAS,GACR,GACA,GACA,GACU;AAGV,QAFI,GAA6B,GAAK,EAAa,GAAS,KACvD,IAEJ,GAA2B,EAAI,QAAQ,MAAM,EAAa,IAC1D,GAAsB,EAAI,QAAQ,QAAQ,EAAa,GAH9B;;AAO3B,SAAS,GAAsB,GAA4B,GAA4B;AACtF,KAAI,CAAC,EAAQ,QAAO;AACpB,KAAI;AAEH,SAAO,IADY,IAAI,EAChB,CAAO,WAAW,EAAa;SAC/B;AACP,SAAO;;;AAIT,SAAS,GAA2B,GAA4B,GAA4B;CAC3F,IAAM,IAAS,GAAgB,EAAO;AACtC,KAAI,CAAC,EAAQ,QAAO;CACpB,IAAM,IAAe,EAAa,OAC/B,OAAO,EAAa,KAAK,GACzB,EAAa,aAAa,WACzB,MACA,IACE,IAAa,EAAO,QAAQ;AAClC,QACC,EAAc,EAAO,KAAK,KAAK,EAAc,EAAa,SAAS,IACnE,MAAe;;AAIjB,SAAS,EAAa,GAAwB;CAC7C,IAAM,IAAU,EAAO,SAAS;AAEhC,QADI,CAAC,KAAW,OAAO,KAAY,WAAiB,IAC7C,EAAQ;;AAGhB,SAAS,GAAiB,GAAsB;AAC/C,QAAO,EAAK,EAAK,KAAK,IAAI,IAAI,EAAK,KAAK;;AAGzC,SAAS,GAAa,GAAgB,GAAsB;AAC3D,QAAO,UAAU,GAAiB,EAAK,CAAC,GAAG,EAAa,EAAO,CAAC;;AAGjE,SAAS,GAAgB,GAAmB,GAAmB;AAK9D,QAJI,EAAa,aAAa,YAC1B,EAAe,EAAa,SAAS,IAAI,EAAc,EAAa,SAAS,KAAK,QAC9E,IAAI,IAAI,EAAa,OAAO,GAE7B,IAAI,IAAI,oBAAoB,EAAK,GAAG;;AAG5C,SAAS,GAAsB,GAA2B;AACzD,QACC,MAAa,eACb,MAAa,gBACb,MAAa,YACb,MAAa,aACb,MAAa,qBACb,MAAa,6CACb,MAAa;;AAIf,SAAS,GAAmB,GAA0B;AACrD,QAAO,EAAS,SAAS,IAAI,EAAS,QAAQ,QAAQ,GAAG,GAAG;;AAG7D,SAAS,GACR,GACC;AACD,SAAQ,GAAc,GAAe,MAAuB;EAC3D,IAAM,IAAW,EAAI;AACrB,MAAI,CAAC,GAAmB,EAAS,CAAE,QAAO,GAAM;EAChD,IAAI,GACE,IAAe,EAAI,KAAK,KAAK,EAAI;AAoBvC,EAnBA,EAAI,SAAS,OACZ,IAAe,GACR,EAAa,EAAK,IAE1B,EAAI,GAAG,gBAAgB;AACtB,OAAI,MAAa,kBAAmB;GACpC,IAAM,IAAO,GAAyB,GAAU,EAAI;AACpD,OAAI,CAAC,EAAM;GACX,IAAM,IAAQ,GAAsB,GAAK,EAAa,EAChD,IAAW,GAAmB,GAAK,EAAa;AACtD,KACC,EAAqB,GAAM;IAC1B,SAAS,IAAQ,WAAW;IAC5B,GAAI,IAAQ,EAAE,QAAQ,GAAO,GAAG,EAAE;IAClC,GAAI,IAAW,EAAE,aAAU,GAAG,EAAE;IAChC,eAAe,EAAI,OAAO,iBAAiB,KAAA;IAC3C,CAAC,CACF;IACA,EACF,GAAM;;;AAIR,SAAS,GAAmB,GAA2B;AACtD,QAAO,GAAsB,EAAS,KAAK;;AAG5C,SAAS,GACR,GACA,GAC2E;AAE3E,QADI,MAAa,YAAY,EAAI,MAAM,eAAe,kBAAwB,YACvE,GAAsB,EAAS;;AAGvC,SAAS,GACR,GAC+D;AAK/D,QAJI,MAAa,cAAoB,iBACjC,MAAa,eAAqB,cAClC,MAAa,WAAiB,UAC9B,MAAa,YAAkB,eAC5B;;AAGR,SAAS,GAAsB,GAAe,GAAmC;CAChF,IAAM,IAAY,GAAkB,EAAK;AACzC,KAAI,EAAW,QAAO;CACtB,IAAM,IAAW,EAAI,UAAU,WAAW;AACtC,YAAO,KAAa,SACxB,KAAI;AAEH,SAAO,IADc,IAAI,EAClB,CAAS,aAAa,IAAI,QAAQ,IAAI,KAAA;SACtC;AACP;;;AAIF,SAAS,GAAkB,GAAmC;AAC7D,KACC,OAAO,KAAS,YAChB,KACA,WAAW,KACX,OAAO,EAAK,SAAU,SAEtB,QAAO,EAAK;;AAKd,SAAS,GAAmB,GAAc,GAAmC;AAY5E,QAVC,OAAO,KAAS,YAChB,KACA,eAAe,KACf,OAAO,EAAK,aAAc,WAEnB,EAAK,aAEQ,OAAO,EAAI,MAAM,aAAc,WAAW,EAAI,KAAK,YAAY,KAAA,OAE9D,OAAO,EAAI,MAAM,aAAc,WAAW,EAAI,MAAM,YAAY,KAAA;;AAIvF,SAAS,GACR,GACA,GACC;AACD,SAAQ,GAAc,GAAgB,MAAuB;EAC5D,IAAM,IAAS,GAA6B,EAAI,QAAQ,eAAe,EAAW;AAUlF,EATI,KACH,EACC,EAAqB,UAAU;GAC9B,SAAS;GACT;GACA,eAAe,EAAI,OAAO,iBAAiB,KAAA;GAC3C,CAAC,CACF,EAEF,GAAM;;;AAIR,SAAS,GACR,GACA,GAC0B;AAC1B,KAAI,CAAC,EAAQ,QAAO;CACpB,IAAM,CAAC,GAAQ,GAAO,KAAS,EAAO,MAAM,CAAC,MAAM,MAAM;AACzD,KAAI,CAAC,KAAU,CAAC,KAAS,KAAS,EAAO,aAAa,KAAK,SAC1D,QAAO;CAER,IAAM,IAAe,EAAW,YAAY,EAAM;AAElD,QADK,EAAa,KACX,OADsB,EAAa;;AAI3C,SAAS,GAAe,GAA4C;AACnE,QACC,GACG,MAAM,MAAM,CACb,KAAK,MAAS,EAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,IAAI,EAAE;;AAIzB,SAAS,KAAwB;CAChC,IAAM,IAAS,QAAQ,KAAK;AAC5B,QAAO,IAAS,OAAO,KAAK,QAAQ,EAAc,EAAO,CAAC,OAAO;;AAGlE,eAAe,KAAO;CACrB,IAAM,IAAa,MAAM,IAA2B;AACpD,SAAQ,MAAM,wCAAwC,EAAW,MAAM;CAEvE,IAAM,IAAW,YAAY;AAC5B,MAAI;AACH,SAAM,EAAW,OAAO;UACjB;AAGR,UAAQ,KAAK,EAAE;;AAIhB,CADA,QAAQ,GAAG,UAAU,EAAS,EAC9B,QAAQ,GAAG,WAAW,EAAS;;AAG5B,IAAc,IACjB,IAAM,CAAC,OAAO,MAAQ;AAErB,CADA,QAAQ,MAAM,4CAA4C,EAAI,EAC9D,QAAQ,KAAK,EAAE;EACd"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* @codemem/mcp — MCP stdio server.
|
|
4
|
-
*
|
|
5
|
-
* Runs as a separate process spawned by the host (OpenCode/Claude).
|
|
6
|
-
* Owns its own better-sqlite3 connection. Communicates via stdio JSON-RPC.
|
|
7
|
-
*
|
|
8
|
-
* Port of codemem/mcp_server.py — all 13 tools.
|
|
9
|
-
*/
|
|
10
|
-
export {};
|
|
1
|
+
export { createCodememMcpServer } from "./server.js";
|
|
2
|
+
export type { CodememMcpServerOptions } from "./tool-context.js";
|
|
11
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC"}
|