From d8c6a6f7b8c8e573c90305836abc4dafc2b1a62b Mon Sep 17 00:00:00 2001 From: deloreyj Date: Wed, 30 Apr 2025 18:17:38 -0500 Subject: [PATCH 1/3] feat: vectorize tools --- packages/mcp-common/src/tools/vectorize.ts | 484 +++++++++++++++++++++ packages/mcp-common/src/types/vectorize.ts | 171 ++++++++ 2 files changed, 655 insertions(+) create mode 100644 packages/mcp-common/src/tools/vectorize.ts create mode 100644 packages/mcp-common/src/types/vectorize.ts diff --git a/packages/mcp-common/src/tools/vectorize.ts b/packages/mcp-common/src/tools/vectorize.ts new file mode 100644 index 00000000..7e2b0eaf --- /dev/null +++ b/packages/mcp-common/src/tools/vectorize.ts @@ -0,0 +1,484 @@ +import { getCloudflareClient } from '../cloudflare-api' +import { MISSING_ACCOUNT_ID_RESPONSE } from '../constants' +import { + VectorizeIndexConfigSchema, + VectorizeIndexDescriptionSchema, + VectorizeIndexNameSchema, + VectorizeListDirectionParam, + VectorizeListOrderParam, + VectorizeListPageParam, + VectorizeListPerPageParam, + VectorizeNdjsonBodySchema, + VectorizeQueryFilterSchema, + VectorizeQueryReturnMetadataSchema, + VectorizeQueryReturnValuesSchema, + VectorizeQueryTopKSchema, + VectorizeQueryVectorSchema, + VectorizeUnparsableBehaviorSchema, + VectorizeVectorIdListSchema, +} from '../types/vectorize' + +import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' + +/** + * Registers Vectorize Index management tools with the MCP agent. + * @param agent - The Cloudflare MCP agent instance. + */ +export function registerVectorizeTools(agent: CloudflareMcpAgent) { + // --- vectorize_index_create --- + agent.server.tool( + 'vectorize_index_create', + 'Creates a new Vectorize Index. Use this when a user wants to set up a new vector database.', + { + name: VectorizeIndexNameSchema, + config: VectorizeIndexConfigSchema, + description: VectorizeIndexDescriptionSchema, + }, + async (params) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.create({ + account_id, + ...params, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result ?? 'Index created successfully (no detailed response).'), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error creating Vectorize Index: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_list --- + agent.server.tool( + 'vectorize_index_list', + 'Lists Vectorize Indexes in the current account, with optional pagination. Use this when a user asks to see their indexes.', + { + page: VectorizeListPageParam, + per_page: VectorizeListPerPageParam, + order: VectorizeListOrderParam, + direction: VectorizeListDirectionParam, + }, + async ({ page, per_page, order, direction }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const response = await client.vectorize.indexes.list( + { account_id }, + { + query: { + page: page ?? undefined, + per_page: per_page ?? undefined, + order: order ?? undefined, + direction: direction ?? undefined, + }, + } + ) + + return { + content: [ + { + type: 'text', + text: JSON.stringify(response), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error listing Vectorize Indexes: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_get --- + agent.server.tool( + 'vectorize_index_get', + 'Retrieves the details and configuration of a specific Vectorize Index by its name.', + { + name: VectorizeIndexNameSchema, + }, + async ({ name }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.get(name, { account_id }) + + if (!result) { + return { + content: [ + { + type: 'text', + text: `Error: Vectorize Index "${name}" not found.`, + }, + ], + } + } + return { + content: [ + { + type: 'text', + text: JSON.stringify(result), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_delete --- + agent.server.tool( + 'vectorize_index_delete', + 'Deletes a specific Vectorize Index by its name. This action is permanent.', + { + name: VectorizeIndexNameSchema, + }, + async ({ name }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + await client.vectorize.indexes.delete(name, { account_id }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + success: true, + message: `Vectorize Index "${name}" deleted successfully.`, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error deleting Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_info --- + agent.server.tool( + 'vectorize_index_info', + 'Gets operational information about a Vectorize Index, such as the number of vectors it contains.', + { + name: VectorizeIndexNameSchema, + }, + async ({ name }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.info(name, { account_id }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result ?? `Could not retrieve info for index "${name}".`), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting info for Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_insert --- + agent.server.tool( + 'vectorize_index_insert', + 'Inserts vectors into a specified Vectorize Index using NDJSON format. Returns a mutation ID.', + { + name: VectorizeIndexNameSchema, + vectors_ndjson: VectorizeNdjsonBodySchema, + unparsable_behavior: VectorizeUnparsableBehaviorSchema, + }, + async ({ name, vectors_ndjson, unparsable_behavior }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.insert(name, { + account_id, + body: vectors_ndjson, + 'unparsable-behavior': unparsable_behavior, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result ?? 'Insert operation initiated (no detailed response).'), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error inserting vectors into Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_upsert --- + agent.server.tool( + 'vectorize_index_upsert', + 'Upserts vectors into a specified Vectorize Index using NDJSON format (inserts new, updates existing). Returns a mutation ID.', + { + name: VectorizeIndexNameSchema, + vectors_ndjson: VectorizeNdjsonBodySchema, + unparsable_behavior: VectorizeUnparsableBehaviorSchema, + }, + async ({ name, vectors_ndjson, unparsable_behavior }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.upsert(name, { + account_id, + body: vectors_ndjson, + 'unparsable-behavior': unparsable_behavior, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result ?? 'Upsert operation initiated (no detailed response).'), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error upserting vectors into Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_query --- + agent.server.tool( + 'vectorize_index_query', + 'Finds vectors in an index that are closest (nearest neighbors) to a given query vector. Can optionally filter by metadata.', + { + name: VectorizeIndexNameSchema, + vector: VectorizeQueryVectorSchema, + filter: VectorizeQueryFilterSchema, + return_metadata: VectorizeQueryReturnMetadataSchema, + return_values: VectorizeQueryReturnValuesSchema, + top_k: VectorizeQueryTopKSchema, + }, + async ({ name, vector, filter, return_metadata, return_values, top_k }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.query(name, { + account_id, + vector, + filter, // Pass filter directly as SDK expects 'unknown' + returnMetadata: return_metadata, + returnValues: return_values, + topK: top_k, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify(result ?? 'Query executed, but no results returned.'), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error querying Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_get_by_ids --- + agent.server.tool( + 'vectorize_index_get_by_ids', + 'Retrieves specific vectors from an index by their unique identifiers.', + { + name: VectorizeIndexNameSchema, + ids: VectorizeVectorIdListSchema, + }, + async ({ name, ids }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.getByIds(name, { + account_id, + ids, + }) + + // The SDK types this response as 'unknown', needs careful handling + if (result === null || result === undefined) { + return { + content: [ + { + type: 'text', + text: `Error: No vectors found for the provided IDs in index "${name}".`, + }, + ], + } + } + // Format success response + return { + content: [ + { + type: 'text', + text: JSON.stringify(result), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting vectors by ID from Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // --- vectorize_index_delete_by_ids --- + agent.server.tool( + 'vectorize_index_delete_by_ids', + 'Deletes specific vectors from an index by their unique identifiers. Returns a mutation ID.', + { + name: VectorizeIndexNameSchema, + ids: VectorizeVectorIdListSchema, + }, + async ({ name, ids }) => { + try { + const account_id = await agent.getActiveAccountId() + if (!account_id) { + return MISSING_ACCOUNT_ID_RESPONSE + } + const client = getCloudflareClient(agent.props.accessToken) + + const result = await client.vectorize.indexes.deleteByIds(name, { + account_id, + ids, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify( + result ?? 'Delete by IDs operation initiated (no detailed response).' + ), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error deleting vectors by ID from Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) +} diff --git a/packages/mcp-common/src/types/vectorize.ts b/packages/mcp-common/src/types/vectorize.ts new file mode 100644 index 00000000..9ea5d562 --- /dev/null +++ b/packages/mcp-common/src/types/vectorize.ts @@ -0,0 +1,171 @@ +import { z } from 'zod' + +import type { + IndexCreateParams, + IndexDeleteByIDsParams, + IndexDimensionConfigurationParam, + IndexGetByIDsParams, + IndexInsertParams, + IndexQueryParams, + IndexUpsertParams, +} from 'cloudflare/resources/vectorize/indexes/indexes' + +/** Zod schema for a Vectorize Index name. */ +export const VectorizeIndexNameSchema = z + .string() + .min(1, 'Index name cannot be empty.') + .max(64, 'Index name cannot exceed 64 characters.') + .regex( + /^[a-zA-Z0-9_-]+$/, + 'Index name can only contain alphanumeric characters, underscores, and hyphens.' + ) + .describe('The unique name of the Vectorize Index.') + +/** Zod schema for a Vectorize Index description. */ +export const VectorizeIndexDescriptionSchema: z.ZodType = z + .string() + .max(1024, 'Description cannot exceed 1024 characters.') + .optional() + .describe('An optional description for the Vectorize Index.') + +/** Zod schema for Vectorize Index dimensions. */ +export const VectorizeIndexDimensionSchema: z.ZodType< + IndexDimensionConfigurationParam['dimensions'] +> = z + .number() + .int() + .positive('Dimensions must be a positive integer.') + .describe('The number of dimensions for the vectors in the index.') + +/** Zod schema for Vectorize Index distance metric. */ +export const VectorizeIndexMetricSchema: z.ZodType = z + .enum(['cosine', 'euclidean', 'dot-product']) + .describe('The distance metric to use for similarity calculations.') + +/** Zod schema for explicit dimension/metric configuration. */ +export const VectorizeIndexDimensionConfigSchema: z.ZodType = z + .object({ + dimensions: VectorizeIndexDimensionSchema, + metric: VectorizeIndexMetricSchema, + }) + .describe('Configuration specifying the dimensions and distance metric.') + +/** Zod schema for Vectorize Index preset models. */ +export const VectorizeIndexPresetSchema: z.ZodType< + IndexCreateParams.VectorizeIndexPresetConfiguration['preset'] +> = z.enum([ + '@cf/baai/bge-small-en-v1.5', + '@cf/baai/bge-base-en-v1.5', + '@cf/baai/bge-large-en-v1.5', + 'openai/text-embedding-ada-002', + 'cohere/embed-multilingual-v2.0', +]) + +/** Zod schema for preset-based configuration. */ +export const VectorizeIndexPresetConfigSchema: z.ZodType = + z + .object({ + preset: VectorizeIndexPresetSchema, + }) + .describe('Configuration specifying a pre-defined embedding model preset.') + +/** Zod schema for Vectorize Index configuration (either dimensions/metric or preset). */ +export const VectorizeIndexConfigSchema: z.ZodType = z + .union([VectorizeIndexDimensionConfigSchema, VectorizeIndexPresetConfigSchema]) + .describe( + 'The configuration for the Vectorize Index, specifying either dimensions/metric or a preset model.' + ) + +/** Zod schema for a list of vector IDs. */ +export const VectorizeVectorIdListSchema = z + .array(z.string().min(1)) + .min(1, 'At least one vector ID must be provided.') + .describe('A list of vector identifiers.') + +/** Zod schema for the NDJSON body used in insert/upsert operations. */ +export const VectorizeNdjsonBodySchema: z.ZodType< + IndexInsertParams['body'] | IndexUpsertParams['body'] +> = z + .string() + .min(1, 'NDJSON body cannot be empty.') + .describe( + 'A string containing newline-delimited JSON objects representing vectors to insert or upsert.' + ) + +/** Zod schema for handling unparsable lines in NDJSON. */ +export const VectorizeUnparsableBehaviorSchema: z.ZodType< + IndexInsertParams['unparsable-behavior'] | IndexUpsertParams['unparsable-behavior'] +> = z + .enum(['error', 'discard']) + .optional() + .describe('Behavior for handling unparsable lines in NDJSON input.') + +/** Zod schema for the query vector. */ +export const VectorizeQueryVectorSchema: z.ZodType = z + .array(z.number()) + .min(1, 'Query vector cannot be empty.') + .describe('The vector used to find nearest neighbors.') + +/** Zod schema for the query metadata filter. */ +export const VectorizeQueryFilterSchema: z.ZodType = z + .record(z.unknown()) // Using z.record(z.unknown()) to represent a generic JSON object + .optional() + .describe('A metadata filter expression (JSON object) used to limit search results.') + +/** Zod schema for controlling metadata return in queries. */ +export const VectorizeQueryReturnMetadataSchema: z.ZodType = z + .enum(['none', 'indexed', 'all']) + .optional() + .describe('Specifies whether to return no metadata, only indexed metadata, or all metadata.') + +/** Zod schema for controlling value return in queries. */ +export const VectorizeQueryReturnValuesSchema: z.ZodType = z + .boolean() + .optional() + .describe('Specifies whether to return the vector values themselves in the results.') + +/** Zod schema for the number of nearest neighbors to return in queries. */ +export const VectorizeQueryTopKSchema: z.ZodType = z + .number() + .int() + .positive('topK must be a positive integer.') + .optional() + .describe('The number of nearest neighbors to retrieve.') + +/** Zod schema for the page number for pagination. */ +export const VectorizeListPageParam = z // Corresponds roughly to PaginationPageParam in shared + .number() + .int() + .positive() + .optional() + .describe('Page number for pagination.') + +/** Zod schema for the number of items per page for pagination. */ +export const VectorizeListPerPageParam = z // Corresponds roughly to PaginationPerPageParam in shared + .number() + .int() + .positive() + .max(100) // Assuming a max page size, adjust if needed + .optional() + .describe('Number of indexes to return per page (max 100).') + +/** Zod schema for the order field for pagination. */ +export const VectorizeListOrderParam = z // Corresponds roughly to PaginationOrderParam in shared + .string() // Usually specific fields like 'name', 'created_on' - let LLM decide or refine later + .optional() + .describe('Field to order results by (e.g., "name", "created_on").') + +/** Zod schema for the direction for pagination. */ +export const VectorizeListDirectionParam = z // Corresponds roughly to PaginationDirectionParam in shared + .enum(['asc', 'desc']) + .optional() + .describe('Direction to order results (ascending or descending).') + +// Combine into a single schema for the list tool parameters (optional) +// Although the tool registration takes individual params, this can be useful internally +export const VectorizeIndexListParamsSchema = z.object({ + page: VectorizeListPageParam, + per_page: VectorizeListPerPageParam, + order: VectorizeListOrderParam, + direction: VectorizeListDirectionParam, +}) // Note: SDK IndexListParams only has account_id, these go in options.query From 0cfa4cad8b3d1cc2aa5d882ddd22ad319b3a0358 Mon Sep 17 00:00:00 2001 From: deloreyj Date: Wed, 30 Apr 2025 18:17:56 -0500 Subject: [PATCH 2/3] feat: vectorize tools --- .../evals/kv_namespaces.eval.ts | 10 +- apps/workers-bindings/evals/vectorize.eval.ts | 197 +++++++++++++ apps/workers-bindings/src/index.ts | 2 + packages/mcp-common/src/tools/vectorize.ts | 271 ++---------------- packages/mcp-common/src/types/vectorize.ts | 39 ++- 5 files changed, 238 insertions(+), 281 deletions(-) create mode 100644 apps/workers-bindings/evals/vectorize.eval.ts diff --git a/apps/workers-bindings/evals/kv_namespaces.eval.ts b/apps/workers-bindings/evals/kv_namespaces.eval.ts index bd9ecfd7..53f9cc64 100644 --- a/apps/workers-bindings/evals/kv_namespaces.eval.ts +++ b/apps/workers-bindings/evals/kv_namespaces.eval.ts @@ -29,7 +29,7 @@ eachModel('$modelName', ({ model }) => { }, scorers: [checkFactuality], threshold: 1, - timeout: 60000, // 60 seconds + timeout: 60000, }) describeEval('List Cloudflare KV Namespaces', { data: async () => [ @@ -51,7 +51,7 @@ eachModel('$modelName', ({ model }) => { }, scorers: [checkFactuality], threshold: 1, - timeout: 60000, // 60 seconds + timeout: 60000, }) describeEval('Rename Cloudflare KV Namespace', { data: async () => [ @@ -74,7 +74,7 @@ eachModel('$modelName', ({ model }) => { }, scorers: [checkFactuality], threshold: 1, - timeout: 60000, // 60 seconds + timeout: 60000, }) describeEval('Get Cloudflare KV Namespace Details', { data: async () => [ @@ -96,7 +96,7 @@ eachModel('$modelName', ({ model }) => { }, scorers: [checkFactuality], threshold: 1, - timeout: 60000, // 60 seconds + timeout: 60000, }) describeEval('Delete Cloudflare KV Namespace', { data: async () => [ @@ -118,6 +118,6 @@ eachModel('$modelName', ({ model }) => { }, scorers: [checkFactuality], threshold: 1, - timeout: 60000, // 60 seconds + timeout: 60000, }) }) diff --git a/apps/workers-bindings/evals/vectorize.eval.ts b/apps/workers-bindings/evals/vectorize.eval.ts new file mode 100644 index 00000000..b1ec3932 --- /dev/null +++ b/apps/workers-bindings/evals/vectorize.eval.ts @@ -0,0 +1,197 @@ +import { expect } from 'vitest' +import { describeEval } from 'vitest-evals' + +import { runTask } from '@repo/eval-tools/src/runTask' +import { checkFactuality } from '@repo/eval-tools/src/scorers' +import { eachModel } from '@repo/eval-tools/src/test-models' +import { VECTORIZE_TOOLS } from '@repo/mcp-common/src/tools/vectorize' + +import { initializeClient } from './utils' // Assuming utils.ts will exist here + +const MOCK_INDEX_NAME = 'test-vectorize-index' +const MOCK_INDEX_DESCRIPTION = 'A test index for evaluation' +const MOCK_DIMENSIONS = 32 +const MOCK_METRIC = 'cosine' +const MOCK_PRESET = '@cf/baai/bge-small-en-v1.5' + +eachModel('$modelName', ({ model }) => { + describeEval('Create Vectorize Index (Dimensions/Metric)', { + data: async () => [ + { + input: `Create a Vectorize index named "${MOCK_INDEX_NAME}" with ${MOCK_DIMENSIONS} dimensions using the "${MOCK_METRIC}" metric. Add description: "${MOCK_INDEX_DESCRIPTION}".`, + expected: `The ${VECTORIZE_TOOLS.vectorize_index_create} tool should be called with name "${MOCK_INDEX_NAME}", config specifying ${MOCK_DIMENSIONS} dimensions and "${MOCK_METRIC}" metric, and description "${MOCK_INDEX_DESCRIPTION}".`, + }, + ], + task: async (input: string) => { + const client = await initializeClient() + const { promptOutput, toolCalls } = await runTask(client, model, input) + const toolCall = toolCalls.find( + (call) => call.toolName === VECTORIZE_TOOLS.vectorize_index_create + ) + expect(toolCall, 'Tool vectorize_index_create was not called').toBeDefined() + expect(toolCall?.args, 'Arguments did not match').toEqual( + expect.objectContaining({ + name: MOCK_INDEX_NAME, + config: expect.objectContaining({ + dimensions: MOCK_DIMENSIONS, + metric: MOCK_METRIC, + }), + description: MOCK_INDEX_DESCRIPTION, + }) + ) + return promptOutput + }, + scorers: [checkFactuality], + threshold: 1, + timeout: 60000, + }) + + // --- Test vectorize_index_create (with preset) --- + describeEval('Create Vectorize Index (Preset)', { + data: async () => [ + { + input: `Create a Vectorize index named "${MOCK_INDEX_NAME}-preset" using the "${MOCK_PRESET}" preset.`, + expected: `The ${VECTORIZE_TOOLS.vectorize_index_create} tool should be called with name "${MOCK_INDEX_NAME}-preset" and config specifying the preset "${MOCK_PRESET}".`, + }, + ], + task: async (input: string) => { + const client = await initializeClient() + const { promptOutput, toolCalls } = await runTask(client, model, input) + const toolCall = toolCalls.find( + (call) => call.toolName === VECTORIZE_TOOLS.vectorize_index_create + ) + expect(toolCall, 'Tool vectorize_index_create was not called').toBeDefined() + expect(toolCall?.args, 'Arguments did not match').toEqual( + expect.objectContaining({ + name: `${MOCK_INDEX_NAME}-preset`, + config: expect.objectContaining({ + preset: MOCK_PRESET, + }), + }) + ) + return promptOutput + }, + scorers: [checkFactuality], + threshold: 1, + timeout: 60000, + }) + + // --- Test vectorize_index_list --- + describeEval('List Vectorize Indexes', { + data: async () => [ + { + input: 'List my Vectorize indexes.', + expected: `The ${VECTORIZE_TOOLS.vectorize_index_list} tool should be called.`, + }, + { + input: 'Show me page 2 of my Vectorize indexes, 10 per page, ordered by name descending.', + expected: + 'The ${VECTORIZE_TOOLS.vectorize_index_list} tool should be called with page 2, per_page 10, order name, direction desc.', + }, + ], + task: async (input: string) => { + const client = await initializeClient() + const { promptOutput, toolCalls } = await runTask(client, model, input) + const toolCall = toolCalls.find( + (call) => call.toolName === VECTORIZE_TOOLS.vectorize_index_list + ) + expect(toolCall, 'Tool vectorize_index_list was not called').toBeDefined() + + // Check specific args only for the pagination case + if (input.includes('page 2')) { + expect(toolCall?.args, 'Pagination arguments did not match').toEqual( + expect.objectContaining({ + page: 2, + per_page: 10, + order: 'name', + direction: 'desc', + }) + ) + } + + return promptOutput + }, + scorers: [checkFactuality], + threshold: 1, + timeout: 60000, + }) + + // --- Test vectorize_index_get --- + describeEval('Get Vectorize Index Details', { + data: async () => [ + { + input: `Get the details for the Vectorize index named "${MOCK_INDEX_NAME}".`, + expected: `The ${VECTORIZE_TOOLS.vectorize_index_get} tool should be called with name "${MOCK_INDEX_NAME}".`, + }, + ], + task: async (input: string) => { + const client = await initializeClient() + const { promptOutput, toolCalls } = await runTask(client, model, input) + const toolCall = toolCalls.find( + (call) => call.toolName === VECTORIZE_TOOLS.vectorize_index_get + ) + expect(toolCall, 'Tool vectorize_index_get was not called').toBeDefined() + expect(toolCall?.args, 'Arguments did not match').toEqual( + expect.objectContaining({ + name: MOCK_INDEX_NAME, + }) + ) + return promptOutput + }, + scorers: [checkFactuality], + threshold: 1, + timeout: 60000, + }) + + describeEval('Get Vectorize Index Info', { + data: async () => [ + { + input: `Get operational info for the Vectorize index "${MOCK_INDEX_NAME}".`, + expected: `The ${VECTORIZE_TOOLS.vectorize_index_info} tool should be called with name "${MOCK_INDEX_NAME}".`, + }, + ], + task: async (input: string) => { + const client = await initializeClient() + const { promptOutput, toolCalls } = await runTask(client, model, input) + const toolCall = toolCalls.find( + (call) => call.toolName === VECTORIZE_TOOLS.vectorize_index_info + ) + expect(toolCall, 'Tool vectorize_index_info was not called').toBeDefined() + expect(toolCall?.args, 'Arguments did not match').toEqual( + expect.objectContaining({ + name: MOCK_INDEX_NAME, + }) + ) + return promptOutput + }, + scorers: [checkFactuality], + threshold: 1, + timeout: 60000, + }) + + describeEval('Delete Vectorize Index', { + data: async () => [ + { + input: `Delete the Vectorize index named "${MOCK_INDEX_NAME}".`, + expected: `The ${VECTORIZE_TOOLS.vectorize_index_delete} tool should be called with name "${MOCK_INDEX_NAME}".`, + }, + ], + task: async (input: string) => { + const client = await initializeClient() + const { promptOutput, toolCalls } = await runTask(client, model, input) + const toolCall = toolCalls.find( + (call) => call.toolName === VECTORIZE_TOOLS.vectorize_index_delete + ) + expect(toolCall, 'Tool vectorize_index_delete was not called').toBeDefined() + expect(toolCall?.args, 'Arguments did not match').toEqual( + expect.objectContaining({ + name: MOCK_INDEX_NAME, + }) + ) + return promptOutput + }, + scorers: [checkFactuality], + threshold: 1, + timeout: 60000, + }) +}) diff --git a/apps/workers-bindings/src/index.ts b/apps/workers-bindings/src/index.ts index 66cb15a8..a21c4d68 100644 --- a/apps/workers-bindings/src/index.ts +++ b/apps/workers-bindings/src/index.ts @@ -15,6 +15,7 @@ import { registerD1Tools } from '@repo/mcp-common/src/tools/d1' import { registerHyperdriveTools } from '@repo/mcp-common/src/tools/hyperdrive' import { registerKVTools } from '@repo/mcp-common/src/tools/kv_namespace' import { registerR2BucketTools } from '@repo/mcp-common/src/tools/r2_bucket' +import { registerVectorizeTools } from '@repo/mcp-common/src/tools/vectorize' import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker' import { MetricsTracker } from '@repo/mcp-observability' @@ -74,6 +75,7 @@ export class WorkersBindingsMCP extends McpAgent { - try { - const account_id = await agent.getActiveAccountId() - if (!account_id) { - return MISSING_ACCOUNT_ID_RESPONSE - } - const client = getCloudflareClient(agent.props.accessToken) - - const result = await client.vectorize.indexes.insert(name, { - account_id, - body: vectors_ndjson, - 'unparsable-behavior': unparsable_behavior, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify(result ?? 'Insert operation initiated (no detailed response).'), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error inserting vectors into Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, - }, - ], - } - } - } - ) - - // --- vectorize_index_upsert --- - agent.server.tool( - 'vectorize_index_upsert', - 'Upserts vectors into a specified Vectorize Index using NDJSON format (inserts new, updates existing). Returns a mutation ID.', - { - name: VectorizeIndexNameSchema, - vectors_ndjson: VectorizeNdjsonBodySchema, - unparsable_behavior: VectorizeUnparsableBehaviorSchema, - }, - async ({ name, vectors_ndjson, unparsable_behavior }) => { - try { - const account_id = await agent.getActiveAccountId() - if (!account_id) { - return MISSING_ACCOUNT_ID_RESPONSE - } - const client = getCloudflareClient(agent.props.accessToken) - - const result = await client.vectorize.indexes.upsert(name, { - account_id, - body: vectors_ndjson, - 'unparsable-behavior': unparsable_behavior, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify(result ?? 'Upsert operation initiated (no detailed response).'), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error upserting vectors into Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, - }, - ], - } - } - } - ) - - // --- vectorize_index_query --- - agent.server.tool( - 'vectorize_index_query', - 'Finds vectors in an index that are closest (nearest neighbors) to a given query vector. Can optionally filter by metadata.', - { - name: VectorizeIndexNameSchema, - vector: VectorizeQueryVectorSchema, - filter: VectorizeQueryFilterSchema, - return_metadata: VectorizeQueryReturnMetadataSchema, - return_values: VectorizeQueryReturnValuesSchema, - top_k: VectorizeQueryTopKSchema, - }, - async ({ name, vector, filter, return_metadata, return_values, top_k }) => { - try { - const account_id = await agent.getActiveAccountId() - if (!account_id) { - return MISSING_ACCOUNT_ID_RESPONSE - } - const client = getCloudflareClient(agent.props.accessToken) - - const result = await client.vectorize.indexes.query(name, { - account_id, - vector, - filter, // Pass filter directly as SDK expects 'unknown' - returnMetadata: return_metadata, - returnValues: return_values, - topK: top_k, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify(result ?? 'Query executed, but no results returned.'), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error querying Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, - }, - ], - } - } - } - ) - - // --- vectorize_index_get_by_ids --- - agent.server.tool( - 'vectorize_index_get_by_ids', - 'Retrieves specific vectors from an index by their unique identifiers.', - { - name: VectorizeIndexNameSchema, - ids: VectorizeVectorIdListSchema, - }, - async ({ name, ids }) => { - try { - const account_id = await agent.getActiveAccountId() - if (!account_id) { - return MISSING_ACCOUNT_ID_RESPONSE - } - const client = getCloudflareClient(agent.props.accessToken) - - const result = await client.vectorize.indexes.getByIds(name, { - account_id, - ids, - }) - - // The SDK types this response as 'unknown', needs careful handling - if (result === null || result === undefined) { - return { - content: [ - { - type: 'text', - text: `Error: No vectors found for the provided IDs in index "${name}".`, - }, - ], - } - } - // Format success response - return { - content: [ - { - type: 'text', - text: JSON.stringify(result), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting vectors by ID from Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, - }, - ], - } - } - } - ) - - // --- vectorize_index_delete_by_ids --- - agent.server.tool( - 'vectorize_index_delete_by_ids', - 'Deletes specific vectors from an index by their unique identifiers. Returns a mutation ID.', - { - name: VectorizeIndexNameSchema, - ids: VectorizeVectorIdListSchema, - }, - async ({ name, ids }) => { - try { - const account_id = await agent.getActiveAccountId() - if (!account_id) { - return MISSING_ACCOUNT_ID_RESPONSE - } - const client = getCloudflareClient(agent.props.accessToken) - - const result = await client.vectorize.indexes.deleteByIds(name, { - account_id, - ids, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify( - result ?? 'Delete by IDs operation initiated (no detailed response).' - ), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error deleting vectors by ID from Vectorize Index "${name}": ${error instanceof Error ? error.message : String(error)}`, - }, - ], - } - } - } - ) } diff --git a/packages/mcp-common/src/types/vectorize.ts b/packages/mcp-common/src/types/vectorize.ts index 9ea5d562..ade49595 100644 --- a/packages/mcp-common/src/types/vectorize.ts +++ b/packages/mcp-common/src/types/vectorize.ts @@ -2,16 +2,14 @@ import { z } from 'zod' import type { IndexCreateParams, - IndexDeleteByIDsParams, IndexDimensionConfigurationParam, - IndexGetByIDsParams, IndexInsertParams, IndexQueryParams, IndexUpsertParams, } from 'cloudflare/resources/vectorize/indexes/indexes' /** Zod schema for a Vectorize Index name. */ -export const VectorizeIndexNameSchema = z +export const VectorizeIndexNameSchema: z.ZodType = z .string() .min(1, 'Index name cannot be empty.') .max(64, 'Index name cannot exceed 64 characters.') @@ -19,7 +17,7 @@ export const VectorizeIndexNameSchema = z /^[a-zA-Z0-9_-]+$/, 'Index name can only contain alphanumeric characters, underscores, and hyphens.' ) - .describe('The unique name of the Vectorize Index.') + .describe('The name of the Vectorize Index.') /** Zod schema for a Vectorize Index description. */ export const VectorizeIndexDescriptionSchema: z.ZodType = z @@ -34,6 +32,8 @@ export const VectorizeIndexDimensionSchema: z.ZodType< > = z .number() .int() + .min(32, 'Dimensions must be at least 32.') + .max(1536, 'Dimensions must be at most 1536.') .positive('Dimensions must be a positive integer.') .describe('The number of dimensions for the vectors in the index.') @@ -76,10 +76,12 @@ export const VectorizeIndexConfigSchema: z.ZodType 'The configuration for the Vectorize Index, specifying either dimensions/metric or a preset model.' ) -/** Zod schema for a list of vector IDs. */ +/** Zod schema for a list of vector IDs. Parameter itself is optional in tools. */ export const VectorizeVectorIdListSchema = z .array(z.string().min(1)) .min(1, 'At least one vector ID must be provided.') + .optional() + .nullable() .describe('A list of vector identifiers.') /** Zod schema for the NDJSON body used in insert/upsert operations. */ @@ -108,7 +110,7 @@ export const VectorizeQueryVectorSchema: z.ZodType = /** Zod schema for the query metadata filter. */ export const VectorizeQueryFilterSchema: z.ZodType = z - .record(z.unknown()) // Using z.record(z.unknown()) to represent a generic JSON object + .record(z.unknown()) .optional() .describe('A metadata filter expression (JSON object) used to limit search results.') @@ -133,39 +135,34 @@ export const VectorizeQueryTopKSchema: z.ZodType = z .describe('The number of nearest neighbors to retrieve.') /** Zod schema for the page number for pagination. */ -export const VectorizeListPageParam = z // Corresponds roughly to PaginationPageParam in shared +export const VectorizeListPageParam = z .number() .int() .positive() .optional() + .nullable() .describe('Page number for pagination.') /** Zod schema for the number of items per page for pagination. */ -export const VectorizeListPerPageParam = z // Corresponds roughly to PaginationPerPageParam in shared +export const VectorizeListPerPageParam = z .number() .int() .positive() - .max(100) // Assuming a max page size, adjust if needed + .max(100) .optional() + .nullable() .describe('Number of indexes to return per page (max 100).') /** Zod schema for the order field for pagination. */ -export const VectorizeListOrderParam = z // Corresponds roughly to PaginationOrderParam in shared - .string() // Usually specific fields like 'name', 'created_on' - let LLM decide or refine later +export const VectorizeListOrderParam = z + .string() .optional() + .nullable() .describe('Field to order results by (e.g., "name", "created_on").') /** Zod schema for the direction for pagination. */ -export const VectorizeListDirectionParam = z // Corresponds roughly to PaginationDirectionParam in shared +export const VectorizeListDirectionParam = z .enum(['asc', 'desc']) .optional() + .nullable() .describe('Direction to order results (ascending or descending).') - -// Combine into a single schema for the list tool parameters (optional) -// Although the tool registration takes individual params, this can be useful internally -export const VectorizeIndexListParamsSchema = z.object({ - page: VectorizeListPageParam, - per_page: VectorizeListPerPageParam, - order: VectorizeListOrderParam, - direction: VectorizeListDirectionParam, -}) // Note: SDK IndexListParams only has account_id, these go in options.query From df77a69da24614928dff764860824ab7b3e214df Mon Sep 17 00:00:00 2001 From: Matt Carey Date: Mon, 2 Mar 2026 15:03:44 +0000 Subject: [PATCH 3/3] Merge main and resolve conflicts - Resolve conflict in bindings.app.ts: keep both vectorize tools registration and docs tools from main - Rename vectorize.ts -> vectorize.tools.ts and types/vectorize.ts -> types/vectorize.types.ts to match main's naming convention - Update all imports accordingly --- .changeset/config.json | 6 +- .changeset/dark-plants-drive.md | 5 + .github/actions/setup/action.yml | 25 + .github/workflows/branches.yml | 47 + .github/workflows/evals.yml | 35 - .github/workflows/main.yml | 37 + .github/workflows/release.yml | 66 + .github/workflows/test-and-check.yml | 31 - .prettierignore | 1 + .syncpackrc.cjs | 6 + .vscode/launch.json | 26 + README.md | 46 +- apps/ai-gateway/.dev.vars.example | 2 +- apps/ai-gateway/CHANGELOG.md | 119 + apps/ai-gateway/CONTRIBUTING.md | 2 +- apps/ai-gateway/README.md | 4 +- apps/ai-gateway/package.json | 8 +- .../src/{index.ts => ai-gateway.app.ts} | 35 +- .../src/{context.ts => ai-gateway.context.ts} | 5 +- .../{ai-gateway.ts => ai-gateway.tools.ts} | 18 +- apps/ai-gateway/vitest.config.ts | 2 +- apps/ai-gateway/wrangler.jsonc | 8 +- apps/auditlogs/.dev.vars.example | 1 - apps/auditlogs/CHANGELOG.md | 125 + apps/auditlogs/README.md | 6 +- apps/auditlogs/package.json | 10 +- .../src/{index.ts => auditlogs.app.ts} | 35 +- .../src/{context.ts => auditlogs.context.ts} | 5 +- .../{auditlogs.ts => auditlogs.tools.ts} | 6 +- apps/auditlogs/vitest.config.ts | 2 +- apps/auditlogs/wrangler.jsonc | 8 +- apps/autorag/.dev.vars.example | 2 +- apps/autorag/CHANGELOG.md | 119 + apps/autorag/CONTRIBUTING.md | 2 +- apps/autorag/README.md | 4 +- apps/autorag/package.json | 8 +- apps/autorag/src/{index.ts => autorag.app.ts} | 35 +- .../src/{context.ts => autorag.context.ts} | 5 +- .../tools/{autorag.ts => autorag.tools.ts} | 17 +- apps/autorag/vitest.config.ts | 2 +- apps/autorag/wrangler.jsonc | 8 +- apps/browser-rendering/.dev.vars.example | 2 +- apps/browser-rendering/CHANGELOG.md | 119 + apps/browser-rendering/CONTRIBUTING.md | 2 +- apps/browser-rendering/README.md | 4 +- apps/browser-rendering/package.json | 8 +- .../src/{index.ts => browser.app.ts} | 35 +- .../src/{context.ts => browser.context.ts} | 5 +- .../tools/{browser.ts => browser.tools.ts} | 40 +- apps/browser-rendering/vitest.config.ts | 2 +- apps/browser-rendering/wrangler.jsonc | 8 +- apps/cloudflare-one-casb/.dev.vars.example | 1 - apps/cloudflare-one-casb/CHANGELOG.md | 112 + apps/cloudflare-one-casb/README.md | 2 +- apps/cloudflare-one-casb/package.json | 8 +- .../src/{index.ts => cf1-casb.app.ts} | 35 +- .../src/{context.ts => cf1-casb.context.ts} | 2 +- ...{integrations.ts => integrations.tools.ts} | 10 +- apps/cloudflare-one-casb/vitest.config.ts | 2 +- apps/cloudflare-one-casb/wrangler.jsonc | 8 +- apps/demo-day/CHANGELOG.md | 118 + apps/demo-day/package.json | 7 +- .../src/{index.ts => demo-day.app.ts} | 0 apps/demo-day/tsconfig.json | 2 +- apps/demo-day/wrangler.json | 8 +- apps/dex-analysis/.dev.vars.example | 1 - apps/dex-analysis/CHANGELOG.md | 149 + apps/dex-analysis/README.md | 157 +- apps/dex-analysis/package.json | 9 +- apps/dex-analysis/src/api/dex.ts | 47 - .../src/{index.ts => dex-analysis.app.ts} | 39 +- .../{context.ts => dex-analysis.context.ts} | 7 +- .../src/tools/dex-analysis.tools.ts | 673 ++ apps/dex-analysis/src/tools/dex.ts | 123 - apps/dex-analysis/src/warp_diag_reader.ts | 136 + apps/dex-analysis/vitest.config.ts | 2 +- apps/dex-analysis/wrangler.jsonc | 28 +- apps/dns-analytics/CHANGELOG.md | 125 + apps/dns-analytics/CONTRIBUTING.md | 4 +- apps/dns-analytics/README.md | 2 +- apps/dns-analytics/package.json | 10 +- .../src/{index.ts => dns-analytics.app.ts} | 37 +- .../{context.ts => dns-analytics.context.ts} | 5 +- .../{analytics.ts => dex-analytics.tools.ts} | 16 +- apps/dns-analytics/worker-configuration.d.ts | 8 +- apps/dns-analytics/wrangler.jsonc | 8 +- apps/docs-ai-search/.eslintrc.cjs | 5 + apps/docs-ai-search/CHANGELOG.md | 143 + apps/docs-ai-search/README.md | 42 + apps/docs-ai-search/package.json | 34 + apps/docs-ai-search/src/docs-ai-search.app.ts | 76 + .../src/docs-ai-search.context.ts | 14 + apps/docs-ai-search/tsconfig.json | 4 + apps/docs-ai-search/vitest.config.ts | 24 + apps/docs-ai-search/worker-configuration.d.ts | 5694 ++++++++++++++++ apps/docs-ai-search/wrangler.jsonc | 106 + apps/docs-autorag/CHANGELOG.md | 118 + apps/docs-autorag/README.md | 2 +- apps/docs-autorag/package.json | 8 +- .../src/{index.ts => docs-autorag.app.ts} | 4 +- .../{context.ts => docs-autorag.context.ts} | 2 +- .../tools/{docs.ts => docs-autorag.tools.ts} | 6 +- apps/docs-autorag/vitest.config.ts | 2 +- apps/docs-autorag/wrangler.jsonc | 8 +- apps/docs-vectorize/CHANGELOG.md | 140 + apps/docs-vectorize/README.md | 10 +- apps/docs-vectorize/package.json | 8 +- apps/docs-vectorize/src/docs-vectorize.app.ts | 76 + .../{context.ts => docs-vectorize.context.ts} | 6 +- apps/docs-vectorize/src/index.ts | 39 - apps/docs-vectorize/vitest.config.ts | 2 +- apps/docs-vectorize/wrangler.jsonc | 25 +- apps/graphql/.eslintrc.cjs | 5 + apps/graphql/CHANGELOG.md | 115 + apps/graphql/README.md | 45 + apps/graphql/package.json | 34 + apps/graphql/src/graphql.app.ts | 147 + apps/graphql/src/graphql.context.ts | 21 + apps/graphql/src/tools/graphql.tools.ts | 1119 ++++ apps/graphql/tsconfig.json | 4 + apps/graphql/types.d.ts | 5 + apps/graphql/worker-configuration.d.ts | 5694 ++++++++++++++++ apps/graphql/wrangler.jsonc | 132 + apps/logpush/.dev.vars.example | 1 - apps/logpush/CHANGELOG.md | 125 + apps/logpush/README.md | 4 +- apps/logpush/package.json | 10 +- apps/logpush/src/{index.ts => logpush.app.ts} | 35 +- .../src/{context.ts => logpush.context.ts} | 5 +- .../src/tools/{logs.ts => logpush.tools.ts} | 8 +- apps/logpush/vitest.config.ts | 2 +- apps/logpush/wrangler.jsonc | 8 +- apps/radar/.dev.vars.example | 2 +- apps/radar/CHANGELOG.md | 119 + apps/radar/CONTRIBUTING.md | 3 +- apps/radar/README.md | 191 +- apps/radar/package.json | 8 +- apps/radar/src/context.ts | 41 - apps/radar/src/{index.ts => radar.app.ts} | 39 +- apps/radar/src/radar.context.ts | 113 + apps/radar/src/tools/radar.tools.ts | 2976 +++++++++ apps/radar/src/tools/radar.ts | 730 -- apps/radar/src/tools/url-scanner.tools.ts | 324 + apps/radar/src/tools/url-scanner.ts | 94 - apps/radar/src/types/radar.ts | 753 ++- apps/radar/src/types/url-scanner.ts | 32 + apps/radar/vitest.config.ts | 2 +- apps/radar/wrangler.jsonc | 8 +- apps/sandbox-container/.dev.vars.example | 1 - apps/sandbox-container/CHANGELOG.md | 142 + apps/sandbox-container/CONTRIBUTING.md | 4 +- apps/sandbox-container/README.md | 2 +- .../{index.ts => sandbox.container.app.ts} | 0 apps/sandbox-container/package.json | 17 +- .../server/containerManager.ts | 5 +- apps/sandbox-container/server/containerMcp.ts | 46 +- .../{index.ts => sandbox.server.app.ts} | 13 +- .../{context.ts => sandbox.server.context.ts} | 6 +- .../sandbox-container/server/userContainer.ts | 2 +- apps/sandbox-container/types.d.ts | 3 + apps/sandbox-container/wrangler.jsonc | 8 +- apps/workers-bindings/.dev.vars.example | 1 - apps/workers-bindings/CHANGELOG.md | 146 + apps/workers-bindings/CONTRIBUTING.md | 3 +- apps/workers-bindings/README.md | 5 +- .../workers-bindings/evals/hyperdrive.eval.ts | 2 +- .../evals/kv_namespaces.eval.ts | 14 +- apps/workers-bindings/evals/vectorize.eval.ts | 2 +- apps/workers-bindings/package.json | 10 +- .../src/{index.ts => bindings.app.ts} | 55 +- .../src/{context.ts => bindings.context.ts} | 15 +- apps/workers-bindings/vitest.config.ts | 2 +- apps/workers-bindings/wrangler.jsonc | 35 +- apps/workers-builds/.dev.vars.example | 6 + apps/workers-builds/.eslintrc.cjs | 5 + apps/workers-builds/CHANGELOG.md | 125 + apps/workers-builds/CONTRIBUTING.md | 64 + apps/workers-builds/README.md | 50 + apps/workers-builds/package.json | 35 + .../src/tools/workers-builds.tools.ts | 282 + apps/workers-builds/src/workers-builds.app.ts | 203 + .../src/workers-builds.context.ts | 22 + apps/workers-builds/tsconfig.json | 4 + apps/workers-builds/types.d.ts | 5 + apps/workers-builds/vite.config.mts | 9 + apps/workers-builds/vitest.config.ts | 24 + apps/workers-builds/worker-configuration.d.ts | 5694 ++++++++++++++++ apps/workers-builds/wrangler.jsonc | 134 + apps/workers-observability/.dev.vars.example | 2 +- apps/workers-observability/CHANGELOG.md | 140 + apps/workers-observability/CONTRIBUTING.md | 4 +- apps/workers-observability/README.md | 4 +- apps/workers-observability/package.json | 11 +- ...lity.ts => workers-observability.tools.ts} | 32 +- ...{index.ts => workers-observability.app.ts} | 49 +- ...xt.ts => workers-observability.context.ts} | 7 +- apps/workers-observability/vitest.config.ts | 2 +- apps/workers-observability/wrangler.jsonc | 35 +- implementation-guides/tools.md | 2 +- implementation-guides/type-validators.md | 8 +- package.json | 20 +- packages/eslint-config/CHANGELOG.md | 13 + packages/eslint-config/package.json | 2 +- packages/eval-tools/CHANGELOG.md | 41 + packages/eval-tools/package.json | 7 +- packages/eval-tools/src/runTask.ts | 15 +- packages/eval-tools/src/test-models.ts | 62 +- packages/eval-tools/wrangler.json | 5 +- packages/mcp-common/CHANGELOG.md | 108 + packages/mcp-common/package.json | 11 +- packages/mcp-common/src/api-token-mode.ts | 75 + .../src/api/{account.ts => account.api.ts} | 9 +- ...-integration.ts => cf1-integration.api.ts} | 4 +- .../mcp-common/src/api/workers-builds.api.ts | 88 + ...bility.ts => workers-observability.api.ts} | 4 +- .../src/api/{workers.ts => workers.api.ts} | 28 + .../src/api/{zone.ts => zone.api.ts} | 0 packages/mcp-common/src/cloudflare-api.ts | 8 +- packages/mcp-common/src/cloudflare-auth.ts | 11 +- .../src/cloudflare-oauth-handler.ts | 508 +- packages/mcp-common/src/dev-mode.ts | 24 - .../{user_details.ts => user_details.do.ts} | 0 packages/mcp-common/src/format.spec.ts | 126 + packages/mcp-common/src/format.ts | 35 + packages/mcp-common/src/get-props.ts | 9 + packages/mcp-common/src/{utils => }/poll.ts | 0 .../src/prompts/docs-ai-search.prompts.ts | 27 + .../src/prompts/docs-vectorize.prompts.ts | 27 + packages/mcp-common/src/sentry.ts | 2 +- .../mcp-common/src/tools/account.tools.ts | 118 + packages/mcp-common/src/tools/account.ts | 94 - .../src/tools/{d1.ts => d1.tools.ts} | 55 +- .../src/tools/docs-ai-search.tools.ts | 218 + .../src/tools/docs-vectorize.tools.ts | 77 +- .../{hyperdrive.ts => hyperdrive.tools.ts} | 46 +- ...{kv_namespace.ts => kv_namespace.tools.ts} | 55 +- .../{r2_bucket.ts => r2_bucket.tools.ts} | 81 +- .../{vectorize.ts => vectorize.tools.ts} | 4 +- packages/mcp-common/src/tools/worker.tools.ts | 228 + packages/mcp-common/src/tools/worker.ts | 119 - .../src/tools/{zone.ts => zone.tools.ts} | 25 +- .../cf1-integrations.types.ts} | 0 ...agent.ts => cloudflare-mcp-agent.types.ts} | 16 +- .../src/types/{d1.ts => d1.types.ts} | 0 .../{hyperdrive.ts => hyperdrive.types.ts} | 0 ...{kv_namespace.ts => kv_namespace.types.ts} | 0 .../{r2_bucket.ts => r2_bucket.types.ts} | 0 .../src/types/{shared.ts => shared.types.ts} | 0 .../src/types/{tools.ts => tools.types.ts} | 0 .../{vectorize.ts => vectorize.types.ts} | 0 .../src/types/workers-builds.types.ts | 96 + ...-logs-schemas.ts => workers-logs.types.ts} | 65 +- .../mcp-common/src/types/workers.types.ts | 19 + packages/mcp-common/src/utils.spec.ts | 35 + packages/mcp-common/src/utils.ts | 40 + packages/mcp-common/src/v4-api.ts | 52 +- .../mcp-common/src/workers-oauth-utils.ts | 779 +++ packages/mcp-observability/CHANGELOG.md | 41 + packages/mcp-observability/package.json | 4 +- packages/tools/.eslintrc.cjs | 5 + packages/tools/CHANGELOG.md | 41 + packages/tools/README.md | 36 + packages/tools/bin/run-changeset-new | 18 + packages/tools/bin/run-wrangler-deploy | 5 +- packages/tools/bin/runx | 13 + packages/tools/package.json | 15 +- packages/tools/src/bin/runx.ts | 20 + packages/tools/src/changesets.spec.ts | 72 + packages/tools/src/changesets.ts | 42 + .../src/cmd/deploy-published-packages.ts | 26 + packages/tools/src/proc.ts | 7 + .../test/fixtures/changesets/empty/.gitkeep | 0 .../invalid-json/published-packages.json | 1 + .../invalid-schema/published-packages.json | 6 + .../changesets/valid/published-packages.json | 10 + packages/tools/src/test/setup.ts | 7 + packages/tools/src/tsconfig.ts | 71 + packages/tools/tsconfig.json | 5 + packages/tools/vitest.config.ts | 8 + packages/typescript-config/CHANGELOG.md | 31 + packages/typescript-config/package.json | 5 +- pnpm-lock.yaml | 5847 ++++++++++++++++- server.json | 358 + turbo.json | 34 +- 284 files changed, 37673 insertions(+), 2655 deletions(-) create mode 100644 .changeset/dark-plants-drive.md create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/branches.yml delete mode 100644 .github/workflows/evals.yml create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/test-and-check.yml create mode 100644 apps/ai-gateway/CHANGELOG.md rename apps/ai-gateway/src/{index.ts => ai-gateway.app.ts} (74%) rename apps/ai-gateway/src/{context.ts => ai-gateway.context.ts} (82%) rename apps/ai-gateway/src/tools/{ai-gateway.ts => ai-gateway.tools.ts} (89%) create mode 100644 apps/auditlogs/CHANGELOG.md rename apps/auditlogs/src/{index.ts => auditlogs.app.ts} (75%) rename apps/auditlogs/src/{context.ts => auditlogs.context.ts} (83%) rename apps/auditlogs/src/tools/{auditlogs.ts => auditlogs.tools.ts} (97%) create mode 100644 apps/autorag/CHANGELOG.md rename apps/autorag/src/{index.ts => autorag.app.ts} (74%) rename apps/autorag/src/{context.ts => autorag.context.ts} (82%) rename apps/autorag/src/tools/{autorag.ts => autorag.tools.ts} (89%) create mode 100644 apps/browser-rendering/CHANGELOG.md rename apps/browser-rendering/src/{index.ts => browser.app.ts} (75%) rename apps/browser-rendering/src/{context.ts => browser.context.ts} (82%) rename apps/browser-rendering/src/tools/{browser.ts => browser.tools.ts} (77%) create mode 100644 apps/cloudflare-one-casb/CHANGELOG.md rename apps/cloudflare-one-casb/src/{index.ts => cf1-casb.app.ts} (74%) rename apps/cloudflare-one-casb/src/{context.ts => cf1-casb.context.ts} (87%) rename apps/cloudflare-one-casb/src/tools/{integrations.ts => integrations.tools.ts} (96%) create mode 100644 apps/demo-day/CHANGELOG.md rename apps/demo-day/src/{index.ts => demo-day.app.ts} (100%) create mode 100644 apps/dex-analysis/CHANGELOG.md delete mode 100644 apps/dex-analysis/src/api/dex.ts rename apps/dex-analysis/src/{index.ts => dex-analysis.app.ts} (71%) rename apps/dex-analysis/src/{context.ts => dex-analysis.context.ts} (68%) create mode 100644 apps/dex-analysis/src/tools/dex-analysis.tools.ts delete mode 100644 apps/dex-analysis/src/tools/dex.ts create mode 100644 apps/dex-analysis/src/warp_diag_reader.ts create mode 100644 apps/dns-analytics/CHANGELOG.md rename apps/dns-analytics/src/{index.ts => dns-analytics.app.ts} (75%) rename apps/dns-analytics/src/{context.ts => dns-analytics.context.ts} (84%) rename apps/dns-analytics/src/tools/{analytics.ts => dex-analytics.tools.ts} (88%) create mode 100644 apps/docs-ai-search/.eslintrc.cjs create mode 100644 apps/docs-ai-search/CHANGELOG.md create mode 100644 apps/docs-ai-search/README.md create mode 100644 apps/docs-ai-search/package.json create mode 100644 apps/docs-ai-search/src/docs-ai-search.app.ts create mode 100644 apps/docs-ai-search/src/docs-ai-search.context.ts create mode 100644 apps/docs-ai-search/tsconfig.json create mode 100644 apps/docs-ai-search/vitest.config.ts create mode 100644 apps/docs-ai-search/worker-configuration.d.ts create mode 100644 apps/docs-ai-search/wrangler.jsonc create mode 100644 apps/docs-autorag/CHANGELOG.md rename apps/docs-autorag/src/{index.ts => docs-autorag.app.ts} (87%) rename apps/docs-autorag/src/{context.ts => docs-autorag.context.ts} (80%) rename apps/docs-autorag/src/tools/{docs.ts => docs-autorag.tools.ts} (95%) create mode 100644 apps/docs-vectorize/CHANGELOG.md create mode 100644 apps/docs-vectorize/src/docs-vectorize.app.ts rename apps/docs-vectorize/src/{context.ts => docs-vectorize.context.ts} (60%) delete mode 100644 apps/docs-vectorize/src/index.ts create mode 100644 apps/graphql/.eslintrc.cjs create mode 100644 apps/graphql/CHANGELOG.md create mode 100644 apps/graphql/README.md create mode 100644 apps/graphql/package.json create mode 100644 apps/graphql/src/graphql.app.ts create mode 100644 apps/graphql/src/graphql.context.ts create mode 100644 apps/graphql/src/tools/graphql.tools.ts create mode 100644 apps/graphql/tsconfig.json create mode 100644 apps/graphql/types.d.ts create mode 100644 apps/graphql/worker-configuration.d.ts create mode 100644 apps/graphql/wrangler.jsonc create mode 100644 apps/logpush/CHANGELOG.md rename apps/logpush/src/{index.ts => logpush.app.ts} (76%) rename apps/logpush/src/{context.ts => logpush.context.ts} (83%) rename apps/logpush/src/tools/{logs.ts => logpush.tools.ts} (96%) create mode 100644 apps/radar/CHANGELOG.md delete mode 100644 apps/radar/src/context.ts rename apps/radar/src/{index.ts => radar.app.ts} (73%) create mode 100644 apps/radar/src/radar.context.ts create mode 100644 apps/radar/src/tools/radar.tools.ts delete mode 100644 apps/radar/src/tools/radar.ts create mode 100644 apps/radar/src/tools/url-scanner.tools.ts delete mode 100644 apps/radar/src/tools/url-scanner.ts create mode 100644 apps/sandbox-container/CHANGELOG.md rename apps/sandbox-container/container/{index.ts => sandbox.container.app.ts} (100%) rename apps/sandbox-container/server/{index.ts => sandbox.server.app.ts} (82%) rename apps/sandbox-container/server/{context.ts => sandbox.server.context.ts} (80%) create mode 100644 apps/workers-bindings/CHANGELOG.md rename apps/workers-bindings/src/{index.ts => bindings.app.ts} (72%) rename apps/workers-bindings/src/{context.ts => bindings.context.ts} (72%) create mode 100644 apps/workers-builds/.dev.vars.example create mode 100644 apps/workers-builds/.eslintrc.cjs create mode 100644 apps/workers-builds/CHANGELOG.md create mode 100644 apps/workers-builds/CONTRIBUTING.md create mode 100644 apps/workers-builds/README.md create mode 100644 apps/workers-builds/package.json create mode 100644 apps/workers-builds/src/tools/workers-builds.tools.ts create mode 100644 apps/workers-builds/src/workers-builds.app.ts create mode 100644 apps/workers-builds/src/workers-builds.context.ts create mode 100644 apps/workers-builds/tsconfig.json create mode 100644 apps/workers-builds/types.d.ts create mode 100644 apps/workers-builds/vite.config.mts create mode 100644 apps/workers-builds/vitest.config.ts create mode 100644 apps/workers-builds/worker-configuration.d.ts create mode 100644 apps/workers-builds/wrangler.jsonc create mode 100644 apps/workers-observability/CHANGELOG.md rename apps/workers-observability/src/tools/{observability.ts => workers-observability.tools.ts} (85%) rename apps/workers-observability/src/{index.ts => workers-observability.app.ts} (71%) rename apps/workers-observability/src/{context.ts => workers-observability.context.ts} (79%) create mode 100644 packages/eslint-config/CHANGELOG.md create mode 100644 packages/eval-tools/CHANGELOG.md create mode 100644 packages/mcp-common/CHANGELOG.md create mode 100644 packages/mcp-common/src/api-token-mode.ts rename packages/mcp-common/src/api/{account.ts => account.api.ts} (87%) rename packages/mcp-common/src/api/{cf1-integration.ts => cf1-integration.api.ts} (98%) create mode 100644 packages/mcp-common/src/api/workers-builds.api.ts rename packages/mcp-common/src/api/{workers-observability.ts => workers-observability.api.ts} (96%) rename packages/mcp-common/src/api/{workers.ts => workers.api.ts} (57%) rename packages/mcp-common/src/api/{zone.ts => zone.api.ts} (100%) delete mode 100644 packages/mcp-common/src/dev-mode.ts rename packages/mcp-common/src/durable-objects/{user_details.ts => user_details.do.ts} (100%) create mode 100644 packages/mcp-common/src/format.spec.ts create mode 100644 packages/mcp-common/src/format.ts create mode 100644 packages/mcp-common/src/get-props.ts rename packages/mcp-common/src/{utils => }/poll.ts (100%) create mode 100644 packages/mcp-common/src/prompts/docs-ai-search.prompts.ts create mode 100644 packages/mcp-common/src/prompts/docs-vectorize.prompts.ts create mode 100644 packages/mcp-common/src/tools/account.tools.ts delete mode 100644 packages/mcp-common/src/tools/account.ts rename packages/mcp-common/src/tools/{d1.ts => d1.tools.ts} (80%) create mode 100644 packages/mcp-common/src/tools/docs-ai-search.tools.ts rename apps/docs-vectorize/src/tools/docs.ts => packages/mcp-common/src/tools/docs-vectorize.tools.ts (57%) rename packages/mcp-common/src/tools/{hyperdrive.ts => hyperdrive.tools.ts} (90%) rename packages/mcp-common/src/tools/{kv_namespace.ts => kv_namespace.tools.ts} (82%) rename packages/mcp-common/src/tools/{r2_bucket.ts => r2_bucket.tools.ts} (90%) rename packages/mcp-common/src/tools/{vectorize.ts => vectorize.tools.ts} (99%) create mode 100644 packages/mcp-common/src/tools/worker.tools.ts delete mode 100644 packages/mcp-common/src/tools/worker.ts rename packages/mcp-common/src/tools/{zone.ts => zone.tools.ts} (85%) rename packages/mcp-common/src/{schemas/cf1-integrations.ts => types/cf1-integrations.types.ts} (100%) rename packages/mcp-common/src/types/{cloudflare-mcp-agent.ts => cloudflare-mcp-agent.types.ts} (63%) rename packages/mcp-common/src/types/{d1.ts => d1.types.ts} (100%) rename packages/mcp-common/src/types/{hyperdrive.ts => hyperdrive.types.ts} (100%) rename packages/mcp-common/src/types/{kv_namespace.ts => kv_namespace.types.ts} (100%) rename packages/mcp-common/src/types/{r2_bucket.ts => r2_bucket.types.ts} (100%) rename packages/mcp-common/src/types/{shared.ts => shared.types.ts} (100%) rename packages/mcp-common/src/types/{tools.ts => tools.types.ts} (100%) rename packages/mcp-common/src/types/{vectorize.ts => vectorize.types.ts} (100%) create mode 100644 packages/mcp-common/src/types/workers-builds.types.ts rename packages/mcp-common/src/types/{workers-logs-schemas.ts => workers-logs.types.ts} (83%) create mode 100644 packages/mcp-common/src/types/workers.types.ts create mode 100644 packages/mcp-common/src/utils.spec.ts create mode 100644 packages/mcp-common/src/utils.ts create mode 100644 packages/mcp-common/src/workers-oauth-utils.ts create mode 100644 packages/mcp-observability/CHANGELOG.md create mode 100644 packages/tools/.eslintrc.cjs create mode 100644 packages/tools/CHANGELOG.md create mode 100644 packages/tools/README.md create mode 100755 packages/tools/bin/run-changeset-new create mode 100755 packages/tools/bin/runx create mode 100644 packages/tools/src/bin/runx.ts create mode 100644 packages/tools/src/changesets.spec.ts create mode 100644 packages/tools/src/changesets.ts create mode 100644 packages/tools/src/cmd/deploy-published-packages.ts create mode 100644 packages/tools/src/proc.ts create mode 100644 packages/tools/src/test/fixtures/changesets/empty/.gitkeep create mode 100644 packages/tools/src/test/fixtures/changesets/invalid-json/published-packages.json create mode 100644 packages/tools/src/test/fixtures/changesets/invalid-schema/published-packages.json create mode 100644 packages/tools/src/test/fixtures/changesets/valid/published-packages.json create mode 100644 packages/tools/src/test/setup.ts create mode 100644 packages/tools/src/tsconfig.ts create mode 100644 packages/tools/tsconfig.json create mode 100644 packages/tools/vitest.config.ts create mode 100644 packages/typescript-config/CHANGELOG.md create mode 100644 server.json diff --git a/.changeset/config.json b/.changeset/config.json index 432b15a0..15d20aa4 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,9 @@ "access": "restricted", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": [], + "privatePackages": { + "version": true, + "tag": true + } } diff --git a/.changeset/dark-plants-drive.md b/.changeset/dark-plants-drive.md new file mode 100644 index 00000000..8e8132d1 --- /dev/null +++ b/.changeset/dark-plants-drive.md @@ -0,0 +1,5 @@ +--- +'dex-analysis': minor +--- + +Stronger validation of DEX tool params diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 00000000..7ada2967 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,25 @@ +name: 'Setup Node.js Environment' +description: 'Install pnpm, Node.js, and project dependencies' + +inputs: + node-version: + description: 'Node.js version to use' + required: false + default: '22' + +runs: + using: "composite" + steps: + - name: Install pnpm + # note: version is inferred from the packageManager field in package.json + uses: pnpm/action-setup@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: 'pnpm' + + - name: Install dependencies + shell: bash + run: pnpm install --frozen-lockfile --child-concurrency=10 diff --git a/.github/workflows/branches.yml b/.github/workflows/branches.yml new file mode 100644 index 00000000..d88bfa34 --- /dev/null +++ b/.github/workflows/branches.yml @@ -0,0 +1,47 @@ +name: CI +on: + push: + branches-ignore: ['main'] + pull_request: + branches: ['main'] + +env: + FORCE_COLOR: 1 + +jobs: + test: + name: Test & Check + runs-on: ubuntu-24.04 + permissions: + contents: read + timeout-minutes: 10 + strategy: + matrix: + node-version: [20, 22] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + with: + node-version: ${{ matrix.node-version }} + + - name: Syncpack lint + run: pnpm check:deps + - name: Run linter + run: pnpm check:turbo + - name: Run linter (formatting) + run: pnpm check:format + - name: Run tests + run: pnpm test + + build-workers: + name: Build Workers + runs-on: ubuntu-24.04 + permissions: + contents: read + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + + - name: Build Workers + run: pnpm turbo deploy -- -e staging --dry-run \ No newline at end of file diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml deleted file mode 100644 index b303cdef..00000000 --- a/.github/workflows/evals.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Evals -on: - push: - -jobs: - eval: - runs-on: ubuntu-24.04 - strategy: - matrix: - node-version: [22] - steps: - - uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.8.0 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' - - name: Create .dev.vars file - run: | - echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > ./apps/sandbox-container/.dev.vars - echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > ./apps/workers-bindings/.dev.vars - echo "DEV_CLOUDFLARE_API_TOKEN=${{ secrets.DEV_CLOUDFLARE_API_TOKEN }}" >> ./apps/sandbox-container/.dev.vars - echo "DEV_CLOUDFLARE_API_TOKEN=${{ secrets.DEV_CLOUDFLARE_API_TOKEN }}" >> ./apps/workers-bindings/.dev.vars - - name: Verify .dev.vars file - run: | - du -h ./apps/sandbox-container/.dev.vars - du -h ./apps/workers-bindings/.dev.vars - - name: Install dependencies - run: pnpm install - - name: Run evals - run: pnpm eval:ci diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..69cbd4b7 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,37 @@ +name: Main + +on: + push: + branches: ['main'] + +env: + FORCE_COLOR: 1 + +jobs: + deploy-staging: + name: Deploy (staging) + runs-on: ubuntu-24.04 + permissions: + contents: read + timeout-minutes: 10 + concurrency: ${{ github.workflow }}-deploy-staging + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + - uses: ./.github/actions/setup + + # Run tests & checks before deploying + - name: Syncpack lint + run: pnpm check:deps + - name: Run linter + run: pnpm check:turbo + - name: Run linter (formatting) + run: pnpm check:format + - name: Run tests + run: pnpm test + + - name: Deploy Workers (staging) + run: pnpm turbo deploy -- -e staging + env: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..20926ece --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,66 @@ +name: Release + +on: + push: + branches: ['main'] + +env: + FORCE_COLOR: 1 + +jobs: + create-release-pr: + name: Create Release PR + runs-on: ubuntu-24.04 + permissions: + contents: write + pull-requests: write + timeout-minutes: 5 + concurrency: ${{ github.workflow }}-create-release-pr + outputs: + published: ${{ steps.create-release-pr.outputs.published }} + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + - uses: ./.github/actions/setup + - name: Create Release PR + id: create-release-pr + uses: changesets/action@v1 + with: + publish: pnpm changeset publish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Save Published Packages + if: steps.create-release-pr.outputs.published == 'true' + run: | + echo '${{steps.create-release-pr.outputs.publishedPackages}}' \ + > ${{ github.workspace }}/published-packages.json + - name: Upload Published Packages + if: steps.create-release-pr.outputs.published == 'true' + uses: actions/upload-artifact@v4 + with: + name: published-packages + path: ${{ github.workspace }}/published-packages.json + + deploy-production: + name: Deploy (production) + needs: create-release-pr + if: needs.create-release-pr.outputs.published == 'true' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + concurrency: ${{ github.workflow }}-deploy-production + permissions: + contents: read + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + - name: Download published packages + uses: actions/download-artifact@v4 + with: + name: published-packages + path: ${{ runner.temp }} + - uses: ./.github/actions/setup + - name: Deploy Published Workers (production) + run: pnpm runx deploy-published-workers --env production + env: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/test-and-check.yml b/.github/workflows/test-and-check.yml deleted file mode 100644 index 6425dfd8..00000000 --- a/.github/workflows/test-and-check.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Test and check -on: - push: - -jobs: - test: - runs-on: ubuntu-24.04 - strategy: - matrix: - node-version: [20, 22] - steps: - - uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.8.0 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' - - name: Install dependencies - run: pnpm install - - name: Syncpack lint - run: pnpm syncpack lint - - name: Run linter - run: pnpm check:turbo - - name: Run linter (formatting) - run: pnpm check:format - - name: Run tests - run: pnpm test diff --git a/.prettierignore b/.prettierignore index 062d9d6d..3a9059c6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,3 +5,4 @@ vitest.config.ts.timestamp* vite.config.ts.timestamp* worker-configuration.d.ts **/dist/** +packages/tools/src/test/fixtures/changesets/invalid-json/*.json diff --git a/.syncpackrc.cjs b/.syncpackrc.cjs index c126f7da..0e175a84 100644 --- a/.syncpackrc.cjs +++ b/.syncpackrc.cjs @@ -45,6 +45,12 @@ const config = { // snapTo removes it from syncpack update list, which is the main goal snapTo: ['@repo/eslint-config'], }, + { + label: 'use zod v4 in packages/tools', + dependencies: ['zod'], + pinVersion: '4.0.0-beta.20250505T195954', + packages: ['@repo/tools'], + }, ], semverGroups: [ { diff --git a/.vscode/launch.json b/.vscode/launch.json index 54ebd676..60caad93 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,6 +10,32 @@ "attachExistingChildren": false, "autoAttachChildProcesses": false, "sourceMaps": true // works with or without this line + }, + { + "type": "node", + "request": "launch", + "name": "Open inspector with Vitest", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "eval:dev"], + "console": "integratedTerminal", + "cwd": "${workspaceFolder}/apps/workers-bindings" + }, + { + "name": "Attach to Workers Runtime", + "type": "node", + "request": "attach", + "port": 9229, + "cwd": "/", + "resolveSourceMapLocations": null, + "attachExistingChildren": false, + "autoAttachChildProcesses": false + } + ], + "compounds": [ + { + "name": "Debug Workers tests", + "configurations": ["Open inspector with Vitest", "Attach to Workers Runtime"], + "stopAll": true } ] } diff --git a/README.md b/README.md index 0f85134a..ac7b7d36 100644 --- a/README.md +++ b/README.md @@ -2,47 +2,59 @@ Model Context Protocol (MCP) is a [new, standardized protocol](https://modelcontextprotocol.io/introduction) for managing context between large language models (LLMs) and external systems. In this repository, you can find several MCP servers allowing you to connect to Cloudflare's service from an MCP client (e.g. Cursor, Claude) and use natural language to accomplish tasks through your Cloudflare account. -These MCP servers allow your [MCP Client](https://modelcontextprotocol.io/clients) to read configurations from your account, process information, make suggestions based on data, and even make those suggested changes for you. All of these actions can happen across cloudflare's many services including application development, security and performance. +These MCP servers allow your [MCP Client](https://modelcontextprotocol.io/clients) to read configurations from your account, process information, make suggestions based on data, and even make those suggested changes for you. All of these actions can happen across Cloudflare's many services including application development, security and performance. + +They support both the `streamble-http` transport via `/mcp` and the `sse` transport (deprecated) via `/sse`. The following servers are included in this repository: | Server Name | Description | Server URL | | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------- | -| [**Documentation server**](/apps/docs-vectorize) | Get up to date reference information on Cloudflare | `https://docs.mcp.cloudflare.com/sse` | -| [**Workers Bindings server**](/apps/workers-bindings) | Build Workers applications with storage, AI, and compute primitives | `https://bindings.mcp.cloudflare.com/sse` | -| [**Observability server**](/apps/workers-observability) | Debug and get insight into your application’s logs and analytics | `https://observability.mcp.cloudflare.com/sse` | -| [**Radar server**](/apps/radar) | Get global Internet traffic insights, trends, URL scans, and other utilities | `https://radar.mcp.cloudflare.com/sse` | -| [**Container server**](/apps/sandbox-container) | Spin up a sandbox development environment | `https://containers.mcp.cloudflare.com/sse` | -| [**Browser rendering server**](/apps/browser-rendering) | Fetch web pages, convert them to markdown and take screenshots | `https://browser.mcp.cloudflare.com/sse` | -| [**Logpush server**](/apps/logpush) | Get quick summaries for Logpush job health | `https://logs.mcp.cloudflare.com/sse` | -| [**AI Gateway server**](/apps/ai-gateway) | Search your logs, get details about the prompts and responses | `https://ai-gateway.mcp.cloudflare.com/sse` | -| [**AutoRAG server**](/apps/autorag) | List and search documents on your AutoRAGs | `https://autorag.mcp.cloudflare.com/sse` | -| [**Audit Logs server**](/apps/auditlogs) | Query audit logs and generate reports for review | `https://auditlogs.mcp.cloudflare.com/sse` | -| [**DNS Analytics server**](/apps/dns-analytics) | Optimize DNS performance and debug issues based on current set up | `https://dns-analytics.mcp.cloudflare.com/sse` | -| [**Digital Experience Monitoring server**](/apps/dex-analysis) | Get quick insight on critical applications for your organization | `https://dex.mcp.cloudflare.com/sse` | -| [**Cloudflare One CASB server**](/apps/cloudflare-one-casb) | Quickly identify any security misconfigurations for SaaS applications to safeguard users & data | `https://casb.mcp.cloudflare.com/sse` | +| [**Documentation server**](/apps/docs-vectorize) | Get up to date reference information on Cloudflare | `https://docs.mcp.cloudflare.com/mcp` | +| [**Workers Bindings server**](/apps/workers-bindings) | Build Workers applications with storage, AI, and compute primitives | `https://bindings.mcp.cloudflare.com/mcp` | +| [**Workers Builds server**](/apps/workers-builds) | Get insights and manage your Cloudflare Workers Builds | `https://builds.mcp.cloudflare.com/mcp` | +| [**Observability server**](/apps/workers-observability) | Debug and get insight into your application's logs and analytics | `https://observability.mcp.cloudflare.com/mcp` | +| [**Radar server**](/apps/radar) | Get global Internet traffic insights, trends, URL scans, and other utilities | `https://radar.mcp.cloudflare.com/mcp` | +| [**Container server**](/apps/sandbox-container) | Spin up a sandbox development environment | `https://containers.mcp.cloudflare.com/mcp` | +| [**Browser rendering server**](/apps/browser-rendering) | Fetch web pages, convert them to markdown and take screenshots | `https://browser.mcp.cloudflare.com/mcp` | +| [**Logpush server**](/apps/logpush) | Get quick summaries for Logpush job health | `https://logs.mcp.cloudflare.com/mcp` | +| [**AI Gateway server**](/apps/ai-gateway) | Search your logs, get details about the prompts and responses | `https://ai-gateway.mcp.cloudflare.com/mcp` | +| [**AutoRAG server**](/apps/autorag) | List and search documents on your AutoRAGs | `https://autorag.mcp.cloudflare.com/mcp` | +| [**Audit Logs server**](/apps/auditlogs) | Query audit logs and generate reports for review | `https://auditlogs.mcp.cloudflare.com/mcp` | +| [**DNS Analytics server**](/apps/dns-analytics) | Optimize DNS performance and debug issues based on current set up | `https://dns-analytics.mcp.cloudflare.com/mcp` | +| [**Digital Experience Monitoring server**](/apps/dex-analysis) | Get quick insight on critical applications for your organization | `https://dex.mcp.cloudflare.com/mcp` | +| [**Cloudflare One CASB server**](/apps/cloudflare-one-casb) | Quickly identify any security misconfigurations for SaaS applications to safeguard users & data | `https://casb.mcp.cloudflare.com/mcp` | +| [**GraphQL server**](/apps/graphql/) | Get analytics data using Cloudflare’s GraphQL API | `https://graphql.mcp.cloudflare.com/mcp` | ## Access the remote MCP server from any MCP client If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL directly within its interface (e.g. [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)) -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. ```json { "mcpServers": { "cloudflare-observability": { "command": "npx", - "args": ["mcp-remote", "https://observability.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://observability.mcp.cloudflare.com/mcp"] }, "cloudflare-bindings": { "command": "npx", - "args": ["mcp-remote", "https://bindings.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://bindings.mcp.cloudflare.com/mcp"] } } } ``` +## Using Cloudflare's MCP servers from the OpenAI Responses API + +To use one of Cloudflare's MCP servers with [OpenAI's responses API](https://openai.com/index/new-tools-and-features-in-the-responses-api/), you will need to provide the Responses API with an API token that has the scopes (permissions) required for that particular MCP server. + +For example, to use the [Browser Rendering MCP server](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/browser-rendering) with OpenAI, create an API token in the Cloudflare dashboard [here](https://dash.cloudflare.com/profile/api-tokens), with the following permissions: + +Screenshot 2025-05-21 at 10 38 02 AM + ## Need access to more Cloudflare tools? We're continuing to add more functionality to this remote MCP server repo. If you'd like to leave feedback, file a bug or provide a feature request, [please open an issue](https://github.com/cloudflare/mcp-server-cloudflare/issues/new/choose) on this repository diff --git a/apps/ai-gateway/.dev.vars.example b/apps/ai-gateway/.dev.vars.example index 860dc82f..5950b164 100644 --- a/apps/ai-gateway/.dev.vars.example +++ b/apps/ai-gateway/.dev.vars.example @@ -2,4 +2,4 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= + diff --git a/apps/ai-gateway/CHANGELOG.md b/apps/ai-gateway/CHANGELOG.md new file mode 100644 index 00000000..109a5150 --- /dev/null +++ b/apps/ai-gateway/CHANGELOG.md @@ -0,0 +1,119 @@ +# cloudflare-ai-gateway-mcp-server + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/ai-gateway/CONTRIBUTING.md b/apps/ai-gateway/CONTRIBUTING.md index b3e55a1e..797f595a 100644 --- a/apps/ai-gateway/CONTRIBUTING.md +++ b/apps/ai-gateway/CONTRIBUTING.md @@ -27,7 +27,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/ai-gateway/README.md b/apps/ai-gateway/README.md index 721a86a4..a5929004 100644 --- a/apps/ai-gateway/README.md +++ b/apps/ai-gateway/README.md @@ -32,7 +32,7 @@ This MCP server is still a work in progress, and we plan to add more tools in th If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://ai-gateway.mcp.cloudflare.com`) directly within its interface (for example in[Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -41,7 +41,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://ai-gateway.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://ai-gateway.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/ai-gateway/package.json b/apps/ai-gateway/package.json index fdf72246..406dd7ff 100644 --- a/apps/ai-gateway/package.json +++ b/apps/ai-gateway/package.json @@ -1,6 +1,6 @@ { "name": "cloudflare-ai-gateway-mcp-server", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,12 +12,12 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/ai-gateway/src/index.ts b/apps/ai-gateway/src/ai-gateway.app.ts similarity index 74% rename from apps/ai-gateway/src/index.ts rename to apps/ai-gateway/src/ai-gateway.app.ts index 8deac108..28473e2e 100644 --- a/apps/ai-gateway/src/index.ts +++ b/apps/ai-gateway/src/ai-gateway.app.ts @@ -1,22 +1,23 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerAIGatewayTools } from './tools/ai-gateway' +import { registerAIGatewayTools } from './tools/ai-gateway.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './ai-gateway.context' const env = getEnv() @@ -50,8 +51,12 @@ export class AIGatewayMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -67,9 +72,14 @@ export class AIGatewayMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -79,7 +89,12 @@ export class AIGatewayMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -95,8 +110,8 @@ const AIGatewayScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(AIGatewayMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(AIGatewayMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/ai-gateway/src/context.ts b/apps/ai-gateway/src/ai-gateway.context.ts similarity index 82% rename from apps/ai-gateway/src/context.ts rename to apps/ai-gateway/src/ai-gateway.context.ts index 5275533c..0077268c 100644 --- a/apps/ai-gateway/src/context.ts +++ b/apps/ai-gateway/src/ai-gateway.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { AIGatewayMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { AIGatewayMCP } from './ai-gateway.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string diff --git a/apps/ai-gateway/src/tools/ai-gateway.ts b/apps/ai-gateway/src/tools/ai-gateway.tools.ts similarity index 89% rename from apps/ai-gateway/src/tools/ai-gateway.ts rename to apps/ai-gateway/src/tools/ai-gateway.tools.ts index ea1fafe1..ce34eeae 100644 --- a/apps/ai-gateway/src/tools/ai-gateway.ts +++ b/apps/ai-gateway/src/tools/ai-gateway.tools.ts @@ -1,9 +1,10 @@ import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' import { GatewayIdParam, ListLogsParams, LogIdParam, pageParam, perPageParam } from '../types' import type { LogListParams } from 'cloudflare/resources/ai-gateway' -import type { AIGatewayMCP } from '../index' +import type { AIGatewayMCP } from '../ai-gateway.app' export function registerAIGatewayTools(agent: AIGatewayMCP) { agent.server.tool( @@ -26,7 +27,8 @@ export function registerAIGatewayTools(agent: AIGatewayMCP) { } } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = await client.aiGateway.list({ account_id: accountId, page: params.page, @@ -73,7 +75,8 @@ export function registerAIGatewayTools(agent: AIGatewayMCP) { const { gateway_id, ...filters } = params - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = await client.aiGateway.logs.list(gateway_id, { ...filters, account_id: accountId, @@ -123,7 +126,8 @@ export function registerAIGatewayTools(agent: AIGatewayMCP) { } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = await client.aiGateway.logs.get(params.gateway_id, params.log_id, { account_id: accountId, }) @@ -172,7 +176,8 @@ export function registerAIGatewayTools(agent: AIGatewayMCP) { } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = await client.aiGateway.logs.request(params.gateway_id, params.log_id, { account_id: accountId, }) @@ -221,7 +226,8 @@ export function registerAIGatewayTools(agent: AIGatewayMCP) { } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = await client.aiGateway.logs.response(params.gateway_id, params.log_id, { account_id: accountId, }) diff --git a/apps/ai-gateway/vitest.config.ts b/apps/ai-gateway/vitest.config.ts index c201c144..ad7e3b5a 100644 --- a/apps/ai-gateway/vitest.config.ts +++ b/apps/ai-gateway/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/ai-gateway.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/ai-gateway/wrangler.jsonc b/apps/ai-gateway/wrangler.jsonc index 3d14bfbd..1880de3d 100644 --- a/apps/ai-gateway/wrangler.jsonc +++ b/apps/ai-gateway/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/ai-gateway.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-ai-gateway-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/auditlogs/.dev.vars.example b/apps/auditlogs/.dev.vars.example index c087f669..d2d3cd20 100644 --- a/apps/auditlogs/.dev.vars.example +++ b/apps/auditlogs/.dev.vars.example @@ -2,4 +2,3 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file diff --git a/apps/auditlogs/CHANGELOG.md b/apps/auditlogs/CHANGELOG.md new file mode 100644 index 00000000..08b14c71 --- /dev/null +++ b/apps/auditlogs/CHANGELOG.md @@ -0,0 +1,125 @@ +# auditlogs + +## 0.1.10 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.8 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.7 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.6 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.5 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/auditlogs/README.md b/apps/auditlogs/README.md index 31748907..617e5a77 100644 --- a/apps/auditlogs/README.md +++ b/apps/auditlogs/README.md @@ -20,9 +20,9 @@ Currently available tools: ## Access the remote MCP server from any MCP Client -If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://auditlogs.mcp.cloudflare.com/sse`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). +If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://auditlogs.mcp.cloudflare.com/mcp`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -31,7 +31,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://auditlogs.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://auditlogs.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/auditlogs/package.json b/apps/auditlogs/package.json index 5ca3eaf8..1dbbf488 100644 --- a/apps/auditlogs/package.json +++ b/apps/auditlogs/package.json @@ -1,23 +1,23 @@ { "name": "auditlogs", - "version": "0.0.1", + "version": "0.1.10", "private": true, "scripts": { "check:lint": "run-eslint-workers", "check:types": "run-tsc", - "deploy": "wrangler deploy", + "deploy": "run-wrangler-deploy", "dev": "wrangler dev", "start": "wrangler dev", "types": "wrangler types --include-env=false", "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/auditlogs/src/index.ts b/apps/auditlogs/src/auditlogs.app.ts similarity index 75% rename from apps/auditlogs/src/index.ts rename to apps/auditlogs/src/auditlogs.app.ts index f0200083..81afec04 100644 --- a/apps/auditlogs/src/index.ts +++ b/apps/auditlogs/src/auditlogs.app.ts @@ -1,22 +1,23 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerAuditLogTools } from './tools/auditlogs' +import { registerAuditLogTools } from './tools/auditlogs.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './auditlogs.context' const env = getEnv() @@ -51,8 +52,12 @@ export class AuditlogMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -67,9 +72,14 @@ export class AuditlogMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -79,7 +89,12 @@ export class AuditlogMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -95,8 +110,8 @@ const AuditlogScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(AuditlogMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(AuditlogMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/auditlogs/src/context.ts b/apps/auditlogs/src/auditlogs.context.ts similarity index 83% rename from apps/auditlogs/src/context.ts rename to apps/auditlogs/src/auditlogs.context.ts index 313f2d90..300ebd2d 100644 --- a/apps/auditlogs/src/context.ts +++ b/apps/auditlogs/src/auditlogs.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { AuditlogMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { AuditlogMCP } from './auditlogs.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string diff --git a/apps/auditlogs/src/tools/auditlogs.ts b/apps/auditlogs/src/tools/auditlogs.tools.ts similarity index 97% rename from apps/auditlogs/src/tools/auditlogs.ts rename to apps/auditlogs/src/tools/auditlogs.tools.ts index 2fe93261..26308602 100644 --- a/apps/auditlogs/src/tools/auditlogs.ts +++ b/apps/auditlogs/src/tools/auditlogs.tools.ts @@ -1,8 +1,9 @@ import { z } from 'zod' import { fetchCloudflareApi } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' -import type { AuditlogMCP } from '../index' +import type { AuditlogMCP } from '../auditlogs.app' export const actionResults = z.enum(['success', 'failure', '']) export const actionTypes = z.enum(['create', 'delete', 'view', 'update', 'login']) @@ -253,7 +254,8 @@ export function registerAuditLogTools(agent: AuditlogMCP) { } } try { - const result = await handleGetAuditLogs(accountId, agent.props.accessToken, params) + const props = getProps(agent) + const result = await handleGetAuditLogs(accountId, props.accessToken, params) return { content: [ { diff --git a/apps/auditlogs/vitest.config.ts b/apps/auditlogs/vitest.config.ts index c201c144..c57ade2f 100644 --- a/apps/auditlogs/vitest.config.ts +++ b/apps/auditlogs/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/auditlogs.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/auditlogs/wrangler.jsonc b/apps/auditlogs/wrangler.jsonc index e7dc5ad1..5ee77351 100644 --- a/apps/auditlogs/wrangler.jsonc +++ b/apps/auditlogs/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/auditlogs.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-auditlogs-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/autorag/.dev.vars.example b/apps/autorag/.dev.vars.example index 860dc82f..5950b164 100644 --- a/apps/autorag/.dev.vars.example +++ b/apps/autorag/.dev.vars.example @@ -2,4 +2,4 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= + diff --git a/apps/autorag/CHANGELOG.md b/apps/autorag/CHANGELOG.md new file mode 100644 index 00000000..be5260e0 --- /dev/null +++ b/apps/autorag/CHANGELOG.md @@ -0,0 +1,119 @@ +# cloudflare-autorag-mcp-server + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/autorag/CONTRIBUTING.md b/apps/autorag/CONTRIBUTING.md index 570ec03a..0c520d19 100644 --- a/apps/autorag/CONTRIBUTING.md +++ b/apps/autorag/CONTRIBUTING.md @@ -27,7 +27,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/autorag/README.md b/apps/autorag/README.md index 19f5aeb8..68801c69 100644 --- a/apps/autorag/README.md +++ b/apps/autorag/README.md @@ -27,7 +27,7 @@ This MCP server is still a work in progress, and we plan to add more tools in th If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://autorag.mcp.cloudflare.com`) directly within its interface (for example in[Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -36,7 +36,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://autorag.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://autorag.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/autorag/package.json b/apps/autorag/package.json index fd6e1473..d7324b94 100644 --- a/apps/autorag/package.json +++ b/apps/autorag/package.json @@ -1,6 +1,6 @@ { "name": "cloudflare-autorag-mcp-server", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,12 +12,12 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/autorag/src/index.ts b/apps/autorag/src/autorag.app.ts similarity index 74% rename from apps/autorag/src/index.ts rename to apps/autorag/src/autorag.app.ts index 2fe55d14..d8f62503 100644 --- a/apps/autorag/src/index.ts +++ b/apps/autorag/src/autorag.app.ts @@ -1,22 +1,23 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerAutoRAGTools } from './tools/autorag' +import { registerAutoRAGTools } from './tools/autorag.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './autorag.context' const env = getEnv() @@ -50,8 +51,12 @@ export class AutoRAGMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -67,9 +72,14 @@ export class AutoRAGMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -79,7 +89,12 @@ export class AutoRAGMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -95,8 +110,8 @@ const LogPushScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(AutoRAGMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(AutoRAGMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/autorag/src/context.ts b/apps/autorag/src/autorag.context.ts similarity index 82% rename from apps/autorag/src/context.ts rename to apps/autorag/src/autorag.context.ts index 4d34ead4..fe8fdd4c 100644 --- a/apps/autorag/src/context.ts +++ b/apps/autorag/src/autorag.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { AutoRAGMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { AutoRAGMCP } from './autorag.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string diff --git a/apps/autorag/src/tools/autorag.ts b/apps/autorag/src/tools/autorag.tools.ts similarity index 89% rename from apps/autorag/src/tools/autorag.ts rename to apps/autorag/src/tools/autorag.tools.ts index 9c71b6df..924aa216 100644 --- a/apps/autorag/src/tools/autorag.ts +++ b/apps/autorag/src/tools/autorag.tools.ts @@ -1,13 +1,12 @@ -import Cloudflare from 'cloudflare' +import { V4PagePaginationArray } from 'cloudflare/src/pagination.js' import { z } from 'zod' import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' import { pageParam, perPageParam } from '../types' -import type { AutoRAGMCP } from '../index' - -import V4PagePaginationArray = Cloudflare.V4PagePaginationArray +import type { AutoRAGMCP } from '../autorag.app' export function registerAutoRAGTools(agent: AutoRAGMCP) { agent.server.tool( @@ -30,9 +29,11 @@ export function registerAutoRAGTools(agent: AutoRAGMCP) { } } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = (await client.getAPIList( `/accounts/${accountId}/autorag/rags`, + // @ts-ignore V4PagePaginationArray, { query: { page: params.page, per_page: params.per_page } } )) as unknown as { @@ -91,7 +92,8 @@ export function registerAutoRAGTools(agent: AutoRAGMCP) { } } - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = (await client.post( `/accounts/${accountId}/autorag/rags/${params.rag_id}/search`, { @@ -156,7 +158,8 @@ export function registerAutoRAGTools(agent: AutoRAGMCP) { } } - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = (await client.post( `/accounts/${accountId}/autorag/rags/${params.rag_id}/ai-search`, { diff --git a/apps/autorag/vitest.config.ts b/apps/autorag/vitest.config.ts index c201c144..908a50ce 100644 --- a/apps/autorag/vitest.config.ts +++ b/apps/autorag/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/autorag.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/autorag/wrangler.jsonc b/apps/autorag/wrangler.jsonc index ab9ea715..a69ff76e 100644 --- a/apps/autorag/wrangler.jsonc +++ b/apps/autorag/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/autorag.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-autorag-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/browser-rendering/.dev.vars.example b/apps/browser-rendering/.dev.vars.example index 860dc82f..5950b164 100644 --- a/apps/browser-rendering/.dev.vars.example +++ b/apps/browser-rendering/.dev.vars.example @@ -2,4 +2,4 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= + diff --git a/apps/browser-rendering/CHANGELOG.md b/apps/browser-rendering/CHANGELOG.md new file mode 100644 index 00000000..2d886660 --- /dev/null +++ b/apps/browser-rendering/CHANGELOG.md @@ -0,0 +1,119 @@ +# cloudflare-browser-mcp-server + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/browser-rendering/CONTRIBUTING.md b/apps/browser-rendering/CONTRIBUTING.md index b3e55a1e..797f595a 100644 --- a/apps/browser-rendering/CONTRIBUTING.md +++ b/apps/browser-rendering/CONTRIBUTING.md @@ -27,7 +27,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/browser-rendering/README.md b/apps/browser-rendering/README.md index 287b512e..d548ad6e 100644 --- a/apps/browser-rendering/README.md +++ b/apps/browser-rendering/README.md @@ -30,7 +30,7 @@ This MCP server is still a work in progress, and we plan to add more tools in th If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://browser.mcp.cloudflare.com`) directly within its interface (for example in[Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using mcp-remote (https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -39,7 +39,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://browser.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://browser.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/browser-rendering/package.json b/apps/browser-rendering/package.json index 3557021b..16f748a6 100644 --- a/apps/browser-rendering/package.json +++ b/apps/browser-rendering/package.json @@ -1,6 +1,6 @@ { "name": "cloudflare-browser-mcp-server", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,12 +12,12 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/browser-rendering/src/index.ts b/apps/browser-rendering/src/browser.app.ts similarity index 75% rename from apps/browser-rendering/src/index.ts rename to apps/browser-rendering/src/browser.app.ts index adbcc1c7..04f795d1 100644 --- a/apps/browser-rendering/src/index.ts +++ b/apps/browser-rendering/src/browser.app.ts @@ -1,22 +1,23 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerBrowserTools } from './tools/browser' +import { registerBrowserTools } from './tools/browser.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './browser.context' const env = getEnv() @@ -50,8 +51,12 @@ export class BrowserMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -67,9 +72,14 @@ export class BrowserMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -79,7 +89,12 @@ export class BrowserMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -95,8 +110,8 @@ const BrowserScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(BrowserMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(BrowserMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/browser-rendering/src/context.ts b/apps/browser-rendering/src/browser.context.ts similarity index 82% rename from apps/browser-rendering/src/context.ts rename to apps/browser-rendering/src/browser.context.ts index 5d64e0b0..cef58106 100644 --- a/apps/browser-rendering/src/context.ts +++ b/apps/browser-rendering/src/browser.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { BrowserMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { BrowserMCP } from './browser.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string diff --git a/apps/browser-rendering/src/tools/browser.ts b/apps/browser-rendering/src/tools/browser.tools.ts similarity index 77% rename from apps/browser-rendering/src/tools/browser.ts rename to apps/browser-rendering/src/tools/browser.tools.ts index 1b51a6c0..fa42d8ee 100644 --- a/apps/browser-rendering/src/tools/browser.ts +++ b/apps/browser-rendering/src/tools/browser.tools.ts @@ -1,8 +1,9 @@ import { z } from 'zod' import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' -import type { BrowserMCP } from '../index' +import type { BrowserMCP } from '../browser.app' export function registerBrowserTools(agent: BrowserMCP) { agent.server.tool( @@ -24,7 +25,8 @@ export function registerBrowserTools(agent: BrowserMCP) { } } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = await client.browserRendering.content.create({ account_id: accountId, url: params.url, @@ -72,7 +74,8 @@ export function registerBrowserTools(agent: BrowserMCP) { } } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const r = (await client.post(`/accounts/${accountId}/browser-rendering/markdown`, { body: { url: params.url, @@ -127,28 +130,17 @@ export function registerBrowserTools(agent: BrowserMCP) { } } try { - // Cf client appears to be broken, so we use the raw API instead. - // const client = getCloudflareClient(agent.props.accessToken) - // const r = await client.browserRendering.screenshot.create({ - // account_id: accountId, - // url: params.url, - // viewport: params.viewport, - // }) - - const r = await fetch( - `https://api.cloudflare.com/client/v4/accounts/${accountId}/browser-rendering/screenshot`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${agent.props.accessToken}`, - }, - body: JSON.stringify({ + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client + .post(`/accounts/${accountId}/browser-rendering/screenshot`, { + body: { url: params.url, viewport: params.viewport, - }), - } - ) + }, + __binaryResponse: true, + }) + .asResponse() const arrayBuffer = await r.arrayBuffer() const base64Image = Buffer.from(arrayBuffer).toString('base64') @@ -167,7 +159,7 @@ export function registerBrowserTools(agent: BrowserMCP) { content: [ { type: 'text', - text: `Error getting page in markdown: ${error instanceof Error && error.message}`, + text: `Error getting page screenshot: ${error instanceof Error && error.message}`, }, ], } diff --git a/apps/browser-rendering/vitest.config.ts b/apps/browser-rendering/vitest.config.ts index c201c144..3e37b49d 100644 --- a/apps/browser-rendering/vitest.config.ts +++ b/apps/browser-rendering/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/browser.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/browser-rendering/wrangler.jsonc b/apps/browser-rendering/wrangler.jsonc index 04f4eb3c..2974997a 100644 --- a/apps/browser-rendering/wrangler.jsonc +++ b/apps/browser-rendering/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/browser.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-browser-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/cloudflare-one-casb/.dev.vars.example b/apps/cloudflare-one-casb/.dev.vars.example index c087f669..d2d3cd20 100644 --- a/apps/cloudflare-one-casb/.dev.vars.example +++ b/apps/cloudflare-one-casb/.dev.vars.example @@ -2,4 +2,3 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file diff --git a/apps/cloudflare-one-casb/CHANGELOG.md b/apps/cloudflare-one-casb/CHANGELOG.md new file mode 100644 index 00000000..a2382787 --- /dev/null +++ b/apps/cloudflare-one-casb/CHANGELOG.md @@ -0,0 +1,112 @@ +# cloudflare-casb-mcp-server + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 diff --git a/apps/cloudflare-one-casb/README.md b/apps/cloudflare-one-casb/README.md index 57c413bf..d4aafe13 100644 --- a/apps/cloudflare-one-casb/README.md +++ b/apps/cloudflare-one-casb/README.md @@ -2,7 +2,7 @@ This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP connections, with Cloudflare OAuth built-in. -You should use this as a template to build an MCP server for Cloudflare, provided by Cloudflare at `server-name.mcp.cloudflare.com`. It has a basic set of tools `apps/template-start-here/src/tools/logs.ts` — you can modify these to do what you need +You should use this as a template to build an MCP server for Cloudflare, provided by Cloudflare at `server-name.mcp.cloudflare.com`. It has a basic set of tools `apps/template-start-here/src/tools/logpush.tools.ts` — you can modify these to do what you need ## Getting Started diff --git a/apps/cloudflare-one-casb/package.json b/apps/cloudflare-one-casb/package.json index d334757c..ce062768 100644 --- a/apps/cloudflare-one-casb/package.json +++ b/apps/cloudflare-one-casb/package.json @@ -1,6 +1,6 @@ { "name": "cloudflare-casb-mcp-server", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,11 +12,11 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/cloudflare-one-casb/src/index.ts b/apps/cloudflare-one-casb/src/cf1-casb.app.ts similarity index 74% rename from apps/cloudflare-one-casb/src/index.ts rename to apps/cloudflare-one-casb/src/cf1-casb.app.ts index c76bd820..27837403 100644 --- a/apps/cloudflare-one-casb/src/index.ts +++ b/apps/cloudflare-one-casb/src/cf1-casb.app.ts @@ -1,22 +1,23 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerIntegrationsTools } from './tools/integrations' +import { registerIntegrationsTools } from './tools/integrations.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './cf1-casb.context' export { UserDetails } @@ -51,8 +52,12 @@ export class CASBMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -66,9 +71,14 @@ export class CASBMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -78,7 +88,12 @@ export class CASBMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -93,8 +108,8 @@ const CloudflareOneCasbScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(CASBMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(CASBMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/cloudflare-one-casb/src/context.ts b/apps/cloudflare-one-casb/src/cf1-casb.context.ts similarity index 87% rename from apps/cloudflare-one-casb/src/context.ts rename to apps/cloudflare-one-casb/src/cf1-casb.context.ts index b8bda3f0..6bce5fc8 100644 --- a/apps/cloudflare-one-casb/src/context.ts +++ b/apps/cloudflare-one-casb/src/cf1-casb.context.ts @@ -1,4 +1,4 @@ -import type { CASBMCP, UserDetails } from './index' +import type { CASBMCP, UserDetails } from './cf1-casb.app' export interface Env { ENVIRONMENT: 'development' | 'staging' | 'production' diff --git a/apps/cloudflare-one-casb/src/tools/integrations.ts b/apps/cloudflare-one-casb/src/tools/integrations.tools.ts similarity index 96% rename from apps/cloudflare-one-casb/src/tools/integrations.ts rename to apps/cloudflare-one-casb/src/tools/integrations.tools.ts index 0d8072a0..d38521c6 100644 --- a/apps/cloudflare-one-casb/src/tools/integrations.ts +++ b/apps/cloudflare-one-casb/src/tools/integrations.tools.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { withAccountCheck } from '@repo/mcp-common/src/api/account' +import { withAccountCheck } from '@repo/mcp-common/src/api/account.api' import { handleAssetById, handleAssetCategories, @@ -10,14 +10,14 @@ import { handleAssetsSearch, handleIntegrationById, handleIntegrations, -} from '@repo/mcp-common/src/api/cf1-integration' +} from '@repo/mcp-common/src/api/cf1-integration.api' import { assetCategoryTypeParam, assetCategoryVendorParam, -} from '@repo/mcp-common/src/schemas/cf1-integrations' +} from '@repo/mcp-common/src/types/cf1-integrations.types' -import type { ToolDefinition } from '@repo/mcp-common/src/types/tools' -import type { CASBMCP } from '../index' +import type { ToolDefinition } from '@repo/mcp-common/src/types/tools.types' +import type { CASBMCP } from '../cf1-casb.app' const PAGE_SIZE = 3 diff --git a/apps/cloudflare-one-casb/vitest.config.ts b/apps/cloudflare-one-casb/vitest.config.ts index c201c144..20e9e31b 100644 --- a/apps/cloudflare-one-casb/vitest.config.ts +++ b/apps/cloudflare-one-casb/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/cf1-casb.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/cloudflare-one-casb/wrangler.jsonc b/apps/cloudflare-one-casb/wrangler.jsonc index bdec102c..2fd22c09 100644 --- a/apps/cloudflare-one-casb/wrangler.jsonc +++ b/apps/cloudflare-one-casb/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/cf1-casb.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "migrations": [ @@ -14,7 +14,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "vars": { "MCP_SERVER_NAME": "PLACEHOLDER", diff --git a/apps/demo-day/CHANGELOG.md b/apps/demo-day/CHANGELOG.md new file mode 100644 index 00000000..89c95831 --- /dev/null +++ b/apps/demo-day/CHANGELOG.md @@ -0,0 +1,118 @@ +# demo-day + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/demo-day/package.json b/apps/demo-day/package.json index f2c3168e..2fe5646e 100644 --- a/apps/demo-day/package.json +++ b/apps/demo-day/package.json @@ -1,6 +1,6 @@ { "name": "demo-day", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:types": "run-tsc", @@ -11,11 +11,10 @@ "test": "vitest run" }, "dependencies": { - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "@types/node": "22.14.1", - "agents": "0.0.67", + "agents": "0.2.19", "zod": "3.24.2" }, "devDependencies": { diff --git a/apps/demo-day/src/index.ts b/apps/demo-day/src/demo-day.app.ts similarity index 100% rename from apps/demo-day/src/index.ts rename to apps/demo-day/src/demo-day.app.ts diff --git a/apps/demo-day/tsconfig.json b/apps/demo-day/tsconfig.json index adc5f137..e607bfb0 100644 --- a/apps/demo-day/tsconfig.json +++ b/apps/demo-day/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "@repo/typescript-config/workers.json", - "include": ["src/index.ts"], + "include": ["src/demo-day.app.ts"], "compilerOptions": { "types": ["@types/node"] } diff --git a/apps/demo-day/wrangler.json b/apps/demo-day/wrangler.json index d67c5cae..1180c9b7 100644 --- a/apps/demo-day/wrangler.json +++ b/apps/demo-day/wrangler.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/demo-day.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", @@ -13,7 +13,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/dex-analysis/.dev.vars.example b/apps/dex-analysis/.dev.vars.example index c087f669..d2d3cd20 100644 --- a/apps/dex-analysis/.dev.vars.example +++ b/apps/dex-analysis/.dev.vars.example @@ -2,4 +2,3 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file diff --git a/apps/dex-analysis/CHANGELOG.md b/apps/dex-analysis/CHANGELOG.md new file mode 100644 index 00000000..c61f366e --- /dev/null +++ b/apps/dex-analysis/CHANGELOG.md @@ -0,0 +1,149 @@ +# dex-analysis + +## 0.3.0 + +### Minor Changes + +- 198c2c2: disallow arbitrary urls in DEX remote commands explore tools + +## 0.2.5 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.2.4 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.2.3 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.2.2 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.2.1 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.2.0 + +### Minor Changes + +- 9496e21: Add tool for analyzing WARP-diags for common issues via bonobo + +## 0.1.7 + +### Patch Changes + +- dffbd36: Improve DEX remote captures tools, separate by type for clarity + +## 0.1.6 + +### Patch Changes + +- d672471: dex-analysis: add WARP diag analysis tools and reader D.O. + +## 0.1.5 + +### Patch Changes + +- 016cb73: Add more DEX tools, including remote captures + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/dex-analysis/README.md b/apps/dex-analysis/README.md index 4f5f7aa9..82fbaf4b 100644 --- a/apps/dex-analysis/README.md +++ b/apps/dex-analysis/README.md @@ -1,126 +1,67 @@ -# Model Context Protocol (MCP) Server + Cloudflare OAuth +# Cloudflare DEX MCP Server 📡 -This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP connections, with Cloudflare OAuth built-in. +This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP +connections, with Cloudflare OAuth built-in. -You can deploy it to your own Cloudflare account, and after you create your own Cloudflare OAuth client app, you'll have a fully functional remote MCP server that you can build off. Users will be able to connect to your MCP server by signing in with their Cloudflare account. +It integrates tools powered by the [Cloudflare DEX API](https://developers.cloudflare.com/api/resources/zero_trust/subresources/dex/) to provide visibility into device, network, and application performance across your Zero Trust organization -You can use this as a reference example for how to integrate other OAuth providers with an MCP server deployed to Cloudflare, using the [`workers-oauth-provider` library](https://github.com/cloudflare/workers-oauth-provider). +## 🔨 Available Tools -The MCP server (powered by [Cloudflare Workers](https://developers.cloudflare.com/workers/)): +Currently available tools: -- Acts as OAuth _Server_ to your MCP clients -- Acts as OAuth _Client_ to your _real_ OAuth server (in this case, Cloudflare) +| **Category** | **Tool** | **Description** | +| ------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Synthetic Application Monitoring** | `dex_test_statistics` | Analyze Cloudflare DEX Test Results by quartile given a Test ID | +| | `dex_list_tests` | List configured Cloudflare DEX tests along with overview performance metrics. | +| | `dex_http_test_details` | Retrieve detailed time series results for an HTTP DEX test by id. | +| | `dex_traceroute_test_details` | Retrieve detailed time series results for a Traceroute DEX test by id. | +| | `dex_traceroute_test_network_path` | Retrieve detailed time series results for the network path of a traceroute test by test id and device id. | +| | `dex_traceroute_test_result_network_path` | Retrieve the hop-by-hop network path for a specific Traceroute DEX test result by id. Use `dex_traceroute_test_network_path` to obain test result ids. | +| **Remote Captures** | `dex_list_remote_capture_eligible_devices` | Retrieve a list of devices eligible for remote captures like packet captures or WARP diagnostics. | +| | `dex_create_remote_pcap` | Initiate a remote packet capture on a specific device by id. | +| | `dex_create_remote_warp_diag` | Initiate a remote Warp diagnostic capture on a specific device by id. | +| | `dex_list_remote_captures` | Retrieve a list of previously created remote captures along with their details and status. | +| | `dex_list_remote_warp_diag_contents` | List the filenames included in a remote WARP diag capture returned by `dex_list_remote_captures`. | +| | `dex_explore_remote_warp_diag_output` | Retreive remote WARP diag file contents by filepath returned by `dex_list_remote_warp_diag_contents`. | +| | `dex_analyze_warp_diag` | Analyze successful WARP-diag remote captures for common issues. | +| **Fleet Status** | `dex_fleet_status_live` | View live metrics for your fleet of zero trust devices for up to the past 1 hour. | +| | `dex_fleet_status_over_time` | View historical metrics for your fleet of zero trust devices over time. | +| | `dex_fleet_status_logs` | View historical logs for your fleet of zero trust devices for up to the past 7 days. | +| | `dex_list_warp_change_events` | View logs of users toggling WARP connection or changing configuration. | +| **Misc** | `dex_list_colos` | List Cloudflare colos, optionally sorted by their frequency of appearance in DEX test or fleet status results. | -## Getting Started +This MCP server is still a work in progress, and we plan to add more tools in the future. -### For Production +### Prompt Examples -- Set secrets via Wrangler +- `Are there any anomolies in the DEX test to the internal wiki in the past 24 hours?` +- `Can you see any bottlenecks in user@cloudflare.com's network path for Zoom today between 1 and 2 PM?` +- `How many macOS devices are connected right now in DFW?` +- `Do you notice any unusual performance metrics for user@cloudflare.com's device in the past few hours?` +- `Capture a WARP diag for user@cloudflare.com and make sure to test all routes` +- `Which users have toggled off WARP recently?` +- `Which Cloudflare colo is most used by my users in the EU running DEX application tests?` +- `Look at the latest WARP diag for user@cloudflare.com and tell me if you see anything notable in dns logs` -```bash -wrangler secret put CLOUDFLARE_CLIENT_ID -wrangler secret put CLOUDFLARE_CLIENT_SECRET -``` - -#### Set up a KV namespace - -- Create the KV namespace: - `wrangler kv:namespace create "OAUTH_KV"` -- Update the Wrangler file with the KV ID - -#### Deploy & Test - -Deploy the MCP server to make it available on your workers.dev domain -` wrangler deploy` - -Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): - -``` -npx @modelcontextprotocol/inspector@latest -``` - -Enter `https://mcp-cloudflare-staging..workers.dev/sse` and hit connect. Once you go through the authentication flow, you'll see the Tools working: +## Access the remote MCP server from any MCP Client -image +If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://dex.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -You now have a remote MCP server deployed! +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. -#### Access the remote MCP server from Claude Desktop +Replace the content with the following configuration: -Open Claude Desktop and navigate to Settings -> Developer -> Edit Config. This opens the configuration file that controls which MCP servers Claude can access. - -Replace the content with the following configuration. Once you restart Claude Desktop, a browser window will open showing your OAuth login page. Complete the authentication flow to grant Claude access to your MCP server. After you grant access, the tools will become available for you to use. - -``` +```json { - "mcpServers": { - "cloudflare": { - "command": "npx", - "args": [ - "mcp-remote", - "https://.workers.dev/sse" - ] - } - } + "mcpServers": { + "cloudflare": { + "command": "npx", + "args": ["mcp-remote", "https://dex.mcp.cloudflare.com/mcp"] + } + } } ``` -Once the Tools (under 🔨) show up in the interface, you can ask Claude to use them. For example: "Could you use the math tool to add 23 and 19?". Claude should invoke the tool and show the result generated by the MCP server. - -### For Local Development - -If you'd like to iterate and test your MCP server, you can do so in local development. This will require you to create another OAuth App on Cloudflare: - -- Create a `.dev.vars` file in your project root with: - -``` -CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id -CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret -``` - -#### Develop & Test - -Run the server locally to make it available at `http://localhost:8788` -`wrangler dev` - -To test the local server, enter `http://localhost:8788/sse` into Inspector and hit connect. Once you follow the prompts, you'll be able to "List Tools". - -#### Using Claude and other MCP Clients - -When using Claude to connect to your remote MCP server, you may see some error messages. This is because Claude Desktop doesn't yet support remote MCP servers, so it sometimes gets confused. To verify whether the MCP server is connected, hover over the 🔨 icon in the bottom right corner of Claude's interface. You should see your tools available there. - -#### Using Cursor and other MCP Clients - -To connect Cursor with your MCP server, choose `Type`: "Command" and in the `Command` field, combine the command and args fields into one (e.g. `npx mcp-remote https://..workers.dev/sse`). - -Note that while Cursor supports HTTP+SSE servers, it doesn't support authentication, so you still need to use `mcp-remote` (and to use a STDIO server, not an HTTP one). - -You can connect your MCP server to other MCP clients like Windsurf by opening the client's configuration file, adding the same JSON that was used for the Claude setup, and restarting the MCP client. - -## How does it work? - -#### OAuth Provider - -The OAuth Provider library serves as a complete OAuth 2.1 server implementation for Cloudflare Workers. It handles the complexities of the OAuth flow, including token issuance, validation, and management. In this project, it plays the dual role of: - -- Authenticating MCP clients that connect to your server -- Managing the connection to Cloudflare's OAuth services -- Securely storing tokens and authentication state in KV storage - -#### Durable MCP - -Durable MCP extends the base MCP functionality with Cloudflare's Durable Objects, providing: - -- Persistent state management for your MCP server -- Secure storage of authentication context between requests -- Access to authenticated user information via `this.props` -- Support for conditional tool availability based on user identity - -#### MCP Remote - -The MCP Remote library enables your server to expose tools that can be invoked by MCP clients like the Inspector. It: +Once you've set up your configuration file, restart MCP client and a browser window will open showing your OAuth login page. Proceed through the authentication flow to grant the client access to your MCP server. After you grant access, the tools will become available for you to use. -- Defines the protocol for communication between clients and your server -- Provides a structured way to define tools -- Handles serialization and deserialization of requests and responses -- Maintains the Server-Sent Events (SSE) connection between clients and your server +Interested in contributing, and running this server locally? See [CONTRIBUTING.md](CONTRIBUTING.md) to get started. diff --git a/apps/dex-analysis/package.json b/apps/dex-analysis/package.json index 1a21a39a..5c568940 100644 --- a/apps/dex-analysis/package.json +++ b/apps/dex-analysis/package.json @@ -1,6 +1,6 @@ { "name": "dex-analysis", - "version": "0.0.1", + "version": "0.3.0", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -13,14 +13,15 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", + "jszip": "3.10.1", "zod": "3.24.2" }, "devDependencies": { diff --git a/apps/dex-analysis/src/api/dex.ts b/apps/dex-analysis/src/api/dex.ts deleted file mode 100644 index cce4af99..00000000 --- a/apps/dex-analysis/src/api/dex.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { fetchCloudflareApi } from '@repo/mcp-common/src/cloudflare-api' - -export const fetchDexTestAnalyzation = async ({ - dexTestId, - accountId, - accessToken, - timeStart, - timeEnd, -}: { - dexTestId: string - accountId: string - accessToken: string - timeStart: string - timeEnd: string -}) => { - return await fetchCloudflareApi({ - endpoint: `/dex/test-results/by-quartile?from=${timeStart}&to=${timeEnd}&limit=4&testId=${dexTestId}`, - accountId, - apiToken: accessToken, - options: { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }, - }) -} - -export const fetchDexTests = async ({ - accountId, - accessToken, -}: { - accountId: string - accessToken: string -}) => { - return await fetchCloudflareApi({ - endpoint: '/dex/tests/overview?per_page=50', - accountId, - apiToken: accessToken, - options: { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }, - }) -} diff --git a/apps/dex-analysis/src/index.ts b/apps/dex-analysis/src/dex-analysis.app.ts similarity index 71% rename from apps/dex-analysis/src/index.ts rename to apps/dex-analysis/src/dex-analysis.app.ts index 5804677e..2afa9d27 100644 --- a/apps/dex-analysis/src/index.ts +++ b/apps/dex-analysis/src/dex-analysis.app.ts @@ -1,24 +1,26 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '@repo/mcp-observability' -import { registerDEXTools } from './tools/dex' +import { registerDEXTools } from './tools/dex-analysis.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './dex-analysis.context' export { UserDetails } +export { WarpDiagReader } from './warp_diag_reader' const env = getEnv() @@ -52,8 +54,12 @@ export class CloudflareDEXMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -67,9 +73,14 @@ export class CloudflareDEXMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -79,7 +90,12 @@ export class CloudflareDEXMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -90,13 +106,14 @@ export class CloudflareDEXMCP extends McpAgent { const DexScopes = { ...RequiredScopes, 'account:read': 'See your account info such as account details, analytics, and memberships.', - 'dex:read': 'See Cloudflare Cloudflare DEX data for your account', + 'dex:write': + 'Grants write level access to DEX resources like tests, fleet status, and remote captures.', } as const export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(CloudflareDEXMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(CloudflareDEXMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/dex-analysis/src/context.ts b/apps/dex-analysis/src/dex-analysis.context.ts similarity index 68% rename from apps/dex-analysis/src/context.ts rename to apps/dex-analysis/src/dex-analysis.context.ts index b2fbe2a7..81c599a1 100644 --- a/apps/dex-analysis/src/context.ts +++ b/apps/dex-analysis/src/dex-analysis.context.ts @@ -1,8 +1,10 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { CloudflareDEXMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { CloudflareDEXMCP } from './dex-analysis.app' +import type { WarpDiagReader } from './warp_diag_reader' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string @@ -10,6 +12,7 @@ export interface Env { CLOUDFLARE_CLIENT_SECRET: string MCP_OBJECT: DurableObjectNamespace USER_DETAILS: DurableObjectNamespace + WARP_DIAG_READER: DurableObjectNamespace MCP_METRICS: AnalyticsEngineDataset DEV_DISABLE_OAUTH: string DEV_CLOUDFLARE_API_TOKEN: string diff --git a/apps/dex-analysis/src/tools/dex-analysis.tools.ts b/apps/dex-analysis/src/tools/dex-analysis.tools.ts new file mode 100644 index 00000000..c3f39d2a --- /dev/null +++ b/apps/dex-analysis/src/tools/dex-analysis.tools.ts @@ -0,0 +1,673 @@ +import { z } from 'zod' + +import { fetchCloudflareApi } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' + +import { getReader } from '../warp_diag_reader' + +import type { ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js' +import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js' +import type { ZodRawShape, ZodTypeAny } from 'zod' +import type { CloudflareDEXMCP } from '../dex-analysis.app' + +export function registerDEXTools(agent: CloudflareDEXMCP) { + registerTool({ + name: 'dex_test_statistics', + description: 'Analyze Cloudflare DEX Test Results by quartile given a Test ID', + schema: { + testId: testIdParam.describe('The DEX Test ID to analyze details of.'), + from: timeStartParam, + to: timeEndParam, + }, + llmContext: + "The quartiles are sorted by 'resource fetch time' from LEAST performant in quartile 1 to MOST performant in quartile 4. For each quartile-based entry, it provides extensive information about the up-to-20 specific test results that are within that quartile of performance.", + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/test-results/by-quartile?${new URLSearchParams({ ...(params as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_list_tests', + description: 'Retrieve a list of all Cloudflare DEX Tests configured.', + agent, + schema: { page: pageParam }, + callback: async ({ accountId, accessToken, page }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/tests/overview?${new URLSearchParams({ page: String(page), per_page: '50' })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_http_test_details', + description: 'Retrieve detailed time series results for an HTTP DEX test by id.', + schema: { + testId: testIdParam.describe('The HTTP DEX Test ID to get details for.'), + deviceId: deviceIdParam + .optional() + .describe( + "Optionally limit results to specific device(s). Can't be used in conjunction with the colo parameter." + ), + colo: coloParam.optional(), + from: timeStartParam, + to: timeEndParam, + interval: aggregationIntervalParam, + }, + agent, + callback: async ({ testId, accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/http-tests/${testId}?${new URLSearchParams({ ...(params as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_traceroute_test_details', + description: 'Retrieve detailed time series results for a Traceroute DEX test by id.', + schema: { + testId: testIdParam.describe('The traceroute DEX Test ID to get details for.'), + deviceId: deviceIdParam + .optional() + .describe( + "Optionally limit results to specific device(s). Can't be used in conjunction with the colo parameter." + ), + colo: coloParam.optional(), + timeStart: timeStartParam, + timeEnd: timeEndParam, + interval: aggregationIntervalParam, + }, + agent, + callback: async ({ testId, accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/traceroute-tests/${testId}?${new URLSearchParams({ ...(params as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_traceroute_test_network_path', + description: + 'Retrieve aggregate network path data for a Traceroute DEX test by id. Use the dex_traceroute_test_result_network_path tool to further explore individual test runs hop-by-hop.', + schema: { + testId: testIdParam.describe('The traceroute DEX Test ID to get network path details for.'), + deviceId: deviceIdParam.describe('The ID of the device to get network path details for.'), + from: timeStartParam, + to: timeEndParam, + interval: aggregationIntervalParam, + }, + agent, + callback: async ({ testId, accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/traceroute-tests/${testId}/network-path?${new URLSearchParams({ ...(params as unknown as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_traceroute_test_result_network_path', + description: + 'Retrieve the hop-by-hop network path for a specific Traceroute DEX test result by id.', + schema: { + testResultId: z + .string() + .uuid() + .describe('The traceroute DEX Test Result ID to get network path details for.'), + }, + agent, + callback: async ({ testResultId, accountId, accessToken }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/traceroute-test-results/${testResultId}/network-path`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_list_remote_capture_eligible_devices', + description: + "Retrieve a list of devices eligible for remote captures. You'll need the device_id and user_email from this " + + 'response in order to create a remote capture for a specific device. It can also be used as a generic source to find ' + + 'devices registered to the account, filtering by user email if necessary.', + schema: { + page: pageParam, + search: z.string().optional().describe('Filter devices by name or email.'), + }, + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/commands/devices?${new URLSearchParams({ ...(params as unknown as Record) })}&per_page=50`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_create_remote_pcap', + description: + 'Create a remote packet capture (PCAP) for a device. This is a resource intensive and privacy-sensitive operation on a real user device.' + + 'Always ask for confirmation from the user that the targeted email and device are correct before executing a capture', + schema: { + device_id: z.string().uuid().describe('The device ID to target.'), + user_email: z.string().email().describe('The email of the user associated with the device.'), + 'max-file-size-mb': z + .number() + .min(1) + .default(5) + .optional() + .describe( + 'Maximum file size in MB for the capture file. Specifies the maximum file size of the warp-daig zip artifact that can be uploaded. ' + + 'If the zip artifact exceeds the specified max file size it will NOT be uploaded.' + ), + 'packet-size-bytes': z + .number() + .min(1) + .default(160) + .optional() + .describe('Maximum number of bytes to save for each packet.'), + 'time-limit-min': z + .number() + .min(1) + .default(5) + .describe('Limit on capture duration in minutes'), + }, + agent, + llmContext: + 'If the request was successful, the capture has been initiated. You can poll the dex_list_remote_commands tool periodically to check on the completion status.', + callback: async ({ accountId, accessToken, device_id, user_email, ...command_args }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/commands`, + accountId, + apiToken: accessToken, + options: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + commands: [ + { + type: 'pcap', + device_id, + user_email, + args: command_args, + version: 1, + }, + ], + }), + }, + }) + }, + }) + + registerTool({ + name: 'dex_create_remote_warp_diag', + description: + 'Create a remote Warp Diagnostic (WARP-diag) for a device. This is a resource intensive and privacy-sensitive operation on a real user device.' + + 'Always ask for confirmation from the user that the targeted email and device are correct before executing a capture', + schema: { + device_id: z.string().uuid().describe('The device ID to target.'), + user_email: z.string().email().describe('The email of the user associated with the device.'), + 'test-all-routes': z + .boolean() + .default(true) + .describe( + 'Test an IP address from all included or excluded ranges. Tests an IP address from all included or excluded ranges.' + + "Essentially the same as running 'route get '' and collecting the results. This option may increase the time taken to collect the warp-diag" + ), + }, + agent, + llmContext: + 'If the request was successful, the diagnostic has been initiated. You can poll the dex_list_remote_commands tool periodically to check on the completion status.' + + 'See https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/warp-logs/ for more info on warp-diags', + callback: async ({ accountId, accessToken, device_id, user_email, ...command_args }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/commands`, + accountId, + apiToken: accessToken, + options: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + commands: [ + { + type: 'warp-diag', + device_id, + user_email, + args: command_args, + version: 1, + }, + ], + }), + }, + }) + }, + }) + + registerTool({ + name: 'dex_list_remote_captures', + description: + 'Retrieve a list of remote captures for device debugging, like PCAPs or WARP Diags.', + schema: { page: pageParam }, + agent, + callback: async ({ accountId, accessToken, page }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/commands?${new URLSearchParams({ page: String(page), per_page: `50` })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_fleet_status_live', + description: + 'Retrieve details about the real-time status of the fleet of devices broken down by dimension (mode, status, colo, platform, version)', + schema: { + since_minutes: z + .number() + .min(1) + .max(60) + .default(10) + .describe( + 'Number of minutes before current time to use as cutoff for device states to include.' + ), + colo: coloParam.optional(), + }, + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/fleet-status/live?${new URLSearchParams({ ...(params as unknown as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_fleet_status_over_time', + description: + 'Retrieve aggregate time series details about the status of the fleet of devices, or performance metrics for a specific device, over the specified time period.', + schema: { + from: timeStartParam, + to: timeEndParam, + interval: aggregationIntervalParam, + colo: coloParam + .optional() + .describe('Filter results to WARP devices connected to a specific colo.'), + device_id: z.string().uuid().optional().describe('Filter results to a specific device.'), + }, + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/fleet-status/over-time?${new URLSearchParams({ ...(params as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_fleet_status_logs', + description: + 'Retrieve raw fleet status device logs with a variety of levels of granularity and filtering. Use `source=last_seen` to view logs showing the last known ' + + 'state per device within the specified time period. Use `source=hourly` to view logs showing an hourly rollup per device where values are the average value of all' + + 'events within the time period. Use `source=raw` to view all logs for the specified period.', + schema: { + page: pageParam, + from: timeStartParam, + to: timeEndParam, + source: z + .enum(['last_seen', 'hourly', 'raw']) + .describe('Specifies the granularity of results.'), + colo: coloParam.optional(), + device_id: z.string().uuid().optional().describe('Filter results to a specific device.'), + mode: z.string().optional().describe('Filter results to devices with a specific WARP mode.'), + platform: z + .string() + .optional() + .describe('Filter results to devices on a specific operating system.'), + status: z + .string() + .optional() + .describe('Filter results to devices with a specific WARP connection status.'), + version: z + .string() + .optional() + .describe('Filter results to devices with a specific WARP client version.'), + }, + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/fleet-status/devices?${new URLSearchParams({ ...(params as unknown as Record) })}&per_page=50`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_list_warp_change_events', + description: 'View logs of events when users toggle WARP on or off, or change configurations.', + schema: { + from: timeStartParam, + to: timeEndParam, + page: pageParam, + account_name: z.string().optional().describe('Optionally filter events by account name.'), + config_name: z + .string() + .optional() + .describe( + 'Optionally filter events by WARP configuration name changed from or to. Applicable to `type=config` events only.' + ), + sort_order: z + .enum(['ASC', 'DESC']) + .optional() + .default('ASC') + .describe('Set timestamp sort order.'), + toggle: z + .enum(['on', 'off']) + .optional() + .describe( + 'Optionally filter events by toggle value. Applicable to `type=toggle` events only.' + ), + type: z.enum(['config', 'toggle']).optional().describe('Optionally filter events by type.'), + }, + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/warp-change-events?${new URLSearchParams({ ...(params as unknown as Record) })}&per_page=50`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_list_colos', + description: + 'View a list of Cloudflare colos sorted alphabetically or by frequency encountered in fleet status or DEX test data.', + schema: { + from: timeStartParam, + to: timeEndParam, + sortBy: z + .enum(['fleet-status-usage', 'application-tests-usage']) + .optional() + .describe( + 'Use `fleet-status-usage` to sort by frequency seen in device state checkins.' + + 'Use `application-tests-usage` to sort by frequency seen in DEX test results. Omit to sort alphabetically.' + ), + }, + agent, + callback: async ({ accountId, accessToken, ...params }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/colos?${new URLSearchParams({ ...(params as unknown as Record) })}`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) + + registerTool({ + name: 'dex_list_remote_warp_diag_contents', + description: + 'Given a WARP diag remote capture id and device_id, returns a list of the files contained in the archive.', + schema: { + deviceId: deviceIdParam.describe( + 'The device_id field of the successful WARP-diag remote capture response to list contents of.' + ), + commandId: z + .string() + .uuid() + .describe( + 'The id of the successful WARP-diag remote capture response to list contents of.' + ), + }, + llmContext: + 'Use the dex_explore_remote_warp_diag_output tool for specific file paths to explore the file contents for analysis. ' + + 'Hint: you can call dex_explore_remote_warp_diag_output multiple times in parallel if necessary to take advantage of in-memory caching for best performance.' + + 'See https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/warp-logs/ for more info on warp-diags', + agent, + callback: async ({ accessToken, deviceId, commandId }) => { + const reader = await getReader({ accessToken, deviceId, commandId }) + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return new Error(`Failed to get active account id`) + } + + return await reader.list({ accessToken, accountId, commandId, deviceId }) + }, + }) + + registerTool({ + name: 'dex_explore_remote_warp_diag_output', + description: + 'Explore the contents of remote capture WARP diag archive filepaths returned by the dex_list_remote_warp_diag_contents tool for analysis.', + schema: { + commandId: z.string().uuid().describe('The id of the command results to explore'), + deviceId: deviceIdParam.describe('The device_id field of command to explore'), + filepath: z.string().describe('The file path from the archive to retrieve contents for.'), + }, + llmContext: + 'To avoid hitting conversation and memory limits, avoid outputting the whole contents of these files to the user unless specifically asked to. Instead prefer to show relevant snippets only.', + agent, + callback: async ({ accessToken, deviceId, commandId, filepath }) => { + const reader = await getReader({ accessToken, deviceId, commandId }) + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return new Error(`Failed to get active account id`) + } + + return await reader.read({ accessToken, accountId, deviceId, commandId, filepath }) + }, + }) + + registerTool({ + name: 'dex_analyze_warp_diag', + description: + 'Analyze successful WARP-diag remote captures for common issues. This should be the first place you start when trying to narrow down device-level issues with WARP.', + schema: { + command_id: z + .string() + .uuid() + .describe('The command_id of the successful WARP-diag remote capture to analyze.'), + }, + llmContext: + 'Detections with 0 occurences can be ruled out. Focus on detections with the highest severity.', + agent, + callback: async ({ accessToken, accountId, command_id }) => { + return await fetchCloudflareApi({ + endpoint: `/dex/commands/${command_id}/analysis`, + accountId, + apiToken: accessToken, + options: { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + }) + }, + }) +} + +// Helper to simplify tool registration by reducing boilerplate for accountId and accessToken +const registerTool = ({ + name, + description, + agent, + callback, + schema = {}, + llmContext = '', +}: { + name: string + description: string + schema?: T | ToolAnnotations + llmContext?: string + agent: CloudflareDEXMCP + callback: ( + p: { extra: unknown; accountId: string; accessToken: string } & z.objectOutputType< + T, + ZodTypeAny + > + ) => Promise +}) => { + agent.server.tool(name, description, schema, (async (params, extra) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const accessToken = props.accessToken + const res = await callback({ ...(params as T), extra, accountId, accessToken }) + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + data: res, + llmContext, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error with tool ${name}: ${error instanceof Error && error.message}`, + }), + }, + ], + } + } + }) as ToolCallback) +} + +// Shared parameter schemas +const timeStartParam = z + .string() + .describe( + 'The datetime of the beginning point of time range for results. Must be in ISO 8601 datetime string in the extended format with UTC time (e.g, 2025-04-21T18:00:00Z).' + ) +const timeEndParam = z + .string() + .describe( + 'The datetime of the ending point of time range for results. Must be in ISO 8601 datetime string in the extended format with UTC time (e.g, 2025-04-22T00:00:00Z).' + ) +const aggregationIntervalParam = z + .enum(['minute', 'hour']) + .describe('The time interval to group results by.') + +const pageParam = z.number().min(1).describe('The page of results to retrieve.') +const coloParam = z + .string() + .regex(/^[A-Z]{3}$/, '3-letter colo codes only') + .describe('Optionally filter results to a specific Cloudflare colo.') +const deviceIdParam = z.string().uuid() +const testIdParam = z.string().uuid() diff --git a/apps/dex-analysis/src/tools/dex.ts b/apps/dex-analysis/src/tools/dex.ts deleted file mode 100644 index 6354d366..00000000 --- a/apps/dex-analysis/src/tools/dex.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { z } from 'zod' - -import { fetchDexTestAnalyzation, fetchDexTests } from '../api/dex' - -import type { CloudflareDEXMCP } from '../index' - -// DEX parameter schema -const dexTestIdParam = z.string().describe('The DEX Test ID to analyze details of.') -const dexTestTimeStart = z - .string() - .describe( - 'The datetime of the beginning point of time range for DEX test analyzation. Must be in ISO 8601 datetime string in the extended format with UTC time (e.g, 2025-04-21T18:00:00Z).' - ) -const dexTestTimeEnd = z - .string() - .describe( - 'The datetime of the ending point of time range for DEX test analyzation. Must be in ISO 8601 datetime string in the extended format with UTC time (e.g, 2025-04-22T00:00:00Z).' - ) - -export function registerDEXTools(agent: CloudflareDEXMCP) { - // Register the dex test analysis tool by test id - agent.server.tool( - 'dex_test_statistics', - 'Analyze Cloudflare DEX Test Results given a Test ID', - { - dexTestId: dexTestIdParam, - timeStart: dexTestTimeStart, - timeEnd: dexTestTimeEnd, - }, - async (params) => { - const accountId = await agent.getActiveAccountId() - if (!accountId) { - return { - content: [ - { - type: 'text', - text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', - }, - ], - } - } - try { - const { dexTestId, timeStart, timeEnd } = params - const accessToken = agent.props.accessToken - const data = await fetchDexTestAnalyzation({ - dexTestId, - accountId, - accessToken, - timeStart, - timeEnd, - }) - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - data, - llmContext: - "The quartiles are sorted by 'resource fetch time' from LEAST performant in quartile 1 to MOST performant in quartile 4. For each quartile-based entry, it provides extensive information about the up-to-20 specific test results that are within that quartile of performance.", - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - error: `Error retreiving DEX HTTP Tests: ${error instanceof Error && error.message}`, - }), - }, - ], - } - } - } - ) - - // Register the dex test analysis tool by test id - agent.server.tool( - 'dex_list_tests', - 'Retrieve a list of all Cloudflare DEX Tests configured.', - {}, - async () => { - const accountId = await agent.getActiveAccountId() - if (!accountId) { - return { - content: [ - { - type: 'text', - text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', - }, - ], - } - } - try { - const accessToken = agent.props.accessToken - const data = await fetchDexTests({ accountId, accessToken }) - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - data, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - error: `Error retreiving DEX Tests: ${error instanceof Error && error.message}`, - }), - }, - ], - } - } - } - ) -} diff --git a/apps/dex-analysis/src/warp_diag_reader.ts b/apps/dex-analysis/src/warp_diag_reader.ts new file mode 100644 index 00000000..10beb0b3 --- /dev/null +++ b/apps/dex-analysis/src/warp_diag_reader.ts @@ -0,0 +1,136 @@ +import { DurableObject } from 'cloudflare:workers' +import JSZip from 'jszip' + +import { getEnv } from '@repo/mcp-common/src/env' + +import { type Env } from './dex-analysis.context' + +const env = getEnv() + +// Helper for reading large WARP diag zip archives. +// Holds the contents in memory between requests from the agent for specific files +// instead of having the worker download the zip on every request. +// +// Each DO represents one remote capture zip +export class WarpDiagReader extends DurableObject { + #cache?: { files: string[]; zip: JSZip } + + // List the files in the zip for the agent + async list({ + accessToken, + accountId, + deviceId, + commandId, + }: { + accessToken: string + accountId: string + deviceId: string + commandId: string + }) { + const { files } = await this.#getZip({ accessToken, accountId, deviceId, commandId }) + return files + } + + // Return the contents of a file by path + async read({ + accessToken, + accountId, + deviceId, + commandId, + filepath, + }: { + accessToken: string + accountId: string + deviceId: string + commandId: string + filepath: string + }) { + const { zip } = await this.#getZip({ accessToken, accountId, deviceId, commandId }) + const file = zip.file(filepath) + const content = await file?.async('text') + return content + } + + async #getZip({ + accessToken, + accountId, + deviceId, + commandId, + }: { + accessToken: string + accountId: string + deviceId: string + commandId: string + }) { + if (this.#cache) { + return this.#cache + } + + const url = new URL( + `https://api.cloudflare.com/client/v4/accounts/${accountId}/dex/devices/${deviceId}/commands/downloads/${commandId}.zip` + ) + + let headers = { + Authorization: `Bearer ${accessToken}`, + } + + if (env.DEV_DISABLE_OAUTH) { + headers = { + Authorization: `Bearer ${env.DEV_CLOUDFLARE_API_TOKEN}`, + } + } + + const res = await fetch(url, { headers }) + + if (res.status !== 200) { + throw new Error(`failed to download zip, non-200 status code: ${res.status}`) + } + + const zip = await new JSZip().loadAsync(await res.arrayBuffer()) + const files: string[] = [] + for (const [relativePath, file] of Object.entries(zip.files)) { + if (!file.dir) { + files.push(relativePath) + } + } + + const cache = { files, zip } + this.#cache = cache + return cache + } +} + +async function hashToken(accessToken: string) { + const hashArr = Array.from( + new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(accessToken))) + ) + return hashArr.map((b) => b.toString(16).padStart(2, '0')).join('') +} + +// Create unique name based on accessToken hash and the download details. In order to read cached zip from memory +// you need to have the same access token that was used to fetch it. +async function readerName({ + accessToken, + deviceId, + commandId, +}: { + accessToken: string + deviceId: string + commandId: string +}) { + return (await hashToken(accessToken)) + deviceId + commandId +} + +export async function getReader({ + accessToken, + deviceId, + commandId, +}: { + accessToken: string + deviceId: string + commandId: string +}) { + const name = await readerName({ accessToken, deviceId, commandId }) + const id = env.WARP_DIAG_READER.idFromName(name) + return env.WARP_DIAG_READER.get(id) +} diff --git a/apps/dex-analysis/vitest.config.ts b/apps/dex-analysis/vitest.config.ts index c201c144..c705a874 100644 --- a/apps/dex-analysis/vitest.config.ts +++ b/apps/dex-analysis/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/dex-analysis.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/dex-analysis/wrangler.jsonc b/apps/dex-analysis/wrangler.jsonc index 1429356e..edeb775d 100644 --- a/apps/dex-analysis/wrangler.jsonc +++ b/apps/dex-analysis/wrangler.jsonc @@ -4,17 +4,25 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/dex-analysis.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "migrations": [ { "new_sqlite_classes": ["CloudflareDEXMCP"], "tag": "v1" + }, + { + "new_sqlite_classes": ["WarpDiagReader"], + "tag": "v2" } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ @@ -25,6 +33,10 @@ { "class_name": "UserDetails", "name": "USER_DETAILS" + }, + { + "class_name": "WarpDiagReader", + "name": "WARP_DIAG_READER" } ] }, @@ -35,7 +47,9 @@ } ], "vars": { - "ENVIRONMENT": "development" + "ENVIRONMENT": "development", + "MCP_SERVER_NAME": "PLACEHOLDER", + "MCP_SERVER_VERSION": "PLACEHOLDER" }, "dev": { "port": 8976 @@ -63,6 +77,10 @@ "class_name": "UserDetails", "name": "USER_DETAILS", "script_name": "mcp-cloudflare-workers-observability-staging" + }, + { + "class_name": "WarpDiagReader", + "name": "WARP_DIAG_READER" } ] }, @@ -96,6 +114,10 @@ "class_name": "UserDetails", "name": "USER_DETAILS", "script_name": "mcp-cloudflare-workers-observability-production" + }, + { + "class_name": "WarpDiagReader", + "name": "WARP_DIAG_READER" } ] }, diff --git a/apps/dns-analytics/CHANGELOG.md b/apps/dns-analytics/CHANGELOG.md new file mode 100644 index 00000000..fd035258 --- /dev/null +++ b/apps/dns-analytics/CHANGELOG.md @@ -0,0 +1,125 @@ +# dns-analytics + +## 0.1.10 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.8 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.7 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.6 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.5 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/dns-analytics/CONTRIBUTING.md b/apps/dns-analytics/CONTRIBUTING.md index e86a3f2d..67449380 100644 --- a/apps/dns-analytics/CONTRIBUTING.md +++ b/apps/dns-analytics/CONTRIBUTING.md @@ -18,7 +18,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo ``` DEV_DISABLE_OAUTH=true - DEV_CLOUDFLARE_EMAIL=your_cloudflare_email + your_cloudflare_email # This is your api token with endpoint access. DEV_CLOUDFLARE_API_TOKEN=your_development_api_token ``` @@ -29,7 +29,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/dns-analytics/README.md b/apps/dns-analytics/README.md index 91687d1c..aeae44bc 100644 --- a/apps/dns-analytics/README.md +++ b/apps/dns-analytics/README.md @@ -41,7 +41,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://dns-analytics.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://dns-analytics.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/dns-analytics/package.json b/apps/dns-analytics/package.json index 4e1c4eca..9ff46bca 100644 --- a/apps/dns-analytics/package.json +++ b/apps/dns-analytics/package.json @@ -1,23 +1,23 @@ { "name": "dns-analytics", - "version": "0.0.1", + "version": "0.1.10", "private": true, "scripts": { "check:lint": "run-eslint-workers", "check:types": "run-tsc", - "deploy": "wrangler deploy", + "deploy": "run-wrangler-deploy", "dev": "wrangler dev", "start": "wrangler dev", "types": "wrangler types --include-env=false", "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/dns-analytics/src/index.ts b/apps/dns-analytics/src/dns-analytics.app.ts similarity index 75% rename from apps/dns-analytics/src/index.ts rename to apps/dns-analytics/src/dns-analytics.app.ts index 67dc5fee..7b836cec 100644 --- a/apps/dns-analytics/src/index.ts +++ b/apps/dns-analytics/src/dns-analytics.app.ts @@ -1,23 +1,24 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' -import { registerZoneTools } from '@repo/mcp-common/src/tools/zone' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' +import { registerZoneTools } from '@repo/mcp-common/src/tools/zone.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerAnalyticTools } from './tools/analytics' +import { registerAnalyticTools } from './tools/dex-analytics.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './dns-analytics.context' export { UserDetails } @@ -53,8 +54,12 @@ export class DNSAnalyticsMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -72,9 +77,14 @@ export class DNSAnalyticsMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -84,7 +94,12 @@ export class DNSAnalyticsMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -102,8 +117,8 @@ const AnalyticsScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(DNSAnalyticsMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(DNSAnalyticsMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/dns-analytics/src/context.ts b/apps/dns-analytics/src/dns-analytics.context.ts similarity index 84% rename from apps/dns-analytics/src/context.ts rename to apps/dns-analytics/src/dns-analytics.context.ts index 27c56cb5..700206be 100644 --- a/apps/dns-analytics/src/context.ts +++ b/apps/dns-analytics/src/dns-analytics.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { DNSAnalyticsMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { DNSAnalyticsMCP } from './dns-analytics.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string diff --git a/apps/dns-analytics/src/tools/analytics.ts b/apps/dns-analytics/src/tools/dex-analytics.tools.ts similarity index 88% rename from apps/dns-analytics/src/tools/analytics.ts rename to apps/dns-analytics/src/tools/dex-analytics.tools.ts index 02a67d40..0949fb13 100644 --- a/apps/dns-analytics/src/tools/analytics.ts +++ b/apps/dns-analytics/src/tools/dex-analytics.tools.ts @@ -1,15 +1,12 @@ import { z } from 'zod' import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' -import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import type { AccountGetParams } from 'cloudflare/resources/accounts/accounts.mjs' import type { ReportGetParams } from 'cloudflare/resources/dns/analytics.mjs' import type { ZoneGetParams } from 'cloudflare/resources/dns/settings.mjs' -import type { Env } from '../context' -import type { DNSAnalyticsMCP } from '../index' - -const env = getEnv() +import type { DNSAnalyticsMCP } from '../dns-analytics.app' function getStartDate(days: number) { const today = new Date() @@ -28,7 +25,8 @@ export function registerAnalyticTools(agent: DNSAnalyticsMCP) { }, async ({ zone, days }) => { try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const start_date = getStartDate(days) const params: ReportGetParams = { zone_id: zone, @@ -76,7 +74,8 @@ export function registerAnalyticTools(agent: DNSAnalyticsMCP) { ], } } - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const params: AccountGetParams = { account_id: accountId, } @@ -112,7 +111,8 @@ export function registerAnalyticTools(agent: DNSAnalyticsMCP) { }, async ({ zone }) => { try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const params: ZoneGetParams = { zone_id: zone, } diff --git a/apps/dns-analytics/worker-configuration.d.ts b/apps/dns-analytics/worker-configuration.d.ts index 4aba3e6d..c3e3fa01 100644 --- a/apps/dns-analytics/worker-configuration.d.ts +++ b/apps/dns-analytics/worker-configuration.d.ts @@ -10,7 +10,7 @@ declare namespace Cloudflare { CLOUDFLARE_CLIENT_ID: "PLACEHOLDER"; CLOUDFLARE_CLIENT_SECRET: "PLACEHOLDER"; DEV_CLOUDFLARE_API_TOKEN: "PLACEHOLDER"; - MCP_OBJECT: DurableObjectNamespace; + MCP_OBJECT: DurableObjectNamespace; MCP_METRICS: AnalyticsEngineDataset; } } @@ -4567,7 +4567,7 @@ interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { certNotAfter: ""; } /** Possible outcomes of TLS verification */ -declare type CertVerificationStatus = +declare type CertVerificationStatus = /** Authentication succeeded */ "SUCCESS" /** No certificate was presented */ @@ -4626,7 +4626,7 @@ interface D1ExecResult { count: number; duration: number; } -type D1SessionConstraint = +type D1SessionConstraint = // Indicates that the first query should go to the primary, and the rest queries // using the same D1DatabaseSession will go to any replica that is consistent with // the bookmark maintained by the session (returned by the first query). @@ -5064,7 +5064,7 @@ declare namespace Rpc { // The reason for using a generic type here is to build a serializable subset of structured // cloneable composite types. This allows types defined with the "interface" keyword to pass the // serializable check as well. Otherwise, only types defined with the "type" keyword would pass. - type Serializable = + type Serializable = // Structured cloneables BaseType // Structured cloneable composites diff --git a/apps/dns-analytics/wrangler.jsonc b/apps/dns-analytics/wrangler.jsonc index f07c3e59..efcc3605 100644 --- a/apps/dns-analytics/wrangler.jsonc +++ b/apps/dns-analytics/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/dns-analytics.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-dns-analytics-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/docs-ai-search/.eslintrc.cjs b/apps/docs-ai-search/.eslintrc.cjs new file mode 100644 index 00000000..f6bf291a --- /dev/null +++ b/apps/docs-ai-search/.eslintrc.cjs @@ -0,0 +1,5 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ['@repo/eslint-config/default.cjs'], +} diff --git a/apps/docs-ai-search/CHANGELOG.md b/apps/docs-ai-search/CHANGELOG.md new file mode 100644 index 00000000..96e4bb8b --- /dev/null +++ b/apps/docs-ai-search/CHANGELOG.md @@ -0,0 +1,143 @@ +# docs-ai-search + +## 0.4.4 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.5.0 + +### Minor Changes + +- Changed backend from Vectorize to AI Search for documentation search + - Now uses Cloudflare AI Search (AutoRAG) for contextual search of the Cloudflare Developer Documentation + - Maintains full backward compatibility - same XML response format and tool interface + - Package renamed from `docs-vectorize` to `docs-ai-search` to reflect the new backend + +## 0.4.3 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.4.2 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.4.1 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.4.0 + +### Minor Changes + +- dee0a7b: Updated the model for docs search to embeddinggemma-300m + +## 0.3.3 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.3.2 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.3.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.3.0 + +### Minor Changes + +- f885d07: Add search docs tool to bindings and obs servers + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.2.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.2.0 + +### Minor Changes + +- 89bfaf4: feat: add Pages to Workers migration guide to docs-vectorize MCP server + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/docs-ai-search/README.md b/apps/docs-ai-search/README.md new file mode 100644 index 00000000..6c89c21d --- /dev/null +++ b/apps/docs-ai-search/README.md @@ -0,0 +1,42 @@ +# Cloudflare Documentation MCP Server (via AI Search) 🔭 + +This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP connections. It uses Cloudflare AI Search (AutoRAG) to provide contextual search of the Cloudflare Developer Documentation. + +The Cloudflare account this worker is deployed on has an AI Search instance configured with the complete Cloudflare Developer Documentation. + +## 🔨 Available Tools + +Currently available tools: + +| **Category** | **Tool** | **Description** | +| ---------------------------- | --------------------------------- | ------------------------------------ | +| **Cloudflare Documentation** | `search_cloudflare_documentation` | Search the Cloudflare documentation. | + +### Prompt Examples + +- `Do Cloudflare Workers costs depend on response sizes? I want to serve some images (map tiles) from an R2 bucket and I'm concerned about costs.` +- `How many indexes are supported in Workers Analytics Engine? Give an example using the Workers binding api.` +- `Can you give me some information on how to use the Workers AI Search binding` + +## Access the remote MCP server from any MCP Client + +If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://docs.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). + +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. + +Replace the content with the following configuration: + +```json +{ + "mcpServers": { + "cloudflare": { + "command": "npx", + "args": ["mcp-remote", "https://docs.mcp.cloudflare.com/mcp"] + } + } +} +``` + +Once you've set up your configuration file, restart MCP client and a browser window will open showing your OAuth login page. Proceed through the authentication flow to grant the client access to your MCP server. After you grant access, the tools will become available for you to use. + +Interested in contributing, and running this server locally? See [CONTRIBUTING.md](CONTRIBUTING.md) to get started. diff --git a/apps/docs-ai-search/package.json b/apps/docs-ai-search/package.json new file mode 100644 index 00000000..bff74752 --- /dev/null +++ b/apps/docs-ai-search/package.json @@ -0,0 +1,34 @@ +{ + "name": "docs-ai-search", + "version": "0.4.4", + "private": true, + "scripts": { + "check:lint": "run-eslint-workers", + "check:types": "run-tsc", + "deploy": "run-wrangler-deploy", + "dev": "wrangler dev", + "start": "npm run dev", + "types": "wrangler types --include-env=false", + "test": "vitest run" + }, + "dependencies": { + "@cloudflare/workers-oauth-provider": "0.0.13", + "@hono/zod-validator": "0.4.3", + "@modelcontextprotocol/sdk": "1.20.2", + "@repo/mcp-common": "workspace:*", + "@repo/mcp-observability": "workspace:*", + "agents": "0.2.19", + "cloudflare": "4.2.0", + "hono": "4.7.6", + "mime": "4.0.6", + "zod": "3.24.2" + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "0.8.14", + "@types/node": "22.14.1", + "prettier": "3.5.3", + "typescript": "5.5.4", + "vitest": "3.0.9", + "wrangler": "4.10.0" + } +} diff --git a/apps/docs-ai-search/src/docs-ai-search.app.ts b/apps/docs-ai-search/src/docs-ai-search.app.ts new file mode 100644 index 00000000..04e1dc1b --- /dev/null +++ b/apps/docs-ai-search/src/docs-ai-search.app.ts @@ -0,0 +1,76 @@ +import { createMcpHandler, McpAgent } from 'agents/mcp' + +import { getEnv } from '@repo/mcp-common/src/env' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-ai-search.prompts' +import { initSentry } from '@repo/mcp-common/src/sentry' +import { CloudflareMCPServer } from '@repo/mcp-common/src/server' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-ai-search.tools' + +import type { Env } from './docs-ai-search.context' + +const env = getEnv() + +export class CloudflareDocumentationMCP extends McpAgent { + _server: CloudflareMCPServer | undefined + set server(server: CloudflareMCPServer) { + this._server = server + } + get server(): CloudflareMCPServer { + if (!this._server) { + throw new Error('Tried to access server before it was initialized') + } + return this._server + } + + constructor( + public ctx: DurableObjectState, + public env: Env + ) { + super(ctx, env) + } + + async init() { + this.server = createMcpServer(env, this.ctx) + } +} + +const sseHandler = CloudflareDocumentationMCP.serveSSE('/sse') + +export default { + fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { + const url = new URL(req.url) + if (url.pathname === '/sse' || url.pathname === '/sse/message') { + return sseHandler.fetch(req, env, ctx) + } + if (url.pathname === '/mcp') { + const server = createMcpServer(env, ctx, req) + const mcpHandler = createMcpHandler(server) + return mcpHandler(req, env, ctx) + } + return new Response('Not found', { status: 404 }) + }, +} + +function createMcpServer( + env: Env, + ctx: { + waitUntil: ExecutionContext['waitUntil'] + }, + req?: Request +) { + const sentry = initSentry(env, ctx, req) + + const server = new CloudflareMCPServer({ + wae: env.MCP_METRICS, + serverInfo: { + name: env.MCP_SERVER_NAME, + version: env.MCP_SERVER_VERSION, + }, + sentry, + }) + + registerDocsTools(server, env) + registerPrompts(server) + + return server +} diff --git a/apps/docs-ai-search/src/docs-ai-search.context.ts b/apps/docs-ai-search/src/docs-ai-search.context.ts new file mode 100644 index 00000000..af23fd86 --- /dev/null +++ b/apps/docs-ai-search/src/docs-ai-search.context.ts @@ -0,0 +1,14 @@ +import type { CloudflareDocumentationMCP } from './docs-ai-search.app' + +export interface Env { + ENVIRONMENT: 'development' | 'staging' | 'production' + MCP_SERVER_NAME: string + MCP_SERVER_VERSION: string + MCP_OBJECT: DurableObjectNamespace + MCP_METRICS: AnalyticsEngineDataset + SENTRY_ACCESS_CLIENT_ID: string + SENTRY_ACCESS_CLIENT_SECRET: string + GIT_HASH: string + SENTRY_DSN: string + AI: Ai +} diff --git a/apps/docs-ai-search/tsconfig.json b/apps/docs-ai-search/tsconfig.json new file mode 100644 index 00000000..386756e0 --- /dev/null +++ b/apps/docs-ai-search/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@repo/typescript-config/workers.json", + "include": ["*/**.ts", "./vitest.config.ts"] +} diff --git a/apps/docs-ai-search/vitest.config.ts b/apps/docs-ai-search/vitest.config.ts new file mode 100644 index 00000000..01ba8ec5 --- /dev/null +++ b/apps/docs-ai-search/vitest.config.ts @@ -0,0 +1,24 @@ +import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' + +import type { Env } from './src/docs-ai-search.context' + +export interface TestEnv extends Env { + CLOUDFLARE_MOCK_ACCOUNT_ID: string + CLOUDFLARE_MOCK_API_TOKEN: string +} + +export default defineWorkersConfig({ + test: { + poolOptions: { + workers: { + wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, + miniflare: { + bindings: { + CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', + CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', + } satisfies Partial, + }, + }, + }, + }, +}) diff --git a/apps/docs-ai-search/worker-configuration.d.ts b/apps/docs-ai-search/worker-configuration.d.ts new file mode 100644 index 00000000..305dc71b --- /dev/null +++ b/apps/docs-ai-search/worker-configuration.d.ts @@ -0,0 +1,5694 @@ +// Runtime types generated with workerd@1.20250409.0 2025-03-10 nodejs_compat +// Begin runtime types +/*! ***************************************************************************** +Copyright (c) Cloudflare. All rights reserved. +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* eslint-disable */ +// noinspection JSUnusedGlobalSymbols +declare var onmessage: never; +/** + * An abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException) + */ +declare class DOMException extends Error { + constructor(message?: string, name?: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */ + readonly message: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ + readonly name: string; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code) + */ + readonly code: number; + static readonly INDEX_SIZE_ERR: number; + static readonly DOMSTRING_SIZE_ERR: number; + static readonly HIERARCHY_REQUEST_ERR: number; + static readonly WRONG_DOCUMENT_ERR: number; + static readonly INVALID_CHARACTER_ERR: number; + static readonly NO_DATA_ALLOWED_ERR: number; + static readonly NO_MODIFICATION_ALLOWED_ERR: number; + static readonly NOT_FOUND_ERR: number; + static readonly NOT_SUPPORTED_ERR: number; + static readonly INUSE_ATTRIBUTE_ERR: number; + static readonly INVALID_STATE_ERR: number; + static readonly SYNTAX_ERR: number; + static readonly INVALID_MODIFICATION_ERR: number; + static readonly NAMESPACE_ERR: number; + static readonly INVALID_ACCESS_ERR: number; + static readonly VALIDATION_ERR: number; + static readonly TYPE_MISMATCH_ERR: number; + static readonly SECURITY_ERR: number; + static readonly NETWORK_ERR: number; + static readonly ABORT_ERR: number; + static readonly URL_MISMATCH_ERR: number; + static readonly QUOTA_EXCEEDED_ERR: number; + static readonly TIMEOUT_ERR: number; + static readonly INVALID_NODE_TYPE_ERR: number; + static readonly DATA_CLONE_ERR: number; + get stack(): any; + set stack(value: any); +} +type WorkerGlobalScopeEventMap = { + fetch: FetchEvent; + scheduled: ScheduledEvent; + queue: QueueEvent; + unhandledrejection: PromiseRejectionEvent; + rejectionhandled: PromiseRejectionEvent; +}; +declare abstract class WorkerGlobalScope extends EventTarget { + EventTarget: typeof EventTarget; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console) */ +interface Console { + "assert"(condition?: boolean, ...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear_static) */ + clear(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count_static) */ + count(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/countreset_static) */ + countReset(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug_static) */ + debug(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir_static) */ + dir(item?: any, options?: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml_static) */ + dirxml(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error_static) */ + error(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group_static) */ + group(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupcollapsed_static) */ + groupCollapsed(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupend_static) */ + groupEnd(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info_static) */ + info(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static) */ + log(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table_static) */ + table(tabularData?: any, properties?: string[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time_static) */ + time(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeend_static) */ + timeEnd(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timelog_static) */ + timeLog(label?: string, ...data: any[]): void; + timeStamp(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace_static) */ + trace(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static) */ + warn(...data: any[]): void; +} +declare const console: Console; +type BufferSource = ArrayBufferView | ArrayBuffer; +type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array; +declare namespace WebAssembly { + class CompileError extends Error { + constructor(message?: string); + } + class RuntimeError extends Error { + constructor(message?: string); + } + type ValueType = "anyfunc" | "externref" | "f32" | "f64" | "i32" | "i64" | "v128"; + interface GlobalDescriptor { + value: ValueType; + mutable?: boolean; + } + class Global { + constructor(descriptor: GlobalDescriptor, value?: any); + value: any; + valueOf(): any; + } + type ImportValue = ExportValue | number; + type ModuleImports = Record; + type Imports = Record; + type ExportValue = Function | Global | Memory | Table; + type Exports = Record; + class Instance { + constructor(module: Module, imports?: Imports); + readonly exports: Exports; + } + interface MemoryDescriptor { + initial: number; + maximum?: number; + shared?: boolean; + } + class Memory { + constructor(descriptor: MemoryDescriptor); + readonly buffer: ArrayBuffer; + grow(delta: number): number; + } + type ImportExportKind = "function" | "global" | "memory" | "table"; + interface ModuleExportDescriptor { + kind: ImportExportKind; + name: string; + } + interface ModuleImportDescriptor { + kind: ImportExportKind; + module: string; + name: string; + } + abstract class Module { + static customSections(module: Module, sectionName: string): ArrayBuffer[]; + static exports(module: Module): ModuleExportDescriptor[]; + static imports(module: Module): ModuleImportDescriptor[]; + } + type TableKind = "anyfunc" | "externref"; + interface TableDescriptor { + element: TableKind; + initial: number; + maximum?: number; + } + class Table { + constructor(descriptor: TableDescriptor, value?: any); + readonly length: number; + get(index: number): any; + grow(delta: number, value?: any): number; + set(index: number, value?: any): void; + } + function instantiate(module: Module, imports?: Imports): Promise; + function validate(bytes: BufferSource): boolean; +} +/** + * This ServiceWorker API interface represents the global execution context of a service worker. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope) + */ +interface ServiceWorkerGlobalScope extends WorkerGlobalScope { + DOMException: typeof DOMException; + WorkerGlobalScope: typeof WorkerGlobalScope; + btoa(data: string): string; + atob(data: string): string; + setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; + setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearTimeout(timeoutId: number | null): void; + setInterval(callback: (...args: any[]) => void, msDelay?: number): number; + setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearInterval(timeoutId: number | null): void; + queueMicrotask(task: Function): void; + structuredClone(value: T, options?: StructuredSerializeOptions): T; + reportError(error: any): void; + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + self: ServiceWorkerGlobalScope; + crypto: Crypto; + caches: CacheStorage; + scheduler: Scheduler; + performance: Performance; + Cloudflare: Cloudflare; + readonly origin: string; + Event: typeof Event; + ExtendableEvent: typeof ExtendableEvent; + CustomEvent: typeof CustomEvent; + PromiseRejectionEvent: typeof PromiseRejectionEvent; + FetchEvent: typeof FetchEvent; + TailEvent: typeof TailEvent; + TraceEvent: typeof TailEvent; + ScheduledEvent: typeof ScheduledEvent; + MessageEvent: typeof MessageEvent; + CloseEvent: typeof CloseEvent; + ReadableStreamDefaultReader: typeof ReadableStreamDefaultReader; + ReadableStreamBYOBReader: typeof ReadableStreamBYOBReader; + ReadableStream: typeof ReadableStream; + WritableStream: typeof WritableStream; + WritableStreamDefaultWriter: typeof WritableStreamDefaultWriter; + TransformStream: typeof TransformStream; + ByteLengthQueuingStrategy: typeof ByteLengthQueuingStrategy; + CountQueuingStrategy: typeof CountQueuingStrategy; + ErrorEvent: typeof ErrorEvent; + EventSource: typeof EventSource; + ReadableStreamBYOBRequest: typeof ReadableStreamBYOBRequest; + ReadableStreamDefaultController: typeof ReadableStreamDefaultController; + ReadableByteStreamController: typeof ReadableByteStreamController; + WritableStreamDefaultController: typeof WritableStreamDefaultController; + TransformStreamDefaultController: typeof TransformStreamDefaultController; + CompressionStream: typeof CompressionStream; + DecompressionStream: typeof DecompressionStream; + TextEncoderStream: typeof TextEncoderStream; + TextDecoderStream: typeof TextDecoderStream; + Headers: typeof Headers; + Body: typeof Body; + Request: typeof Request; + Response: typeof Response; + WebSocket: typeof WebSocket; + WebSocketPair: typeof WebSocketPair; + WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair; + AbortController: typeof AbortController; + AbortSignal: typeof AbortSignal; + TextDecoder: typeof TextDecoder; + TextEncoder: typeof TextEncoder; + navigator: Navigator; + Navigator: typeof Navigator; + URL: typeof URL; + URLSearchParams: typeof URLSearchParams; + URLPattern: typeof URLPattern; + Blob: typeof Blob; + File: typeof File; + FormData: typeof FormData; + Crypto: typeof Crypto; + SubtleCrypto: typeof SubtleCrypto; + CryptoKey: typeof CryptoKey; + CacheStorage: typeof CacheStorage; + Cache: typeof Cache; + FixedLengthStream: typeof FixedLengthStream; + IdentityTransformStream: typeof IdentityTransformStream; + HTMLRewriter: typeof HTMLRewriter; +} +declare function addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; +declare function removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; +/** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ +declare function dispatchEvent(event: WorkerGlobalScopeEventMap[keyof WorkerGlobalScopeEventMap]): boolean; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/btoa) */ +declare function btoa(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/atob) */ +declare function atob(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearTimeout) */ +declare function clearTimeout(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearInterval) */ +declare function clearInterval(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/queueMicrotask) */ +declare function queueMicrotask(task: Function): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +declare function structuredClone(value: T, options?: StructuredSerializeOptions): T; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/reportError) */ +declare function reportError(error: any): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/fetch) */ +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +declare const self: ServiceWorkerGlobalScope; +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare const crypto: Crypto; +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare const caches: CacheStorage; +declare const scheduler: Scheduler; +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +declare const performance: Performance; +declare const Cloudflare: Cloudflare; +declare const origin: string; +declare const navigator: Navigator; +interface TestController { +} +interface ExecutionContext { + waitUntil(promise: Promise): void; + passThroughOnException(): void; + props: any; +} +type ExportedHandlerFetchHandler = (request: Request>, env: Env, ctx: ExecutionContext) => Response | Promise; +type ExportedHandlerTailHandler = (events: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTraceHandler = (traces: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTailStreamHandler = (event: TailStream.TailEvent, env: Env, ctx: ExecutionContext) => TailStream.TailEventHandlerType | Promise; +type ExportedHandlerScheduledHandler = (controller: ScheduledController, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerQueueHandler = (batch: MessageBatch, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTestHandler = (controller: TestController, env: Env, ctx: ExecutionContext) => void | Promise; +interface ExportedHandler { + fetch?: ExportedHandlerFetchHandler; + tail?: ExportedHandlerTailHandler; + trace?: ExportedHandlerTraceHandler; + tailStream?: ExportedHandlerTailStreamHandler; + scheduled?: ExportedHandlerScheduledHandler; + test?: ExportedHandlerTestHandler; + email?: EmailExportedHandler; + queue?: ExportedHandlerQueueHandler; +} +interface StructuredSerializeOptions { + transfer?: any[]; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent) */ +declare abstract class PromiseRejectionEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/promise) */ + readonly promise: Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/reason) */ + readonly reason: any; +} +declare abstract class Navigator { + sendBeacon(url: string, body?: (ReadableStream | string | (ArrayBuffer | ArrayBufferView) | Blob | FormData | URLSearchParams | URLSearchParams)): boolean; + readonly userAgent: string; + readonly hardwareConcurrency: number; +} +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +interface Performance { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */ + readonly timeOrigin: number; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancenow) */ + now(): number; +} +interface AlarmInvocationInfo { + readonly isRetry: boolean; + readonly retryCount: number; +} +interface Cloudflare { + readonly compatibilityFlags: Record; +} +interface DurableObject { + fetch(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; +} +type DurableObjectStub = Fetcher & { + readonly id: DurableObjectId; + readonly name?: string; +}; +interface DurableObjectId { + toString(): string; + equals(other: DurableObjectId): boolean; + readonly name?: string; +} +interface DurableObjectNamespace { + newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions): DurableObjectId; + idFromName(name: string): DurableObjectId; + idFromString(id: string): DurableObjectId; + get(id: DurableObjectId, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub; + jurisdiction(jurisdiction: DurableObjectJurisdiction): DurableObjectNamespace; +} +type DurableObjectJurisdiction = "eu" | "fedramp"; +interface DurableObjectNamespaceNewUniqueIdOptions { + jurisdiction?: DurableObjectJurisdiction; +} +type DurableObjectLocationHint = "wnam" | "enam" | "sam" | "weur" | "eeur" | "apac" | "oc" | "afr" | "me"; +interface DurableObjectNamespaceGetDurableObjectOptions { + locationHint?: DurableObjectLocationHint; +} +interface DurableObjectState { + waitUntil(promise: Promise): void; + readonly id: DurableObjectId; + readonly storage: DurableObjectStorage; + container?: Container; + blockConcurrencyWhile(callback: () => Promise): Promise; + acceptWebSocket(ws: WebSocket, tags?: string[]): void; + getWebSockets(tag?: string): WebSocket[]; + setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void; + getWebSocketAutoResponse(): WebSocketRequestResponsePair | null; + getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null; + setHibernatableWebSocketEventTimeout(timeoutMs?: number): void; + getHibernatableWebSocketEventTimeout(): number | null; + getTags(ws: WebSocket): string[]; + abort(reason?: string): void; +} +interface DurableObjectTransaction { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + rollback(): void; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; +} +interface DurableObjectStorage { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + deleteAll(options?: DurableObjectPutOptions): Promise; + transaction(closure: (txn: DurableObjectTransaction) => Promise): Promise; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; + sync(): Promise; + sql: SqlStorage; + transactionSync(closure: () => T): T; + getCurrentBookmark(): Promise; + getBookmarkForTime(timestamp: number | Date): Promise; + onNextSessionRestoreBookmark(bookmark: string): Promise; +} +interface DurableObjectListOptions { + start?: string; + startAfter?: string; + end?: string; + prefix?: string; + reverse?: boolean; + limit?: number; + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetOptions { + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetAlarmOptions { + allowConcurrency?: boolean; +} +interface DurableObjectPutOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; + noCache?: boolean; +} +interface DurableObjectSetAlarmOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; +} +declare class WebSocketRequestResponsePair { + constructor(request: string, response: string); + get request(): string; + get response(): string; +} +interface AnalyticsEngineDataset { + writeDataPoint(event?: AnalyticsEngineDataPoint): void; +} +interface AnalyticsEngineDataPoint { + indexes?: ((ArrayBuffer | string) | null)[]; + doubles?: number[]; + blobs?: ((ArrayBuffer | string) | null)[]; +} +/** + * An event which takes place in the DOM. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event) + */ +declare class Event { + constructor(type: string, init?: EventInit); + /** + * Returns the type of event, e.g. "click", "hashchange", or "submit". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type) + */ + get type(): string; + /** + * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase) + */ + get eventPhase(): number; + /** + * Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed) + */ + get composed(): boolean; + /** + * Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles) + */ + get bubbles(): boolean; + /** + * Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable) + */ + get cancelable(): boolean; + /** + * Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented) + */ + get defaultPrevented(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue) + */ + get returnValue(): boolean; + /** + * Returns the object whose event listener's callback is currently being invoked. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget) + */ + get currentTarget(): EventTarget | undefined; + /** + * Returns the object to which event is dispatched (its target). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target) + */ + get target(): EventTarget | undefined; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement) + */ + get srcElement(): EventTarget | undefined; + /** + * Returns the event's timestamp as the number of milliseconds measured relative to the time origin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp) + */ + get timeStamp(): number; + /** + * Returns true if event was dispatched by the user agent, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted) + */ + get isTrusted(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + get cancelBubble(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + set cancelBubble(value: boolean); + /** + * Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation) + */ + stopImmediatePropagation(): void; + /** + * If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) + */ + preventDefault(): void; + /** + * When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) + */ + stopPropagation(): void; + /** + * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath) + */ + composedPath(): EventTarget[]; + static readonly NONE: number; + static readonly CAPTURING_PHASE: number; + static readonly AT_TARGET: number; + static readonly BUBBLING_PHASE: number; +} +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} +type EventListener = (event: EventType) => void; +interface EventListenerObject { + handleEvent(event: EventType): void; +} +type EventListenerOrEventListenerObject = EventListener | EventListenerObject; +/** + * EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget) + */ +declare class EventTarget = Record> { + constructor(); + /** + * Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. + * + * The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. + * + * When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. + * + * When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. + * + * When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. + * + * If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. + * + * The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) + */ + addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; + /** + * Removes the event listener in target's event listener list with the same type, callback, and options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener) + */ + removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; + /** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ + dispatchEvent(event: EventMap[keyof EventMap]): boolean; +} +interface EventTargetEventListenerOptions { + capture?: boolean; +} +interface EventTargetAddEventListenerOptions { + capture?: boolean; + passive?: boolean; + once?: boolean; + signal?: AbortSignal; +} +interface EventTargetHandlerObject { + handleEvent: (event: Event) => any | undefined; +} +/** + * A controller object that allows you to abort one or more DOM requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +declare class AbortController { + constructor(); + /** + * Returns the AbortSignal object associated with this object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + get signal(): AbortSignal; + /** + * Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} +/** + * A signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +declare abstract class AbortSignal extends EventTarget { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) */ + static abort(reason?: any): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) */ + static timeout(delay: number): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) */ + static any(signals: AbortSignal[]): AbortSignal; + /** + * Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + get aborted(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) */ + get reason(): any; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + get onabort(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + set onabort(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) */ + throwIfAborted(): void; +} +interface Scheduler { + wait(delay: number, maybeOptions?: SchedulerWaitOptions): Promise; +} +interface SchedulerWaitOptions { + signal?: AbortSignal; +} +/** + * Extends the lifetime of the install and activate events dispatched on the global scope as part of the service worker lifecycle. This ensures that any functional events (like FetchEvent) are not dispatched until it upgrades database schemas and deletes the outdated cache entries. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent) + */ +declare abstract class ExtendableEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) */ + waitUntil(promise: Promise): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent) */ +declare class CustomEvent extends Event { + constructor(type: string, init?: CustomEventCustomEventInit); + /** + * Returns any custom data event was created with. Typically used for synthetic events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail) + */ + get detail(): T; +} +interface CustomEventCustomEventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; + detail?: any; +} +/** + * A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob) + */ +declare class Blob { + constructor(type?: ((ArrayBuffer | ArrayBufferView) | string | Blob)[], options?: BlobOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + get size(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + get type(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number, type?: string): Blob; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/stream) */ + stream(): ReadableStream; +} +interface BlobOptions { + type?: string; +} +/** + * Provides information about files and allows JavaScript in a web page to access their content. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File) + */ +declare class File extends Blob { + constructor(bits: ((ArrayBuffer | ArrayBufferView) | string | Blob)[] | undefined, name: string, options?: FileOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + get name(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + get lastModified(): number; +} +interface FileOptions { + type?: string; + lastModified?: number; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class CacheStorage { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CacheStorage/open) */ + open(cacheName: string): Promise; + readonly default: Cache; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class Cache { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */ + delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#match) */ + match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#put) */ + put(request: RequestInfo | URL, response: Response): Promise; +} +interface CacheQueryOptions { + ignoreMethod?: boolean; +} +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare abstract class Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + get subtle(): SubtleCrypto; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(buffer: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): string; + DigestStream: typeof DigestStream; +} +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +declare abstract class SubtleCrypto { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, plainText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, cipherText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, signature: ArrayBuffer | ArrayBufferView, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: string | SubtleCryptoHashAlgorithm, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: string | SubtleCryptoGenerateKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, derivedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, length?: number | null): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: string, keyData: (ArrayBuffer | ArrayBufferView) | JsonWebKey, algorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: string, key: CryptoKey): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: string, wrappedKey: ArrayBuffer | ArrayBufferView, unwrappingKey: CryptoKey, unwrapAlgorithm: string | SubtleCryptoEncryptAlgorithm, unwrappedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + timingSafeEqual(a: ArrayBuffer | ArrayBufferView, b: ArrayBuffer | ArrayBufferView): boolean; +} +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +declare abstract class CryptoKey { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: CryptoKeyKeyAlgorithm | CryptoKeyAesKeyAlgorithm | CryptoKeyHmacKeyAlgorithm | CryptoKeyRsaKeyAlgorithm | CryptoKeyEllipticKeyAlgorithm | CryptoKeyArbitraryKeyAlgorithm; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: string[]; +} +interface CryptoKeyPair { + publicKey: CryptoKey; + privateKey: CryptoKey; +} +interface JsonWebKey { + kty: string; + use?: string; + key_ops?: string[]; + alg?: string; + ext?: boolean; + crv?: string; + x?: string; + y?: string; + d?: string; + n?: string; + e?: string; + p?: string; + q?: string; + dp?: string; + dq?: string; + qi?: string; + oth?: RsaOtherPrimesInfo[]; + k?: string; +} +interface RsaOtherPrimesInfo { + r?: string; + d?: string; + t?: string; +} +interface SubtleCryptoDeriveKeyAlgorithm { + name: string; + salt?: (ArrayBuffer | ArrayBufferView); + iterations?: number; + hash?: (string | SubtleCryptoHashAlgorithm); + $public?: CryptoKey; + info?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoEncryptAlgorithm { + name: string; + iv?: (ArrayBuffer | ArrayBufferView); + additionalData?: (ArrayBuffer | ArrayBufferView); + tagLength?: number; + counter?: (ArrayBuffer | ArrayBufferView); + length?: number; + label?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoGenerateKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + modulusLength?: number; + publicExponent?: (ArrayBuffer | ArrayBufferView); + length?: number; + namedCurve?: string; +} +interface SubtleCryptoHashAlgorithm { + name: string; +} +interface SubtleCryptoImportKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + length?: number; + namedCurve?: string; + compressed?: boolean; +} +interface SubtleCryptoSignAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + dataLength?: number; + saltLength?: number; +} +interface CryptoKeyKeyAlgorithm { + name: string; +} +interface CryptoKeyAesKeyAlgorithm { + name: string; + length: number; +} +interface CryptoKeyHmacKeyAlgorithm { + name: string; + hash: CryptoKeyKeyAlgorithm; + length: number; +} +interface CryptoKeyRsaKeyAlgorithm { + name: string; + modulusLength: number; + publicExponent: ArrayBuffer | ArrayBufferView; + hash?: CryptoKeyKeyAlgorithm; +} +interface CryptoKeyEllipticKeyAlgorithm { + name: string; + namedCurve: string; +} +interface CryptoKeyArbitraryKeyAlgorithm { + name: string; + hash?: CryptoKeyKeyAlgorithm; + namedCurve?: string; + length?: number; +} +declare class DigestStream extends WritableStream { + constructor(algorithm: string | SubtleCryptoHashAlgorithm); + readonly digest: Promise; + get bytesWritten(): number | bigint; +} +/** + * A decoder for a specific method, that is a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261, gbk, etc. A decoder takes a stream of bytes as input and emits a stream of code points. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder) + */ +declare class TextDecoder { + constructor(label?: string, options?: TextDecoderConstructorOptions); + /** + * Returns the result of running encoding's decoder. The method can be invoked zero or more times with options's stream set to true, and then once without options's stream (or set to false), to process a fragmented input. If the invocation without options's stream (or set to false) has no input, it's clearest to omit both arguments. + * + * ``` + * var string = "", decoder = new TextDecoder(encoding), buffer; + * while(buffer = next_chunk()) { + * string += decoder.decode(buffer, {stream:true}); + * } + * string += decoder.decode(); // end-of-queue + * ``` + * + * If the error mode is "fatal" and encoding's decoder returns error, throws a TypeError. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode) + */ + decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string; + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +/** + * TextEncoder takes a stream of code points as input and emits a stream of bytes. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder) + */ +declare class TextEncoder { + constructor(); + /** + * Returns the result of running UTF-8's encoder. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encode) + */ + encode(input?: string): Uint8Array; + /** + * Runs the UTF-8 encoder on source, stores the result of that operation into destination, and returns the progress made as an object wherein read is the number of converted code units of source and written is the number of bytes modified in destination. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encodeInto) + */ + encodeInto(input: string, buffer: ArrayBuffer | ArrayBufferView): TextEncoderEncodeIntoResult; + get encoding(): string; +} +interface TextDecoderConstructorOptions { + fatal: boolean; + ignoreBOM: boolean; +} +interface TextDecoderDecodeOptions { + stream: boolean; +} +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} +/** + * Events providing information related to errors in scripts or in files. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent) + */ +declare class ErrorEvent extends Event { + constructor(type: string, init?: ErrorEventErrorEventInit); + get filename(): string; + get message(): string; + get lineno(): number; + get colno(): number; + get error(): any; +} +interface ErrorEventErrorEventInit { + message?: string; + filename?: string; + lineno?: number; + colno?: number; + error?: any; +} +/** + * Provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData) + */ +declare class FormData { + constructor(); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: Blob, filename?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/delete) */ + delete(name: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get) */ + get(name: string): (File | string) | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/getAll) */ + getAll(name: string): (File | string)[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: Blob, filename?: string): void; + /* Returns an array of key, value pairs for every entry in the list. */ + entries(): IterableIterator<[ + key: string, + value: File | string + ]>; + /* Returns a list of keys in the list. */ + keys(): IterableIterator; + /* Returns a list of values in the list. */ + values(): IterableIterator<(File | string)>; + forEach(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: File | string + ]>; +} +interface ContentOptions { + html?: boolean; +} +declare class HTMLRewriter { + constructor(); + on(selector: string, handlers: HTMLRewriterElementContentHandlers): HTMLRewriter; + onDocument(handlers: HTMLRewriterDocumentContentHandlers): HTMLRewriter; + transform(response: Response): Response; +} +interface HTMLRewriterElementContentHandlers { + element?(element: Element): void | Promise; + comments?(comment: Comment): void | Promise; + text?(element: Text): void | Promise; +} +interface HTMLRewriterDocumentContentHandlers { + doctype?(doctype: Doctype): void | Promise; + comments?(comment: Comment): void | Promise; + text?(text: Text): void | Promise; + end?(end: DocumentEnd): void | Promise; +} +interface Doctype { + readonly name: string | null; + readonly publicId: string | null; + readonly systemId: string | null; +} +interface Element { + tagName: string; + readonly attributes: IterableIterator; + readonly removed: boolean; + readonly namespaceURI: string; + getAttribute(name: string): string | null; + hasAttribute(name: string): boolean; + setAttribute(name: string, value: string): Element; + removeAttribute(name: string): Element; + before(content: string | ReadableStream | Response, options?: ContentOptions): Element; + after(content: string | ReadableStream | Response, options?: ContentOptions): Element; + prepend(content: string | ReadableStream | Response, options?: ContentOptions): Element; + append(content: string | ReadableStream | Response, options?: ContentOptions): Element; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Element; + remove(): Element; + removeAndKeepContent(): Element; + setInnerContent(content: string | ReadableStream | Response, options?: ContentOptions): Element; + onEndTag(handler: (tag: EndTag) => void | Promise): void; +} +interface EndTag { + name: string; + before(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + after(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + remove(): EndTag; +} +interface Comment { + text: string; + readonly removed: boolean; + before(content: string, options?: ContentOptions): Comment; + after(content: string, options?: ContentOptions): Comment; + replace(content: string, options?: ContentOptions): Comment; + remove(): Comment; +} +interface Text { + readonly text: string; + readonly lastInTextNode: boolean; + readonly removed: boolean; + before(content: string | ReadableStream | Response, options?: ContentOptions): Text; + after(content: string | ReadableStream | Response, options?: ContentOptions): Text; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Text; + remove(): Text; +} +interface DocumentEnd { + append(content: string, options?: ContentOptions): DocumentEnd; +} +/** + * This is the event type for fetch events dispatched on the service worker global scope. It contains information about the fetch, including the request and how the receiver will treat the response. It provides the event.respondWith() method, which allows us to provide a response to this fetch. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent) + */ +declare abstract class FetchEvent extends ExtendableEvent { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/request) */ + readonly request: Request; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith) */ + respondWith(promise: Response | Promise): void; + passThroughOnException(): void; +} +type HeadersInit = Headers | Iterable> | Record; +/** + * This Fetch API interface allows you to perform various actions on HTTP request and response headers. These actions include retrieving, setting, adding to, and removing. A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.  You can add to this using methods like append() (see Examples.) In all methods of this interface, header names are matched by case-insensitive byte sequence. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers) + */ +declare class Headers { + constructor(init?: HeadersInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get) */ + get(name: string): string | null; + getAll(name: string): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie) */ + getSetCookie(): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete) */ + delete(name: string): void; + forEach(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void; + /* Returns an iterator allowing to go through all key/value pairs contained in this object. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */ + keys(): IterableIterator; + /* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */ + values(): IterableIterator; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +type BodyInit = ReadableStream | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData; +declare abstract class Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */ + get body(): ReadableStream | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + get bodyUsed(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ + json(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ + formData(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob(): Promise; +} +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +declare var Response: { + prototype: Response; + new (body?: BodyInit | null, init?: ResponseInit): Response; + error(): Response; + redirect(url: string, status?: number): Response; + json(any: any, maybeInit?: (ResponseInit | Response)): Response; +}; +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +interface Response extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/clone) */ + clone(): Response; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status) */ + status: number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText) */ + statusText: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/headers) */ + headers: Headers; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok) */ + ok: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected) */ + redirected: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url) */ + url: string; + webSocket: WebSocket | null; + cf: any | undefined; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/type) */ + type: "default" | "error"; +} +interface ResponseInit { + status?: number; + statusText?: string; + headers?: HeadersInit; + cf?: any; + webSocket?: (WebSocket | null); + encodeBody?: "automatic" | "manual"; +} +type RequestInfo> = Request | string; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +declare var Request: { + prototype: Request; + new >(input: RequestInfo | URL, init?: RequestInit): Request; +}; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +interface Request> extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/clone) */ + clone(): Request; + /** + * Returns request's HTTP method, which is "GET" by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method) + */ + method: string; + /** + * Returns the URL of request as a string. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url) + */ + url: string; + /** + * Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/headers) + */ + headers: Headers; + /** + * Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/redirect) + */ + redirect: string; + fetcher: Fetcher | null; + /** + * Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/signal) + */ + signal: AbortSignal; + cf: Cf | undefined; + /** + * Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI] + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity) + */ + integrity: string; + /* Returns a boolean indicating whether or not request can outlive the global in which it was created. */ + keepalive: boolean; + /** + * Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/cache) + */ + cache?: "no-store"; +} +interface RequestInit { + /* A string to set request's method. */ + method?: string; + /* A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /* A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: string; + fetcher?: (Fetcher | null); + cf?: Cf; + /* A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: "no-store"; + /* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /* An AbortSignal to set request's signal. */ + signal?: (AbortSignal | null); + encodeResponseBody?: "automatic" | "manual"; +} +type Service = Fetcher; +type Fetcher = (T extends Rpc.EntrypointBranded ? Rpc.Provider : unknown) & { + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + connect(address: SocketAddress | string, options?: SocketOptions): Socket; +}; +interface KVNamespaceListKey { + name: Key; + expiration?: number; + metadata?: Metadata; +} +type KVNamespaceListResult = { + list_complete: false; + keys: KVNamespaceListKey[]; + cursor: string; + cacheStatus: string | null; +} | { + list_complete: true; + keys: KVNamespaceListKey[]; + cacheStatus: string | null; +}; +interface KVNamespace { + get(key: Key, options?: Partial>): Promise; + get(key: Key, type: "text"): Promise; + get(key: Key, type: "json"): Promise; + get(key: Key, type: "arrayBuffer"): Promise; + get(key: Key, type: "stream"): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"text">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"json">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"arrayBuffer">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"stream">): Promise; + get(key: Array, type: "text"): Promise>; + get(key: Array, type: "json"): Promise>; + get(key: Array, options?: Partial>): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>; + list(options?: KVNamespaceListOptions): Promise>; + put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise; + getWithMetadata(key: Key, options?: Partial>): Promise>; + getWithMetadata(key: Key, type: "text"): Promise>; + getWithMetadata(key: Key, type: "json"): Promise>; + getWithMetadata(key: Key, type: "arrayBuffer"): Promise>; + getWithMetadata(key: Key, type: "stream"): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"text">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"json">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"arrayBuffer">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"stream">): Promise>; + getWithMetadata(key: Array, type: "text"): Promise>>; + getWithMetadata(key: Array, type: "json"): Promise>>; + getWithMetadata(key: Array, options?: Partial>): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>>; + delete(key: Key): Promise; +} +interface KVNamespaceListOptions { + limit?: number; + prefix?: (string | null); + cursor?: (string | null); +} +interface KVNamespaceGetOptions { + type: Type; + cacheTtl?: number; +} +interface KVNamespacePutOptions { + expiration?: number; + expirationTtl?: number; + metadata?: (any | null); +} +interface KVNamespaceGetWithMetadataResult { + value: Value | null; + metadata: Metadata | null; + cacheStatus: string | null; +} +type QueueContentType = "text" | "bytes" | "json" | "v8"; +interface Queue { + send(message: Body, options?: QueueSendOptions): Promise; + sendBatch(messages: Iterable>, options?: QueueSendBatchOptions): Promise; +} +interface QueueSendOptions { + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueSendBatchOptions { + delaySeconds?: number; +} +interface MessageSendRequest { + body: Body; + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueRetryOptions { + delaySeconds?: number; +} +interface Message { + readonly id: string; + readonly timestamp: Date; + readonly body: Body; + readonly attempts: number; + retry(options?: QueueRetryOptions): void; + ack(): void; +} +interface QueueEvent extends ExtendableEvent { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface MessageBatch { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface R2Error extends Error { + readonly name: string; + readonly code: number; + readonly message: string; + readonly action: string; + readonly stack: any; +} +interface R2ListOptions { + limit?: number; + prefix?: string; + cursor?: string; + delimiter?: string; + startAfter?: string; + include?: ("httpMetadata" | "customMetadata")[]; +} +declare abstract class R2Bucket { + head(key: string): Promise; + get(key: string, options: R2GetOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + get(key: string, options?: R2GetOptions): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise; + createMultipartUpload(key: string, options?: R2MultipartOptions): Promise; + resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload; + delete(keys: string | string[]): Promise; + list(options?: R2ListOptions): Promise; +} +interface R2MultipartUpload { + readonly key: string; + readonly uploadId: string; + uploadPart(partNumber: number, value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, options?: R2UploadPartOptions): Promise; + abort(): Promise; + complete(uploadedParts: R2UploadedPart[]): Promise; +} +interface R2UploadedPart { + partNumber: number; + etag: string; +} +declare abstract class R2Object { + readonly key: string; + readonly version: string; + readonly size: number; + readonly etag: string; + readonly httpEtag: string; + readonly checksums: R2Checksums; + readonly uploaded: Date; + readonly httpMetadata?: R2HTTPMetadata; + readonly customMetadata?: Record; + readonly range?: R2Range; + readonly storageClass: string; + readonly ssecKeyMd5?: string; + writeHttpMetadata(headers: Headers): void; +} +interface R2ObjectBody extends R2Object { + get body(): ReadableStream; + get bodyUsed(): boolean; + arrayBuffer(): Promise; + text(): Promise; + json(): Promise; + blob(): Promise; +} +type R2Range = { + offset: number; + length?: number; +} | { + offset?: number; + length: number; +} | { + suffix: number; +}; +interface R2Conditional { + etagMatches?: string; + etagDoesNotMatch?: string; + uploadedBefore?: Date; + uploadedAfter?: Date; + secondsGranularity?: boolean; +} +interface R2GetOptions { + onlyIf?: (R2Conditional | Headers); + range?: (R2Range | Headers); + ssecKey?: (ArrayBuffer | string); +} +interface R2PutOptions { + onlyIf?: (R2Conditional | Headers); + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + md5?: (ArrayBuffer | string); + sha1?: (ArrayBuffer | string); + sha256?: (ArrayBuffer | string); + sha384?: (ArrayBuffer | string); + sha512?: (ArrayBuffer | string); + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2MultipartOptions { + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2Checksums { + readonly md5?: ArrayBuffer; + readonly sha1?: ArrayBuffer; + readonly sha256?: ArrayBuffer; + readonly sha384?: ArrayBuffer; + readonly sha512?: ArrayBuffer; + toJSON(): R2StringChecksums; +} +interface R2StringChecksums { + md5?: string; + sha1?: string; + sha256?: string; + sha384?: string; + sha512?: string; +} +interface R2HTTPMetadata { + contentType?: string; + contentLanguage?: string; + contentDisposition?: string; + contentEncoding?: string; + cacheControl?: string; + cacheExpiry?: Date; +} +type R2Objects = { + objects: R2Object[]; + delimitedPrefixes: string[]; +} & ({ + truncated: true; + cursor: string; +} | { + truncated: false; +}); +interface R2UploadPartOptions { + ssecKey?: (ArrayBuffer | string); +} +declare abstract class ScheduledEvent extends ExtendableEvent { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface ScheduledController { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface QueuingStrategy { + highWaterMark?: (number | bigint); + size?: (chunk: T) => number | bigint; +} +interface UnderlyingSink { + type?: string; + start?: (controller: WritableStreamDefaultController) => void | Promise; + write?: (chunk: W, controller: WritableStreamDefaultController) => void | Promise; + abort?: (reason: any) => void | Promise; + close?: () => void | Promise; +} +interface UnderlyingByteSource { + type: "bytes"; + autoAllocateChunkSize?: number; + start?: (controller: ReadableByteStreamController) => void | Promise; + pull?: (controller: ReadableByteStreamController) => void | Promise; + cancel?: (reason: any) => void | Promise; +} +interface UnderlyingSource { + type?: "" | undefined; + start?: (controller: ReadableStreamDefaultController) => void | Promise; + pull?: (controller: ReadableStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: (number | bigint); +} +interface Transformer { + readableType?: string; + writableType?: string; + start?: (controller: TransformStreamDefaultController) => void | Promise; + transform?: (chunk: I, controller: TransformStreamDefaultController) => void | Promise; + flush?: (controller: TransformStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: number; +} +interface StreamPipeOptions { + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + preventAbort?: boolean; + preventCancel?: boolean; + signal?: AbortSignal; +} +type ReadableStreamReadResult = { + done: false; + value: R; +} | { + done: true; + value?: undefined; +}; +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +interface ReadableStream { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel) */ + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(): ReadableStreamDefaultReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(options: ReadableStreamGetReaderOptions): ReadableStreamBYOBReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough) */ + pipeThrough(transform: ReadableWritablePair, options?: StreamPipeOptions): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo) */ + pipeTo(destination: WritableStream, options?: StreamPipeOptions): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee) */ + tee(): [ + ReadableStream, + ReadableStream + ]; + values(options?: ReadableStreamValuesOptions): AsyncIterableIterator; + [Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator; +} +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +declare const ReadableStream: { + prototype: ReadableStream; + new (underlyingSource: UnderlyingByteSource, strategy?: QueuingStrategy): ReadableStream; + new (underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; +}; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader) */ +declare class ReadableStreamDefaultReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read) */ + read(): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader) */ +declare class ReadableStreamBYOBReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read) */ + read(view: T): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock) */ + releaseLock(): void; + readAtLeast(minElements: number, view: T): Promise>; +} +interface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions { + min?: number; +} +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode: "byob"; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest) */ +declare abstract class ReadableStreamBYOBRequest { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/view) */ + get view(): Uint8Array | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respond) */ + respond(bytesWritten: number): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respondWithNewView) */ + respondWithNewView(view: ArrayBuffer | ArrayBufferView): void; + get atLeast(): number | null; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController) */ +declare abstract class ReadableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/enqueue) */ + enqueue(chunk?: R): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/error) */ + error(reason: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController) */ +declare abstract class ReadableByteStreamController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/byobRequest) */ + get byobRequest(): ReadableStreamBYOBRequest | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/enqueue) */ + enqueue(chunk: ArrayBuffer | ArrayBufferView): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/error) */ + error(reason: any): void; +} +/** + * This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController) + */ +declare abstract class WritableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/signal) */ + get signal(): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/error) */ + error(reason?: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController) */ +declare abstract class TransformStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/enqueue) */ + enqueue(chunk?: O): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/error) */ + error(reason: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/terminate) */ + terminate(): void; +} +interface ReadableWritablePair { + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; + readable: ReadableStream; +} +/** + * This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream) + */ +declare class WritableStream { + constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter) */ + getWriter(): WritableStreamDefaultWriter; +} +/** + * This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter) + */ +declare class WritableStreamDefaultWriter { + constructor(stream: WritableStream); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/closed) */ + get closed(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/ready) */ + get ready(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/write) */ + write(chunk?: W): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream) */ +declare class TransformStream { + constructor(transformer?: Transformer, writableStrategy?: QueuingStrategy, readableStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/readable) */ + get readable(): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/writable) */ + get writable(): WritableStream; +} +declare class FixedLengthStream extends IdentityTransformStream { + constructor(expectedLength: number | bigint, queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +declare class IdentityTransformStream extends TransformStream { + constructor(queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +interface IdentityTransformStreamQueuingStrategy { + highWaterMark?: (number | bigint); +} +interface ReadableStreamValuesOptions { + preventCancel?: boolean; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CompressionStream) */ +declare class CompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DecompressionStream) */ +declare class DecompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoderStream) */ +declare class TextEncoderStream extends TransformStream { + constructor(); + get encoding(): string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoderStream) */ +declare class TextDecoderStream extends TransformStream { + constructor(label?: string, options?: TextDecoderStreamTextDecoderStreamInit); + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +interface TextDecoderStreamTextDecoderStreamInit { + fatal?: boolean; + ignoreBOM?: boolean; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy) + */ +declare class ByteLengthQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy) + */ +declare class CountQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} +interface ScriptVersion { + id?: string; + tag?: string; + message?: string; +} +declare abstract class TailEvent extends ExtendableEvent { + readonly events: TraceItem[]; + readonly traces: TraceItem[]; +} +interface TraceItem { + readonly event: (TraceItemFetchEventInfo | TraceItemJsRpcEventInfo | TraceItemScheduledEventInfo | TraceItemAlarmEventInfo | TraceItemQueueEventInfo | TraceItemEmailEventInfo | TraceItemTailEventInfo | TraceItemCustomEventInfo | TraceItemHibernatableWebSocketEventInfo) | null; + readonly eventTimestamp: number | null; + readonly logs: TraceLog[]; + readonly exceptions: TraceException[]; + readonly diagnosticsChannelEvents: TraceDiagnosticChannelEvent[]; + readonly scriptName: string | null; + readonly entrypoint?: string; + readonly scriptVersion?: ScriptVersion; + readonly dispatchNamespace?: string; + readonly scriptTags?: string[]; + readonly outcome: string; + readonly executionModel: string; + readonly truncated: boolean; + readonly cpuTime: number; + readonly wallTime: number; +} +interface TraceItemAlarmEventInfo { + readonly scheduledTime: Date; +} +interface TraceItemCustomEventInfo { +} +interface TraceItemScheduledEventInfo { + readonly scheduledTime: number; + readonly cron: string; +} +interface TraceItemQueueEventInfo { + readonly queue: string; + readonly batchSize: number; +} +interface TraceItemEmailEventInfo { + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; +} +interface TraceItemTailEventInfo { + readonly consumedEvents: TraceItemTailEventInfoTailItem[]; +} +interface TraceItemTailEventInfoTailItem { + readonly scriptName: string | null; +} +interface TraceItemFetchEventInfo { + readonly response?: TraceItemFetchEventInfoResponse; + readonly request: TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoRequest { + readonly cf?: any; + readonly headers: Record; + readonly method: string; + readonly url: string; + getUnredacted(): TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoResponse { + readonly status: number; +} +interface TraceItemJsRpcEventInfo { + readonly rpcMethod: string; +} +interface TraceItemHibernatableWebSocketEventInfo { + readonly getWebSocketEvent: TraceItemHibernatableWebSocketEventInfoMessage | TraceItemHibernatableWebSocketEventInfoClose | TraceItemHibernatableWebSocketEventInfoError; +} +interface TraceItemHibernatableWebSocketEventInfoMessage { + readonly webSocketEventType: string; +} +interface TraceItemHibernatableWebSocketEventInfoClose { + readonly webSocketEventType: string; + readonly code: number; + readonly wasClean: boolean; +} +interface TraceItemHibernatableWebSocketEventInfoError { + readonly webSocketEventType: string; +} +interface TraceLog { + readonly timestamp: number; + readonly level: string; + readonly message: any; +} +interface TraceException { + readonly timestamp: number; + readonly message: string; + readonly name: string; + readonly stack?: string; +} +interface TraceDiagnosticChannelEvent { + readonly timestamp: number; + readonly channel: string; + readonly message: any; +} +interface TraceMetrics { + readonly cpuTime: number; + readonly wallTime: number; +} +interface UnsafeTraceMetrics { + fromTrace(item: TraceItem): TraceMetrics; +} +/** + * The URL interface represents an object providing static methods used for creating object URLs. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL) + */ +declare class URL { + constructor(url: string | URL, base?: string | URL); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/origin) */ + get origin(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + get href(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + set href(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + get protocol(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + set protocol(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + get username(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + set username(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + get password(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + set password(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + get host(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + set host(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + get hostname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + set hostname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + get port(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + set port(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + get pathname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + set pathname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + get search(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + set search(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + get hash(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + set hash(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/searchParams) */ + get searchParams(): URLSearchParams; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/toJSON) */ + toJSON(): string; + /*function toString() { [native code] }*/ + toString(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/canParse_static) */ + static canParse(url: string, base?: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/parse_static) */ + static parse(url: string, base?: string): URL | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static) */ + static createObjectURL(object: File | Blob): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static) */ + static revokeObjectURL(object_url: string): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams) */ +declare class URLSearchParams { + constructor(init?: (Iterable> | Record | string)); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/size) */ + get size(): number; + /** + * Appends a specified key/value pair as a new search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append) + */ + append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete) + */ + delete(name: string, value?: string): void; + /** + * Returns the first value associated to the given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) + */ + get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) + */ + getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has) + */ + has(name: string, value?: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set) + */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/sort) */ + sort(): void; + /* Returns an array of key, value pairs for every entry in the search params. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns a list of keys in the search params. */ + keys(): IterableIterator; + /* Returns a list of values in the search params. */ + values(): IterableIterator; + forEach(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void; + /*function toString() { [native code] } Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */ + toString(): string; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +declare class URLPattern { + constructor(input?: (string | URLPatternURLPatternInit), baseURL?: (string | URLPatternURLPatternOptions), patternOptions?: URLPatternURLPatternOptions); + get protocol(): string; + get username(): string; + get password(): string; + get hostname(): string; + get port(): string; + get pathname(): string; + get search(): string; + get hash(): string; + test(input?: (string | URLPatternURLPatternInit), baseURL?: string): boolean; + exec(input?: (string | URLPatternURLPatternInit), baseURL?: string): URLPatternURLPatternResult | null; +} +interface URLPatternURLPatternInit { + protocol?: string; + username?: string; + password?: string; + hostname?: string; + port?: string; + pathname?: string; + search?: string; + hash?: string; + baseURL?: string; +} +interface URLPatternURLPatternComponentResult { + input: string; + groups: Record; +} +interface URLPatternURLPatternResult { + inputs: (string | URLPatternURLPatternInit)[]; + protocol: URLPatternURLPatternComponentResult; + username: URLPatternURLPatternComponentResult; + password: URLPatternURLPatternComponentResult; + hostname: URLPatternURLPatternComponentResult; + port: URLPatternURLPatternComponentResult; + pathname: URLPatternURLPatternComponentResult; + search: URLPatternURLPatternComponentResult; + hash: URLPatternURLPatternComponentResult; +} +interface URLPatternURLPatternOptions { + ignoreCase?: boolean; +} +/** + * A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent) + */ +declare class CloseEvent extends Event { + constructor(type: string, initializer?: CloseEventInit); + /** + * Returns the WebSocket connection close code provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/code) + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/reason) + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/wasClean) + */ + readonly wasClean: boolean; +} +interface CloseEventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} +/** + * A message received by a target object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent) + */ +declare class MessageEvent extends Event { + constructor(type: string, initializer: MessageEventInit); + /** + * Returns the data of the message. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data) + */ + readonly data: ArrayBuffer | string; +} +interface MessageEventInit { + data: ArrayBuffer | string; +} +type WebSocketEventMap = { + close: CloseEvent; + message: MessageEvent; + open: Event; + error: ErrorEvent; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +declare var WebSocket: { + prototype: WebSocket; + new (url: string, protocols?: (string[] | string)): WebSocket; + readonly READY_STATE_CONNECTING: number; + readonly CONNECTING: number; + readonly READY_STATE_OPEN: number; + readonly OPEN: number; + readonly READY_STATE_CLOSING: number; + readonly CLOSING: number; + readonly READY_STATE_CLOSED: number; + readonly CLOSED: number; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +interface WebSocket extends EventTarget { + accept(): void; + /** + * Transmits data using the WebSocket connection. data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/send) + */ + send(message: (ArrayBuffer | ArrayBufferView) | string): void; + /** + * Closes the WebSocket connection, optionally using code as the the WebSocket connection close code and reason as the the WebSocket connection close reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/close) + */ + close(code?: number, reason?: string): void; + serializeAttachment(attachment: any): void; + deserializeAttachment(): any | null; + /** + * Returns the state of the WebSocket object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/readyState) + */ + readyState: number; + /** + * Returns the URL that was used to establish the WebSocket connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/url) + */ + url: string | null; + /** + * Returns the subprotocol selected by the server, if any. It can be used in conjunction with the array form of the constructor's second argument to perform subprotocol negotiation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/protocol) + */ + protocol: string | null; + /** + * Returns the extensions selected by the server, if any. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/extensions) + */ + extensions: string | null; +} +declare const WebSocketPair: { + new (): { + 0: WebSocket; + 1: WebSocket; + }; +}; +interface SqlStorage { + exec>(query: string, ...bindings: any[]): SqlStorageCursor; + get databaseSize(): number; + Cursor: typeof SqlStorageCursor; + Statement: typeof SqlStorageStatement; +} +declare abstract class SqlStorageStatement { +} +type SqlStorageValue = ArrayBuffer | string | number | null; +declare abstract class SqlStorageCursor> { + next(): { + done?: false; + value: T; + } | { + done: true; + value?: never; + }; + toArray(): T[]; + one(): T; + raw(): IterableIterator; + columnNames: string[]; + get rowsRead(): number; + get rowsWritten(): number; + [Symbol.iterator](): IterableIterator; +} +interface Socket { + get readable(): ReadableStream; + get writable(): WritableStream; + get closed(): Promise; + get opened(): Promise; + close(): Promise; + startTls(options?: TlsOptions): Socket; +} +interface SocketOptions { + secureTransport?: string; + allowHalfOpen: boolean; + highWaterMark?: (number | bigint); +} +interface SocketAddress { + hostname: string; + port: number; +} +interface TlsOptions { + expectedServerHostname?: string; +} +interface SocketInfo { + remoteAddress?: string; + localAddress?: string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource) */ +declare class EventSource extends EventTarget { + constructor(url: string, init?: EventSourceEventSourceInit); + /** + * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close) + */ + close(): void; + /** + * Returns the URL providing the event stream. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url) + */ + get url(): string; + /** + * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials) + */ + get withCredentials(): boolean; + /** + * Returns the state of this EventSource object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState) + */ + get readyState(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + get onopen(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + set onopen(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + get onmessage(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + set onmessage(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + get onerror(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + set onerror(value: any | null); + static readonly CONNECTING: number; + static readonly OPEN: number; + static readonly CLOSED: number; + static from(stream: ReadableStream): EventSource; +} +interface EventSourceEventSourceInit { + withCredentials?: boolean; + fetcher?: Fetcher; +} +interface Container { + get running(): boolean; + start(options?: ContainerStartupOptions): void; + monitor(): Promise; + destroy(error?: any): Promise; + signal(signo: number): void; + getTcpPort(port: number): Fetcher; +} +interface ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; +} +type AiImageClassificationInput = { + image: number[]; +}; +type AiImageClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiImageClassification { + inputs: AiImageClassificationInput; + postProcessedOutputs: AiImageClassificationOutput; +} +type AiImageToTextInput = { + image: number[]; + prompt?: string; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageToText { + inputs: AiImageToTextInput; + postProcessedOutputs: AiImageToTextOutput; +} +type AiImageTextToTextInput = { + image: string; + prompt?: string; + max_tokens?: number; + temperature?: number; + ignore_eos?: boolean; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageTextToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageTextToText { + inputs: AiImageTextToTextInput; + postProcessedOutputs: AiImageTextToTextOutput; +} +type AiObjectDetectionInput = { + image: number[]; +}; +type AiObjectDetectionOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiObjectDetection { + inputs: AiObjectDetectionInput; + postProcessedOutputs: AiObjectDetectionOutput; +} +type AiSentenceSimilarityInput = { + source: string; + sentences: string[]; +}; +type AiSentenceSimilarityOutput = number[]; +declare abstract class BaseAiSentenceSimilarity { + inputs: AiSentenceSimilarityInput; + postProcessedOutputs: AiSentenceSimilarityOutput; +} +type AiAutomaticSpeechRecognitionInput = { + audio: number[]; +}; +type AiAutomaticSpeechRecognitionOutput = { + text?: string; + words?: { + word: string; + start: number; + end: number; + }[]; + vtt?: string; +}; +declare abstract class BaseAiAutomaticSpeechRecognition { + inputs: AiAutomaticSpeechRecognitionInput; + postProcessedOutputs: AiAutomaticSpeechRecognitionOutput; +} +type AiSummarizationInput = { + input_text: string; + max_length?: number; +}; +type AiSummarizationOutput = { + summary: string; +}; +declare abstract class BaseAiSummarization { + inputs: AiSummarizationInput; + postProcessedOutputs: AiSummarizationOutput; +} +type AiTextClassificationInput = { + text: string; +}; +type AiTextClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiTextClassification { + inputs: AiTextClassificationInput; + postProcessedOutputs: AiTextClassificationOutput; +} +type AiTextEmbeddingsInput = { + text: string | string[]; +}; +type AiTextEmbeddingsOutput = { + shape: number[]; + data: number[][]; +}; +declare abstract class BaseAiTextEmbeddings { + inputs: AiTextEmbeddingsInput; + postProcessedOutputs: AiTextEmbeddingsOutput; +} +type RoleScopedChatInput = { + role: "user" | "assistant" | "system" | "tool" | (string & NonNullable); + content: string; + name?: string; +}; +type AiTextGenerationToolLegacyInput = { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; +}; +type AiTextGenerationToolInput = { + type: "function" | (string & NonNullable); + function: { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; + }; +}; +type AiTextGenerationFunctionsInput = { + name: string; + code: string; +}; +type AiTextGenerationResponseFormat = { + type: string; + json_schema?: any; +}; +type AiTextGenerationInput = { + prompt?: string; + raw?: boolean; + stream?: boolean; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + messages?: RoleScopedChatInput[]; + response_format?: AiTextGenerationResponseFormat; + tools?: AiTextGenerationToolInput[] | AiTextGenerationToolLegacyInput[] | (object & NonNullable); + functions?: AiTextGenerationFunctionsInput[]; +}; +type AiTextGenerationOutput = { + response?: string; + tool_calls?: { + name: string; + arguments: unknown; + }[]; +} | ReadableStream; +declare abstract class BaseAiTextGeneration { + inputs: AiTextGenerationInput; + postProcessedOutputs: AiTextGenerationOutput; +} +type AiTextToSpeechInput = { + prompt: string; + lang?: string; +}; +type AiTextToSpeechOutput = Uint8Array | { + audio: string; +}; +declare abstract class BaseAiTextToSpeech { + inputs: AiTextToSpeechInput; + postProcessedOutputs: AiTextToSpeechOutput; +} +type AiTextToImageInput = { + prompt: string; + negative_prompt?: string; + height?: number; + width?: number; + image?: number[]; + image_b64?: string; + mask?: number[]; + num_steps?: number; + strength?: number; + guidance?: number; + seed?: number; +}; +type AiTextToImageOutput = ReadableStream; +declare abstract class BaseAiTextToImage { + inputs: AiTextToImageInput; + postProcessedOutputs: AiTextToImageOutput; +} +type AiTranslationInput = { + text: string; + target_lang: string; + source_lang?: string; +}; +type AiTranslationOutput = { + translated_text?: string; +}; +declare abstract class BaseAiTranslation { + inputs: AiTranslationInput; + postProcessedOutputs: AiTranslationOutput; +} +type Ai_Cf_Openai_Whisper_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper { + inputs: Ai_Cf_Openai_Whisper_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Output; +} +type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | { + /** + * The input text prompt for the model to generate a response. + */ + prompt?: string; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + image: number[] | (string & NonNullable); + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; +}; +interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output { + description?: string; +} +declare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M { + inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input; + postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output; +} +type Ai_Cf_Openai_Whisper_Tiny_En_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Tiny_En_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En { + inputs: Ai_Cf_Openai_Whisper_Tiny_En_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input { + /** + * Base64 encoded value of the audio data. + */ + audio: string; + /** + * Supported tasks are 'translate' or 'transcribe'. + */ + task?: string; + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * Preprocess the audio with a voice activity detection model. + */ + vad_filter?: string; + /** + * A text prompt to help provide context to the model on the contents of the audio. + */ + initial_prompt?: string; + /** + * The prefix it appended the the beginning of the output of the transcription and can guide the transcription result. + */ + prefix?: string; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output { + transcription_info?: { + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * The confidence level or probability of the detected language being accurate, represented as a decimal between 0 and 1. + */ + language_probability?: number; + /** + * The total duration of the original audio file, in seconds. + */ + duration?: number; + /** + * The duration of the audio after applying Voice Activity Detection (VAD) to remove silent or irrelevant sections, in seconds. + */ + duration_after_vad?: number; + }; + /** + * The complete transcription of the audio. + */ + text: string; + /** + * The total number of words in the transcription. + */ + word_count?: number; + segments?: { + /** + * The starting time of the segment within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the segment within the audio, in seconds. + */ + end?: number; + /** + * The transcription of the segment. + */ + text?: string; + /** + * The temperature used in the decoding process, controlling randomness in predictions. Lower values result in more deterministic outputs. + */ + temperature?: number; + /** + * The average log probability of the predictions for the words in this segment, indicating overall confidence. + */ + avg_logprob?: number; + /** + * The compression ratio of the input to the output, measuring how much the text was compressed during the transcription process. + */ + compression_ratio?: number; + /** + * The probability that the segment contains no speech, represented as a decimal between 0 and 1. + */ + no_speech_prob?: number; + words?: { + /** + * The individual word transcribed from the audio. + */ + word?: string; + /** + * The starting time of the word within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the word within the audio, in seconds. + */ + end?: number; + }[]; + }[]; + /** + * The transcription in WebVTT format, which includes timing and text information for use in subtitles. + */ + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo { + inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output; +} +type Ai_Cf_Baai_Bge_M3_Input = BGEM3InputQueryAndContexts | BGEM3InputEmbedding; +interface BGEM3InputQueryAndContexts { + /** + * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts + */ + query?: string; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +interface BGEM3InputEmbedding { + text: string | string[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +type Ai_Cf_Baai_Bge_M3_Output = BGEM3OuputQuery | BGEM3OutputEmbeddingForContexts | BGEM3OuputEmbedding; +interface BGEM3OuputQuery { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +interface BGEM3OutputEmbeddingForContexts { + response?: number[][]; + shape?: number[]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +interface BGEM3OuputEmbedding { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +declare abstract class Base_Ai_Cf_Baai_Bge_M3 { + inputs: Ai_Cf_Baai_Bge_M3_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_M3_Output; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input { + /** + * A text description of the image you want to generate. + */ + prompt: string; + /** + * The number of diffusion steps; higher values can improve quality but take longer. + */ + steps?: number; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output { + /** + * The generated image in Base64 format. + */ + image?: string; +} +declare abstract class Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell { + inputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input; + postProcessedOutputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Prompt | Messages; +interface Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + image?: number[] | (string & NonNullable); + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; +} +interface Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role: string; + /** + * The content of the message as a string. + */ + content: string; + }[]; + image?: number[] | string; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * If true, the response will be streamed back incrementally. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output = { + /** + * The generated text response from the model + */ + response?: string; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | ReadableStream; +declare abstract class Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct { + inputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Input { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender must alternate between 'user' and 'assistant'. + */ + role: "user" | "assistant"; + /** + * The content of the message as a string. + */ + content: string; + }[]; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Dictate the output format of the generated response. + */ + response_format?: { + /** + * Set to json_object to process and output generated text as JSON. + */ + type?: string; + }; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Output { + response?: string | { + /** + * Whether the conversation is safe or not. + */ + safe?: boolean; + /** + * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe. + */ + categories?: string[]; + }; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; +} +declare abstract class Base_Ai_Cf_Meta_Llama_Guard_3_8B { + inputs: Ai_Cf_Meta_Llama_Guard_3_8B_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_Guard_3_8B_Output; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Input { + /** + * A query you wish to perform against the provided contexts. + */ + /** + * Number of returned results starting with the best score. + */ + top_k?: number; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Output { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +declare abstract class Base_Ai_Cf_Baai_Bge_Reranker_Base { + inputs: Ai_Cf_Baai_Bge_Reranker_Base_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Reranker_Base_Output; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input = Ai_Cf_Meta_Llama_4_Prompt | Ai_Cf_Meta_Llama_4_Messages; +interface Ai_Cf_Meta_Llama_4_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fulfilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Ai_Cf_Meta_Llama_4_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | string; +declare abstract class Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct { + inputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output; +} +interface AiModels { + "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; + "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-inpainting": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-img2img": BaseAiTextToImage; + "@cf/lykon/dreamshaper-8-lcm": BaseAiTextToImage; + "@cf/bytedance/stable-diffusion-xl-lightning": BaseAiTextToImage; + "@cf/myshell-ai/melotts": BaseAiTextToSpeech; + "@cf/baai/bge-base-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-small-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-large-en-v1.5": BaseAiTextEmbeddings; + "@cf/microsoft/resnet-50": BaseAiImageClassification; + "@cf/facebook/detr-resnet-50": BaseAiObjectDetection; + "@cf/meta/llama-2-7b-chat-int8": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.1": BaseAiTextGeneration; + "@cf/meta/llama-2-7b-chat-fp16": BaseAiTextGeneration; + "@hf/thebloke/llama-2-13b-chat-awq": BaseAiTextGeneration; + "@hf/thebloke/mistral-7b-instruct-v0.1-awq": BaseAiTextGeneration; + "@hf/thebloke/zephyr-7b-beta-awq": BaseAiTextGeneration; + "@hf/thebloke/openhermes-2.5-mistral-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/neural-chat-7b-v3-1-awq": BaseAiTextGeneration; + "@hf/thebloke/llamaguard-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-base-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-instruct-awq": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-math-7b-instruct": BaseAiTextGeneration; + "@cf/defog/sqlcoder-7b-2": BaseAiTextGeneration; + "@cf/openchat/openchat-3.5-0106": BaseAiTextGeneration; + "@cf/tiiuae/falcon-7b-instruct": BaseAiTextGeneration; + "@cf/thebloke/discolm-german-7b-v1-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-0.5b-chat": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-7b-chat-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-14b-chat-awq": BaseAiTextGeneration; + "@cf/tinyllama/tinyllama-1.1b-chat-v1.0": BaseAiTextGeneration; + "@cf/microsoft/phi-2": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-1.8b-chat": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.2-lora": BaseAiTextGeneration; + "@hf/nousresearch/hermes-2-pro-mistral-7b": BaseAiTextGeneration; + "@hf/nexusflow/starling-lm-7b-beta": BaseAiTextGeneration; + "@hf/google/gemma-7b-it": BaseAiTextGeneration; + "@cf/meta-llama/llama-2-7b-chat-hf-lora": BaseAiTextGeneration; + "@cf/google/gemma-2b-it-lora": BaseAiTextGeneration; + "@cf/google/gemma-7b-it-lora": BaseAiTextGeneration; + "@hf/mistral/mistral-7b-instruct-v0.2": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/fblgit/una-cybertron-7b-v2-bf16": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct-awq": BaseAiTextGeneration; + "@hf/meta-llama/meta-llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-fp8": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-awq": BaseAiTextGeneration; + "@cf/meta/llama-3.2-3b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.2-1b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.3-70b-instruct-fp8-fast": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": BaseAiTextGeneration; + "@cf/meta/m2m100-1.2b": BaseAiTranslation; + "@cf/facebook/bart-large-cnn": BaseAiSummarization; + "@cf/llava-hf/llava-1.5-7b-hf": BaseAiImageToText; + "@cf/openai/whisper": Base_Ai_Cf_Openai_Whisper; + "@cf/unum/uform-gen2-qwen-500m": Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M; + "@cf/openai/whisper-tiny-en": Base_Ai_Cf_Openai_Whisper_Tiny_En; + "@cf/openai/whisper-large-v3-turbo": Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo; + "@cf/baai/bge-m3": Base_Ai_Cf_Baai_Bge_M3; + "@cf/black-forest-labs/flux-1-schnell": Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell; + "@cf/meta/llama-3.2-11b-vision-instruct": Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct; + "@cf/meta/llama-guard-3-8b": Base_Ai_Cf_Meta_Llama_Guard_3_8B; + "@cf/baai/bge-reranker-base": Base_Ai_Cf_Baai_Bge_Reranker_Base; + "@cf/meta/llama-4-scout-17b-16e-instruct": Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct; +} +type AiOptions = { + gateway?: GatewayOptions; + returnRawResponse?: boolean; + prefix?: string; + extraHeaders?: object; +}; +type ConversionResponse = { + name: string; + mimeType: string; + format: "markdown"; + tokens: number; + data: string; +}; +type AiModelsSearchParams = { + author?: string; + hide_experimental?: boolean; + page?: number; + per_page?: number; + search?: string; + source?: number; + task?: string; +}; +type AiModelsSearchObject = { + id: string; + source: number; + name: string; + description: string; + task: { + id: string; + name: string; + description: string; + }; + tags: string[]; + properties: { + property_id: string; + value: string; + }[]; +}; +interface InferenceUpstreamError extends Error { +} +interface AiInternalError extends Error { +} +type AiModelListType = Record; +declare abstract class Ai { + aiGatewayLogId: string | null; + gateway(gatewayId: string): AiGateway; + autorag(autoragId: string): AutoRAG; + run(model: Name, inputs: AiModelList[Name]["inputs"], options?: Options): Promise; + models(params?: AiModelsSearchParams): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }[], options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }, options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; +} +type GatewayOptions = { + id: string; + cacheKey?: string; + cacheTtl?: number; + skipCache?: boolean; + metadata?: Record; + collectLog?: boolean; +}; +type AiGatewayPatchLog = { + score?: number | null; + feedback?: -1 | 1 | null; + metadata?: Record | null; +}; +type AiGatewayLog = { + id: string; + provider: string; + model: string; + model_type?: string; + path: string; + duration: number; + request_type?: string; + request_content_type?: string; + status_code: number; + response_content_type?: string; + success: boolean; + cached: boolean; + tokens_in?: number; + tokens_out?: number; + metadata?: Record; + step?: number; + cost?: number; + custom_cost?: boolean; + request_size: number; + request_head?: string; + request_head_complete: boolean; + response_size: number; + response_head?: string; + response_head_complete: boolean; + created_at: Date; +}; +type AIGatewayProviders = "workers-ai" | "anthropic" | "aws-bedrock" | "azure-openai" | "google-vertex-ai" | "huggingface" | "openai" | "perplexity-ai" | "replicate" | "groq" | "cohere" | "google-ai-studio" | "mistral" | "grok" | "openrouter" | "deepseek" | "cerebras" | "cartesia" | "elevenlabs" | "adobe-firefly"; +type AIGatewayHeaders = { + "cf-aig-metadata": Record | string; + "cf-aig-custom-cost": { + per_token_in?: number; + per_token_out?: number; + } | { + total_cost?: number; + } | string; + "cf-aig-cache-ttl": number | string; + "cf-aig-skip-cache": boolean | string; + "cf-aig-cache-key": string; + "cf-aig-collect-log": boolean | string; + Authorization: string; + "Content-Type": string; + [key: string]: string | number | boolean | object; +}; +type AIGatewayUniversalRequest = { + provider: AIGatewayProviders | string; // eslint-disable-line + endpoint: string; + headers: Partial; + query: unknown; +}; +interface AiGatewayInternalError extends Error { +} +interface AiGatewayLogNotFound extends Error { +} +declare abstract class AiGateway { + patchLog(logId: string, data: AiGatewayPatchLog): Promise; + getLog(logId: string): Promise; + run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[]): Promise; + getUrl(provider?: AIGatewayProviders | string): Promise; // eslint-disable-line +} +interface AutoRAGInternalError extends Error { +} +interface AutoRAGNotFoundError extends Error { +} +interface AutoRAGUnauthorizedError extends Error { +} +type AutoRagSearchRequest = { + query: string; + max_num_results?: number; + ranking_options?: { + ranker?: string; + score_threshold?: number; + }; + rewrite_query?: boolean; +}; +type AutoRagSearchResponse = { + object: "vector_store.search_results.page"; + search_query: string; + data: { + file_id: string; + filename: string; + score: number; + attributes: Record; + content: { + type: "text"; + text: string; + }[]; + }[]; + has_more: boolean; + next_page: string | null; +}; +type AutoRagAiSearchResponse = AutoRagSearchResponse & { + response: string; +}; +declare abstract class AutoRAG { + search(params: AutoRagSearchRequest): Promise; + aiSearch(params: AutoRagSearchRequest): Promise; +} +interface BasicImageTransformations { + /** + * Maximum width in image pixels. The value must be an integer. + */ + width?: number; + /** + * Maximum height in image pixels. The value must be an integer. + */ + height?: number; + /** + * Resizing mode as a string. It affects interpretation of width and height + * options: + * - scale-down: Similar to contain, but the image is never enlarged. If + * the image is larger than given width or height, it will be resized. + * Otherwise its original size will be kept. + * - contain: Resizes to maximum size that fits within the given width and + * height. If only a single dimension is given (e.g. only width), the + * image will be shrunk or enlarged to exactly match that dimension. + * Aspect ratio is always preserved. + * - cover: Resizes (shrinks or enlarges) to fill the entire area of width + * and height. If the image has an aspect ratio different from the ratio + * of width and height, it will be cropped to fit. + * - crop: The image will be shrunk and cropped to fit within the area + * specified by width and height. The image will not be enlarged. For images + * smaller than the given dimensions it's the same as scale-down. For + * images larger than the given dimensions, it's the same as cover. + * See also trim. + * - pad: Resizes to the maximum size that fits within the given width and + * height, and then fills the remaining area with a background color + * (white by default). Use of this mode is not recommended, as the same + * effect can be more efficiently achieved with the contain mode and the + * CSS object-fit: contain property. + * - squeeze: Stretches and deforms to the width and height given, even if it + * breaks aspect ratio + */ + fit?: "scale-down" | "contain" | "cover" | "crop" | "pad" | "squeeze"; + /** + * When cropping with fit: "cover", this defines the side or point that should + * be left uncropped. The value is either a string + * "left", "right", "top", "bottom", "auto", or "center" (the default), + * or an object {x, y} containing focal point coordinates in the original + * image expressed as fractions ranging from 0.0 (top or left) to 1.0 + * (bottom or right), 0.5 being the center. {fit: "cover", gravity: "top"} will + * crop bottom or left and right sides as necessary, but won’t crop anything + * from the top. {fit: "cover", gravity: {x:0.5, y:0.2}} will crop each side to + * preserve as much as possible around a point at 20% of the height of the + * source image. + */ + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | BasicImageTransformationsGravityCoordinates; + /** + * Background color to add underneath the image. Applies only to images with + * transparency (such as PNG). Accepts any CSS color (#RRGGBB, rgba(…), + * hsl(…), etc.) + */ + background?: string; + /** + * Number of degrees (90, 180, 270) to rotate the image by. width and height + * options refer to axes after rotation. + */ + rotate?: 0 | 90 | 180 | 270 | 360; +} +interface BasicImageTransformationsGravityCoordinates { + x?: number; + y?: number; + mode?: 'remainder' | 'box-center'; +} +/** + * In addition to the properties you can set in the RequestInit dict + * that you pass as an argument to the Request constructor, you can + * set certain properties of a `cf` object to control how Cloudflare + * features are applied to that new Request. + * + * Note: Currently, these properties cannot be tested in the + * playground. + */ +interface RequestInitCfProperties extends Record { + cacheEverything?: boolean; + /** + * A request's cache key is what determines if two requests are + * "the same" for caching purposes. If a request has the same cache key + * as some previous request, then we can serve the same cached response for + * both. (e.g. 'some-key') + * + * Only available for Enterprise customers. + */ + cacheKey?: string; + /** + * This allows you to append additional Cache-Tag response headers + * to the origin response without modifications to the origin server. + * This will allow for greater control over the Purge by Cache Tag feature + * utilizing changes only in the Workers process. + * + * Only available for Enterprise customers. + */ + cacheTags?: string[]; + /** + * Force response to be cached for a given number of seconds. (e.g. 300) + */ + cacheTtl?: number; + /** + * Force response to be cached for a given number of seconds based on the Origin status code. + * (e.g. { '200-299': 86400, '404': 1, '500-599': 0 }) + */ + cacheTtlByStatus?: Record; + scrapeShield?: boolean; + apps?: boolean; + image?: RequestInitCfPropertiesImage; + minify?: RequestInitCfPropertiesImageMinify; + mirage?: boolean; + polish?: "lossy" | "lossless" | "off"; + r2?: RequestInitCfPropertiesR2; + /** + * Redirects the request to an alternate origin server. You can use this, + * for example, to implement load balancing across several origins. + * (e.g.us-east.example.com) + * + * Note - For security reasons, the hostname set in resolveOverride must + * be proxied on the same Cloudflare zone of the incoming request. + * Otherwise, the setting is ignored. CNAME hosts are allowed, so to + * resolve to a host under a different domain or a DNS only domain first + * declare a CNAME record within your own zone’s DNS mapping to the + * external hostname, set proxy on Cloudflare, then set resolveOverride + * to point to that CNAME record. + */ + resolveOverride?: string; +} +interface RequestInitCfPropertiesImageDraw extends BasicImageTransformations { + /** + * Absolute URL of the image file to use for the drawing. It can be any of + * the supported file formats. For drawing of watermarks or non-rectangular + * overlays we recommend using PNG or WebP images. + */ + url: string; + /** + * Floating-point number between 0 (transparent) and 1 (opaque). + * For example, opacity: 0.5 makes overlay semitransparent. + */ + opacity?: number; + /** + * - If set to true, the overlay image will be tiled to cover the entire + * area. This is useful for stock-photo-like watermarks. + * - If set to "x", the overlay image will be tiled horizontally only + * (form a line). + * - If set to "y", the overlay image will be tiled vertically only + * (form a line). + */ + repeat?: true | "x" | "y"; + /** + * Position of the overlay image relative to a given edge. Each property is + * an offset in pixels. 0 aligns exactly to the edge. For example, left: 10 + * positions left side of the overlay 10 pixels from the left edge of the + * image it's drawn over. bottom: 0 aligns bottom of the overlay with bottom + * of the background image. + * + * Setting both left & right, or both top & bottom is an error. + * + * If no position is specified, the image will be centered. + */ + top?: number; + left?: number; + bottom?: number; + right?: number; +} +interface RequestInitCfPropertiesImage extends BasicImageTransformations { + /** + * Device Pixel Ratio. Default 1. Multiplier for width/height that makes it + * easier to specify higher-DPI sizes in . + */ + dpr?: number; + /** + * Allows you to trim your image. Takes dpr into account and is performed before + * resizing or rotation. + * + * It can be used as: + * - left, top, right, bottom - it will specify the number of pixels to cut + * off each side + * - width, height - the width/height you'd like to end up with - can be used + * in combination with the properties above + * - border - this will automatically trim the surroundings of an image based on + * it's color. It consists of three properties: + * - color: rgb or hex representation of the color you wish to trim (todo: verify the rgba bit) + * - tolerance: difference from color to treat as color + * - keep: the number of pixels of border to keep + */ + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; + /** + * Quality setting from 1-100 (useful values are in 60-90 range). Lower values + * make images look worse, but load faster. The default is 85. It applies only + * to JPEG and WebP images. It doesn’t have any effect on PNG. + */ + quality?: number | "low" | "medium-low" | "medium-high" | "high"; + /** + * Output format to generate. It can be: + * - avif: generate images in AVIF format. + * - webp: generate images in Google WebP format. Set quality to 100 to get + * the WebP-lossless format. + * - json: instead of generating an image, outputs information about the + * image, in JSON format. The JSON object will contain image size + * (before and after resizing), source image’s MIME type, file size, etc. + * - jpeg: generate images in JPEG format. + * - png: generate images in PNG format. + */ + format?: "avif" | "webp" | "json" | "jpeg" | "png" | "baseline-jpeg" | "png-force" | "svg"; + /** + * Whether to preserve animation frames from input files. Default is true. + * Setting it to false reduces animations to still images. This setting is + * recommended when enlarging images or processing arbitrary user content, + * because large GIF animations can weigh tens or even hundreds of megabytes. + * It is also useful to set anim:false when using format:"json" to get the + * response quicker without the number of frames. + */ + anim?: boolean; + /** + * What EXIF data should be preserved in the output image. Note that EXIF + * rotation and embedded color profiles are always applied ("baked in" into + * the image), and aren't affected by this option. Note that if the Polish + * feature is enabled, all metadata may have been removed already and this + * option may have no effect. + * - keep: Preserve most of EXIF metadata, including GPS location if there's + * any. + * - copyright: Only keep the copyright tag, and discard everything else. + * This is the default behavior for JPEG files. + * - none: Discard all invisible EXIF metadata. Currently WebP and PNG + * output formats always discard metadata. + */ + metadata?: "keep" | "copyright" | "none"; + /** + * Strength of sharpening filter to apply to the image. Floating-point + * number between 0 (no sharpening, default) and 10 (maximum). 1.0 is a + * recommended value for downscaled images. + */ + sharpen?: number; + /** + * Radius of a blur filter (approximate gaussian). Maximum supported radius + * is 250. + */ + blur?: number; + /** + * Overlays are drawn in the order they appear in the array (last array + * entry is the topmost layer). + */ + draw?: RequestInitCfPropertiesImageDraw[]; + /** + * Fetching image from authenticated origin. Setting this property will + * pass authentication headers (Authorization, Cookie, etc.) through to + * the origin. + */ + "origin-auth"?: "share-publicly"; + /** + * Adds a border around the image. The border is added after resizing. Border + * width takes dpr into account, and can be specified either using a single + * width property, or individually for each side. + */ + border?: { + color: string; + width: number; + } | { + color: string; + top: number; + right: number; + bottom: number; + left: number; + }; + /** + * Increase brightness by a factor. A value of 1.0 equals no change, a value + * of 0.5 equals half brightness, and a value of 2.0 equals twice as bright. + * 0 is ignored. + */ + brightness?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + contrast?: number; + /** + * Increase exposure by a factor. A value of 1.0 equals no change, a value of + * 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored. + */ + gamma?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + saturation?: number; + /** + * Flips the images horizontally, vertically, or both. Flipping is applied before + * rotation, so if you apply flip=h,rotate=90 then the image will be flipped + * horizontally, then rotated by 90 degrees. + */ + flip?: 'h' | 'v' | 'hv'; + /** + * Slightly reduces latency on a cache miss by selecting a + * quickest-to-compress file format, at a cost of increased file size and + * lower image quality. It will usually override the format option and choose + * JPEG over WebP or AVIF. We do not recommend using this option, except in + * unusual circumstances like resizing uncacheable dynamically-generated + * images. + */ + compression?: "fast"; +} +interface RequestInitCfPropertiesImageMinify { + javascript?: boolean; + css?: boolean; + html?: boolean; +} +interface RequestInitCfPropertiesR2 { + /** + * Colo id of bucket that an object is stored in + */ + bucketColoId?: number; +} +/** + * Request metadata provided by Cloudflare's edge. + */ +type IncomingRequestCfProperties = IncomingRequestCfPropertiesBase & IncomingRequestCfPropertiesBotManagementEnterprise & IncomingRequestCfPropertiesCloudflareForSaaSEnterprise & IncomingRequestCfPropertiesGeographicInformation & IncomingRequestCfPropertiesCloudflareAccessOrApiShield; +interface IncomingRequestCfPropertiesBase extends Record { + /** + * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request. + * + * @example 395747 + */ + asn: number; + /** + * The organization which owns the ASN of the incoming request. + * + * @example "Google Cloud" + */ + asOrganization: string; + /** + * The original value of the `Accept-Encoding` header if Cloudflare modified it. + * + * @example "gzip, deflate, br" + */ + clientAcceptEncoding?: string; + /** + * The number of milliseconds it took for the request to reach your worker. + * + * @example 22 + */ + clientTcpRtt?: number; + /** + * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code) + * airport code of the data center that the request hit. + * + * @example "DFW" + */ + colo: string; + /** + * Represents the upstream's response to a + * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) + * from cloudflare. + * + * For workers with no upstream, this will always be `1`. + * + * @example 3 + */ + edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus; + /** + * The HTTP Protocol the request used. + * + * @example "HTTP/2" + */ + httpProtocol: string; + /** + * The browser-requested prioritization information in the request object. + * + * If no information was set, defaults to the empty string `""` + * + * @example "weight=192;exclusive=0;group=3;group-weight=127" + * @default "" + */ + requestPriority: string; + /** + * The TLS version of the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "TLSv1.3" + */ + tlsVersion: string; + /** + * The cipher for the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "AEAD-AES128-GCM-SHA256" + */ + tlsCipher: string; + /** + * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake. + * + * If the incoming request was served over plaintext (without TLS) this field is undefined. + */ + tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata; +} +interface IncomingRequestCfPropertiesBotManagementBase { + /** + * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot, + * represented as an integer percentage between `1` (almost certainly a bot) and `99` (almost certainly human). + * + * @example 54 + */ + score: number; + /** + * A boolean value that is true if the request comes from a good bot, like Google or Bing. + * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots). + */ + verifiedBot: boolean; + /** + * A boolean value that is true if the request originates from a + * Cloudflare-verified proxy service. + */ + corporateProxy: boolean; + /** + * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources. + */ + staticResource: boolean; + /** + * List of IDs that correlate to the Bot Management heuristic detections made on a request (you can have multiple heuristic detections on the same request). + */ + detectionIds: number[]; +} +interface IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase; + /** + * Duplicate of `botManagement.score`. + * + * @deprecated + */ + clientTrustScore: number; +} +interface IncomingRequestCfPropertiesBotManagementEnterprise extends IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase & { + /** + * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients + * across different destination IPs, Ports, and X509 certificates. + */ + ja3Hash: string; + }; +} +interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise { + /** + * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/). + * + * This field is only present if you have Cloudflare for SaaS enabled on your account + * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)). + */ + hostMetadata: HostMetadata; +} +interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield { + /** + * Information about the client certificate presented to Cloudflare. + * + * This is populated when the incoming request is served over TLS using + * either Cloudflare Access or API Shield (mTLS) + * and the presented SSL certificate has a valid + * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number) + * (i.e., not `null` or `""`). + * + * Otherwise, a set of placeholder values are used. + * + * The property `certPresented` will be set to `"1"` when + * the object is populated (i.e. the above conditions were met). + */ + tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth | IncomingRequestCfPropertiesTLSClientAuthPlaceholder; +} +/** + * Metadata about the request's TLS handshake + */ +interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata { + /** + * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + clientHandshake: string; + /** + * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + serverHandshake: string; + /** + * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + clientFinished: string; + /** + * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + serverFinished: string; +} +/** + * Geographic data about the request's origin. + */ +interface IncomingRequestCfPropertiesGeographicInformation { + /** + * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from. + * + * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR. + * + * If Cloudflare is unable to determine where the request originated this property is omitted. + * + * The country code `"T1"` is used for requests originating on TOR. + * + * @example "GB" + */ + country?: Iso3166Alpha2Code | "T1"; + /** + * If present, this property indicates that the request originated in the EU + * + * @example "1" + */ + isEUCountry?: "1"; + /** + * A two-letter code indicating the continent the request originated from. + * + * @example "AN" + */ + continent?: ContinentCode; + /** + * The city the request originated from + * + * @example "Austin" + */ + city?: string; + /** + * Postal code of the incoming request + * + * @example "78701" + */ + postalCode?: string; + /** + * Latitude of the incoming request + * + * @example "30.27130" + */ + latitude?: string; + /** + * Longitude of the incoming request + * + * @example "-97.74260" + */ + longitude?: string; + /** + * Timezone of the incoming request + * + * @example "America/Chicago" + */ + timezone?: string; + /** + * If known, the ISO 3166-2 name for the first level region associated with + * the IP address of the incoming request + * + * @example "Texas" + */ + region?: string; + /** + * If known, the ISO 3166-2 code for the first-level region associated with + * the IP address of the incoming request + * + * @example "TX" + */ + regionCode?: string; + /** + * Metro code (DMA) of the incoming request + * + * @example "635" + */ + metroCode?: string; +} +/** Data about the incoming request's TLS certificate */ +interface IncomingRequestCfPropertiesTLSClientAuth { + /** Always `"1"`, indicating that the certificate was presented */ + certPresented: "1"; + /** + * Result of certificate verification. + * + * @example "FAILED:self signed certificate" + */ + certVerified: Exclude; + /** The presented certificate's revokation status. + * + * - A value of `"1"` indicates the certificate has been revoked + * - A value of `"0"` indicates the certificate has not been revoked + */ + certRevoked: "1" | "0"; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDN: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDN: string; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDNRFC2253: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDNRFC2253: string; + /** The certificate issuer's distinguished name (legacy policies) */ + certIssuerDNLegacy: string; + /** The certificate subject's distinguished name (legacy policies) */ + certSubjectDNLegacy: string; + /** + * The certificate's serial number + * + * @example "00936EACBE07F201DF" + */ + certSerial: string; + /** + * The certificate issuer's serial number + * + * @example "2489002934BDFEA34" + */ + certIssuerSerial: string; + /** + * The certificate's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certSKI: string; + /** + * The certificate issuer's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certIssuerSKI: string; + /** + * The certificate's SHA-1 fingerprint + * + * @example "6b9109f323999e52259cda7373ff0b4d26bd232e" + */ + certFingerprintSHA1: string; + /** + * The certificate's SHA-256 fingerprint + * + * @example "acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea" + */ + certFingerprintSHA256: string; + /** + * The effective starting date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotBefore: string; + /** + * The effective expiration date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotAfter: string; +} +/** Placeholder values for TLS Client Authorization */ +interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { + certPresented: "0"; + certVerified: "NONE"; + certRevoked: "0"; + certIssuerDN: ""; + certSubjectDN: ""; + certIssuerDNRFC2253: ""; + certSubjectDNRFC2253: ""; + certIssuerDNLegacy: ""; + certSubjectDNLegacy: ""; + certSerial: ""; + certIssuerSerial: ""; + certSKI: ""; + certIssuerSKI: ""; + certFingerprintSHA1: ""; + certFingerprintSHA256: ""; + certNotBefore: ""; + certNotAfter: ""; +} +/** Possible outcomes of TLS verification */ +declare type CertVerificationStatus = +/** Authentication succeeded */ +"SUCCESS" +/** No certificate was presented */ + | "NONE" +/** Failed because the certificate was self-signed */ + | "FAILED:self signed certificate" +/** Failed because the certificate failed a trust chain check */ + | "FAILED:unable to verify the first certificate" +/** Failed because the certificate not yet valid */ + | "FAILED:certificate is not yet valid" +/** Failed because the certificate is expired */ + | "FAILED:certificate has expired" +/** Failed for another unspecified reason */ + | "FAILED"; +/** + * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare. + */ +declare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = 0 /** Unknown */ | 1 /** no keepalives (not found) */ | 2 /** no connection re-use, opening keepalive connection failed */ | 3 /** no connection re-use, keepalive accepted and saved */ | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ | 5; /** connection re-use, accepted by the origin server */ +/** ISO 3166-1 Alpha-2 codes */ +declare type Iso3166Alpha2Code = "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AO" | "AQ" | "AR" | "AS" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "FI" | "FJ" | "FK" | "FM" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GS" | "GT" | "GU" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MH" | "MK" | "ML" | "MM" | "MN" | "MO" | "MP" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PR" | "PS" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZM" | "ZW"; +/** The 2-letter continent codes Cloudflare uses */ +declare type ContinentCode = "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; +type CfProperties = IncomingRequestCfProperties | RequestInitCfProperties; +interface D1Meta { + duration: number; + size_after: number; + rows_read: number; + rows_written: number; + last_row_id: number; + changed_db: boolean; + changes: number; + /** + * The region of the database instance that executed the query. + */ + served_by_region?: string; + /** + * True if-and-only-if the database instance that executed the query was the primary. + */ + served_by_primary?: boolean; + timings?: { + /** + * The duration of the SQL query execution by the database instance. It doesn't include any network time. + */ + sql_duration_ms: number; + }; +} +interface D1Response { + success: true; + meta: D1Meta & Record; + error?: never; +} +type D1Result = D1Response & { + results: T[]; +}; +interface D1ExecResult { + count: number; + duration: number; +} +type D1SessionConstraint = +// Indicates that the first query should go to the primary, and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). +"first-primary" +// Indicates that the first query can go anywhere (primary or replica), and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). + | "first-unconstrained"; +type D1SessionBookmark = string; +declare abstract class D1Database { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + exec(query: string): Promise; + /** + * Creates a new D1 Session anchored at the given constraint or the bookmark. + * All queries executed using the created session will have sequential consistency, + * meaning that all writes done through the session will be visible in subsequent reads. + * + * @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session. + */ + withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession; + /** + * @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases. + */ + dump(): Promise; +} +declare abstract class D1DatabaseSession { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + /** + * @returns The latest session bookmark across all executed queries on the session. + * If no query has been executed yet, `null` is returned. + */ + getBookmark(): D1SessionBookmark | null; +} +declare abstract class D1PreparedStatement { + bind(...values: unknown[]): D1PreparedStatement; + first(colName: string): Promise; + first>(): Promise; + run>(): Promise>; + all>(): Promise>; + raw(options: { + columnNames: true; + }): Promise<[ + string[], + ...T[] + ]>; + raw(options?: { + columnNames?: false; + }): Promise; +} +// `Disposable` was added to TypeScript's standard lib types in version 5.2. +// To support older TypeScript versions, define an empty `Disposable` interface. +// Users won't be able to use `using`/`Symbol.dispose` without upgrading to 5.2, +// but this will ensure type checking on older versions still passes. +// TypeScript's interface merging will ensure our empty interface is effectively +// ignored when `Disposable` is included in the standard lib. +interface Disposable { +} +/** + * An email message that can be sent from a Worker. + */ +interface EmailMessage { + /** + * Envelope From attribute of the email message. + */ + readonly from: string; + /** + * Envelope To attribute of the email message. + */ + readonly to: string; +} +/** + * An email message that is sent to a consumer Worker and can be rejected/forwarded. + */ +interface ForwardableEmailMessage extends EmailMessage { + /** + * Stream of the email message content. + */ + readonly raw: ReadableStream; + /** + * An [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + */ + readonly headers: Headers; + /** + * Size of the email message content. + */ + readonly rawSize: number; + /** + * Reject this email message by returning a permanent SMTP error back to the connecting client including the given reason. + * @param reason The reject reason. + * @returns void + */ + setReject(reason: string): void; + /** + * Forward this email message to a verified destination address of the account. + * @param rcptTo Verified destination address. + * @param headers A [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + * @returns A promise that resolves when the email message is forwarded. + */ + forward(rcptTo: string, headers?: Headers): Promise; + /** + * Reply to the sender of this email message with a new EmailMessage object. + * @param message The reply message. + * @returns A promise that resolves when the email message is replied. + */ + reply(message: EmailMessage): Promise; +} +/** + * A binding that allows a Worker to send email messages. + */ +interface SendEmail { + send(message: EmailMessage): Promise; +} +declare abstract class EmailEvent extends ExtendableEvent { + readonly message: ForwardableEmailMessage; +} +declare type EmailExportedHandler = (message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) => void | Promise; +declare module "cloudflare:email" { + let _EmailMessage: { + prototype: EmailMessage; + new (from: string, to: string, raw: ReadableStream | string): EmailMessage; + }; + export { _EmailMessage as EmailMessage }; +} +interface Hyperdrive { + /** + * Connect directly to Hyperdrive as if it's your database, returning a TCP socket. + * + * Calling this method returns an idential socket to if you call + * `connect("host:port")` using the `host` and `port` fields from this object. + * Pick whichever approach works better with your preferred DB client library. + * + * Note that this socket is not yet authenticated -- it's expected that your + * code (or preferably, the client library of your choice) will authenticate + * using the information in this class's readonly fields. + */ + connect(): Socket; + /** + * A valid DB connection string that can be passed straight into the typical + * client library/driver/ORM. This will typically be the easiest way to use + * Hyperdrive. + */ + readonly connectionString: string; + /* + * A randomly generated hostname that is only valid within the context of the + * currently running Worker which, when passed into `connect()` function from + * the "cloudflare:sockets" module, will connect to the Hyperdrive instance + * for your database. + */ + readonly host: string; + /* + * The port that must be paired the the host field when connecting. + */ + readonly port: number; + /* + * The username to use when authenticating to your database via Hyperdrive. + * Unlike the host and password, this will be the same every time + */ + readonly user: string; + /* + * The randomly generated password to use when authenticating to your + * database via Hyperdrive. Like the host field, this password is only valid + * within the context of the currently running Worker instance from which + * it's read. + */ + readonly password: string; + /* + * The name of the database to connect to. + */ + readonly database: string; +} +// Copyright (c) 2024 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +type ImageInfoResponse = { + format: 'image/svg+xml'; +} | { + format: string; + fileSize: number; + width: number; + height: number; +}; +type ImageTransform = { + width?: number; + height?: number; + background?: string; + blur?: number; + border?: { + color?: string; + width?: number; + } | { + top?: number; + bottom?: number; + left?: number; + right?: number; + }; + brightness?: number; + contrast?: number; + fit?: 'scale-down' | 'contain' | 'pad' | 'squeeze' | 'cover' | 'crop'; + flip?: 'h' | 'v' | 'hv'; + gamma?: number; + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | { + x?: number; + y?: number; + mode: 'remainder' | 'box-center'; + }; + rotate?: 0 | 90 | 180 | 270; + saturation?: number; + sharpen?: number; + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; +}; +type ImageDrawOptions = { + opacity?: number; + repeat?: boolean | string; + top?: number; + left?: number; + bottom?: number; + right?: number; +}; +type ImageOutputOptions = { + format: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/avif' | 'rgb' | 'rgba'; + quality?: number; + background?: string; +}; +interface ImagesBinding { + /** + * Get image metadata (type, width and height) + * @throws {@link ImagesError} with code 9412 if input is not an image + * @param stream The image bytes + */ + info(stream: ReadableStream): Promise; + /** + * Begin applying a series of transformations to an image + * @param stream The image bytes + * @returns A transform handle + */ + input(stream: ReadableStream): ImageTransformer; +} +interface ImageTransformer { + /** + * Apply transform next, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param transform + */ + transform(transform: ImageTransform): ImageTransformer; + /** + * Draw an image on this transformer, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param image The image (or transformer that will give the image) to draw + * @param options The options configuring how to draw the image + */ + draw(image: ReadableStream | ImageTransformer, options?: ImageDrawOptions): ImageTransformer; + /** + * Retrieve the image that results from applying the transforms to the + * provided input + * @param options Options that apply to the output e.g. output format + */ + output(options: ImageOutputOptions): Promise; +} +interface ImageTransformationResult { + /** + * The image as a response, ready to store in cache or return to users + */ + response(): Response; + /** + * The content type of the returned image + */ + contentType(): string; + /** + * The bytes of the response + */ + image(): ReadableStream; +} +interface ImagesError extends Error { + readonly code: number; + readonly message: string; + readonly stack?: string; +} +type Params

= Record; +type EventContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; +}; +type PagesFunction = Record> = (context: EventContext) => Response | Promise; +type EventPluginContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; + pluginArgs: PluginArgs; +}; +type PagesPluginFunction = Record, PluginArgs = unknown> = (context: EventPluginContext) => Response | Promise; +declare module "assets:*" { + export const onRequest: PagesFunction; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +declare module "cloudflare:pipelines" { + export abstract class PipelineTransformationEntrypoint { + protected env: Env; + protected ctx: ExecutionContext; + constructor(ctx: ExecutionContext, env: Env); + /** + * run recieves an array of PipelineRecord which can be + * transformed and returned to the pipeline + * @param records Incoming records from the pipeline to be transformed + * @param metadata Information about the specific pipeline calling the transformation entrypoint + * @returns A promise containing the transformed PipelineRecord array + */ + public run(records: I[], metadata: PipelineBatchMetadata): Promise; + } + export type PipelineRecord = Record; + export type PipelineBatchMetadata = { + pipelineId: string; + pipelineName: string; + }; + export interface Pipeline { + /** + * The Pipeline interface represents the type of a binding to a Pipeline + * + * @param records The records to send to the pipeline + */ + send(records: T[]): Promise; + } +} +// PubSubMessage represents an incoming PubSub message. +// The message includes metadata about the broker, the client, and the payload +// itself. +// https://developers.cloudflare.com/pub-sub/ +interface PubSubMessage { + // Message ID + readonly mid: number; + // MQTT broker FQDN in the form mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:PORT + readonly broker: string; + // The MQTT topic the message was sent on. + readonly topic: string; + // The client ID of the client that published this message. + readonly clientId: string; + // The unique identifier (JWT ID) used by the client to authenticate, if token + // auth was used. + readonly jti?: string; + // A Unix timestamp (seconds from Jan 1, 1970), set when the Pub/Sub Broker + // received the message from the client. + readonly receivedAt: number; + // An (optional) string with the MIME type of the payload, if set by the + // client. + readonly contentType: string; + // Set to 1 when the payload is a UTF-8 string + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901063 + readonly payloadFormatIndicator: number; + // Pub/Sub (MQTT) payloads can be UTF-8 strings, or byte arrays. + // You can use payloadFormatIndicator to inspect this before decoding. + payload: string | Uint8Array; +} +// JsonWebKey extended by kid parameter +interface JsonWebKeyWithKid extends JsonWebKey { + // Key Identifier of the JWK + readonly kid: string; +} +interface RateLimitOptions { + key: string; +} +interface RateLimitOutcome { + success: boolean; +} +interface RateLimit { + /** + * Rate limit a request based on the provided options. + * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/ + * @returns A promise that resolves with the outcome of the rate limit. + */ + limit(options: RateLimitOptions): Promise; +} +// Namespace for RPC utility types. Unfortunately, we can't use a `module` here as these types need +// to referenced by `Fetcher`. This is included in the "importable" version of the types which +// strips all `module` blocks. +declare namespace Rpc { + // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s. + // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`. + // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to + // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape) + export const __RPC_STUB_BRAND: '__RPC_STUB_BRAND'; + export const __RPC_TARGET_BRAND: '__RPC_TARGET_BRAND'; + export const __WORKER_ENTRYPOINT_BRAND: '__WORKER_ENTRYPOINT_BRAND'; + export const __DURABLE_OBJECT_BRAND: '__DURABLE_OBJECT_BRAND'; + export const __WORKFLOW_ENTRYPOINT_BRAND: '__WORKFLOW_ENTRYPOINT_BRAND'; + export interface RpcTargetBranded { + [__RPC_TARGET_BRAND]: never; + } + export interface WorkerEntrypointBranded { + [__WORKER_ENTRYPOINT_BRAND]: never; + } + export interface DurableObjectBranded { + [__DURABLE_OBJECT_BRAND]: never; + } + export interface WorkflowEntrypointBranded { + [__WORKFLOW_ENTRYPOINT_BRAND]: never; + } + export type EntrypointBranded = WorkerEntrypointBranded | DurableObjectBranded | WorkflowEntrypointBranded; + // Types that can be used through `Stub`s + export type Stubable = RpcTargetBranded | ((...args: any[]) => any); + // Types that can be passed over RPC + // The reason for using a generic type here is to build a serializable subset of structured + // cloneable composite types. This allows types defined with the "interface" keyword to pass the + // serializable check as well. Otherwise, only types defined with the "type" keyword would pass. + type Serializable = + // Structured cloneables + BaseType + // Structured cloneable composites + | Map ? Serializable : never, T extends Map ? Serializable : never> | Set ? Serializable : never> | ReadonlyArray ? Serializable : never> | { + [K in keyof T]: K extends number | string ? Serializable : never; + } + // Special types + | Stub + // Serialized as stubs, see `Stubify` + | Stubable; + // Base type for all RPC stubs, including common memory management methods. + // `T` is used as a marker type for unwrapping `Stub`s later. + interface StubBase extends Disposable { + [__RPC_STUB_BRAND]: T; + dup(): this; + } + export type Stub = Provider & StubBase; + // This represents all the types that can be sent as-is over an RPC boundary + type BaseType = void | undefined | null | boolean | number | bigint | string | TypedArray | ArrayBuffer | DataView | Date | Error | RegExp | ReadableStream | WritableStream | Request | Response | Headers; + // Recursively rewrite all `Stubable` types with `Stub`s + // prettier-ignore + type Stubify = T extends Stubable ? Stub : T extends Map ? Map, Stubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: any; + } ? { + [K in keyof T]: Stubify; + } : T; + // Recursively rewrite all `Stub`s with the corresponding `T`s. + // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies: + // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`. + // prettier-ignore + type Unstubify = T extends StubBase ? V : T extends Map ? Map, Unstubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: unknown; + } ? { + [K in keyof T]: Unstubify; + } : T; + type UnstubifyAll = { + [I in keyof A]: Unstubify; + }; + // Utility type for adding `Provider`/`Disposable`s to `object` types only. + // Note `unknown & T` is equivalent to `T`. + type MaybeProvider = T extends object ? Provider : unknown; + type MaybeDisposable = T extends object ? Disposable : unknown; + // Type for method return or property on an RPC interface. + // - Stubable types are replaced by stubs. + // - Serializable types are passed by value, with stubable types replaced by stubs + // and a top-level `Disposer`. + // Everything else can't be passed over PRC. + // Technically, we use custom thenables here, but they quack like `Promise`s. + // Intersecting with `(Maybe)Provider` allows pipelining. + // prettier-ignore + type Result = R extends Stubable ? Promise> & Provider : R extends Serializable ? Promise & MaybeDisposable> & MaybeProvider : never; + // Type for method or property on an RPC interface. + // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s. + // Unwrapping `Stub`s allows calling with `Stubable` arguments. + // For properties, rewrite types to be `Result`s. + // In each case, unwrap `Promise`s. + type MethodOrProperty = V extends (...args: infer P) => infer R ? (...args: UnstubifyAll

) => Result> : Result>; + // Type for the callable part of an `Provider` if `T` is callable. + // This is intersected with methods/properties. + type MaybeCallableProvider = T extends (...args: any[]) => any ? MethodOrProperty : unknown; + // Base type for all other types providing RPC-like interfaces. + // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types. + // `Reserved` names (e.g. stub method names like `dup()`) and symbols can't be accessed over RPC. + export type Provider = MaybeCallableProvider & { + [K in Exclude>]: MethodOrProperty; + }; +} +declare namespace Cloudflare { + interface Env { + } +} +declare module 'cloudflare:workers' { + export type RpcStub = Rpc.Stub; + export const RpcStub: { + new (value: T): Rpc.Stub; + }; + export abstract class RpcTarget implements Rpc.RpcTargetBranded { + [Rpc.__RPC_TARGET_BRAND]: never; + } + // `protected` fields don't appear in `keyof`s, so can't be accessed over RPC + export abstract class WorkerEntrypoint implements Rpc.WorkerEntrypointBranded { + [Rpc.__WORKER_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + fetch?(request: Request): Response | Promise; + tail?(events: TraceItem[]): void | Promise; + trace?(traces: TraceItem[]): void | Promise; + scheduled?(controller: ScheduledController): void | Promise; + queue?(batch: MessageBatch): void | Promise; + test?(controller: TestController): void | Promise; + } + export abstract class DurableObject implements Rpc.DurableObjectBranded { + [Rpc.__DURABLE_OBJECT_BRAND]: never; + protected ctx: DurableObjectState; + protected env: Env; + constructor(ctx: DurableObjectState, env: Env); + fetch?(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; + } + export type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; + export type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number; + export type WorkflowDelayDuration = WorkflowSleepDuration; + export type WorkflowTimeoutDuration = WorkflowSleepDuration; + export type WorkflowBackoff = 'constant' | 'linear' | 'exponential'; + export type WorkflowStepConfig = { + retries?: { + limit: number; + delay: WorkflowDelayDuration | number; + backoff?: WorkflowBackoff; + }; + timeout?: WorkflowTimeoutDuration | number; + }; + export type WorkflowEvent = { + payload: Readonly; + timestamp: Date; + instanceId: string; + }; + export type WorkflowStepEvent = { + payload: Readonly; + timestamp: Date; + type: string; + }; + export abstract class WorkflowStep { + do>(name: string, callback: () => Promise): Promise; + do>(name: string, config: WorkflowStepConfig, callback: () => Promise): Promise; + sleep: (name: string, duration: WorkflowSleepDuration) => Promise; + sleepUntil: (name: string, timestamp: Date | number) => Promise; + waitForEvent>(name: string, options: { + type: string; + timeout?: WorkflowTimeoutDuration | number; + }): Promise>; + } + export abstract class WorkflowEntrypoint | unknown = unknown> implements Rpc.WorkflowEntrypointBranded { + [Rpc.__WORKFLOW_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + run(event: Readonly>, step: WorkflowStep): Promise; + } + export const env: Cloudflare.Env; +} +interface SecretsStoreSecret { + /** + * Get a secret from the Secrets Store, returning a string of the secret value + * if it exists, or throws an error if it does not exist + */ + get(): Promise; +} +declare module "cloudflare:sockets" { + function _connect(address: string | SocketAddress, options?: SocketOptions): Socket; + export { _connect as connect }; +} +declare namespace TailStream { + interface Header { + readonly name: string; + readonly value: string; + } + interface FetchEventInfo { + readonly type: "fetch"; + readonly method: string; + readonly url: string; + readonly cfJson: string; + readonly headers: Header[]; + } + interface JsRpcEventInfo { + readonly type: "jsrpc"; + readonly methodName: string; + } + interface ScheduledEventInfo { + readonly type: "scheduled"; + readonly scheduledTime: Date; + readonly cron: string; + } + interface AlarmEventInfo { + readonly type: "alarm"; + readonly scheduledTime: Date; + } + interface QueueEventInfo { + readonly type: "queue"; + readonly queueName: string; + readonly batchSize: number; + } + interface EmailEventInfo { + readonly type: "email"; + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; + } + interface TraceEventInfo { + readonly type: "trace"; + readonly traces: (string | null)[]; + } + interface HibernatableWebSocketEventInfoMessage { + readonly type: "message"; + } + interface HibernatableWebSocketEventInfoError { + readonly type: "error"; + } + interface HibernatableWebSocketEventInfoClose { + readonly type: "close"; + readonly code: number; + readonly wasClean: boolean; + } + interface HibernatableWebSocketEventInfo { + readonly type: "hibernatableWebSocket"; + readonly info: HibernatableWebSocketEventInfoClose | HibernatableWebSocketEventInfoError | HibernatableWebSocketEventInfoMessage; + } + interface Resume { + readonly type: "resume"; + readonly attachment?: any; + } + interface CustomEventInfo { + readonly type: "custom"; + } + interface FetchResponseInfo { + readonly type: "fetch"; + readonly statusCode: number; + } + type EventOutcome = "ok" | "canceled" | "exception" | "unknown" | "killSwitch" | "daemonDown" | "exceededCpu" | "exceededMemory" | "loadShed" | "responseStreamDisconnected" | "scriptNotFound"; + interface ScriptVersion { + readonly id: string; + readonly tag?: string; + readonly message?: string; + } + interface Trigger { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Onset { + readonly type: "onset"; + readonly dispatchNamespace?: string; + readonly entrypoint?: string; + readonly scriptName?: string; + readonly scriptTags?: string[]; + readonly scriptVersion?: ScriptVersion; + readonly trigger?: Trigger; + readonly info: FetchEventInfo | JsRpcEventInfo | ScheduledEventInfo | AlarmEventInfo | QueueEventInfo | EmailEventInfo | TraceEventInfo | HibernatableWebSocketEventInfo | Resume | CustomEventInfo; + } + interface Outcome { + readonly type: "outcome"; + readonly outcome: EventOutcome; + readonly cpuTime: number; + readonly wallTime: number; + } + interface Hibernate { + readonly type: "hibernate"; + } + interface SpanOpen { + readonly type: "spanOpen"; + readonly op?: string; + readonly info?: FetchEventInfo | JsRpcEventInfo | Attribute[]; + } + interface SpanClose { + readonly type: "spanClose"; + readonly outcome: EventOutcome; + } + interface DiagnosticChannelEvent { + readonly type: "diagnosticChannel"; + readonly channel: string; + readonly message: any; + } + interface Exception { + readonly type: "exception"; + readonly name: string; + readonly message: string; + readonly stack?: string; + } + interface Log { + readonly type: "log"; + readonly level: "debug" | "error" | "info" | "log" | "warn"; + readonly message: string; + } + interface Return { + readonly type: "return"; + readonly info?: FetchResponseInfo | Attribute[]; + } + interface Link { + readonly type: "link"; + readonly label?: string; + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Attribute { + readonly type: "attribute"; + readonly name: string; + readonly value: string | string[] | boolean | boolean[] | number | number[]; + } + type Mark = DiagnosticChannelEvent | Exception | Log | Return | Link | Attribute[]; + interface TailEvent { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + readonly timestamp: Date; + readonly sequence: number; + readonly event: Onset | Outcome | Hibernate | SpanOpen | SpanClose | Mark; + } + type TailEventHandler = (event: TailEvent) => void | Promise; + type TailEventHandlerName = "onset" | "outcome" | "hibernate" | "spanOpen" | "spanClose" | "diagnosticChannel" | "exception" | "log" | "return" | "link" | "attribute"; + type TailEventHandlerObject = Record; + type TailEventHandlerType = TailEventHandler | TailEventHandlerObject; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +/** + * Data types supported for holding vector metadata. + */ +type VectorizeVectorMetadataValue = string | number | boolean | string[]; +/** + * Additional information to associate with a vector. + */ +type VectorizeVectorMetadata = VectorizeVectorMetadataValue | Record; +type VectorFloatArray = Float32Array | Float64Array; +interface VectorizeError { + code?: number; + error: string; +} +/** + * Comparison logic/operation to use for metadata filtering. + * + * This list is expected to grow as support for more operations are released. + */ +type VectorizeVectorMetadataFilterOp = "$eq" | "$ne"; +/** + * Filter criteria for vector metadata used to limit the retrieved query result set. + */ +type VectorizeVectorMetadataFilter = { + [field: string]: Exclude | null | { + [Op in VectorizeVectorMetadataFilterOp]?: Exclude | null; + }; +}; +/** + * Supported distance metrics for an index. + * Distance metrics determine how other "similar" vectors are determined. + */ +type VectorizeDistanceMetric = "euclidean" | "cosine" | "dot-product"; +/** + * Metadata return levels for a Vectorize query. + * + * Default to "none". + * + * @property all Full metadata for the vector return set, including all fields (including those un-indexed) without truncation. This is a more expensive retrieval, as it requires additional fetching & reading of un-indexed data. + * @property indexed Return all metadata fields configured for indexing in the vector return set. This level of retrieval is "free" in that no additional overhead is incurred returning this data. However, note that indexed metadata is subject to truncation (especially for larger strings). + * @property none No indexed metadata will be returned. + */ +type VectorizeMetadataRetrievalLevel = "all" | "indexed" | "none"; +interface VectorizeQueryOptions { + topK?: number; + namespace?: string; + returnValues?: boolean; + returnMetadata?: boolean | VectorizeMetadataRetrievalLevel; + filter?: VectorizeVectorMetadataFilter; +} +/** + * Information about the configuration of an index. + */ +type VectorizeIndexConfig = { + dimensions: number; + metric: VectorizeDistanceMetric; +} | { + preset: string; // keep this generic, as we'll be adding more presets in the future and this is only in a read capacity +}; +/** + * Metadata about an existing index. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeIndexInfo} for its post-beta equivalent. + */ +interface VectorizeIndexDetails { + /** The unique ID of the index */ + readonly id: string; + /** The name of the index. */ + name: string; + /** (optional) A human readable description for the index. */ + description?: string; + /** The index configuration, including the dimension size and distance metric. */ + config: VectorizeIndexConfig; + /** The number of records containing vectors within the index. */ + vectorsCount: number; +} +/** + * Metadata about an existing index. + */ +interface VectorizeIndexInfo { + /** The number of records containing vectors within the index. */ + vectorCount: number; + /** Number of dimensions the index has been configured for. */ + dimensions: number; + /** ISO 8601 datetime of the last processed mutation on in the index. All changes before this mutation will be reflected in the index state. */ + processedUpToDatetime: number; + /** UUIDv4 of the last mutation processed by the index. All changes before this mutation will be reflected in the index state. */ + processedUpToMutation: number; +} +/** + * Represents a single vector value set along with its associated metadata. + */ +interface VectorizeVector { + /** The ID for the vector. This can be user-defined, and must be unique. It should uniquely identify the object, and is best set based on the ID of what the vector represents. */ + id: string; + /** The vector values */ + values: VectorFloatArray | number[]; + /** The namespace this vector belongs to. */ + namespace?: string; + /** Metadata associated with the vector. Includes the values of other fields and potentially additional details. */ + metadata?: Record; +} +/** + * Represents a matched vector for a query along with its score and (if specified) the matching vector information. + */ +type VectorizeMatch = Pick, "values"> & Omit & { + /** The score or rank for similarity, when returned as a result */ + score: number; +}; +/** + * A set of matching {@link VectorizeMatch} for a particular query. + */ +interface VectorizeMatches { + matches: VectorizeMatch[]; + count: number; +} +/** + * Results of an operation that performed a mutation on a set of vectors. + * Here, `ids` is a list of vectors that were successfully processed. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeAsyncMutation} for its post-beta equivalent. + */ +interface VectorizeVectorMutation { + /* List of ids of vectors that were successfully processed. */ + ids: string[]; + /* Total count of the number of processed vectors. */ + count: number; +} +/** + * Result type indicating a mutation on the Vectorize Index. + * Actual mutations are processed async where the `mutationId` is the unique identifier for the operation. + */ +interface VectorizeAsyncMutation { + /** The unique identifier for the async mutation operation containing the changeset. */ + mutationId: string; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link Vectorize} for its new implementation. + */ +declare abstract class VectorizeIndex { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with the ids & count of records that were successfully processed (and thus deleted). + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * Mutations in this version are async, returning a mutation id. + */ +declare abstract class Vectorize { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Use the provided vector-id to perform a similarity search across the index. + * @param vectorId Id for a vector in the index against which the index should be queried. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public queryById(vectorId: string, options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the insert changeset. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the upsert changeset. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with a unique identifier of a mutation containing the delete changeset. + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * The interface for "version_metadata" binding + * providing metadata about the Worker Version using this binding. + */ +type WorkerVersionMetadata = { + /** The ID of the Worker Version using this binding */ + id: string; + /** The tag of the Worker Version using this binding */ + tag: string; + /** The timestamp of when the Worker Version was uploaded */ + timestamp: string; +}; +interface DynamicDispatchLimits { + /** + * Limit CPU time in milliseconds. + */ + cpuMs?: number; + /** + * Limit number of subrequests. + */ + subRequests?: number; +} +interface DynamicDispatchOptions { + /** + * Limit resources of invoked Worker script. + */ + limits?: DynamicDispatchLimits; + /** + * Arguments for outbound Worker script, if configured. + */ + outbound?: { + [key: string]: any; + }; +} +interface DispatchNamespace { + /** + * @param name Name of the Worker script. + * @param args Arguments to Worker script. + * @param options Options for Dynamic Dispatch invocation. + * @returns A Fetcher object that allows you to send requests to the Worker script. + * @throws If the Worker script does not exist in this dispatch namespace, an error will be thrown. + */ + get(name: string, args?: { + [key: string]: any; + }, options?: DynamicDispatchOptions): Fetcher; +} +declare module 'cloudflare:workflows' { + /** + * NonRetryableError allows for a user to throw a fatal error + * that makes a Workflow instance fail immediately without triggering a retry + */ + export class NonRetryableError extends Error { + public constructor(message: string, name?: string); + } +} +declare abstract class Workflow { + /** + * Get a handle to an existing instance of the Workflow. + * @param id Id for the instance of this Workflow + * @returns A promise that resolves with a handle for the Instance + */ + public get(id: string): Promise; + /** + * Create a new instance and return a handle to it. If a provided id exists, an error will be thrown. + * @param options Options when creating an instance including id and params + * @returns A promise that resolves with a handle for the Instance + */ + public create(options?: WorkflowInstanceCreateOptions): Promise; + /** + * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. + * @param batch List of Options when creating an instance including name and params + * @returns A promise that resolves with a list of handles for the created instances. + */ + public createBatch(batch: WorkflowInstanceCreateOptions[]): Promise; +} +interface WorkflowInstanceCreateOptions { + /** + * An id for your Workflow instance. Must be unique within the Workflow. + */ + id?: string; + /** + * The event payload the Workflow instance is triggered with + */ + params?: PARAMS; +} +type InstanceStatus = { + status: 'queued' // means that instance is waiting to be started (see concurrency limits) + | 'running' | 'paused' | 'errored' | 'terminated' // user terminated the instance while it was running + | 'complete' | 'waiting' // instance is hibernating and waiting for sleep or event to finish + | 'waitingForPause' // instance is finishing the current work to pause + | 'unknown'; + error?: string; + output?: object; +}; +interface WorkflowError { + code?: number; + message: string; +} +declare abstract class WorkflowInstance { + public id: string; + /** + * Pause the instance. + */ + public pause(): Promise; + /** + * Resume the instance. If it is already running, an error will be thrown. + */ + public resume(): Promise; + /** + * Terminate the instance. If it is errored, terminated or complete, an error will be thrown. + */ + public terminate(): Promise; + /** + * Restart the instance. + */ + public restart(): Promise; + /** + * Returns the current status of the instance. + */ + public status(): Promise; + /** + * Send an event to this instance. + */ + public sendEvent({ type, payload, }: { + type: string; + payload: unknown; + }): Promise; +} diff --git a/apps/docs-ai-search/wrangler.jsonc b/apps/docs-ai-search/wrangler.jsonc new file mode 100644 index 00000000..fc659265 --- /dev/null +++ b/apps/docs-ai-search/wrangler.jsonc @@ -0,0 +1,106 @@ +/** + * For more details on how to configure Wrangler, refer to: + * https://developers.cloudflare.com/workers/wrangler/configuration/ + */ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": "src/docs-ai-search.app.ts", + "compatibility_date": "2025-03-10", + "compatibility_flags": ["nodejs_compat"], + "name": "mcp-cloudflare-docs-ai-search-dev", + "minify": true, + "migrations": [ + { + "new_sqlite_classes": ["CloudflareDocumentationMCP"], + "tag": "v1" + } + ], + "observability": { + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } + }, + "durable_objects": { + "bindings": [ + { + "class_name": "CloudflareDocumentationMCP", + "name": "MCP_OBJECT" + } + ] + }, + "ai": { + "binding": "AI" + }, + "vars": { + "ENVIRONMENT": "development", + "MCP_SERVER_NAME": "PLACEHOLDER", + "MCP_SERVER_VERSION": "PLACEHOLDER" + }, + "dev": { + "port": 8976 + }, + "workers_dev": false, + "preview_urls": false, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-dev" + } + ], + "env": { + "staging": { + "name": "mcp-cloudflare-docs-ai-search-staging", + "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", + "routes": [{ "pattern": "docs-staging.mcp.cloudflare.com", "custom_domain": true }], + "durable_objects": { + "bindings": [ + { + "class_name": "CloudflareDocumentationMCP", + "name": "MCP_OBJECT" + } + ] + }, + "vars": { + "ENVIRONMENT": "staging", + "SENTRY_DSN": "https://d3ffa014f0daa57b27a174476bc2573f@sentry10.cfdata.org/2090" + }, + "ai": { + "binding": "AI" + }, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-staging" + } + ] + }, + "production": { + "name": "mcp-cloudflare-docs-ai-search-production", + "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", + "routes": [{ "pattern": "docs.mcp.cloudflare.com", "custom_domain": true }], + "durable_objects": { + "bindings": [ + { + "class_name": "CloudflareDocumentationMCP", + "name": "MCP_OBJECT" + } + ] + }, + "vars": { + "ENVIRONMENT": "production", + "SENTRY_DSN": "https://d3ffa014f0daa57b27a174476bc2573f@sentry10.cfdata.org/2090" + }, + "ai": { + "binding": "AI" + }, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-production" + } + ] + } + } +} diff --git a/apps/docs-autorag/CHANGELOG.md b/apps/docs-autorag/CHANGELOG.md new file mode 100644 index 00000000..38c14155 --- /dev/null +++ b/apps/docs-autorag/CHANGELOG.md @@ -0,0 +1,118 @@ +# docs-autorag + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/docs-autorag/README.md b/apps/docs-autorag/README.md index efd4ad45..3150bc96 100644 --- a/apps/docs-autorag/README.md +++ b/apps/docs-autorag/README.md @@ -12,7 +12,7 @@ The Cloudflare account this worker is deployed on already has this Autorag insta pnpm run start ``` -Then connect to the server via remote MCP at `http://localhost:8976/sse` +Then connect to the server via remote MCP at `http://localhost:8976/mcp` ## Deploying diff --git a/apps/docs-autorag/package.json b/apps/docs-autorag/package.json index b69b6b55..106c52cd 100644 --- a/apps/docs-autorag/package.json +++ b/apps/docs-autorag/package.json @@ -1,6 +1,6 @@ { "name": "docs-autorag", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,12 +12,12 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "mime": "4.0.6", diff --git a/apps/docs-autorag/src/index.ts b/apps/docs-autorag/src/docs-autorag.app.ts similarity index 87% rename from apps/docs-autorag/src/index.ts rename to apps/docs-autorag/src/docs-autorag.app.ts index dc7bea2a..a200823d 100644 --- a/apps/docs-autorag/src/index.ts +++ b/apps/docs-autorag/src/docs-autorag.app.ts @@ -4,9 +4,9 @@ import { createApiHandler } from '@repo/mcp-common/src/api-handler' import { getEnv } from '@repo/mcp-common/src/env' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerDocsTools } from './tools/docs' +import { registerDocsTools } from './tools/docs-autorag.tools' -import type { Env } from './context' +import type { Env } from './docs-autorag.context' const env = getEnv() diff --git a/apps/docs-autorag/src/context.ts b/apps/docs-autorag/src/docs-autorag.context.ts similarity index 80% rename from apps/docs-autorag/src/context.ts rename to apps/docs-autorag/src/docs-autorag.context.ts index 85ef234e..6205c127 100644 --- a/apps/docs-autorag/src/context.ts +++ b/apps/docs-autorag/src/docs-autorag.context.ts @@ -1,4 +1,4 @@ -import type { CloudflareDocumentationMCP } from './index' +import type { CloudflareDocumentationMCP } from './docs-autorag.app' export interface Env { ENVIRONMENT: 'development' | 'staging' | 'production' diff --git a/apps/docs-autorag/src/tools/docs.ts b/apps/docs-autorag/src/tools/docs-autorag.tools.ts similarity index 95% rename from apps/docs-autorag/src/tools/docs.ts rename to apps/docs-autorag/src/tools/docs-autorag.tools.ts index 19ac1a43..5c308f27 100644 --- a/apps/docs-autorag/src/tools/docs.ts +++ b/apps/docs-autorag/src/tools/docs-autorag.tools.ts @@ -2,7 +2,7 @@ import { type EmbeddedResource } from '@modelcontextprotocol/sdk/types.js' import mime from 'mime' import { z } from 'zod' -import type { CloudflareDocumentationMCP } from '../index' +import type { CloudflareDocumentationMCP } from '../docs-autorag.app' /** * Registers the docs search tool with the MCP server @@ -12,8 +12,8 @@ export function registerDocsTools(agent: CloudflareDocumentationMCP) { // Register the worker logs analysis tool by worker name agent.server.tool( 'search_cloudflare_documentation', - `Search the Cloudflare documentation. - + `Search the Cloudflare documentation. + You should use this tool when: - A user asks questions about Cloudflare products (Workers, Developer Platform, Zero Trust, CDN, etc) - A user requests information about a Cloudflare feature diff --git a/apps/docs-autorag/vitest.config.ts b/apps/docs-autorag/vitest.config.ts index c201c144..f25d33ea 100644 --- a/apps/docs-autorag/vitest.config.ts +++ b/apps/docs-autorag/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/docs-autorag.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/docs-autorag/wrangler.jsonc b/apps/docs-autorag/wrangler.jsonc index 43ea6eee..4cec6aac 100644 --- a/apps/docs-autorag/wrangler.jsonc +++ b/apps/docs-autorag/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/docs-autorag.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-docs-autorag-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/docs-vectorize/CHANGELOG.md b/apps/docs-vectorize/CHANGELOG.md new file mode 100644 index 00000000..bc95137a --- /dev/null +++ b/apps/docs-vectorize/CHANGELOG.md @@ -0,0 +1,140 @@ +# docs-vectorize + +## 0.5.1 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.5.0 + +### Minor Changes + +- fcbedd1: Moved Docs MCP server to be stateless for /mcp + +## 0.4.3 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.4.2 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.4.1 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.4.0 + +### Minor Changes + +- dee0a7b: Updated the model for docs search to embeddinggemma-300m + +## 0.3.3 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.3.2 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.3.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.3.0 + +### Minor Changes + +- f885d07: Add search docs tool to bindings and obs servers + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.2.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.2.0 + +### Minor Changes + +- 89bfaf4: feat: add Pages to Workers migration guide to docs-vectorize MCP server + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/docs-vectorize/README.md b/apps/docs-vectorize/README.md index 5e36f65d..c9bac474 100644 --- a/apps/docs-vectorize/README.md +++ b/apps/docs-vectorize/README.md @@ -8,9 +8,9 @@ The Cloudflare account this worker is deployed on already has this Vectorize DB Currently available tools: -| **Category** | **Tool** | **Description** | -| --------------------- | --------------------------------- | ------------------------------------ | -| **Workers Analytics** | `search_cloudflare_documentation` | Search the Cloudflare documentation. | +| **Category** | **Tool** | **Description** | +| ---------------------------- | --------------------------------- | ------------------------------------ | +| **Cloudflare Documentation** | `search_cloudflare_documentation` | Search the Cloudflare documentation. | ### Prompt Examples @@ -22,7 +22,7 @@ Currently available tools: If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://docs.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -31,7 +31,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://docs.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://docs.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/docs-vectorize/package.json b/apps/docs-vectorize/package.json index 96de4755..3dcb0bbb 100644 --- a/apps/docs-vectorize/package.json +++ b/apps/docs-vectorize/package.json @@ -1,6 +1,6 @@ { "name": "docs-vectorize", - "version": "0.0.1", + "version": "0.5.1", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,12 +12,12 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "mime": "4.0.6", diff --git a/apps/docs-vectorize/src/docs-vectorize.app.ts b/apps/docs-vectorize/src/docs-vectorize.app.ts new file mode 100644 index 00000000..7887391a --- /dev/null +++ b/apps/docs-vectorize/src/docs-vectorize.app.ts @@ -0,0 +1,76 @@ +import { createMcpHandler, McpAgent } from 'agents/mcp' + +import { getEnv } from '@repo/mcp-common/src/env' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-vectorize.prompts' +import { initSentry } from '@repo/mcp-common/src/sentry' +import { CloudflareMCPServer } from '@repo/mcp-common/src/server' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-vectorize.tools' + +import type { Env } from './docs-vectorize.context' + +const env = getEnv() + +export class CloudflareDocumentationMCP extends McpAgent { + _server: CloudflareMCPServer | undefined + set server(server: CloudflareMCPServer) { + this._server = server + } + get server(): CloudflareMCPServer { + if (!this._server) { + throw new Error('Tried to access server before it was initialized') + } + return this._server + } + + constructor( + public ctx: DurableObjectState, + public env: Env + ) { + super(ctx, env) + } + + async init() { + this.server = createMcpServer(env, this.ctx) + } +} + +const sseHandler = CloudflareDocumentationMCP.serveSSE('/sse') + +export default { + fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { + const url = new URL(req.url) + if (url.pathname === '/sse' || url.pathname === '/sse/message') { + return sseHandler.fetch(req, env, ctx) + } + if (url.pathname === '/mcp') { + const server = createMcpServer(env, ctx, req) + const mcpHandler = createMcpHandler(server) + return mcpHandler(req, env, ctx) + } + return new Response('Not found', { status: 404 }) + }, +} + +function createMcpServer( + env: Env, + ctx: { + waitUntil: ExecutionContext['waitUntil'] + }, + req?: Request +) { + const sentry = initSentry(env, ctx, req) + + const server = new CloudflareMCPServer({ + wae: env.MCP_METRICS, + serverInfo: { + name: env.MCP_SERVER_NAME, + version: env.MCP_SERVER_VERSION, + }, + sentry, + }) + + registerDocsTools(server, env) + registerPrompts(server) + + return server +} diff --git a/apps/docs-vectorize/src/context.ts b/apps/docs-vectorize/src/docs-vectorize.context.ts similarity index 60% rename from apps/docs-vectorize/src/context.ts rename to apps/docs-vectorize/src/docs-vectorize.context.ts index b932560e..af85a356 100644 --- a/apps/docs-vectorize/src/context.ts +++ b/apps/docs-vectorize/src/docs-vectorize.context.ts @@ -1,4 +1,4 @@ -import type { CloudflareDocumentationMCP } from './index' +import type { CloudflareDocumentationMCP } from './docs-vectorize.app' export interface Env { ENVIRONMENT: 'development' | 'staging' | 'production' @@ -6,6 +6,10 @@ export interface Env { MCP_SERVER_VERSION: string MCP_OBJECT: DurableObjectNamespace MCP_METRICS: AnalyticsEngineDataset + SENTRY_ACCESS_CLIENT_ID: string + SENTRY_ACCESS_CLIENT_SECRET: string + GIT_HASH: string + SENTRY_DSN: string AI: Ai VECTORIZE: VectorizeIndex } diff --git a/apps/docs-vectorize/src/index.ts b/apps/docs-vectorize/src/index.ts deleted file mode 100644 index dc7bea2a..00000000 --- a/apps/docs-vectorize/src/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { McpAgent } from 'agents/mcp' - -import { createApiHandler } from '@repo/mcp-common/src/api-handler' -import { getEnv } from '@repo/mcp-common/src/env' -import { CloudflareMCPServer } from '@repo/mcp-common/src/server' - -import { registerDocsTools } from './tools/docs' - -import type { Env } from './context' - -const env = getEnv() - -// The docs MCP server isn't stateful, so we don't have state/props -export type Props = never - -export type State = never - -export class CloudflareDocumentationMCP extends McpAgent { - server = new CloudflareMCPServer({ - wae: env.MCP_METRICS, - serverInfo: { - name: env.MCP_SERVER_NAME, - version: env.MCP_SERVER_VERSION, - }, - }) - - constructor( - public ctx: DurableObjectState, - public env: Env - ) { - super(ctx, env) - } - - async init() { - registerDocsTools(this) - } -} - -export default createApiHandler(CloudflareDocumentationMCP) diff --git a/apps/docs-vectorize/vitest.config.ts b/apps/docs-vectorize/vitest.config.ts index c201c144..8643a897 100644 --- a/apps/docs-vectorize/vitest.config.ts +++ b/apps/docs-vectorize/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/docs-vectorize.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/docs-vectorize/wrangler.jsonc b/apps/docs-vectorize/wrangler.jsonc index b30d5bd4..1ebe370e 100644 --- a/apps/docs-vectorize/wrangler.jsonc +++ b/apps/docs-vectorize/wrangler.jsonc @@ -4,10 +4,11 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/docs-vectorize.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-docs-vectorize-dev", + "minify": true, "migrations": [ { "new_sqlite_classes": ["CloudflareDocumentationMCP"], @@ -15,7 +16,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ @@ -31,7 +36,7 @@ "vectorize": [ { "binding": "VECTORIZE", - "index_name": "docs-bge-base" + "index_name": "docs-embeddinggemma-v1" } ], "vars": { @@ -54,7 +59,7 @@ "staging": { "name": "mcp-cloudflare-docs-vectorize-staging", "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", - "routes": [{ "pattern": "docs-staging.mcp.cloudflare.com", "custom_domain": true }], + "routes": [{ "pattern": "docs-vectorize-staging.mcp.cloudflare.com", "custom_domain": true }], "durable_objects": { "bindings": [ { @@ -64,7 +69,8 @@ ] }, "vars": { - "ENVIRONMENT": "staging" + "ENVIRONMENT": "staging", + "SENTRY_DSN": "https://d3ffa014f0daa57b27a174476bc2573f@sentry10.cfdata.org/2090" }, "ai": { "binding": "AI" @@ -72,7 +78,7 @@ "vectorize": [ { "binding": "VECTORIZE", - "index_name": "docs-bge-base" + "index_name": "docs-embeddinggemma-v1" } ], "analytics_engine_datasets": [ @@ -85,7 +91,7 @@ "production": { "name": "mcp-cloudflare-docs-vectorize-production", "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", - "routes": [{ "pattern": "docs.mcp.cloudflare.com", "custom_domain": true }], + "routes": [{ "pattern": "docs-vectorize.mcp.cloudflare.com", "custom_domain": true }], "durable_objects": { "bindings": [ { @@ -95,7 +101,8 @@ ] }, "vars": { - "ENVIRONMENT": "production" + "ENVIRONMENT": "production", + "SENTRY_DSN": "https://d3ffa014f0daa57b27a174476bc2573f@sentry10.cfdata.org/2090" }, "ai": { "binding": "AI" @@ -103,7 +110,7 @@ "vectorize": [ { "binding": "VECTORIZE", - "index_name": "docs-bge-base" + "index_name": "docs-embeddinggemma-v1" } ], "analytics_engine_datasets": [ diff --git a/apps/graphql/.eslintrc.cjs b/apps/graphql/.eslintrc.cjs new file mode 100644 index 00000000..f6bf291a --- /dev/null +++ b/apps/graphql/.eslintrc.cjs @@ -0,0 +1,5 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ['@repo/eslint-config/default.cjs'], +} diff --git a/apps/graphql/CHANGELOG.md b/apps/graphql/CHANGELOG.md new file mode 100644 index 00000000..73ae2eab --- /dev/null +++ b/apps/graphql/CHANGELOG.md @@ -0,0 +1,115 @@ +# graphql-mcp-server + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/graphql/README.md b/apps/graphql/README.md new file mode 100644 index 00000000..dc33d167 --- /dev/null +++ b/apps/graphql/README.md @@ -0,0 +1,45 @@ +# Cloudflare GraphQL MCP Server + +This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP +connections, with Cloudflare OAuth built-in. It integrates tools powered by the [Cloudflare GraphQL API](https://developers.cloudflare.com/analytics/graphql-api/) to provide insights and utilities for your Cloudflare account. + +## Available Tools + +Currently available tools: + +| **Category** | **Tool** | **Description** | +| --------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------- | +| **GraphQL Schema Search** | `graphql_schema_search` | Search the Cloudflare GraphQL API schema for types, fields, and enum values matching a keyword | +| **GraphQL Schema Overview** | `graphql_schema_overview` | Fetch the high-level overview of the Cloudflare GraphQL API schema | +| **GraphQL Type Details** | `graphql_type_details` | Fetch detailed information about a specific GraphQL type | +| **GraphQL Complete Schema** | `graphql_complete_schema` | Fetch the complete Cloudflare GraphQL API schema (combines overview and important type details) | +| **GraphQL Query Execution** | `graphql_query` | Execute a GraphQL query against the Cloudflare API | +| **GraphQL API Explorer** | `graphql_api_explorer` | Generate a Cloudflare [GraphQL API Explorer](https://graphql.cloudflare.com/explorer) link | + +### Prompt Examples + +- `Show me HTTP traffic for the last 7 days for example.com` +- `Show me which GraphQL datatype I need to use to query firewall events` +- `Can you generate a link to the Cloudflare GraphQL API Explorer with a pre-populated query and variables?` +- `I need to monitor HTTP requests and responses for a specific domain. Can you help me with that using the Cloudflare GraphQL API?` + +## Access the remote MCP server from Claude Desktop + +If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://graphql.mcp.cloudflare.com/mcp`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). + +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. + +Replace the content with the following configuration: + +```json +{ + "mcpServers": { + "cloudflare": { + "command": "npx", + "args": ["mcp-remote", "https://graphql.mcp.cloudflare.com/mcp"] + } + } +} +``` + +Once you've set up your configuration file, restart MCP client and a browser window will open showing your OAuth login page. Proceed through the authentication flow to grant the client access to your MCP server. After you grant access, the tools will become available for you to use. diff --git a/apps/graphql/package.json b/apps/graphql/package.json new file mode 100644 index 00000000..d53347fa --- /dev/null +++ b/apps/graphql/package.json @@ -0,0 +1,34 @@ +{ + "name": "graphql-mcp-server", + "version": "0.1.9", + "private": true, + "scripts": { + "check:lint": "run-eslint-workers", + "check:types": "run-tsc", + "deploy": "run-wrangler-deploy", + "dev": "wrangler dev", + "start": "wrangler dev", + "types": "wrangler types --include-env=false", + "test": "vitest run" + }, + "dependencies": { + "@cloudflare/workers-oauth-provider": "0.0.13", + "@hono/zod-validator": "0.4.3", + "@modelcontextprotocol/sdk": "1.20.2", + "@repo/mcp-common": "workspace:*", + "@repo/mcp-observability": "workspace:*", + "agents": "0.2.19", + "cloudflare": "4.2.0", + "hono": "4.7.6", + "zod": "3.24.2", + "lz-string": "1.5.0" + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "0.8.14", + "@types/node": "22.14.1", + "prettier": "3.5.3", + "typescript": "5.5.4", + "vitest": "3.0.9", + "wrangler": "4.10.0" + } +} diff --git a/apps/graphql/src/graphql.app.ts b/apps/graphql/src/graphql.app.ts new file mode 100644 index 00000000..6f0a9a5c --- /dev/null +++ b/apps/graphql/src/graphql.app.ts @@ -0,0 +1,147 @@ +import OAuthProvider from '@cloudflare/workers-oauth-provider' +import { McpAgent } from 'agents/mcp' + +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' +import { + createAuthHandlers, + handleTokenExchangeCallback, +} from '@repo/mcp-common/src/cloudflare-oauth-handler' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' +import { RequiredScopes } from '@repo/mcp-common/src/scopes' +import { initSentryWithUser } from '@repo/mcp-common/src/sentry' +import { CloudflareMCPServer } from '@repo/mcp-common/src/server' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' +import { registerZoneTools } from '@repo/mcp-common/src/tools/zone.tools' +import { MetricsTracker } from '@repo/mcp-observability' + +import { registerGraphQLTools } from './tools/graphql.tools' + +import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' +import type { Env } from './graphql.context' + +export { UserDetails } + +const env = getEnv() + +const metrics = new MetricsTracker(env.MCP_METRICS, { + name: env.MCP_SERVER_NAME, + version: env.MCP_SERVER_VERSION, +}) + +// Context from the auth process, encrypted & stored in the auth token +// and provided to the DurableMCP as this.props +type Props = AuthProps +type State = { activeAccountId: string | null } + +export class GraphQLMCP extends McpAgent { + _server: CloudflareMCPServer | undefined + set server(server: CloudflareMCPServer) { + this._server = server + } + + get server(): CloudflareMCPServer { + if (!this._server) { + throw new Error('Tried to access server before it was initialized') + } + + return this._server + } + + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env) + } + + async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + const sentry = + props.type === 'user_token' ? initSentryWithUser(env, this.ctx, props.user.id) : undefined + + this.server = new CloudflareMCPServer({ + userId, + wae: this.env.MCP_METRICS, + serverInfo: { + name: this.env.MCP_SERVER_NAME, + version: this.env.MCP_SERVER_VERSION, + }, + sentry, + }) + + // Register account tools + registerAccountTools(this) + + // Register zone tools + registerZoneTools(this) + + // Register GraphQL tools + registerGraphQLTools(this) + } + + async getActiveAccountId() { + try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } + // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it + // we do this so we can persist activeAccountId across sessions + const userDetails = getUserDetails(env, props.user.id) + return await userDetails.getActiveAccountId() + } catch (e) { + this.server.recordError(e) + return null + } + } + + async setActiveAccountId(accountId: string) { + try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) + await userDetails.setActiveAccountId(accountId) + } catch (e) { + this.server.recordError(e) + } + } +} + +const GraphQLScopes = { + ...RequiredScopes, + 'account:read': 'See your account info such as account details, analytics, and memberships.', + 'zone:read': 'See zone data such as settings, analytics, and DNS records.', +} as const + +export default { + fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(GraphQLMCP, req, env, ctx) + } + + return new OAuthProvider({ + apiHandlers: { + '/mcp': GraphQLMCP.serve('/mcp'), + '/sse': GraphQLMCP.serveSSE('/sse'), + }, + // @ts-ignore + defaultHandler: createAuthHandlers({ scopes: GraphQLScopes, metrics }), + authorizeEndpoint: '/oauth/authorize', + tokenEndpoint: '/token', + tokenExchangeCallback: (options) => + handleTokenExchangeCallback( + options, + env.CLOUDFLARE_CLIENT_ID, + env.CLOUDFLARE_CLIENT_SECRET + ), + // Cloudflare access token TTL + accessTokenTTL: 3600, + clientRegistrationEndpoint: '/register', + }).fetch(req, env, ctx) + }, +} diff --git a/apps/graphql/src/graphql.context.ts b/apps/graphql/src/graphql.context.ts new file mode 100644 index 00000000..12ac27bd --- /dev/null +++ b/apps/graphql/src/graphql.context.ts @@ -0,0 +1,21 @@ +import type { GraphQLMCP, UserDetails } from './graphql.app' + +export interface Env { + OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string + ENVIRONMENT: 'development' | 'staging' | 'production' + MCP_SERVER_NAME: string + MCP_SERVER_VERSION: string + CLOUDFLARE_CLIENT_ID: string + CLOUDFLARE_CLIENT_SECRET: string + MCP_OBJECT: DurableObjectNamespace + USER_DETAILS: DurableObjectNamespace + MCP_METRICS: AnalyticsEngineDataset + SENTRY_ACCESS_CLIENT_ID: string + SENTRY_ACCESS_CLIENT_SECRET: string + GIT_HASH: string + SENTRY_DSN: string + DEV_DISABLE_OAUTH: string + DEV_CLOUDFLARE_API_TOKEN: string + DEV_CLOUDFLARE_EMAIL: string +} diff --git a/apps/graphql/src/tools/graphql.tools.ts b/apps/graphql/src/tools/graphql.tools.ts new file mode 100644 index 00000000..aea4f415 --- /dev/null +++ b/apps/graphql/src/tools/graphql.tools.ts @@ -0,0 +1,1119 @@ +import * as LZString from 'lz-string' +import { z } from 'zod' + +import { getProps } from '@repo/mcp-common/src/get-props' + +import type { GraphQLMCP } from '../graphql.app' + +// GraphQL API endpoint +const CLOUDFLARE_GRAPHQL_ENDPOINT = 'https://api.cloudflare.com/client/v4/graphql' + +// Type definitions for GraphQL schema responses +interface GraphQLTypeRef { + kind: string + name: string | null + ofType?: GraphQLTypeRef | null +} + +interface GraphQLField { + name: string + description: string | null + args: Array<{ + name: string + description: string | null + type: GraphQLTypeRef + }> + type: GraphQLTypeRef +} + +interface GraphQLType { + name: string + kind: string + description: string | null + fields?: GraphQLField[] | null + inputFields?: Array<{ + name: string + description: string | null + type: GraphQLTypeRef + }> | null + interfaces?: Array<{ name: string }> | null + enumValues?: Array<{ + name: string + description: string | null + }> | null + possibleTypes?: Array<{ name: string }> | null +} + +interface SchemaOverviewResponse { + data: { + __schema: { + queryType: { name: string } | null + mutationType: { name: string } | null + subscriptionType: { name: string } | null + types: Array<{ + name: string + kind: string + description: string | null + }> + } + } +} + +interface TypeDetailsResponse { + data: { + __type: GraphQLType + } +} + +// Define the structure of a single error +const graphQLErrorSchema = z.object({ + message: z.string(), + path: z.array(z.union([z.string(), z.number()])), + extensions: z.object({ + code: z.string(), + timestamp: z.string(), + ray_id: z.string(), + }), +}) + +// Define the overall GraphQL response schema +const graphQLResponseSchema = z.object({ + data: z.union([z.record(z.unknown()), z.null()]), + errors: z.union([z.array(graphQLErrorSchema), z.null()]), +}) + +/** + * Fetches the high-level overview of the GraphQL schema + * @param apiToken Cloudflare API token + * @returns Basic schema structure + */ +async function fetchSchemaOverview(apiToken: string): Promise { + const overviewQuery = ` + query SchemaOverview { + __schema { + queryType { name } + mutationType { name } + subscriptionType { name } + types { + name + kind + description + } + } + } + ` + + const response = await executeGraphQLRequest(overviewQuery, apiToken) + return response +} + +/** + * Fetches detailed information about a specific GraphQL type + * @param typeName The name of the type to fetch details for + * @param apiToken Cloudflare API token + * @returns Detailed type information + */ +async function fetchTypeDetails(typeName: string, apiToken: string): Promise { + const typeDetailsQuery = ` + query TypeDetails { + __type(name: "${typeName}") { + name + kind + description + fields(includeDeprecated: false) { + name + description + args { + name + description + type { + kind + name + ofType { + kind + name + } + } + } + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + inputFields { + name + description + type { + kind + name + ofType { + kind + name + } + } + } + interfaces { + name + } + enumValues(includeDeprecated: false) { + name + description + } + possibleTypes { + name + } + } + } + ` + + const response = await executeGraphQLRequest(typeDetailsQuery, apiToken) + return response +} + +/** + * Helper function to execute GraphQL requests + * @param query GraphQL query to execute + * @param apiToken Cloudflare API token + * @returns Response data + */ +async function executeGraphQLRequest(query: string, apiToken: string): Promise { + const response = await fetch(CLOUDFLARE_GRAPHQL_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${apiToken}`, + }, + body: JSON.stringify({ query }), + }) + + if (!response.ok) { + throw new Error(`Failed to execute GraphQL request: ${response.statusText}`) + } + + const data = graphQLResponseSchema.parse(await response.json()) + + // Check for GraphQL errors in the response + if (data && data.errors && Array.isArray(data.errors) && data.errors.length > 0) { + const errorMessages = data.errors.map((e: { message: string }) => e.message).join(', ') + console.warn(`GraphQL errors: ${errorMessages}`) + + // If the error is about mutations not being supported, we can handle it gracefully + if (errorMessages.includes('Mutations are not supported')) { + console.info('Mutations are not supported by the Cloudflare GraphQL API') + } + } + + return data as T +} + +/** + * Executes a GraphQL query against Cloudflare's API + * @param query The GraphQL query to execute + * @param variables Variables for the query + * @param apiToken Cloudflare API token + * @returns The query results + */ +async function executeGraphQLQuery(query: string, variables: any, apiToken: string) { + // Clone the variables to avoid modifying the original + const queryVariables = { ...variables } + + const response = await fetch(CLOUDFLARE_GRAPHQL_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${apiToken}`, + }, + body: JSON.stringify({ + query, + variables: queryVariables, + }), + }) + + if (!response.ok) { + throw new Error(`Failed to execute GraphQL query: ${response.statusText}`) + } + + const result = graphQLResponseSchema.parse(await response.json()) + + // Check for GraphQL errors in the response + if (result && result.errors && Array.isArray(result.errors) && result.errors.length > 0) { + const errorMessages = result.errors.map((e: { message: string }) => e.message).join(', ') + console.warn(`GraphQL query errors: ${errorMessages}`) + } + + return result +} + +/** + * Searches for matching types and fields in a GraphQL schema + * @param schema The GraphQL schema to search + * @param keyword The keyword to search for + * @param typeDetails Optional map of type details for deeper searching + * @returns Matching types and fields + */ +async function searchGraphQLSchema( + schema: SchemaOverviewResponse, + keyword: string, + accountId: string, + apiToken: string, + maxDetailsToFetch: number = 10, + onlyObjectTypes: boolean = true +) { + const normalizedKeyword = keyword.toLowerCase() + const results = { + types: [] as Array<{ + name: string + kind: string + description: string | null + matchReason: string + }>, + fields: [] as Array<{ + typeName: string + fieldName: string + description: string | null + matchReason: string + }>, + enumValues: [] as Array<{ + typeName: string + enumValue: string + description: string | null + matchReason: string + }>, + args: [] as Array<{ + typeName: string + fieldName: string + argName: string + description: string | null + matchReason: string + }>, + } + + // First pass: Search through type names and descriptions + const matchingTypeNames: string[] = [] + + for (const type of schema.data.__schema.types || []) { + // Skip internal types (those starting with __) + if (type.name?.startsWith('__')) continue + + // Check if type name or description matches + if (type.name?.toLowerCase().includes(normalizedKeyword)) { + results.types.push({ + ...type, + matchReason: `Type name contains "${keyword}"`, + }) + matchingTypeNames.push(type.name) + } else if (type.description?.toLowerCase().includes(normalizedKeyword)) { + results.types.push({ + ...type, + matchReason: `Type description contains "${keyword}"`, + }) + matchingTypeNames.push(type.name) + } + } + + // Second pass: For potentially relevant types, fetch details and search deeper + // Start with matching types, then add important schema types if we have capacity + let typesToExamine = [...matchingTypeNames] + + // Add root operation types if they're not already included + const rootTypes = [ + schema.data.__schema.queryType?.name, + schema.data.__schema.mutationType?.name, + schema.data.__schema.subscriptionType?.name, + ].filter(Boolean) as string[] + + for (const rootType of rootTypes) { + if (!typesToExamine.includes(rootType)) { + typesToExamine.push(rootType) + } + } + + // Add object types that might contain relevant fields + const objectTypes = schema.data.__schema.types + .filter((t) => { + // If onlyObjectTypes is true, only include OBJECT types + if (onlyObjectTypes) { + return t.kind === 'OBJECT' && !t.name.startsWith('__') + } + // Otherwise include both OBJECT and INTERFACE types + return (t.kind === 'OBJECT' || t.kind === 'INTERFACE') && !t.name.startsWith('__') + }) + .map((t) => t.name) + + // Combine all potential types to examine, but limit to a reasonable number + typesToExamine = [...new Set([...typesToExamine, ...objectTypes])].slice(0, maxDetailsToFetch) + + // Fetch details for these types and search through their fields + for (const typeName of typesToExamine) { + try { + const typeDetails = await fetchTypeDetails(typeName, apiToken) + const type = typeDetails.data.__type + + if (!type) continue + + // Search through fields + if (type.fields) { + for (const field of type.fields) { + // Check if field name or description matches + if (field.name.toLowerCase().includes(normalizedKeyword)) { + results.fields.push({ + typeName: type.name, + fieldName: field.name, + description: field.description, + matchReason: `Field name contains "${keyword}"`, + }) + } else if (field.description?.toLowerCase().includes(normalizedKeyword)) { + results.fields.push({ + typeName: type.name, + fieldName: field.name, + description: field.description, + matchReason: `Field description contains "${keyword}"`, + }) + } + + // Search through field arguments + if (field.args) { + for (const arg of field.args) { + if (arg.name.toLowerCase().includes(normalizedKeyword)) { + results.args.push({ + typeName: type.name, + fieldName: field.name, + argName: arg.name, + description: arg.description, + matchReason: `Argument name contains "${keyword}"`, + }) + } else if (arg.description?.toLowerCase().includes(normalizedKeyword)) { + results.args.push({ + typeName: type.name, + fieldName: field.name, + argName: arg.name, + description: arg.description, + matchReason: `Argument description contains "${keyword}"`, + }) + } + } + } + } + } + + // Search through enum values + if (type.enumValues) { + for (const enumValue of type.enumValues) { + if (enumValue.name.toLowerCase().includes(normalizedKeyword)) { + results.enumValues.push({ + typeName: type.name, + enumValue: enumValue.name, + description: enumValue.description, + matchReason: `Enum value contains "${keyword}"`, + }) + } else if (enumValue.description?.toLowerCase().includes(normalizedKeyword)) { + results.enumValues.push({ + typeName: type.name, + enumValue: enumValue.name, + description: enumValue.description, + matchReason: `Enum value description contains "${keyword}"`, + }) + } + } + } + } catch (error) { + console.error(`Error fetching details for type ${typeName}:`, error) + } + } + + return results +} + +/** + * Registers GraphQL tools with the MCP server + * @param agent The MCP agent instance + */ +export function registerGraphQLTools(agent: GraphQLMCP) { + // Tool to search the GraphQL schema for types, fields, and enum values matching a keyword + agent.server.tool( + 'graphql_schema_search', + `Search the Cloudflare GraphQL API schema for types, fields, and enum values matching a keyword + + Use this tool when: + + - You are unsure which dataset to use for your query. + - A user is looking for specific types, fields, or enum values in the Cloudflare GraphQL API schema. + + IMPORTANT GUIDELINES: + - DO NOT query for dimensions unless the user explicitly asked to group by or show dimensions. + - Only include fields that the user specifically requested in their query. + - Keep queries as simple as possible while fulfilling the user's request. + + Workflow: + 1. Use this tool to search for dataset types by keyword. + 2. When a relevant dataset type is found, immediately use graphql_schema_details to get the complete structure of that dataset. + 3. After understanding the schema structure, proceed directly to constructing and executing queries using the graphql_query tool. + 4. Do not use graphql_schema_overview or graphql_complete_schema after finding the relevant dataset - these are redundant steps. + + This tool searches the Cloudflare GraphQL API schema for any schema elements (such as object types, field names, or enum options) that match a given keyword. It returns schema fragments and definitions to assist in constructing valid and precise GraphQL queries. + `, + { + keyword: z.string().describe('The keyword to search for in the schema'), + maxDetailsToFetch: z + .number() + .min(1) + .max(50) + .default(10) + .describe('Maximum number of types to fetch details for'), + includeInternalTypes: z + .boolean() + .default(false) + .describe( + 'Whether to include internal types (those starting with __) in the search results' + ), + onlyObjectTypes: z + .boolean() + .default(true) + .describe( + 'Whether to only include OBJECT kind types in the search results with descriptions' + ), + }, + async (params) => { + const { + keyword, + maxDetailsToFetch = 10, + includeInternalTypes = false, + onlyObjectTypes = true, + } = params + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + // First fetch the schema overview + const schemaOverview = await fetchSchemaOverview(props.accessToken) + + // Search the schema for the keyword + const searchResults = await searchGraphQLSchema( + schemaOverview, + keyword, + accountId, + props.accessToken, + maxDetailsToFetch, + onlyObjectTypes + ) + + // Filter out internal types if requested + if (!includeInternalTypes) { + searchResults.types = searchResults.types.filter((t) => !t.name.startsWith('__')) + searchResults.fields = searchResults.fields.filter((f) => !f.typeName.startsWith('__')) + searchResults.enumValues = searchResults.enumValues.filter( + (e) => !e.typeName.startsWith('__') + ) + searchResults.args = searchResults.args.filter((a) => !a.typeName.startsWith('__')) + } + + // Filter out items without descriptions when onlyObjectTypes is true + if (onlyObjectTypes) { + searchResults.types = searchResults.types.filter((t) => { + return t.description && t.description.trim() !== '' + }) + searchResults.fields = searchResults.fields.filter((f) => { + return f.description && f.description.trim() !== '' + }) + searchResults.enumValues = searchResults.enumValues.filter((e) => { + return e.description && e.description.trim() !== '' + }) + searchResults.args = searchResults.args.filter((a) => { + return a.description && a.description.trim() !== '' + }) + } + + // Add summary information + const results = { + keyword, + summary: { + totalMatches: + searchResults.types.length + + searchResults.fields.length + + searchResults.enumValues.length + + searchResults.args.length, + typeMatches: searchResults.types.length, + fieldMatches: searchResults.fields.length, + enumValueMatches: searchResults.enumValues.length, + argumentMatches: searchResults.args.length, + }, + results: searchResults, + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(results), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error searching GraphQL schema: ${error instanceof Error ? error.message : String(error)}`, + }), + }, + ], + } + } + } + ) + + // Tool to fetch the GraphQL schema overview (high-level structure) + agent.server.tool( + 'graphql_schema_overview', + `Fetch the high-level overview of the Cloudflare GraphQL API schema + + Use this tool when: + + - A user requests insights into the structure or capabilities of Cloudflare’s GraphQL API. + - You need to explore available types, queries, mutations, or schema relationships exposed by Cloudflare’s GraphQL interface. + - You're generating or validating GraphQL queries against Cloudflare’s schema. + - You are troubleshooting or developing integrations with Cloudflare’s API and require up-to-date schema information. + + This tool returns a high-level summary of the Cloudflare GraphQL API schema. It provides a structured outline of API entry points, data models, and relationships to help guide query construction or system integration. + `, + { + pageSize: z + .number() + .min(10) + .max(1000) + .default(100) + .describe('Number of types to return per page'), + page: z.number().min(1).default(1).describe('Page number to fetch'), + }, + async (params) => { + const { pageSize = 100, page = 1 } = params + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const schemaOverview = await fetchSchemaOverview(props.accessToken) + + // Apply pagination to the types array + const allTypes = schemaOverview.data.__schema.types || [] + const totalTypes = allTypes.length + const totalPages = Math.ceil(totalTypes / pageSize) + + // Calculate start and end indices for the current page + const startIndex = (page - 1) * pageSize + const endIndex = Math.min(startIndex + pageSize, totalTypes) + + // Create a paginated version of the schema + const paginatedSchema = { + data: { + __schema: { + queryType: schemaOverview.data.__schema.queryType, + mutationType: schemaOverview.data.__schema.mutationType, + subscriptionType: schemaOverview.data.__schema.subscriptionType, + types: allTypes.slice(startIndex, endIndex), + }, + }, + pagination: { + page, + pageSize, + totalTypes, + totalPages, + hasNextPage: page < totalPages, + hasPreviousPage: page > 1, + }, + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(paginatedSchema), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error fetching GraphQL schema overview: ${error instanceof Error ? error.message : String(error)}`, + }), + }, + ], + } + } + } + ) + + // Tool to fetch detailed information about a specific GraphQL type + agent.server.tool( + 'graphql_type_details', + `Fetch detailed information about a specific GraphQL type (dataset) + + IMPORTANT: After exploring the schema, DO NOT generate overly complicated GraphQL queries that the user didn't explicitly ask for. Only include fields that were specifically requested. + + Use this tool when: + + - You need to explore the fields by the type name (dataset) for detailed information + - You're building or debugging GraphQL queries and want to ensure the correct usage of schema components + - You need contextual information about how a certain concept or object is represented in Cloudflare's GraphQL API. + + Guidelines for query construction: + - Keep queries as simple as possible while fulfilling the user's request + - Only include fields that the user specifically asked for + - Do not add dimensions or additional fields unless explicitly requested + - When in doubt, ask the user for clarification rather than creating a complex query + `, + { + typeName: z + .string() + .describe('The type name (dataset) of the GraphQL type to fetch details for'), + fieldsPageSize: z + .number() + .min(5) + .max(500) + .default(50) + .describe('Number of fields to return per page'), + fieldsPage: z.number().min(1).default(1).describe('Page number for fields to fetch'), + enumValuesPageSize: z + .number() + .min(5) + .max(500) + .default(50) + .describe('Number of enum values to return per page'), + enumValuesPage: z.number().min(1).default(1).describe('Page number for enum values to fetch'), + }, + async (params) => { + const { + typeName, + fieldsPageSize = 50, + fieldsPage = 1, + enumValuesPageSize = 50, + enumValuesPage = 1, + } = params + + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const typeDetails = await fetchTypeDetails(typeName, props.accessToken) + + // Apply pagination to fields if they exist + const allFields = typeDetails.data.__type.fields || [] + const totalFields = allFields.length + const totalFieldsPages = Math.ceil(totalFields / fieldsPageSize) + + // Calculate start and end indices for the fields page + const fieldsStartIndex = (fieldsPage - 1) * fieldsPageSize + const fieldsEndIndex = Math.min(fieldsStartIndex + fieldsPageSize, totalFields) + + // Apply pagination to enum values if they exist + const allEnumValues = typeDetails.data.__type.enumValues || [] + const totalEnumValues = allEnumValues.length + const totalEnumValuesPages = Math.ceil(totalEnumValues / enumValuesPageSize) + + // Calculate start and end indices for the enum values page + const enumValuesStartIndex = (enumValuesPage - 1) * enumValuesPageSize + const enumValuesEndIndex = Math.min( + enumValuesStartIndex + enumValuesPageSize, + totalEnumValues + ) + + // Create a paginated version of the type details + const paginatedTypeDetails = { + data: { + __type: { + ...typeDetails.data.__type, + fields: allFields.slice(fieldsStartIndex, fieldsEndIndex), + enumValues: allEnumValues.slice(enumValuesStartIndex, enumValuesEndIndex), + }, + }, + pagination: { + fields: { + page: fieldsPage, + pageSize: fieldsPageSize, + totalFields, + totalPages: totalFieldsPages, + hasNextPage: fieldsPage < totalFieldsPages, + hasPreviousPage: fieldsPage > 1, + }, + enumValues: { + page: enumValuesPage, + pageSize: enumValuesPageSize, + totalEnumValues, + totalPages: totalEnumValuesPages, + hasNextPage: enumValuesPage < totalEnumValuesPages, + hasPreviousPage: enumValuesPage > 1, + }, + }, + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(paginatedTypeDetails), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error fetching type details: ${error instanceof Error ? error.message : String(error)}`, + }), + }, + ], + } + } + } + ) + + // Tool to fetch the complete GraphQL schema (combines overview and important type details) + agent.server.tool( + 'graphql_complete_schema', + 'Fetch the complete Cloudflare GraphQL API schema (combines overview and important type details)', + { + typesPageSize: z + .number() + .min(10) + .max(500) + .default(100) + .describe('Number of types to return per page'), + typesPage: z.number().min(1).default(1).describe('Page number for types to fetch'), + includeRootTypeDetails: z + .boolean() + .default(true) + .describe('Whether to include detailed information about root types'), + maxTypeDetailsToFetch: z + .number() + .min(0) + .max(10) + .default(3) + .describe('Maximum number of important types to fetch details for'), + }, + async (params) => { + const { + typesPageSize = 100, + typesPage = 1, + includeRootTypeDetails = true, + maxTypeDetailsToFetch = 3, + } = params + + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + // First fetch the schema overview + const schemaOverview = await fetchSchemaOverview(props.accessToken) + + // Apply pagination to the types array + const allTypes = schemaOverview.data.__schema.types || [] + const totalTypes = allTypes.length + const totalPages = Math.ceil(totalTypes / typesPageSize) + + // Calculate start and end indices for the current page + const startIndex = (typesPage - 1) * typesPageSize + const endIndex = Math.min(startIndex + typesPageSize, totalTypes) + + // Get the paginated types + const paginatedTypes = allTypes.slice(startIndex, endIndex) + + // Create the base schema with paginated types + const schema: { + data: { + __schema: { + queryType: { name: string } | null + mutationType: { name: string } | null + subscriptionType: { name: string } | null + types: Array<{ + name: string + kind: string + description: string | null + }> + } + } + typeDetails: Record + pagination: { + types: { + page: number + pageSize: number + totalTypes: number + totalPages: number + hasNextPage: boolean + hasPreviousPage: boolean + } + } + } = { + data: { + __schema: { + queryType: schemaOverview.data.__schema.queryType, + mutationType: schemaOverview.data.__schema.mutationType, + subscriptionType: schemaOverview.data.__schema.subscriptionType, + types: paginatedTypes, + }, + }, + typeDetails: {} as Record, + pagination: { + types: { + page: typesPage, + pageSize: typesPageSize, + totalTypes, + totalPages, + hasNextPage: typesPage < totalPages, + hasPreviousPage: typesPage > 1, + }, + }, + } + + // If requested, fetch details for root types + if (includeRootTypeDetails) { + // Identify important root types + const rootTypes = [ + schemaOverview.data.__schema.queryType?.name, + ...(schemaOverview.data.__schema.mutationType?.name + ? [schemaOverview.data.__schema.mutationType.name] + : []), + ].filter(Boolean) as string[] + + // Limit the number of types to fetch details for + const typesToFetch = rootTypes.slice(0, maxTypeDetailsToFetch) + + // Fetch details for each type + for (const typeName of typesToFetch) { + try { + const typeDetails = await fetchTypeDetails(typeName, props.accessToken) + if (typeDetails.data.__type) { + schema.typeDetails[typeName] = typeDetails.data.__type + } + } catch (error) { + console.error(`Error fetching details for type ${typeName}:`, error) + } + } + } + + return { + content: [ + { + type: 'text', + text: JSON.stringify(schema), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error fetching GraphQL schema: ${error instanceof Error ? error.message : String(error)}`, + }), + }, + ], + } + } + } + ) + + // Tool to execute a GraphQL query + agent.server.tool( + 'graphql_query', + `Execute a GraphQL query against the Cloudflare API + + IMPORTANT: ONLY execute the EXACT GraphQL query provided by the user. DO NOT generate complicated queries that the user didn't explicitly ask for. + + CRITICAL: When querying, make sure to set a LIMIT (e.g., first: 10, limit: 20) otherwise the response may be too large for the MCP server to process. + + Use this tool when: + + - A user provides a GraphQL query and expects real-time data from Cloudflare's API. + - You need to retrieve live information from Cloudflare, such as analytics, logs, account data, or configuration details. + - You want to validate the behavior of a GraphQL query or inspect its runtime results. + + This tool sends a user-defined GraphQL query to the Cloudflare API and returns the raw response exactly as received. When filtering or querying by time, use ISO 8601 datetime format (e.g., "2020-08-03T02:07:05Z"). + + For each query execution, a clickable GraphQL API Explorer link will be provided in the response. Users can click this link to open the query in Cloudflare's GraphQL Explorer interface where they can further modify and experiment with the query. + + Guidelines: + - Only use the exact query provided by the user. Do not modify or expand it unless explicitly requested. + - Always suggest including limits in queries (e.g., first: 10, limit: 20) to prevent response size issues. + - If a query fails due to size limits, advise the user to add or reduce limits in their query. + `, + { + query: z.string().describe('The GraphQL query to execute'), + variables: z.record(z.any()).optional().describe('Variables for the query'), + }, + async (params) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const { query, variables = {} } = params + + // Execute the GraphQL query and get the raw result + const result = await executeGraphQLQuery(query, variables, props.accessToken) + + // Generate GraphQL API Explorer link for this query + const compressedQuery = LZString.compressToEncodedURIComponent(query) + const compressedVariables = LZString.compressToEncodedURIComponent( + JSON.stringify(variables) + ) + const explorerUrl = `https://graphql.cloudflare.com/explorer?query=${compressedQuery}&variables=${compressedVariables}` + + // Check if the response is too large (MCP server will fail if > 1MB) + const resultString = JSON.stringify(result) + const SIZE_LIMIT = 800000 // Set a safer limit (800KB) to ensure we stay under 1MB + if (resultString.length > SIZE_LIMIT) { + return { + content: [ + { + type: 'text', + text: `ERROR: Query result exceeds size limit (${Math.round(resultString.length / 1024)}KB). MCP server will fail with results larger than 1MB. Please use a lower LIMIT in your GraphQL query to reduce the number of returned items. For example: + +- Add 'first: 10' or 'limit: 10' parameters to your query +- Reduce the number of requested fields +- Add more specific filters to narrow down results`, + }, + ], + } + } + + return { + content: [ + { + type: 'text', + text: `${resultString}\n\n**[Open in GraphQL Explorer](${explorerUrl})**\nClick the link above to view and modify this query in the Cloudflare GraphQL API Explorer.`, + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error executing GraphQL query: ${error instanceof Error ? error.message : String(error)}`, + }), + }, + ], + } + } + } + ) + + // Tool to generate a GraphQL API Explorer link + agent.server.tool( + 'graphql_api_explorer', + `Generate a Cloudflare GraphQL API Explorer link + + Use this tool when: + + - A user asks for any GraphQL queries and wants to explore them in the Cloudflare GraphQL API Explorer. + - You want to provide a shareable link to a specific GraphQL query for the user to explore and modify. + - You need to help the user visualize or interact with GraphQL queries in a user-friendly interface. + + This tool generates a direct link to the Cloudflare GraphQL API Explorer with a pre-populated query and variables. + The response includes a clickable Markdown link that users can click to open the query in Cloudflare's interactive GraphQL playground. + The original query and variables are also displayed for reference. + `, + { + query: z.string().describe('The GraphQL query to include in the explorer link'), + variables: z.record(z.any()).optional().describe('Variables for the query in JSON format'), + }, + async (params) => { + try { + const { query, variables = {} } = params + + // Compress the query and variables using lz-string + const compressedQuery = LZString.compressToEncodedURIComponent(query) + const compressedVariables = LZString.compressToEncodedURIComponent( + JSON.stringify(variables) + ) + + // Generate the GraphQL API Explorer URL + const explorerUrl = `https://graphql.cloudflare.com/explorer?query=${compressedQuery}&variables=${compressedVariables}` + + return { + content: [ + { + type: 'text', + text: `**[Open in GraphQL Explorer](${explorerUrl})**\n\nYou can click the link above to open the Cloudflare GraphQL API Explorer with your query pre-populated.\n\n**Query:**\n\`\`\`graphql\n${query}\n\`\`\`\n\n${Object.keys(variables).length > 0 ? `**Variables:**\n\`\`\`json\n${JSON.stringify(variables, null, 2)}\n\`\`\`\n` : ''}`, + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + error: `Error generating GraphQL API Explorer link: ${error instanceof Error ? error.message : String(error)}`, + }), + }, + ], + } + } + } + ) +} diff --git a/apps/graphql/tsconfig.json b/apps/graphql/tsconfig.json new file mode 100644 index 00000000..9dcfd4e8 --- /dev/null +++ b/apps/graphql/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@repo/typescript-config/workers.json", + "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] +} diff --git a/apps/graphql/types.d.ts b/apps/graphql/types.d.ts new file mode 100644 index 00000000..e090f9a7 --- /dev/null +++ b/apps/graphql/types.d.ts @@ -0,0 +1,5 @@ +import type { TestEnv } from './vitest.config' + +declare module 'cloudflare:test' { + interface ProvidedEnv extends TestEnv {} +} diff --git a/apps/graphql/worker-configuration.d.ts b/apps/graphql/worker-configuration.d.ts new file mode 100644 index 00000000..305dc71b --- /dev/null +++ b/apps/graphql/worker-configuration.d.ts @@ -0,0 +1,5694 @@ +// Runtime types generated with workerd@1.20250409.0 2025-03-10 nodejs_compat +// Begin runtime types +/*! ***************************************************************************** +Copyright (c) Cloudflare. All rights reserved. +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* eslint-disable */ +// noinspection JSUnusedGlobalSymbols +declare var onmessage: never; +/** + * An abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException) + */ +declare class DOMException extends Error { + constructor(message?: string, name?: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */ + readonly message: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ + readonly name: string; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code) + */ + readonly code: number; + static readonly INDEX_SIZE_ERR: number; + static readonly DOMSTRING_SIZE_ERR: number; + static readonly HIERARCHY_REQUEST_ERR: number; + static readonly WRONG_DOCUMENT_ERR: number; + static readonly INVALID_CHARACTER_ERR: number; + static readonly NO_DATA_ALLOWED_ERR: number; + static readonly NO_MODIFICATION_ALLOWED_ERR: number; + static readonly NOT_FOUND_ERR: number; + static readonly NOT_SUPPORTED_ERR: number; + static readonly INUSE_ATTRIBUTE_ERR: number; + static readonly INVALID_STATE_ERR: number; + static readonly SYNTAX_ERR: number; + static readonly INVALID_MODIFICATION_ERR: number; + static readonly NAMESPACE_ERR: number; + static readonly INVALID_ACCESS_ERR: number; + static readonly VALIDATION_ERR: number; + static readonly TYPE_MISMATCH_ERR: number; + static readonly SECURITY_ERR: number; + static readonly NETWORK_ERR: number; + static readonly ABORT_ERR: number; + static readonly URL_MISMATCH_ERR: number; + static readonly QUOTA_EXCEEDED_ERR: number; + static readonly TIMEOUT_ERR: number; + static readonly INVALID_NODE_TYPE_ERR: number; + static readonly DATA_CLONE_ERR: number; + get stack(): any; + set stack(value: any); +} +type WorkerGlobalScopeEventMap = { + fetch: FetchEvent; + scheduled: ScheduledEvent; + queue: QueueEvent; + unhandledrejection: PromiseRejectionEvent; + rejectionhandled: PromiseRejectionEvent; +}; +declare abstract class WorkerGlobalScope extends EventTarget { + EventTarget: typeof EventTarget; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console) */ +interface Console { + "assert"(condition?: boolean, ...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear_static) */ + clear(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count_static) */ + count(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/countreset_static) */ + countReset(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug_static) */ + debug(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir_static) */ + dir(item?: any, options?: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml_static) */ + dirxml(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error_static) */ + error(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group_static) */ + group(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupcollapsed_static) */ + groupCollapsed(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupend_static) */ + groupEnd(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info_static) */ + info(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static) */ + log(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table_static) */ + table(tabularData?: any, properties?: string[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time_static) */ + time(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeend_static) */ + timeEnd(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timelog_static) */ + timeLog(label?: string, ...data: any[]): void; + timeStamp(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace_static) */ + trace(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static) */ + warn(...data: any[]): void; +} +declare const console: Console; +type BufferSource = ArrayBufferView | ArrayBuffer; +type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array; +declare namespace WebAssembly { + class CompileError extends Error { + constructor(message?: string); + } + class RuntimeError extends Error { + constructor(message?: string); + } + type ValueType = "anyfunc" | "externref" | "f32" | "f64" | "i32" | "i64" | "v128"; + interface GlobalDescriptor { + value: ValueType; + mutable?: boolean; + } + class Global { + constructor(descriptor: GlobalDescriptor, value?: any); + value: any; + valueOf(): any; + } + type ImportValue = ExportValue | number; + type ModuleImports = Record; + type Imports = Record; + type ExportValue = Function | Global | Memory | Table; + type Exports = Record; + class Instance { + constructor(module: Module, imports?: Imports); + readonly exports: Exports; + } + interface MemoryDescriptor { + initial: number; + maximum?: number; + shared?: boolean; + } + class Memory { + constructor(descriptor: MemoryDescriptor); + readonly buffer: ArrayBuffer; + grow(delta: number): number; + } + type ImportExportKind = "function" | "global" | "memory" | "table"; + interface ModuleExportDescriptor { + kind: ImportExportKind; + name: string; + } + interface ModuleImportDescriptor { + kind: ImportExportKind; + module: string; + name: string; + } + abstract class Module { + static customSections(module: Module, sectionName: string): ArrayBuffer[]; + static exports(module: Module): ModuleExportDescriptor[]; + static imports(module: Module): ModuleImportDescriptor[]; + } + type TableKind = "anyfunc" | "externref"; + interface TableDescriptor { + element: TableKind; + initial: number; + maximum?: number; + } + class Table { + constructor(descriptor: TableDescriptor, value?: any); + readonly length: number; + get(index: number): any; + grow(delta: number, value?: any): number; + set(index: number, value?: any): void; + } + function instantiate(module: Module, imports?: Imports): Promise; + function validate(bytes: BufferSource): boolean; +} +/** + * This ServiceWorker API interface represents the global execution context of a service worker. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope) + */ +interface ServiceWorkerGlobalScope extends WorkerGlobalScope { + DOMException: typeof DOMException; + WorkerGlobalScope: typeof WorkerGlobalScope; + btoa(data: string): string; + atob(data: string): string; + setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; + setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearTimeout(timeoutId: number | null): void; + setInterval(callback: (...args: any[]) => void, msDelay?: number): number; + setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearInterval(timeoutId: number | null): void; + queueMicrotask(task: Function): void; + structuredClone(value: T, options?: StructuredSerializeOptions): T; + reportError(error: any): void; + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + self: ServiceWorkerGlobalScope; + crypto: Crypto; + caches: CacheStorage; + scheduler: Scheduler; + performance: Performance; + Cloudflare: Cloudflare; + readonly origin: string; + Event: typeof Event; + ExtendableEvent: typeof ExtendableEvent; + CustomEvent: typeof CustomEvent; + PromiseRejectionEvent: typeof PromiseRejectionEvent; + FetchEvent: typeof FetchEvent; + TailEvent: typeof TailEvent; + TraceEvent: typeof TailEvent; + ScheduledEvent: typeof ScheduledEvent; + MessageEvent: typeof MessageEvent; + CloseEvent: typeof CloseEvent; + ReadableStreamDefaultReader: typeof ReadableStreamDefaultReader; + ReadableStreamBYOBReader: typeof ReadableStreamBYOBReader; + ReadableStream: typeof ReadableStream; + WritableStream: typeof WritableStream; + WritableStreamDefaultWriter: typeof WritableStreamDefaultWriter; + TransformStream: typeof TransformStream; + ByteLengthQueuingStrategy: typeof ByteLengthQueuingStrategy; + CountQueuingStrategy: typeof CountQueuingStrategy; + ErrorEvent: typeof ErrorEvent; + EventSource: typeof EventSource; + ReadableStreamBYOBRequest: typeof ReadableStreamBYOBRequest; + ReadableStreamDefaultController: typeof ReadableStreamDefaultController; + ReadableByteStreamController: typeof ReadableByteStreamController; + WritableStreamDefaultController: typeof WritableStreamDefaultController; + TransformStreamDefaultController: typeof TransformStreamDefaultController; + CompressionStream: typeof CompressionStream; + DecompressionStream: typeof DecompressionStream; + TextEncoderStream: typeof TextEncoderStream; + TextDecoderStream: typeof TextDecoderStream; + Headers: typeof Headers; + Body: typeof Body; + Request: typeof Request; + Response: typeof Response; + WebSocket: typeof WebSocket; + WebSocketPair: typeof WebSocketPair; + WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair; + AbortController: typeof AbortController; + AbortSignal: typeof AbortSignal; + TextDecoder: typeof TextDecoder; + TextEncoder: typeof TextEncoder; + navigator: Navigator; + Navigator: typeof Navigator; + URL: typeof URL; + URLSearchParams: typeof URLSearchParams; + URLPattern: typeof URLPattern; + Blob: typeof Blob; + File: typeof File; + FormData: typeof FormData; + Crypto: typeof Crypto; + SubtleCrypto: typeof SubtleCrypto; + CryptoKey: typeof CryptoKey; + CacheStorage: typeof CacheStorage; + Cache: typeof Cache; + FixedLengthStream: typeof FixedLengthStream; + IdentityTransformStream: typeof IdentityTransformStream; + HTMLRewriter: typeof HTMLRewriter; +} +declare function addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; +declare function removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; +/** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ +declare function dispatchEvent(event: WorkerGlobalScopeEventMap[keyof WorkerGlobalScopeEventMap]): boolean; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/btoa) */ +declare function btoa(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/atob) */ +declare function atob(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearTimeout) */ +declare function clearTimeout(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearInterval) */ +declare function clearInterval(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/queueMicrotask) */ +declare function queueMicrotask(task: Function): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +declare function structuredClone(value: T, options?: StructuredSerializeOptions): T; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/reportError) */ +declare function reportError(error: any): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/fetch) */ +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +declare const self: ServiceWorkerGlobalScope; +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare const crypto: Crypto; +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare const caches: CacheStorage; +declare const scheduler: Scheduler; +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +declare const performance: Performance; +declare const Cloudflare: Cloudflare; +declare const origin: string; +declare const navigator: Navigator; +interface TestController { +} +interface ExecutionContext { + waitUntil(promise: Promise): void; + passThroughOnException(): void; + props: any; +} +type ExportedHandlerFetchHandler = (request: Request>, env: Env, ctx: ExecutionContext) => Response | Promise; +type ExportedHandlerTailHandler = (events: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTraceHandler = (traces: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTailStreamHandler = (event: TailStream.TailEvent, env: Env, ctx: ExecutionContext) => TailStream.TailEventHandlerType | Promise; +type ExportedHandlerScheduledHandler = (controller: ScheduledController, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerQueueHandler = (batch: MessageBatch, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTestHandler = (controller: TestController, env: Env, ctx: ExecutionContext) => void | Promise; +interface ExportedHandler { + fetch?: ExportedHandlerFetchHandler; + tail?: ExportedHandlerTailHandler; + trace?: ExportedHandlerTraceHandler; + tailStream?: ExportedHandlerTailStreamHandler; + scheduled?: ExportedHandlerScheduledHandler; + test?: ExportedHandlerTestHandler; + email?: EmailExportedHandler; + queue?: ExportedHandlerQueueHandler; +} +interface StructuredSerializeOptions { + transfer?: any[]; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent) */ +declare abstract class PromiseRejectionEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/promise) */ + readonly promise: Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/reason) */ + readonly reason: any; +} +declare abstract class Navigator { + sendBeacon(url: string, body?: (ReadableStream | string | (ArrayBuffer | ArrayBufferView) | Blob | FormData | URLSearchParams | URLSearchParams)): boolean; + readonly userAgent: string; + readonly hardwareConcurrency: number; +} +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +interface Performance { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */ + readonly timeOrigin: number; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancenow) */ + now(): number; +} +interface AlarmInvocationInfo { + readonly isRetry: boolean; + readonly retryCount: number; +} +interface Cloudflare { + readonly compatibilityFlags: Record; +} +interface DurableObject { + fetch(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; +} +type DurableObjectStub = Fetcher & { + readonly id: DurableObjectId; + readonly name?: string; +}; +interface DurableObjectId { + toString(): string; + equals(other: DurableObjectId): boolean; + readonly name?: string; +} +interface DurableObjectNamespace { + newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions): DurableObjectId; + idFromName(name: string): DurableObjectId; + idFromString(id: string): DurableObjectId; + get(id: DurableObjectId, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub; + jurisdiction(jurisdiction: DurableObjectJurisdiction): DurableObjectNamespace; +} +type DurableObjectJurisdiction = "eu" | "fedramp"; +interface DurableObjectNamespaceNewUniqueIdOptions { + jurisdiction?: DurableObjectJurisdiction; +} +type DurableObjectLocationHint = "wnam" | "enam" | "sam" | "weur" | "eeur" | "apac" | "oc" | "afr" | "me"; +interface DurableObjectNamespaceGetDurableObjectOptions { + locationHint?: DurableObjectLocationHint; +} +interface DurableObjectState { + waitUntil(promise: Promise): void; + readonly id: DurableObjectId; + readonly storage: DurableObjectStorage; + container?: Container; + blockConcurrencyWhile(callback: () => Promise): Promise; + acceptWebSocket(ws: WebSocket, tags?: string[]): void; + getWebSockets(tag?: string): WebSocket[]; + setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void; + getWebSocketAutoResponse(): WebSocketRequestResponsePair | null; + getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null; + setHibernatableWebSocketEventTimeout(timeoutMs?: number): void; + getHibernatableWebSocketEventTimeout(): number | null; + getTags(ws: WebSocket): string[]; + abort(reason?: string): void; +} +interface DurableObjectTransaction { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + rollback(): void; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; +} +interface DurableObjectStorage { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + deleteAll(options?: DurableObjectPutOptions): Promise; + transaction(closure: (txn: DurableObjectTransaction) => Promise): Promise; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; + sync(): Promise; + sql: SqlStorage; + transactionSync(closure: () => T): T; + getCurrentBookmark(): Promise; + getBookmarkForTime(timestamp: number | Date): Promise; + onNextSessionRestoreBookmark(bookmark: string): Promise; +} +interface DurableObjectListOptions { + start?: string; + startAfter?: string; + end?: string; + prefix?: string; + reverse?: boolean; + limit?: number; + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetOptions { + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetAlarmOptions { + allowConcurrency?: boolean; +} +interface DurableObjectPutOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; + noCache?: boolean; +} +interface DurableObjectSetAlarmOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; +} +declare class WebSocketRequestResponsePair { + constructor(request: string, response: string); + get request(): string; + get response(): string; +} +interface AnalyticsEngineDataset { + writeDataPoint(event?: AnalyticsEngineDataPoint): void; +} +interface AnalyticsEngineDataPoint { + indexes?: ((ArrayBuffer | string) | null)[]; + doubles?: number[]; + blobs?: ((ArrayBuffer | string) | null)[]; +} +/** + * An event which takes place in the DOM. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event) + */ +declare class Event { + constructor(type: string, init?: EventInit); + /** + * Returns the type of event, e.g. "click", "hashchange", or "submit". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type) + */ + get type(): string; + /** + * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase) + */ + get eventPhase(): number; + /** + * Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed) + */ + get composed(): boolean; + /** + * Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles) + */ + get bubbles(): boolean; + /** + * Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable) + */ + get cancelable(): boolean; + /** + * Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented) + */ + get defaultPrevented(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue) + */ + get returnValue(): boolean; + /** + * Returns the object whose event listener's callback is currently being invoked. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget) + */ + get currentTarget(): EventTarget | undefined; + /** + * Returns the object to which event is dispatched (its target). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target) + */ + get target(): EventTarget | undefined; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement) + */ + get srcElement(): EventTarget | undefined; + /** + * Returns the event's timestamp as the number of milliseconds measured relative to the time origin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp) + */ + get timeStamp(): number; + /** + * Returns true if event was dispatched by the user agent, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted) + */ + get isTrusted(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + get cancelBubble(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + set cancelBubble(value: boolean); + /** + * Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation) + */ + stopImmediatePropagation(): void; + /** + * If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) + */ + preventDefault(): void; + /** + * When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) + */ + stopPropagation(): void; + /** + * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath) + */ + composedPath(): EventTarget[]; + static readonly NONE: number; + static readonly CAPTURING_PHASE: number; + static readonly AT_TARGET: number; + static readonly BUBBLING_PHASE: number; +} +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} +type EventListener = (event: EventType) => void; +interface EventListenerObject { + handleEvent(event: EventType): void; +} +type EventListenerOrEventListenerObject = EventListener | EventListenerObject; +/** + * EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget) + */ +declare class EventTarget = Record> { + constructor(); + /** + * Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. + * + * The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. + * + * When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. + * + * When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. + * + * When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. + * + * If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. + * + * The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) + */ + addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; + /** + * Removes the event listener in target's event listener list with the same type, callback, and options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener) + */ + removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; + /** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ + dispatchEvent(event: EventMap[keyof EventMap]): boolean; +} +interface EventTargetEventListenerOptions { + capture?: boolean; +} +interface EventTargetAddEventListenerOptions { + capture?: boolean; + passive?: boolean; + once?: boolean; + signal?: AbortSignal; +} +interface EventTargetHandlerObject { + handleEvent: (event: Event) => any | undefined; +} +/** + * A controller object that allows you to abort one or more DOM requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +declare class AbortController { + constructor(); + /** + * Returns the AbortSignal object associated with this object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + get signal(): AbortSignal; + /** + * Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} +/** + * A signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +declare abstract class AbortSignal extends EventTarget { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) */ + static abort(reason?: any): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) */ + static timeout(delay: number): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) */ + static any(signals: AbortSignal[]): AbortSignal; + /** + * Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + get aborted(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) */ + get reason(): any; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + get onabort(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + set onabort(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) */ + throwIfAborted(): void; +} +interface Scheduler { + wait(delay: number, maybeOptions?: SchedulerWaitOptions): Promise; +} +interface SchedulerWaitOptions { + signal?: AbortSignal; +} +/** + * Extends the lifetime of the install and activate events dispatched on the global scope as part of the service worker lifecycle. This ensures that any functional events (like FetchEvent) are not dispatched until it upgrades database schemas and deletes the outdated cache entries. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent) + */ +declare abstract class ExtendableEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) */ + waitUntil(promise: Promise): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent) */ +declare class CustomEvent extends Event { + constructor(type: string, init?: CustomEventCustomEventInit); + /** + * Returns any custom data event was created with. Typically used for synthetic events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail) + */ + get detail(): T; +} +interface CustomEventCustomEventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; + detail?: any; +} +/** + * A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob) + */ +declare class Blob { + constructor(type?: ((ArrayBuffer | ArrayBufferView) | string | Blob)[], options?: BlobOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + get size(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + get type(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number, type?: string): Blob; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/stream) */ + stream(): ReadableStream; +} +interface BlobOptions { + type?: string; +} +/** + * Provides information about files and allows JavaScript in a web page to access their content. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File) + */ +declare class File extends Blob { + constructor(bits: ((ArrayBuffer | ArrayBufferView) | string | Blob)[] | undefined, name: string, options?: FileOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + get name(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + get lastModified(): number; +} +interface FileOptions { + type?: string; + lastModified?: number; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class CacheStorage { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CacheStorage/open) */ + open(cacheName: string): Promise; + readonly default: Cache; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class Cache { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */ + delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#match) */ + match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#put) */ + put(request: RequestInfo | URL, response: Response): Promise; +} +interface CacheQueryOptions { + ignoreMethod?: boolean; +} +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare abstract class Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + get subtle(): SubtleCrypto; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(buffer: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): string; + DigestStream: typeof DigestStream; +} +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +declare abstract class SubtleCrypto { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, plainText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, cipherText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, signature: ArrayBuffer | ArrayBufferView, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: string | SubtleCryptoHashAlgorithm, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: string | SubtleCryptoGenerateKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, derivedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, length?: number | null): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: string, keyData: (ArrayBuffer | ArrayBufferView) | JsonWebKey, algorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: string, key: CryptoKey): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: string, wrappedKey: ArrayBuffer | ArrayBufferView, unwrappingKey: CryptoKey, unwrapAlgorithm: string | SubtleCryptoEncryptAlgorithm, unwrappedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + timingSafeEqual(a: ArrayBuffer | ArrayBufferView, b: ArrayBuffer | ArrayBufferView): boolean; +} +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +declare abstract class CryptoKey { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: CryptoKeyKeyAlgorithm | CryptoKeyAesKeyAlgorithm | CryptoKeyHmacKeyAlgorithm | CryptoKeyRsaKeyAlgorithm | CryptoKeyEllipticKeyAlgorithm | CryptoKeyArbitraryKeyAlgorithm; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: string[]; +} +interface CryptoKeyPair { + publicKey: CryptoKey; + privateKey: CryptoKey; +} +interface JsonWebKey { + kty: string; + use?: string; + key_ops?: string[]; + alg?: string; + ext?: boolean; + crv?: string; + x?: string; + y?: string; + d?: string; + n?: string; + e?: string; + p?: string; + q?: string; + dp?: string; + dq?: string; + qi?: string; + oth?: RsaOtherPrimesInfo[]; + k?: string; +} +interface RsaOtherPrimesInfo { + r?: string; + d?: string; + t?: string; +} +interface SubtleCryptoDeriveKeyAlgorithm { + name: string; + salt?: (ArrayBuffer | ArrayBufferView); + iterations?: number; + hash?: (string | SubtleCryptoHashAlgorithm); + $public?: CryptoKey; + info?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoEncryptAlgorithm { + name: string; + iv?: (ArrayBuffer | ArrayBufferView); + additionalData?: (ArrayBuffer | ArrayBufferView); + tagLength?: number; + counter?: (ArrayBuffer | ArrayBufferView); + length?: number; + label?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoGenerateKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + modulusLength?: number; + publicExponent?: (ArrayBuffer | ArrayBufferView); + length?: number; + namedCurve?: string; +} +interface SubtleCryptoHashAlgorithm { + name: string; +} +interface SubtleCryptoImportKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + length?: number; + namedCurve?: string; + compressed?: boolean; +} +interface SubtleCryptoSignAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + dataLength?: number; + saltLength?: number; +} +interface CryptoKeyKeyAlgorithm { + name: string; +} +interface CryptoKeyAesKeyAlgorithm { + name: string; + length: number; +} +interface CryptoKeyHmacKeyAlgorithm { + name: string; + hash: CryptoKeyKeyAlgorithm; + length: number; +} +interface CryptoKeyRsaKeyAlgorithm { + name: string; + modulusLength: number; + publicExponent: ArrayBuffer | ArrayBufferView; + hash?: CryptoKeyKeyAlgorithm; +} +interface CryptoKeyEllipticKeyAlgorithm { + name: string; + namedCurve: string; +} +interface CryptoKeyArbitraryKeyAlgorithm { + name: string; + hash?: CryptoKeyKeyAlgorithm; + namedCurve?: string; + length?: number; +} +declare class DigestStream extends WritableStream { + constructor(algorithm: string | SubtleCryptoHashAlgorithm); + readonly digest: Promise; + get bytesWritten(): number | bigint; +} +/** + * A decoder for a specific method, that is a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261, gbk, etc. A decoder takes a stream of bytes as input and emits a stream of code points. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder) + */ +declare class TextDecoder { + constructor(label?: string, options?: TextDecoderConstructorOptions); + /** + * Returns the result of running encoding's decoder. The method can be invoked zero or more times with options's stream set to true, and then once without options's stream (or set to false), to process a fragmented input. If the invocation without options's stream (or set to false) has no input, it's clearest to omit both arguments. + * + * ``` + * var string = "", decoder = new TextDecoder(encoding), buffer; + * while(buffer = next_chunk()) { + * string += decoder.decode(buffer, {stream:true}); + * } + * string += decoder.decode(); // end-of-queue + * ``` + * + * If the error mode is "fatal" and encoding's decoder returns error, throws a TypeError. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode) + */ + decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string; + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +/** + * TextEncoder takes a stream of code points as input and emits a stream of bytes. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder) + */ +declare class TextEncoder { + constructor(); + /** + * Returns the result of running UTF-8's encoder. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encode) + */ + encode(input?: string): Uint8Array; + /** + * Runs the UTF-8 encoder on source, stores the result of that operation into destination, and returns the progress made as an object wherein read is the number of converted code units of source and written is the number of bytes modified in destination. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encodeInto) + */ + encodeInto(input: string, buffer: ArrayBuffer | ArrayBufferView): TextEncoderEncodeIntoResult; + get encoding(): string; +} +interface TextDecoderConstructorOptions { + fatal: boolean; + ignoreBOM: boolean; +} +interface TextDecoderDecodeOptions { + stream: boolean; +} +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} +/** + * Events providing information related to errors in scripts or in files. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent) + */ +declare class ErrorEvent extends Event { + constructor(type: string, init?: ErrorEventErrorEventInit); + get filename(): string; + get message(): string; + get lineno(): number; + get colno(): number; + get error(): any; +} +interface ErrorEventErrorEventInit { + message?: string; + filename?: string; + lineno?: number; + colno?: number; + error?: any; +} +/** + * Provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData) + */ +declare class FormData { + constructor(); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: Blob, filename?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/delete) */ + delete(name: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get) */ + get(name: string): (File | string) | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/getAll) */ + getAll(name: string): (File | string)[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: Blob, filename?: string): void; + /* Returns an array of key, value pairs for every entry in the list. */ + entries(): IterableIterator<[ + key: string, + value: File | string + ]>; + /* Returns a list of keys in the list. */ + keys(): IterableIterator; + /* Returns a list of values in the list. */ + values(): IterableIterator<(File | string)>; + forEach(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: File | string + ]>; +} +interface ContentOptions { + html?: boolean; +} +declare class HTMLRewriter { + constructor(); + on(selector: string, handlers: HTMLRewriterElementContentHandlers): HTMLRewriter; + onDocument(handlers: HTMLRewriterDocumentContentHandlers): HTMLRewriter; + transform(response: Response): Response; +} +interface HTMLRewriterElementContentHandlers { + element?(element: Element): void | Promise; + comments?(comment: Comment): void | Promise; + text?(element: Text): void | Promise; +} +interface HTMLRewriterDocumentContentHandlers { + doctype?(doctype: Doctype): void | Promise; + comments?(comment: Comment): void | Promise; + text?(text: Text): void | Promise; + end?(end: DocumentEnd): void | Promise; +} +interface Doctype { + readonly name: string | null; + readonly publicId: string | null; + readonly systemId: string | null; +} +interface Element { + tagName: string; + readonly attributes: IterableIterator; + readonly removed: boolean; + readonly namespaceURI: string; + getAttribute(name: string): string | null; + hasAttribute(name: string): boolean; + setAttribute(name: string, value: string): Element; + removeAttribute(name: string): Element; + before(content: string | ReadableStream | Response, options?: ContentOptions): Element; + after(content: string | ReadableStream | Response, options?: ContentOptions): Element; + prepend(content: string | ReadableStream | Response, options?: ContentOptions): Element; + append(content: string | ReadableStream | Response, options?: ContentOptions): Element; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Element; + remove(): Element; + removeAndKeepContent(): Element; + setInnerContent(content: string | ReadableStream | Response, options?: ContentOptions): Element; + onEndTag(handler: (tag: EndTag) => void | Promise): void; +} +interface EndTag { + name: string; + before(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + after(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + remove(): EndTag; +} +interface Comment { + text: string; + readonly removed: boolean; + before(content: string, options?: ContentOptions): Comment; + after(content: string, options?: ContentOptions): Comment; + replace(content: string, options?: ContentOptions): Comment; + remove(): Comment; +} +interface Text { + readonly text: string; + readonly lastInTextNode: boolean; + readonly removed: boolean; + before(content: string | ReadableStream | Response, options?: ContentOptions): Text; + after(content: string | ReadableStream | Response, options?: ContentOptions): Text; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Text; + remove(): Text; +} +interface DocumentEnd { + append(content: string, options?: ContentOptions): DocumentEnd; +} +/** + * This is the event type for fetch events dispatched on the service worker global scope. It contains information about the fetch, including the request and how the receiver will treat the response. It provides the event.respondWith() method, which allows us to provide a response to this fetch. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent) + */ +declare abstract class FetchEvent extends ExtendableEvent { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/request) */ + readonly request: Request; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith) */ + respondWith(promise: Response | Promise): void; + passThroughOnException(): void; +} +type HeadersInit = Headers | Iterable> | Record; +/** + * This Fetch API interface allows you to perform various actions on HTTP request and response headers. These actions include retrieving, setting, adding to, and removing. A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.  You can add to this using methods like append() (see Examples.) In all methods of this interface, header names are matched by case-insensitive byte sequence. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers) + */ +declare class Headers { + constructor(init?: HeadersInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get) */ + get(name: string): string | null; + getAll(name: string): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie) */ + getSetCookie(): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete) */ + delete(name: string): void; + forEach(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void; + /* Returns an iterator allowing to go through all key/value pairs contained in this object. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */ + keys(): IterableIterator; + /* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */ + values(): IterableIterator; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +type BodyInit = ReadableStream | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData; +declare abstract class Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */ + get body(): ReadableStream | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + get bodyUsed(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ + json(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ + formData(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob(): Promise; +} +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +declare var Response: { + prototype: Response; + new (body?: BodyInit | null, init?: ResponseInit): Response; + error(): Response; + redirect(url: string, status?: number): Response; + json(any: any, maybeInit?: (ResponseInit | Response)): Response; +}; +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +interface Response extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/clone) */ + clone(): Response; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status) */ + status: number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText) */ + statusText: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/headers) */ + headers: Headers; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok) */ + ok: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected) */ + redirected: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url) */ + url: string; + webSocket: WebSocket | null; + cf: any | undefined; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/type) */ + type: "default" | "error"; +} +interface ResponseInit { + status?: number; + statusText?: string; + headers?: HeadersInit; + cf?: any; + webSocket?: (WebSocket | null); + encodeBody?: "automatic" | "manual"; +} +type RequestInfo> = Request | string; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +declare var Request: { + prototype: Request; + new >(input: RequestInfo | URL, init?: RequestInit): Request; +}; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +interface Request> extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/clone) */ + clone(): Request; + /** + * Returns request's HTTP method, which is "GET" by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method) + */ + method: string; + /** + * Returns the URL of request as a string. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url) + */ + url: string; + /** + * Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/headers) + */ + headers: Headers; + /** + * Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/redirect) + */ + redirect: string; + fetcher: Fetcher | null; + /** + * Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/signal) + */ + signal: AbortSignal; + cf: Cf | undefined; + /** + * Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI] + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity) + */ + integrity: string; + /* Returns a boolean indicating whether or not request can outlive the global in which it was created. */ + keepalive: boolean; + /** + * Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/cache) + */ + cache?: "no-store"; +} +interface RequestInit { + /* A string to set request's method. */ + method?: string; + /* A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /* A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: string; + fetcher?: (Fetcher | null); + cf?: Cf; + /* A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: "no-store"; + /* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /* An AbortSignal to set request's signal. */ + signal?: (AbortSignal | null); + encodeResponseBody?: "automatic" | "manual"; +} +type Service = Fetcher; +type Fetcher = (T extends Rpc.EntrypointBranded ? Rpc.Provider : unknown) & { + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + connect(address: SocketAddress | string, options?: SocketOptions): Socket; +}; +interface KVNamespaceListKey { + name: Key; + expiration?: number; + metadata?: Metadata; +} +type KVNamespaceListResult = { + list_complete: false; + keys: KVNamespaceListKey[]; + cursor: string; + cacheStatus: string | null; +} | { + list_complete: true; + keys: KVNamespaceListKey[]; + cacheStatus: string | null; +}; +interface KVNamespace { + get(key: Key, options?: Partial>): Promise; + get(key: Key, type: "text"): Promise; + get(key: Key, type: "json"): Promise; + get(key: Key, type: "arrayBuffer"): Promise; + get(key: Key, type: "stream"): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"text">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"json">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"arrayBuffer">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"stream">): Promise; + get(key: Array, type: "text"): Promise>; + get(key: Array, type: "json"): Promise>; + get(key: Array, options?: Partial>): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>; + list(options?: KVNamespaceListOptions): Promise>; + put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise; + getWithMetadata(key: Key, options?: Partial>): Promise>; + getWithMetadata(key: Key, type: "text"): Promise>; + getWithMetadata(key: Key, type: "json"): Promise>; + getWithMetadata(key: Key, type: "arrayBuffer"): Promise>; + getWithMetadata(key: Key, type: "stream"): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"text">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"json">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"arrayBuffer">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"stream">): Promise>; + getWithMetadata(key: Array, type: "text"): Promise>>; + getWithMetadata(key: Array, type: "json"): Promise>>; + getWithMetadata(key: Array, options?: Partial>): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>>; + delete(key: Key): Promise; +} +interface KVNamespaceListOptions { + limit?: number; + prefix?: (string | null); + cursor?: (string | null); +} +interface KVNamespaceGetOptions { + type: Type; + cacheTtl?: number; +} +interface KVNamespacePutOptions { + expiration?: number; + expirationTtl?: number; + metadata?: (any | null); +} +interface KVNamespaceGetWithMetadataResult { + value: Value | null; + metadata: Metadata | null; + cacheStatus: string | null; +} +type QueueContentType = "text" | "bytes" | "json" | "v8"; +interface Queue { + send(message: Body, options?: QueueSendOptions): Promise; + sendBatch(messages: Iterable>, options?: QueueSendBatchOptions): Promise; +} +interface QueueSendOptions { + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueSendBatchOptions { + delaySeconds?: number; +} +interface MessageSendRequest { + body: Body; + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueRetryOptions { + delaySeconds?: number; +} +interface Message { + readonly id: string; + readonly timestamp: Date; + readonly body: Body; + readonly attempts: number; + retry(options?: QueueRetryOptions): void; + ack(): void; +} +interface QueueEvent extends ExtendableEvent { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface MessageBatch { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface R2Error extends Error { + readonly name: string; + readonly code: number; + readonly message: string; + readonly action: string; + readonly stack: any; +} +interface R2ListOptions { + limit?: number; + prefix?: string; + cursor?: string; + delimiter?: string; + startAfter?: string; + include?: ("httpMetadata" | "customMetadata")[]; +} +declare abstract class R2Bucket { + head(key: string): Promise; + get(key: string, options: R2GetOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + get(key: string, options?: R2GetOptions): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise; + createMultipartUpload(key: string, options?: R2MultipartOptions): Promise; + resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload; + delete(keys: string | string[]): Promise; + list(options?: R2ListOptions): Promise; +} +interface R2MultipartUpload { + readonly key: string; + readonly uploadId: string; + uploadPart(partNumber: number, value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, options?: R2UploadPartOptions): Promise; + abort(): Promise; + complete(uploadedParts: R2UploadedPart[]): Promise; +} +interface R2UploadedPart { + partNumber: number; + etag: string; +} +declare abstract class R2Object { + readonly key: string; + readonly version: string; + readonly size: number; + readonly etag: string; + readonly httpEtag: string; + readonly checksums: R2Checksums; + readonly uploaded: Date; + readonly httpMetadata?: R2HTTPMetadata; + readonly customMetadata?: Record; + readonly range?: R2Range; + readonly storageClass: string; + readonly ssecKeyMd5?: string; + writeHttpMetadata(headers: Headers): void; +} +interface R2ObjectBody extends R2Object { + get body(): ReadableStream; + get bodyUsed(): boolean; + arrayBuffer(): Promise; + text(): Promise; + json(): Promise; + blob(): Promise; +} +type R2Range = { + offset: number; + length?: number; +} | { + offset?: number; + length: number; +} | { + suffix: number; +}; +interface R2Conditional { + etagMatches?: string; + etagDoesNotMatch?: string; + uploadedBefore?: Date; + uploadedAfter?: Date; + secondsGranularity?: boolean; +} +interface R2GetOptions { + onlyIf?: (R2Conditional | Headers); + range?: (R2Range | Headers); + ssecKey?: (ArrayBuffer | string); +} +interface R2PutOptions { + onlyIf?: (R2Conditional | Headers); + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + md5?: (ArrayBuffer | string); + sha1?: (ArrayBuffer | string); + sha256?: (ArrayBuffer | string); + sha384?: (ArrayBuffer | string); + sha512?: (ArrayBuffer | string); + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2MultipartOptions { + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2Checksums { + readonly md5?: ArrayBuffer; + readonly sha1?: ArrayBuffer; + readonly sha256?: ArrayBuffer; + readonly sha384?: ArrayBuffer; + readonly sha512?: ArrayBuffer; + toJSON(): R2StringChecksums; +} +interface R2StringChecksums { + md5?: string; + sha1?: string; + sha256?: string; + sha384?: string; + sha512?: string; +} +interface R2HTTPMetadata { + contentType?: string; + contentLanguage?: string; + contentDisposition?: string; + contentEncoding?: string; + cacheControl?: string; + cacheExpiry?: Date; +} +type R2Objects = { + objects: R2Object[]; + delimitedPrefixes: string[]; +} & ({ + truncated: true; + cursor: string; +} | { + truncated: false; +}); +interface R2UploadPartOptions { + ssecKey?: (ArrayBuffer | string); +} +declare abstract class ScheduledEvent extends ExtendableEvent { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface ScheduledController { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface QueuingStrategy { + highWaterMark?: (number | bigint); + size?: (chunk: T) => number | bigint; +} +interface UnderlyingSink { + type?: string; + start?: (controller: WritableStreamDefaultController) => void | Promise; + write?: (chunk: W, controller: WritableStreamDefaultController) => void | Promise; + abort?: (reason: any) => void | Promise; + close?: () => void | Promise; +} +interface UnderlyingByteSource { + type: "bytes"; + autoAllocateChunkSize?: number; + start?: (controller: ReadableByteStreamController) => void | Promise; + pull?: (controller: ReadableByteStreamController) => void | Promise; + cancel?: (reason: any) => void | Promise; +} +interface UnderlyingSource { + type?: "" | undefined; + start?: (controller: ReadableStreamDefaultController) => void | Promise; + pull?: (controller: ReadableStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: (number | bigint); +} +interface Transformer { + readableType?: string; + writableType?: string; + start?: (controller: TransformStreamDefaultController) => void | Promise; + transform?: (chunk: I, controller: TransformStreamDefaultController) => void | Promise; + flush?: (controller: TransformStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: number; +} +interface StreamPipeOptions { + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + preventAbort?: boolean; + preventCancel?: boolean; + signal?: AbortSignal; +} +type ReadableStreamReadResult = { + done: false; + value: R; +} | { + done: true; + value?: undefined; +}; +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +interface ReadableStream { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel) */ + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(): ReadableStreamDefaultReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(options: ReadableStreamGetReaderOptions): ReadableStreamBYOBReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough) */ + pipeThrough(transform: ReadableWritablePair, options?: StreamPipeOptions): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo) */ + pipeTo(destination: WritableStream, options?: StreamPipeOptions): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee) */ + tee(): [ + ReadableStream, + ReadableStream + ]; + values(options?: ReadableStreamValuesOptions): AsyncIterableIterator; + [Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator; +} +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +declare const ReadableStream: { + prototype: ReadableStream; + new (underlyingSource: UnderlyingByteSource, strategy?: QueuingStrategy): ReadableStream; + new (underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; +}; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader) */ +declare class ReadableStreamDefaultReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read) */ + read(): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader) */ +declare class ReadableStreamBYOBReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read) */ + read(view: T): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock) */ + releaseLock(): void; + readAtLeast(minElements: number, view: T): Promise>; +} +interface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions { + min?: number; +} +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode: "byob"; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest) */ +declare abstract class ReadableStreamBYOBRequest { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/view) */ + get view(): Uint8Array | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respond) */ + respond(bytesWritten: number): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respondWithNewView) */ + respondWithNewView(view: ArrayBuffer | ArrayBufferView): void; + get atLeast(): number | null; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController) */ +declare abstract class ReadableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/enqueue) */ + enqueue(chunk?: R): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/error) */ + error(reason: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController) */ +declare abstract class ReadableByteStreamController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/byobRequest) */ + get byobRequest(): ReadableStreamBYOBRequest | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/enqueue) */ + enqueue(chunk: ArrayBuffer | ArrayBufferView): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/error) */ + error(reason: any): void; +} +/** + * This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController) + */ +declare abstract class WritableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/signal) */ + get signal(): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/error) */ + error(reason?: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController) */ +declare abstract class TransformStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/enqueue) */ + enqueue(chunk?: O): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/error) */ + error(reason: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/terminate) */ + terminate(): void; +} +interface ReadableWritablePair { + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; + readable: ReadableStream; +} +/** + * This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream) + */ +declare class WritableStream { + constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter) */ + getWriter(): WritableStreamDefaultWriter; +} +/** + * This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter) + */ +declare class WritableStreamDefaultWriter { + constructor(stream: WritableStream); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/closed) */ + get closed(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/ready) */ + get ready(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/write) */ + write(chunk?: W): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream) */ +declare class TransformStream { + constructor(transformer?: Transformer, writableStrategy?: QueuingStrategy, readableStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/readable) */ + get readable(): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/writable) */ + get writable(): WritableStream; +} +declare class FixedLengthStream extends IdentityTransformStream { + constructor(expectedLength: number | bigint, queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +declare class IdentityTransformStream extends TransformStream { + constructor(queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +interface IdentityTransformStreamQueuingStrategy { + highWaterMark?: (number | bigint); +} +interface ReadableStreamValuesOptions { + preventCancel?: boolean; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CompressionStream) */ +declare class CompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DecompressionStream) */ +declare class DecompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoderStream) */ +declare class TextEncoderStream extends TransformStream { + constructor(); + get encoding(): string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoderStream) */ +declare class TextDecoderStream extends TransformStream { + constructor(label?: string, options?: TextDecoderStreamTextDecoderStreamInit); + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +interface TextDecoderStreamTextDecoderStreamInit { + fatal?: boolean; + ignoreBOM?: boolean; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy) + */ +declare class ByteLengthQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy) + */ +declare class CountQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} +interface ScriptVersion { + id?: string; + tag?: string; + message?: string; +} +declare abstract class TailEvent extends ExtendableEvent { + readonly events: TraceItem[]; + readonly traces: TraceItem[]; +} +interface TraceItem { + readonly event: (TraceItemFetchEventInfo | TraceItemJsRpcEventInfo | TraceItemScheduledEventInfo | TraceItemAlarmEventInfo | TraceItemQueueEventInfo | TraceItemEmailEventInfo | TraceItemTailEventInfo | TraceItemCustomEventInfo | TraceItemHibernatableWebSocketEventInfo) | null; + readonly eventTimestamp: number | null; + readonly logs: TraceLog[]; + readonly exceptions: TraceException[]; + readonly diagnosticsChannelEvents: TraceDiagnosticChannelEvent[]; + readonly scriptName: string | null; + readonly entrypoint?: string; + readonly scriptVersion?: ScriptVersion; + readonly dispatchNamespace?: string; + readonly scriptTags?: string[]; + readonly outcome: string; + readonly executionModel: string; + readonly truncated: boolean; + readonly cpuTime: number; + readonly wallTime: number; +} +interface TraceItemAlarmEventInfo { + readonly scheduledTime: Date; +} +interface TraceItemCustomEventInfo { +} +interface TraceItemScheduledEventInfo { + readonly scheduledTime: number; + readonly cron: string; +} +interface TraceItemQueueEventInfo { + readonly queue: string; + readonly batchSize: number; +} +interface TraceItemEmailEventInfo { + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; +} +interface TraceItemTailEventInfo { + readonly consumedEvents: TraceItemTailEventInfoTailItem[]; +} +interface TraceItemTailEventInfoTailItem { + readonly scriptName: string | null; +} +interface TraceItemFetchEventInfo { + readonly response?: TraceItemFetchEventInfoResponse; + readonly request: TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoRequest { + readonly cf?: any; + readonly headers: Record; + readonly method: string; + readonly url: string; + getUnredacted(): TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoResponse { + readonly status: number; +} +interface TraceItemJsRpcEventInfo { + readonly rpcMethod: string; +} +interface TraceItemHibernatableWebSocketEventInfo { + readonly getWebSocketEvent: TraceItemHibernatableWebSocketEventInfoMessage | TraceItemHibernatableWebSocketEventInfoClose | TraceItemHibernatableWebSocketEventInfoError; +} +interface TraceItemHibernatableWebSocketEventInfoMessage { + readonly webSocketEventType: string; +} +interface TraceItemHibernatableWebSocketEventInfoClose { + readonly webSocketEventType: string; + readonly code: number; + readonly wasClean: boolean; +} +interface TraceItemHibernatableWebSocketEventInfoError { + readonly webSocketEventType: string; +} +interface TraceLog { + readonly timestamp: number; + readonly level: string; + readonly message: any; +} +interface TraceException { + readonly timestamp: number; + readonly message: string; + readonly name: string; + readonly stack?: string; +} +interface TraceDiagnosticChannelEvent { + readonly timestamp: number; + readonly channel: string; + readonly message: any; +} +interface TraceMetrics { + readonly cpuTime: number; + readonly wallTime: number; +} +interface UnsafeTraceMetrics { + fromTrace(item: TraceItem): TraceMetrics; +} +/** + * The URL interface represents an object providing static methods used for creating object URLs. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL) + */ +declare class URL { + constructor(url: string | URL, base?: string | URL); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/origin) */ + get origin(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + get href(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + set href(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + get protocol(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + set protocol(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + get username(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + set username(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + get password(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + set password(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + get host(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + set host(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + get hostname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + set hostname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + get port(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + set port(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + get pathname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + set pathname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + get search(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + set search(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + get hash(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + set hash(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/searchParams) */ + get searchParams(): URLSearchParams; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/toJSON) */ + toJSON(): string; + /*function toString() { [native code] }*/ + toString(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/canParse_static) */ + static canParse(url: string, base?: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/parse_static) */ + static parse(url: string, base?: string): URL | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static) */ + static createObjectURL(object: File | Blob): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static) */ + static revokeObjectURL(object_url: string): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams) */ +declare class URLSearchParams { + constructor(init?: (Iterable> | Record | string)); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/size) */ + get size(): number; + /** + * Appends a specified key/value pair as a new search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append) + */ + append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete) + */ + delete(name: string, value?: string): void; + /** + * Returns the first value associated to the given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) + */ + get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) + */ + getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has) + */ + has(name: string, value?: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set) + */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/sort) */ + sort(): void; + /* Returns an array of key, value pairs for every entry in the search params. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns a list of keys in the search params. */ + keys(): IterableIterator; + /* Returns a list of values in the search params. */ + values(): IterableIterator; + forEach(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void; + /*function toString() { [native code] } Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */ + toString(): string; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +declare class URLPattern { + constructor(input?: (string | URLPatternURLPatternInit), baseURL?: (string | URLPatternURLPatternOptions), patternOptions?: URLPatternURLPatternOptions); + get protocol(): string; + get username(): string; + get password(): string; + get hostname(): string; + get port(): string; + get pathname(): string; + get search(): string; + get hash(): string; + test(input?: (string | URLPatternURLPatternInit), baseURL?: string): boolean; + exec(input?: (string | URLPatternURLPatternInit), baseURL?: string): URLPatternURLPatternResult | null; +} +interface URLPatternURLPatternInit { + protocol?: string; + username?: string; + password?: string; + hostname?: string; + port?: string; + pathname?: string; + search?: string; + hash?: string; + baseURL?: string; +} +interface URLPatternURLPatternComponentResult { + input: string; + groups: Record; +} +interface URLPatternURLPatternResult { + inputs: (string | URLPatternURLPatternInit)[]; + protocol: URLPatternURLPatternComponentResult; + username: URLPatternURLPatternComponentResult; + password: URLPatternURLPatternComponentResult; + hostname: URLPatternURLPatternComponentResult; + port: URLPatternURLPatternComponentResult; + pathname: URLPatternURLPatternComponentResult; + search: URLPatternURLPatternComponentResult; + hash: URLPatternURLPatternComponentResult; +} +interface URLPatternURLPatternOptions { + ignoreCase?: boolean; +} +/** + * A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent) + */ +declare class CloseEvent extends Event { + constructor(type: string, initializer?: CloseEventInit); + /** + * Returns the WebSocket connection close code provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/code) + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/reason) + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/wasClean) + */ + readonly wasClean: boolean; +} +interface CloseEventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} +/** + * A message received by a target object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent) + */ +declare class MessageEvent extends Event { + constructor(type: string, initializer: MessageEventInit); + /** + * Returns the data of the message. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data) + */ + readonly data: ArrayBuffer | string; +} +interface MessageEventInit { + data: ArrayBuffer | string; +} +type WebSocketEventMap = { + close: CloseEvent; + message: MessageEvent; + open: Event; + error: ErrorEvent; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +declare var WebSocket: { + prototype: WebSocket; + new (url: string, protocols?: (string[] | string)): WebSocket; + readonly READY_STATE_CONNECTING: number; + readonly CONNECTING: number; + readonly READY_STATE_OPEN: number; + readonly OPEN: number; + readonly READY_STATE_CLOSING: number; + readonly CLOSING: number; + readonly READY_STATE_CLOSED: number; + readonly CLOSED: number; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +interface WebSocket extends EventTarget { + accept(): void; + /** + * Transmits data using the WebSocket connection. data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/send) + */ + send(message: (ArrayBuffer | ArrayBufferView) | string): void; + /** + * Closes the WebSocket connection, optionally using code as the the WebSocket connection close code and reason as the the WebSocket connection close reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/close) + */ + close(code?: number, reason?: string): void; + serializeAttachment(attachment: any): void; + deserializeAttachment(): any | null; + /** + * Returns the state of the WebSocket object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/readyState) + */ + readyState: number; + /** + * Returns the URL that was used to establish the WebSocket connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/url) + */ + url: string | null; + /** + * Returns the subprotocol selected by the server, if any. It can be used in conjunction with the array form of the constructor's second argument to perform subprotocol negotiation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/protocol) + */ + protocol: string | null; + /** + * Returns the extensions selected by the server, if any. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/extensions) + */ + extensions: string | null; +} +declare const WebSocketPair: { + new (): { + 0: WebSocket; + 1: WebSocket; + }; +}; +interface SqlStorage { + exec>(query: string, ...bindings: any[]): SqlStorageCursor; + get databaseSize(): number; + Cursor: typeof SqlStorageCursor; + Statement: typeof SqlStorageStatement; +} +declare abstract class SqlStorageStatement { +} +type SqlStorageValue = ArrayBuffer | string | number | null; +declare abstract class SqlStorageCursor> { + next(): { + done?: false; + value: T; + } | { + done: true; + value?: never; + }; + toArray(): T[]; + one(): T; + raw(): IterableIterator; + columnNames: string[]; + get rowsRead(): number; + get rowsWritten(): number; + [Symbol.iterator](): IterableIterator; +} +interface Socket { + get readable(): ReadableStream; + get writable(): WritableStream; + get closed(): Promise; + get opened(): Promise; + close(): Promise; + startTls(options?: TlsOptions): Socket; +} +interface SocketOptions { + secureTransport?: string; + allowHalfOpen: boolean; + highWaterMark?: (number | bigint); +} +interface SocketAddress { + hostname: string; + port: number; +} +interface TlsOptions { + expectedServerHostname?: string; +} +interface SocketInfo { + remoteAddress?: string; + localAddress?: string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource) */ +declare class EventSource extends EventTarget { + constructor(url: string, init?: EventSourceEventSourceInit); + /** + * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close) + */ + close(): void; + /** + * Returns the URL providing the event stream. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url) + */ + get url(): string; + /** + * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials) + */ + get withCredentials(): boolean; + /** + * Returns the state of this EventSource object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState) + */ + get readyState(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + get onopen(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + set onopen(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + get onmessage(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + set onmessage(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + get onerror(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + set onerror(value: any | null); + static readonly CONNECTING: number; + static readonly OPEN: number; + static readonly CLOSED: number; + static from(stream: ReadableStream): EventSource; +} +interface EventSourceEventSourceInit { + withCredentials?: boolean; + fetcher?: Fetcher; +} +interface Container { + get running(): boolean; + start(options?: ContainerStartupOptions): void; + monitor(): Promise; + destroy(error?: any): Promise; + signal(signo: number): void; + getTcpPort(port: number): Fetcher; +} +interface ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; +} +type AiImageClassificationInput = { + image: number[]; +}; +type AiImageClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiImageClassification { + inputs: AiImageClassificationInput; + postProcessedOutputs: AiImageClassificationOutput; +} +type AiImageToTextInput = { + image: number[]; + prompt?: string; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageToText { + inputs: AiImageToTextInput; + postProcessedOutputs: AiImageToTextOutput; +} +type AiImageTextToTextInput = { + image: string; + prompt?: string; + max_tokens?: number; + temperature?: number; + ignore_eos?: boolean; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageTextToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageTextToText { + inputs: AiImageTextToTextInput; + postProcessedOutputs: AiImageTextToTextOutput; +} +type AiObjectDetectionInput = { + image: number[]; +}; +type AiObjectDetectionOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiObjectDetection { + inputs: AiObjectDetectionInput; + postProcessedOutputs: AiObjectDetectionOutput; +} +type AiSentenceSimilarityInput = { + source: string; + sentences: string[]; +}; +type AiSentenceSimilarityOutput = number[]; +declare abstract class BaseAiSentenceSimilarity { + inputs: AiSentenceSimilarityInput; + postProcessedOutputs: AiSentenceSimilarityOutput; +} +type AiAutomaticSpeechRecognitionInput = { + audio: number[]; +}; +type AiAutomaticSpeechRecognitionOutput = { + text?: string; + words?: { + word: string; + start: number; + end: number; + }[]; + vtt?: string; +}; +declare abstract class BaseAiAutomaticSpeechRecognition { + inputs: AiAutomaticSpeechRecognitionInput; + postProcessedOutputs: AiAutomaticSpeechRecognitionOutput; +} +type AiSummarizationInput = { + input_text: string; + max_length?: number; +}; +type AiSummarizationOutput = { + summary: string; +}; +declare abstract class BaseAiSummarization { + inputs: AiSummarizationInput; + postProcessedOutputs: AiSummarizationOutput; +} +type AiTextClassificationInput = { + text: string; +}; +type AiTextClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiTextClassification { + inputs: AiTextClassificationInput; + postProcessedOutputs: AiTextClassificationOutput; +} +type AiTextEmbeddingsInput = { + text: string | string[]; +}; +type AiTextEmbeddingsOutput = { + shape: number[]; + data: number[][]; +}; +declare abstract class BaseAiTextEmbeddings { + inputs: AiTextEmbeddingsInput; + postProcessedOutputs: AiTextEmbeddingsOutput; +} +type RoleScopedChatInput = { + role: "user" | "assistant" | "system" | "tool" | (string & NonNullable); + content: string; + name?: string; +}; +type AiTextGenerationToolLegacyInput = { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; +}; +type AiTextGenerationToolInput = { + type: "function" | (string & NonNullable); + function: { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; + }; +}; +type AiTextGenerationFunctionsInput = { + name: string; + code: string; +}; +type AiTextGenerationResponseFormat = { + type: string; + json_schema?: any; +}; +type AiTextGenerationInput = { + prompt?: string; + raw?: boolean; + stream?: boolean; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + messages?: RoleScopedChatInput[]; + response_format?: AiTextGenerationResponseFormat; + tools?: AiTextGenerationToolInput[] | AiTextGenerationToolLegacyInput[] | (object & NonNullable); + functions?: AiTextGenerationFunctionsInput[]; +}; +type AiTextGenerationOutput = { + response?: string; + tool_calls?: { + name: string; + arguments: unknown; + }[]; +} | ReadableStream; +declare abstract class BaseAiTextGeneration { + inputs: AiTextGenerationInput; + postProcessedOutputs: AiTextGenerationOutput; +} +type AiTextToSpeechInput = { + prompt: string; + lang?: string; +}; +type AiTextToSpeechOutput = Uint8Array | { + audio: string; +}; +declare abstract class BaseAiTextToSpeech { + inputs: AiTextToSpeechInput; + postProcessedOutputs: AiTextToSpeechOutput; +} +type AiTextToImageInput = { + prompt: string; + negative_prompt?: string; + height?: number; + width?: number; + image?: number[]; + image_b64?: string; + mask?: number[]; + num_steps?: number; + strength?: number; + guidance?: number; + seed?: number; +}; +type AiTextToImageOutput = ReadableStream; +declare abstract class BaseAiTextToImage { + inputs: AiTextToImageInput; + postProcessedOutputs: AiTextToImageOutput; +} +type AiTranslationInput = { + text: string; + target_lang: string; + source_lang?: string; +}; +type AiTranslationOutput = { + translated_text?: string; +}; +declare abstract class BaseAiTranslation { + inputs: AiTranslationInput; + postProcessedOutputs: AiTranslationOutput; +} +type Ai_Cf_Openai_Whisper_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper { + inputs: Ai_Cf_Openai_Whisper_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Output; +} +type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | { + /** + * The input text prompt for the model to generate a response. + */ + prompt?: string; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + image: number[] | (string & NonNullable); + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; +}; +interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output { + description?: string; +} +declare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M { + inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input; + postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output; +} +type Ai_Cf_Openai_Whisper_Tiny_En_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Tiny_En_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En { + inputs: Ai_Cf_Openai_Whisper_Tiny_En_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input { + /** + * Base64 encoded value of the audio data. + */ + audio: string; + /** + * Supported tasks are 'translate' or 'transcribe'. + */ + task?: string; + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * Preprocess the audio with a voice activity detection model. + */ + vad_filter?: string; + /** + * A text prompt to help provide context to the model on the contents of the audio. + */ + initial_prompt?: string; + /** + * The prefix it appended the the beginning of the output of the transcription and can guide the transcription result. + */ + prefix?: string; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output { + transcription_info?: { + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * The confidence level or probability of the detected language being accurate, represented as a decimal between 0 and 1. + */ + language_probability?: number; + /** + * The total duration of the original audio file, in seconds. + */ + duration?: number; + /** + * The duration of the audio after applying Voice Activity Detection (VAD) to remove silent or irrelevant sections, in seconds. + */ + duration_after_vad?: number; + }; + /** + * The complete transcription of the audio. + */ + text: string; + /** + * The total number of words in the transcription. + */ + word_count?: number; + segments?: { + /** + * The starting time of the segment within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the segment within the audio, in seconds. + */ + end?: number; + /** + * The transcription of the segment. + */ + text?: string; + /** + * The temperature used in the decoding process, controlling randomness in predictions. Lower values result in more deterministic outputs. + */ + temperature?: number; + /** + * The average log probability of the predictions for the words in this segment, indicating overall confidence. + */ + avg_logprob?: number; + /** + * The compression ratio of the input to the output, measuring how much the text was compressed during the transcription process. + */ + compression_ratio?: number; + /** + * The probability that the segment contains no speech, represented as a decimal between 0 and 1. + */ + no_speech_prob?: number; + words?: { + /** + * The individual word transcribed from the audio. + */ + word?: string; + /** + * The starting time of the word within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the word within the audio, in seconds. + */ + end?: number; + }[]; + }[]; + /** + * The transcription in WebVTT format, which includes timing and text information for use in subtitles. + */ + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo { + inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output; +} +type Ai_Cf_Baai_Bge_M3_Input = BGEM3InputQueryAndContexts | BGEM3InputEmbedding; +interface BGEM3InputQueryAndContexts { + /** + * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts + */ + query?: string; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +interface BGEM3InputEmbedding { + text: string | string[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +type Ai_Cf_Baai_Bge_M3_Output = BGEM3OuputQuery | BGEM3OutputEmbeddingForContexts | BGEM3OuputEmbedding; +interface BGEM3OuputQuery { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +interface BGEM3OutputEmbeddingForContexts { + response?: number[][]; + shape?: number[]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +interface BGEM3OuputEmbedding { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +declare abstract class Base_Ai_Cf_Baai_Bge_M3 { + inputs: Ai_Cf_Baai_Bge_M3_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_M3_Output; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input { + /** + * A text description of the image you want to generate. + */ + prompt: string; + /** + * The number of diffusion steps; higher values can improve quality but take longer. + */ + steps?: number; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output { + /** + * The generated image in Base64 format. + */ + image?: string; +} +declare abstract class Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell { + inputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input; + postProcessedOutputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Prompt | Messages; +interface Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + image?: number[] | (string & NonNullable); + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; +} +interface Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role: string; + /** + * The content of the message as a string. + */ + content: string; + }[]; + image?: number[] | string; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * If true, the response will be streamed back incrementally. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output = { + /** + * The generated text response from the model + */ + response?: string; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | ReadableStream; +declare abstract class Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct { + inputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Input { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender must alternate between 'user' and 'assistant'. + */ + role: "user" | "assistant"; + /** + * The content of the message as a string. + */ + content: string; + }[]; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Dictate the output format of the generated response. + */ + response_format?: { + /** + * Set to json_object to process and output generated text as JSON. + */ + type?: string; + }; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Output { + response?: string | { + /** + * Whether the conversation is safe or not. + */ + safe?: boolean; + /** + * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe. + */ + categories?: string[]; + }; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; +} +declare abstract class Base_Ai_Cf_Meta_Llama_Guard_3_8B { + inputs: Ai_Cf_Meta_Llama_Guard_3_8B_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_Guard_3_8B_Output; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Input { + /** + * A query you wish to perform against the provided contexts. + */ + /** + * Number of returned results starting with the best score. + */ + top_k?: number; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Output { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +declare abstract class Base_Ai_Cf_Baai_Bge_Reranker_Base { + inputs: Ai_Cf_Baai_Bge_Reranker_Base_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Reranker_Base_Output; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input = Ai_Cf_Meta_Llama_4_Prompt | Ai_Cf_Meta_Llama_4_Messages; +interface Ai_Cf_Meta_Llama_4_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fulfilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Ai_Cf_Meta_Llama_4_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | string; +declare abstract class Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct { + inputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output; +} +interface AiModels { + "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; + "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-inpainting": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-img2img": BaseAiTextToImage; + "@cf/lykon/dreamshaper-8-lcm": BaseAiTextToImage; + "@cf/bytedance/stable-diffusion-xl-lightning": BaseAiTextToImage; + "@cf/myshell-ai/melotts": BaseAiTextToSpeech; + "@cf/baai/bge-base-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-small-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-large-en-v1.5": BaseAiTextEmbeddings; + "@cf/microsoft/resnet-50": BaseAiImageClassification; + "@cf/facebook/detr-resnet-50": BaseAiObjectDetection; + "@cf/meta/llama-2-7b-chat-int8": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.1": BaseAiTextGeneration; + "@cf/meta/llama-2-7b-chat-fp16": BaseAiTextGeneration; + "@hf/thebloke/llama-2-13b-chat-awq": BaseAiTextGeneration; + "@hf/thebloke/mistral-7b-instruct-v0.1-awq": BaseAiTextGeneration; + "@hf/thebloke/zephyr-7b-beta-awq": BaseAiTextGeneration; + "@hf/thebloke/openhermes-2.5-mistral-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/neural-chat-7b-v3-1-awq": BaseAiTextGeneration; + "@hf/thebloke/llamaguard-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-base-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-instruct-awq": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-math-7b-instruct": BaseAiTextGeneration; + "@cf/defog/sqlcoder-7b-2": BaseAiTextGeneration; + "@cf/openchat/openchat-3.5-0106": BaseAiTextGeneration; + "@cf/tiiuae/falcon-7b-instruct": BaseAiTextGeneration; + "@cf/thebloke/discolm-german-7b-v1-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-0.5b-chat": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-7b-chat-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-14b-chat-awq": BaseAiTextGeneration; + "@cf/tinyllama/tinyllama-1.1b-chat-v1.0": BaseAiTextGeneration; + "@cf/microsoft/phi-2": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-1.8b-chat": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.2-lora": BaseAiTextGeneration; + "@hf/nousresearch/hermes-2-pro-mistral-7b": BaseAiTextGeneration; + "@hf/nexusflow/starling-lm-7b-beta": BaseAiTextGeneration; + "@hf/google/gemma-7b-it": BaseAiTextGeneration; + "@cf/meta-llama/llama-2-7b-chat-hf-lora": BaseAiTextGeneration; + "@cf/google/gemma-2b-it-lora": BaseAiTextGeneration; + "@cf/google/gemma-7b-it-lora": BaseAiTextGeneration; + "@hf/mistral/mistral-7b-instruct-v0.2": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/fblgit/una-cybertron-7b-v2-bf16": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct-awq": BaseAiTextGeneration; + "@hf/meta-llama/meta-llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-fp8": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-awq": BaseAiTextGeneration; + "@cf/meta/llama-3.2-3b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.2-1b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.3-70b-instruct-fp8-fast": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": BaseAiTextGeneration; + "@cf/meta/m2m100-1.2b": BaseAiTranslation; + "@cf/facebook/bart-large-cnn": BaseAiSummarization; + "@cf/llava-hf/llava-1.5-7b-hf": BaseAiImageToText; + "@cf/openai/whisper": Base_Ai_Cf_Openai_Whisper; + "@cf/unum/uform-gen2-qwen-500m": Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M; + "@cf/openai/whisper-tiny-en": Base_Ai_Cf_Openai_Whisper_Tiny_En; + "@cf/openai/whisper-large-v3-turbo": Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo; + "@cf/baai/bge-m3": Base_Ai_Cf_Baai_Bge_M3; + "@cf/black-forest-labs/flux-1-schnell": Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell; + "@cf/meta/llama-3.2-11b-vision-instruct": Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct; + "@cf/meta/llama-guard-3-8b": Base_Ai_Cf_Meta_Llama_Guard_3_8B; + "@cf/baai/bge-reranker-base": Base_Ai_Cf_Baai_Bge_Reranker_Base; + "@cf/meta/llama-4-scout-17b-16e-instruct": Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct; +} +type AiOptions = { + gateway?: GatewayOptions; + returnRawResponse?: boolean; + prefix?: string; + extraHeaders?: object; +}; +type ConversionResponse = { + name: string; + mimeType: string; + format: "markdown"; + tokens: number; + data: string; +}; +type AiModelsSearchParams = { + author?: string; + hide_experimental?: boolean; + page?: number; + per_page?: number; + search?: string; + source?: number; + task?: string; +}; +type AiModelsSearchObject = { + id: string; + source: number; + name: string; + description: string; + task: { + id: string; + name: string; + description: string; + }; + tags: string[]; + properties: { + property_id: string; + value: string; + }[]; +}; +interface InferenceUpstreamError extends Error { +} +interface AiInternalError extends Error { +} +type AiModelListType = Record; +declare abstract class Ai { + aiGatewayLogId: string | null; + gateway(gatewayId: string): AiGateway; + autorag(autoragId: string): AutoRAG; + run(model: Name, inputs: AiModelList[Name]["inputs"], options?: Options): Promise; + models(params?: AiModelsSearchParams): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }[], options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }, options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; +} +type GatewayOptions = { + id: string; + cacheKey?: string; + cacheTtl?: number; + skipCache?: boolean; + metadata?: Record; + collectLog?: boolean; +}; +type AiGatewayPatchLog = { + score?: number | null; + feedback?: -1 | 1 | null; + metadata?: Record | null; +}; +type AiGatewayLog = { + id: string; + provider: string; + model: string; + model_type?: string; + path: string; + duration: number; + request_type?: string; + request_content_type?: string; + status_code: number; + response_content_type?: string; + success: boolean; + cached: boolean; + tokens_in?: number; + tokens_out?: number; + metadata?: Record; + step?: number; + cost?: number; + custom_cost?: boolean; + request_size: number; + request_head?: string; + request_head_complete: boolean; + response_size: number; + response_head?: string; + response_head_complete: boolean; + created_at: Date; +}; +type AIGatewayProviders = "workers-ai" | "anthropic" | "aws-bedrock" | "azure-openai" | "google-vertex-ai" | "huggingface" | "openai" | "perplexity-ai" | "replicate" | "groq" | "cohere" | "google-ai-studio" | "mistral" | "grok" | "openrouter" | "deepseek" | "cerebras" | "cartesia" | "elevenlabs" | "adobe-firefly"; +type AIGatewayHeaders = { + "cf-aig-metadata": Record | string; + "cf-aig-custom-cost": { + per_token_in?: number; + per_token_out?: number; + } | { + total_cost?: number; + } | string; + "cf-aig-cache-ttl": number | string; + "cf-aig-skip-cache": boolean | string; + "cf-aig-cache-key": string; + "cf-aig-collect-log": boolean | string; + Authorization: string; + "Content-Type": string; + [key: string]: string | number | boolean | object; +}; +type AIGatewayUniversalRequest = { + provider: AIGatewayProviders | string; // eslint-disable-line + endpoint: string; + headers: Partial; + query: unknown; +}; +interface AiGatewayInternalError extends Error { +} +interface AiGatewayLogNotFound extends Error { +} +declare abstract class AiGateway { + patchLog(logId: string, data: AiGatewayPatchLog): Promise; + getLog(logId: string): Promise; + run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[]): Promise; + getUrl(provider?: AIGatewayProviders | string): Promise; // eslint-disable-line +} +interface AutoRAGInternalError extends Error { +} +interface AutoRAGNotFoundError extends Error { +} +interface AutoRAGUnauthorizedError extends Error { +} +type AutoRagSearchRequest = { + query: string; + max_num_results?: number; + ranking_options?: { + ranker?: string; + score_threshold?: number; + }; + rewrite_query?: boolean; +}; +type AutoRagSearchResponse = { + object: "vector_store.search_results.page"; + search_query: string; + data: { + file_id: string; + filename: string; + score: number; + attributes: Record; + content: { + type: "text"; + text: string; + }[]; + }[]; + has_more: boolean; + next_page: string | null; +}; +type AutoRagAiSearchResponse = AutoRagSearchResponse & { + response: string; +}; +declare abstract class AutoRAG { + search(params: AutoRagSearchRequest): Promise; + aiSearch(params: AutoRagSearchRequest): Promise; +} +interface BasicImageTransformations { + /** + * Maximum width in image pixels. The value must be an integer. + */ + width?: number; + /** + * Maximum height in image pixels. The value must be an integer. + */ + height?: number; + /** + * Resizing mode as a string. It affects interpretation of width and height + * options: + * - scale-down: Similar to contain, but the image is never enlarged. If + * the image is larger than given width or height, it will be resized. + * Otherwise its original size will be kept. + * - contain: Resizes to maximum size that fits within the given width and + * height. If only a single dimension is given (e.g. only width), the + * image will be shrunk or enlarged to exactly match that dimension. + * Aspect ratio is always preserved. + * - cover: Resizes (shrinks or enlarges) to fill the entire area of width + * and height. If the image has an aspect ratio different from the ratio + * of width and height, it will be cropped to fit. + * - crop: The image will be shrunk and cropped to fit within the area + * specified by width and height. The image will not be enlarged. For images + * smaller than the given dimensions it's the same as scale-down. For + * images larger than the given dimensions, it's the same as cover. + * See also trim. + * - pad: Resizes to the maximum size that fits within the given width and + * height, and then fills the remaining area with a background color + * (white by default). Use of this mode is not recommended, as the same + * effect can be more efficiently achieved with the contain mode and the + * CSS object-fit: contain property. + * - squeeze: Stretches and deforms to the width and height given, even if it + * breaks aspect ratio + */ + fit?: "scale-down" | "contain" | "cover" | "crop" | "pad" | "squeeze"; + /** + * When cropping with fit: "cover", this defines the side or point that should + * be left uncropped. The value is either a string + * "left", "right", "top", "bottom", "auto", or "center" (the default), + * or an object {x, y} containing focal point coordinates in the original + * image expressed as fractions ranging from 0.0 (top or left) to 1.0 + * (bottom or right), 0.5 being the center. {fit: "cover", gravity: "top"} will + * crop bottom or left and right sides as necessary, but won’t crop anything + * from the top. {fit: "cover", gravity: {x:0.5, y:0.2}} will crop each side to + * preserve as much as possible around a point at 20% of the height of the + * source image. + */ + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | BasicImageTransformationsGravityCoordinates; + /** + * Background color to add underneath the image. Applies only to images with + * transparency (such as PNG). Accepts any CSS color (#RRGGBB, rgba(…), + * hsl(…), etc.) + */ + background?: string; + /** + * Number of degrees (90, 180, 270) to rotate the image by. width and height + * options refer to axes after rotation. + */ + rotate?: 0 | 90 | 180 | 270 | 360; +} +interface BasicImageTransformationsGravityCoordinates { + x?: number; + y?: number; + mode?: 'remainder' | 'box-center'; +} +/** + * In addition to the properties you can set in the RequestInit dict + * that you pass as an argument to the Request constructor, you can + * set certain properties of a `cf` object to control how Cloudflare + * features are applied to that new Request. + * + * Note: Currently, these properties cannot be tested in the + * playground. + */ +interface RequestInitCfProperties extends Record { + cacheEverything?: boolean; + /** + * A request's cache key is what determines if two requests are + * "the same" for caching purposes. If a request has the same cache key + * as some previous request, then we can serve the same cached response for + * both. (e.g. 'some-key') + * + * Only available for Enterprise customers. + */ + cacheKey?: string; + /** + * This allows you to append additional Cache-Tag response headers + * to the origin response without modifications to the origin server. + * This will allow for greater control over the Purge by Cache Tag feature + * utilizing changes only in the Workers process. + * + * Only available for Enterprise customers. + */ + cacheTags?: string[]; + /** + * Force response to be cached for a given number of seconds. (e.g. 300) + */ + cacheTtl?: number; + /** + * Force response to be cached for a given number of seconds based on the Origin status code. + * (e.g. { '200-299': 86400, '404': 1, '500-599': 0 }) + */ + cacheTtlByStatus?: Record; + scrapeShield?: boolean; + apps?: boolean; + image?: RequestInitCfPropertiesImage; + minify?: RequestInitCfPropertiesImageMinify; + mirage?: boolean; + polish?: "lossy" | "lossless" | "off"; + r2?: RequestInitCfPropertiesR2; + /** + * Redirects the request to an alternate origin server. You can use this, + * for example, to implement load balancing across several origins. + * (e.g.us-east.example.com) + * + * Note - For security reasons, the hostname set in resolveOverride must + * be proxied on the same Cloudflare zone of the incoming request. + * Otherwise, the setting is ignored. CNAME hosts are allowed, so to + * resolve to a host under a different domain or a DNS only domain first + * declare a CNAME record within your own zone’s DNS mapping to the + * external hostname, set proxy on Cloudflare, then set resolveOverride + * to point to that CNAME record. + */ + resolveOverride?: string; +} +interface RequestInitCfPropertiesImageDraw extends BasicImageTransformations { + /** + * Absolute URL of the image file to use for the drawing. It can be any of + * the supported file formats. For drawing of watermarks or non-rectangular + * overlays we recommend using PNG or WebP images. + */ + url: string; + /** + * Floating-point number between 0 (transparent) and 1 (opaque). + * For example, opacity: 0.5 makes overlay semitransparent. + */ + opacity?: number; + /** + * - If set to true, the overlay image will be tiled to cover the entire + * area. This is useful for stock-photo-like watermarks. + * - If set to "x", the overlay image will be tiled horizontally only + * (form a line). + * - If set to "y", the overlay image will be tiled vertically only + * (form a line). + */ + repeat?: true | "x" | "y"; + /** + * Position of the overlay image relative to a given edge. Each property is + * an offset in pixels. 0 aligns exactly to the edge. For example, left: 10 + * positions left side of the overlay 10 pixels from the left edge of the + * image it's drawn over. bottom: 0 aligns bottom of the overlay with bottom + * of the background image. + * + * Setting both left & right, or both top & bottom is an error. + * + * If no position is specified, the image will be centered. + */ + top?: number; + left?: number; + bottom?: number; + right?: number; +} +interface RequestInitCfPropertiesImage extends BasicImageTransformations { + /** + * Device Pixel Ratio. Default 1. Multiplier for width/height that makes it + * easier to specify higher-DPI sizes in . + */ + dpr?: number; + /** + * Allows you to trim your image. Takes dpr into account and is performed before + * resizing or rotation. + * + * It can be used as: + * - left, top, right, bottom - it will specify the number of pixels to cut + * off each side + * - width, height - the width/height you'd like to end up with - can be used + * in combination with the properties above + * - border - this will automatically trim the surroundings of an image based on + * it's color. It consists of three properties: + * - color: rgb or hex representation of the color you wish to trim (todo: verify the rgba bit) + * - tolerance: difference from color to treat as color + * - keep: the number of pixels of border to keep + */ + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; + /** + * Quality setting from 1-100 (useful values are in 60-90 range). Lower values + * make images look worse, but load faster. The default is 85. It applies only + * to JPEG and WebP images. It doesn’t have any effect on PNG. + */ + quality?: number | "low" | "medium-low" | "medium-high" | "high"; + /** + * Output format to generate. It can be: + * - avif: generate images in AVIF format. + * - webp: generate images in Google WebP format. Set quality to 100 to get + * the WebP-lossless format. + * - json: instead of generating an image, outputs information about the + * image, in JSON format. The JSON object will contain image size + * (before and after resizing), source image’s MIME type, file size, etc. + * - jpeg: generate images in JPEG format. + * - png: generate images in PNG format. + */ + format?: "avif" | "webp" | "json" | "jpeg" | "png" | "baseline-jpeg" | "png-force" | "svg"; + /** + * Whether to preserve animation frames from input files. Default is true. + * Setting it to false reduces animations to still images. This setting is + * recommended when enlarging images or processing arbitrary user content, + * because large GIF animations can weigh tens or even hundreds of megabytes. + * It is also useful to set anim:false when using format:"json" to get the + * response quicker without the number of frames. + */ + anim?: boolean; + /** + * What EXIF data should be preserved in the output image. Note that EXIF + * rotation and embedded color profiles are always applied ("baked in" into + * the image), and aren't affected by this option. Note that if the Polish + * feature is enabled, all metadata may have been removed already and this + * option may have no effect. + * - keep: Preserve most of EXIF metadata, including GPS location if there's + * any. + * - copyright: Only keep the copyright tag, and discard everything else. + * This is the default behavior for JPEG files. + * - none: Discard all invisible EXIF metadata. Currently WebP and PNG + * output formats always discard metadata. + */ + metadata?: "keep" | "copyright" | "none"; + /** + * Strength of sharpening filter to apply to the image. Floating-point + * number between 0 (no sharpening, default) and 10 (maximum). 1.0 is a + * recommended value for downscaled images. + */ + sharpen?: number; + /** + * Radius of a blur filter (approximate gaussian). Maximum supported radius + * is 250. + */ + blur?: number; + /** + * Overlays are drawn in the order they appear in the array (last array + * entry is the topmost layer). + */ + draw?: RequestInitCfPropertiesImageDraw[]; + /** + * Fetching image from authenticated origin. Setting this property will + * pass authentication headers (Authorization, Cookie, etc.) through to + * the origin. + */ + "origin-auth"?: "share-publicly"; + /** + * Adds a border around the image. The border is added after resizing. Border + * width takes dpr into account, and can be specified either using a single + * width property, or individually for each side. + */ + border?: { + color: string; + width: number; + } | { + color: string; + top: number; + right: number; + bottom: number; + left: number; + }; + /** + * Increase brightness by a factor. A value of 1.0 equals no change, a value + * of 0.5 equals half brightness, and a value of 2.0 equals twice as bright. + * 0 is ignored. + */ + brightness?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + contrast?: number; + /** + * Increase exposure by a factor. A value of 1.0 equals no change, a value of + * 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored. + */ + gamma?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + saturation?: number; + /** + * Flips the images horizontally, vertically, or both. Flipping is applied before + * rotation, so if you apply flip=h,rotate=90 then the image will be flipped + * horizontally, then rotated by 90 degrees. + */ + flip?: 'h' | 'v' | 'hv'; + /** + * Slightly reduces latency on a cache miss by selecting a + * quickest-to-compress file format, at a cost of increased file size and + * lower image quality. It will usually override the format option and choose + * JPEG over WebP or AVIF. We do not recommend using this option, except in + * unusual circumstances like resizing uncacheable dynamically-generated + * images. + */ + compression?: "fast"; +} +interface RequestInitCfPropertiesImageMinify { + javascript?: boolean; + css?: boolean; + html?: boolean; +} +interface RequestInitCfPropertiesR2 { + /** + * Colo id of bucket that an object is stored in + */ + bucketColoId?: number; +} +/** + * Request metadata provided by Cloudflare's edge. + */ +type IncomingRequestCfProperties = IncomingRequestCfPropertiesBase & IncomingRequestCfPropertiesBotManagementEnterprise & IncomingRequestCfPropertiesCloudflareForSaaSEnterprise & IncomingRequestCfPropertiesGeographicInformation & IncomingRequestCfPropertiesCloudflareAccessOrApiShield; +interface IncomingRequestCfPropertiesBase extends Record { + /** + * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request. + * + * @example 395747 + */ + asn: number; + /** + * The organization which owns the ASN of the incoming request. + * + * @example "Google Cloud" + */ + asOrganization: string; + /** + * The original value of the `Accept-Encoding` header if Cloudflare modified it. + * + * @example "gzip, deflate, br" + */ + clientAcceptEncoding?: string; + /** + * The number of milliseconds it took for the request to reach your worker. + * + * @example 22 + */ + clientTcpRtt?: number; + /** + * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code) + * airport code of the data center that the request hit. + * + * @example "DFW" + */ + colo: string; + /** + * Represents the upstream's response to a + * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) + * from cloudflare. + * + * For workers with no upstream, this will always be `1`. + * + * @example 3 + */ + edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus; + /** + * The HTTP Protocol the request used. + * + * @example "HTTP/2" + */ + httpProtocol: string; + /** + * The browser-requested prioritization information in the request object. + * + * If no information was set, defaults to the empty string `""` + * + * @example "weight=192;exclusive=0;group=3;group-weight=127" + * @default "" + */ + requestPriority: string; + /** + * The TLS version of the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "TLSv1.3" + */ + tlsVersion: string; + /** + * The cipher for the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "AEAD-AES128-GCM-SHA256" + */ + tlsCipher: string; + /** + * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake. + * + * If the incoming request was served over plaintext (without TLS) this field is undefined. + */ + tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata; +} +interface IncomingRequestCfPropertiesBotManagementBase { + /** + * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot, + * represented as an integer percentage between `1` (almost certainly a bot) and `99` (almost certainly human). + * + * @example 54 + */ + score: number; + /** + * A boolean value that is true if the request comes from a good bot, like Google or Bing. + * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots). + */ + verifiedBot: boolean; + /** + * A boolean value that is true if the request originates from a + * Cloudflare-verified proxy service. + */ + corporateProxy: boolean; + /** + * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources. + */ + staticResource: boolean; + /** + * List of IDs that correlate to the Bot Management heuristic detections made on a request (you can have multiple heuristic detections on the same request). + */ + detectionIds: number[]; +} +interface IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase; + /** + * Duplicate of `botManagement.score`. + * + * @deprecated + */ + clientTrustScore: number; +} +interface IncomingRequestCfPropertiesBotManagementEnterprise extends IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase & { + /** + * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients + * across different destination IPs, Ports, and X509 certificates. + */ + ja3Hash: string; + }; +} +interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise { + /** + * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/). + * + * This field is only present if you have Cloudflare for SaaS enabled on your account + * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)). + */ + hostMetadata: HostMetadata; +} +interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield { + /** + * Information about the client certificate presented to Cloudflare. + * + * This is populated when the incoming request is served over TLS using + * either Cloudflare Access or API Shield (mTLS) + * and the presented SSL certificate has a valid + * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number) + * (i.e., not `null` or `""`). + * + * Otherwise, a set of placeholder values are used. + * + * The property `certPresented` will be set to `"1"` when + * the object is populated (i.e. the above conditions were met). + */ + tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth | IncomingRequestCfPropertiesTLSClientAuthPlaceholder; +} +/** + * Metadata about the request's TLS handshake + */ +interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata { + /** + * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + clientHandshake: string; + /** + * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + serverHandshake: string; + /** + * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + clientFinished: string; + /** + * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + serverFinished: string; +} +/** + * Geographic data about the request's origin. + */ +interface IncomingRequestCfPropertiesGeographicInformation { + /** + * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from. + * + * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR. + * + * If Cloudflare is unable to determine where the request originated this property is omitted. + * + * The country code `"T1"` is used for requests originating on TOR. + * + * @example "GB" + */ + country?: Iso3166Alpha2Code | "T1"; + /** + * If present, this property indicates that the request originated in the EU + * + * @example "1" + */ + isEUCountry?: "1"; + /** + * A two-letter code indicating the continent the request originated from. + * + * @example "AN" + */ + continent?: ContinentCode; + /** + * The city the request originated from + * + * @example "Austin" + */ + city?: string; + /** + * Postal code of the incoming request + * + * @example "78701" + */ + postalCode?: string; + /** + * Latitude of the incoming request + * + * @example "30.27130" + */ + latitude?: string; + /** + * Longitude of the incoming request + * + * @example "-97.74260" + */ + longitude?: string; + /** + * Timezone of the incoming request + * + * @example "America/Chicago" + */ + timezone?: string; + /** + * If known, the ISO 3166-2 name for the first level region associated with + * the IP address of the incoming request + * + * @example "Texas" + */ + region?: string; + /** + * If known, the ISO 3166-2 code for the first-level region associated with + * the IP address of the incoming request + * + * @example "TX" + */ + regionCode?: string; + /** + * Metro code (DMA) of the incoming request + * + * @example "635" + */ + metroCode?: string; +} +/** Data about the incoming request's TLS certificate */ +interface IncomingRequestCfPropertiesTLSClientAuth { + /** Always `"1"`, indicating that the certificate was presented */ + certPresented: "1"; + /** + * Result of certificate verification. + * + * @example "FAILED:self signed certificate" + */ + certVerified: Exclude; + /** The presented certificate's revokation status. + * + * - A value of `"1"` indicates the certificate has been revoked + * - A value of `"0"` indicates the certificate has not been revoked + */ + certRevoked: "1" | "0"; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDN: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDN: string; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDNRFC2253: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDNRFC2253: string; + /** The certificate issuer's distinguished name (legacy policies) */ + certIssuerDNLegacy: string; + /** The certificate subject's distinguished name (legacy policies) */ + certSubjectDNLegacy: string; + /** + * The certificate's serial number + * + * @example "00936EACBE07F201DF" + */ + certSerial: string; + /** + * The certificate issuer's serial number + * + * @example "2489002934BDFEA34" + */ + certIssuerSerial: string; + /** + * The certificate's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certSKI: string; + /** + * The certificate issuer's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certIssuerSKI: string; + /** + * The certificate's SHA-1 fingerprint + * + * @example "6b9109f323999e52259cda7373ff0b4d26bd232e" + */ + certFingerprintSHA1: string; + /** + * The certificate's SHA-256 fingerprint + * + * @example "acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea" + */ + certFingerprintSHA256: string; + /** + * The effective starting date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotBefore: string; + /** + * The effective expiration date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotAfter: string; +} +/** Placeholder values for TLS Client Authorization */ +interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { + certPresented: "0"; + certVerified: "NONE"; + certRevoked: "0"; + certIssuerDN: ""; + certSubjectDN: ""; + certIssuerDNRFC2253: ""; + certSubjectDNRFC2253: ""; + certIssuerDNLegacy: ""; + certSubjectDNLegacy: ""; + certSerial: ""; + certIssuerSerial: ""; + certSKI: ""; + certIssuerSKI: ""; + certFingerprintSHA1: ""; + certFingerprintSHA256: ""; + certNotBefore: ""; + certNotAfter: ""; +} +/** Possible outcomes of TLS verification */ +declare type CertVerificationStatus = +/** Authentication succeeded */ +"SUCCESS" +/** No certificate was presented */ + | "NONE" +/** Failed because the certificate was self-signed */ + | "FAILED:self signed certificate" +/** Failed because the certificate failed a trust chain check */ + | "FAILED:unable to verify the first certificate" +/** Failed because the certificate not yet valid */ + | "FAILED:certificate is not yet valid" +/** Failed because the certificate is expired */ + | "FAILED:certificate has expired" +/** Failed for another unspecified reason */ + | "FAILED"; +/** + * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare. + */ +declare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = 0 /** Unknown */ | 1 /** no keepalives (not found) */ | 2 /** no connection re-use, opening keepalive connection failed */ | 3 /** no connection re-use, keepalive accepted and saved */ | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ | 5; /** connection re-use, accepted by the origin server */ +/** ISO 3166-1 Alpha-2 codes */ +declare type Iso3166Alpha2Code = "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AO" | "AQ" | "AR" | "AS" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "FI" | "FJ" | "FK" | "FM" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GS" | "GT" | "GU" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MH" | "MK" | "ML" | "MM" | "MN" | "MO" | "MP" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PR" | "PS" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZM" | "ZW"; +/** The 2-letter continent codes Cloudflare uses */ +declare type ContinentCode = "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; +type CfProperties = IncomingRequestCfProperties | RequestInitCfProperties; +interface D1Meta { + duration: number; + size_after: number; + rows_read: number; + rows_written: number; + last_row_id: number; + changed_db: boolean; + changes: number; + /** + * The region of the database instance that executed the query. + */ + served_by_region?: string; + /** + * True if-and-only-if the database instance that executed the query was the primary. + */ + served_by_primary?: boolean; + timings?: { + /** + * The duration of the SQL query execution by the database instance. It doesn't include any network time. + */ + sql_duration_ms: number; + }; +} +interface D1Response { + success: true; + meta: D1Meta & Record; + error?: never; +} +type D1Result = D1Response & { + results: T[]; +}; +interface D1ExecResult { + count: number; + duration: number; +} +type D1SessionConstraint = +// Indicates that the first query should go to the primary, and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). +"first-primary" +// Indicates that the first query can go anywhere (primary or replica), and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). + | "first-unconstrained"; +type D1SessionBookmark = string; +declare abstract class D1Database { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + exec(query: string): Promise; + /** + * Creates a new D1 Session anchored at the given constraint or the bookmark. + * All queries executed using the created session will have sequential consistency, + * meaning that all writes done through the session will be visible in subsequent reads. + * + * @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session. + */ + withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession; + /** + * @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases. + */ + dump(): Promise; +} +declare abstract class D1DatabaseSession { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + /** + * @returns The latest session bookmark across all executed queries on the session. + * If no query has been executed yet, `null` is returned. + */ + getBookmark(): D1SessionBookmark | null; +} +declare abstract class D1PreparedStatement { + bind(...values: unknown[]): D1PreparedStatement; + first(colName: string): Promise; + first>(): Promise; + run>(): Promise>; + all>(): Promise>; + raw(options: { + columnNames: true; + }): Promise<[ + string[], + ...T[] + ]>; + raw(options?: { + columnNames?: false; + }): Promise; +} +// `Disposable` was added to TypeScript's standard lib types in version 5.2. +// To support older TypeScript versions, define an empty `Disposable` interface. +// Users won't be able to use `using`/`Symbol.dispose` without upgrading to 5.2, +// but this will ensure type checking on older versions still passes. +// TypeScript's interface merging will ensure our empty interface is effectively +// ignored when `Disposable` is included in the standard lib. +interface Disposable { +} +/** + * An email message that can be sent from a Worker. + */ +interface EmailMessage { + /** + * Envelope From attribute of the email message. + */ + readonly from: string; + /** + * Envelope To attribute of the email message. + */ + readonly to: string; +} +/** + * An email message that is sent to a consumer Worker and can be rejected/forwarded. + */ +interface ForwardableEmailMessage extends EmailMessage { + /** + * Stream of the email message content. + */ + readonly raw: ReadableStream; + /** + * An [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + */ + readonly headers: Headers; + /** + * Size of the email message content. + */ + readonly rawSize: number; + /** + * Reject this email message by returning a permanent SMTP error back to the connecting client including the given reason. + * @param reason The reject reason. + * @returns void + */ + setReject(reason: string): void; + /** + * Forward this email message to a verified destination address of the account. + * @param rcptTo Verified destination address. + * @param headers A [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + * @returns A promise that resolves when the email message is forwarded. + */ + forward(rcptTo: string, headers?: Headers): Promise; + /** + * Reply to the sender of this email message with a new EmailMessage object. + * @param message The reply message. + * @returns A promise that resolves when the email message is replied. + */ + reply(message: EmailMessage): Promise; +} +/** + * A binding that allows a Worker to send email messages. + */ +interface SendEmail { + send(message: EmailMessage): Promise; +} +declare abstract class EmailEvent extends ExtendableEvent { + readonly message: ForwardableEmailMessage; +} +declare type EmailExportedHandler = (message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) => void | Promise; +declare module "cloudflare:email" { + let _EmailMessage: { + prototype: EmailMessage; + new (from: string, to: string, raw: ReadableStream | string): EmailMessage; + }; + export { _EmailMessage as EmailMessage }; +} +interface Hyperdrive { + /** + * Connect directly to Hyperdrive as if it's your database, returning a TCP socket. + * + * Calling this method returns an idential socket to if you call + * `connect("host:port")` using the `host` and `port` fields from this object. + * Pick whichever approach works better with your preferred DB client library. + * + * Note that this socket is not yet authenticated -- it's expected that your + * code (or preferably, the client library of your choice) will authenticate + * using the information in this class's readonly fields. + */ + connect(): Socket; + /** + * A valid DB connection string that can be passed straight into the typical + * client library/driver/ORM. This will typically be the easiest way to use + * Hyperdrive. + */ + readonly connectionString: string; + /* + * A randomly generated hostname that is only valid within the context of the + * currently running Worker which, when passed into `connect()` function from + * the "cloudflare:sockets" module, will connect to the Hyperdrive instance + * for your database. + */ + readonly host: string; + /* + * The port that must be paired the the host field when connecting. + */ + readonly port: number; + /* + * The username to use when authenticating to your database via Hyperdrive. + * Unlike the host and password, this will be the same every time + */ + readonly user: string; + /* + * The randomly generated password to use when authenticating to your + * database via Hyperdrive. Like the host field, this password is only valid + * within the context of the currently running Worker instance from which + * it's read. + */ + readonly password: string; + /* + * The name of the database to connect to. + */ + readonly database: string; +} +// Copyright (c) 2024 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +type ImageInfoResponse = { + format: 'image/svg+xml'; +} | { + format: string; + fileSize: number; + width: number; + height: number; +}; +type ImageTransform = { + width?: number; + height?: number; + background?: string; + blur?: number; + border?: { + color?: string; + width?: number; + } | { + top?: number; + bottom?: number; + left?: number; + right?: number; + }; + brightness?: number; + contrast?: number; + fit?: 'scale-down' | 'contain' | 'pad' | 'squeeze' | 'cover' | 'crop'; + flip?: 'h' | 'v' | 'hv'; + gamma?: number; + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | { + x?: number; + y?: number; + mode: 'remainder' | 'box-center'; + }; + rotate?: 0 | 90 | 180 | 270; + saturation?: number; + sharpen?: number; + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; +}; +type ImageDrawOptions = { + opacity?: number; + repeat?: boolean | string; + top?: number; + left?: number; + bottom?: number; + right?: number; +}; +type ImageOutputOptions = { + format: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/avif' | 'rgb' | 'rgba'; + quality?: number; + background?: string; +}; +interface ImagesBinding { + /** + * Get image metadata (type, width and height) + * @throws {@link ImagesError} with code 9412 if input is not an image + * @param stream The image bytes + */ + info(stream: ReadableStream): Promise; + /** + * Begin applying a series of transformations to an image + * @param stream The image bytes + * @returns A transform handle + */ + input(stream: ReadableStream): ImageTransformer; +} +interface ImageTransformer { + /** + * Apply transform next, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param transform + */ + transform(transform: ImageTransform): ImageTransformer; + /** + * Draw an image on this transformer, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param image The image (or transformer that will give the image) to draw + * @param options The options configuring how to draw the image + */ + draw(image: ReadableStream | ImageTransformer, options?: ImageDrawOptions): ImageTransformer; + /** + * Retrieve the image that results from applying the transforms to the + * provided input + * @param options Options that apply to the output e.g. output format + */ + output(options: ImageOutputOptions): Promise; +} +interface ImageTransformationResult { + /** + * The image as a response, ready to store in cache or return to users + */ + response(): Response; + /** + * The content type of the returned image + */ + contentType(): string; + /** + * The bytes of the response + */ + image(): ReadableStream; +} +interface ImagesError extends Error { + readonly code: number; + readonly message: string; + readonly stack?: string; +} +type Params

= Record; +type EventContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; +}; +type PagesFunction = Record> = (context: EventContext) => Response | Promise; +type EventPluginContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; + pluginArgs: PluginArgs; +}; +type PagesPluginFunction = Record, PluginArgs = unknown> = (context: EventPluginContext) => Response | Promise; +declare module "assets:*" { + export const onRequest: PagesFunction; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +declare module "cloudflare:pipelines" { + export abstract class PipelineTransformationEntrypoint { + protected env: Env; + protected ctx: ExecutionContext; + constructor(ctx: ExecutionContext, env: Env); + /** + * run recieves an array of PipelineRecord which can be + * transformed and returned to the pipeline + * @param records Incoming records from the pipeline to be transformed + * @param metadata Information about the specific pipeline calling the transformation entrypoint + * @returns A promise containing the transformed PipelineRecord array + */ + public run(records: I[], metadata: PipelineBatchMetadata): Promise; + } + export type PipelineRecord = Record; + export type PipelineBatchMetadata = { + pipelineId: string; + pipelineName: string; + }; + export interface Pipeline { + /** + * The Pipeline interface represents the type of a binding to a Pipeline + * + * @param records The records to send to the pipeline + */ + send(records: T[]): Promise; + } +} +// PubSubMessage represents an incoming PubSub message. +// The message includes metadata about the broker, the client, and the payload +// itself. +// https://developers.cloudflare.com/pub-sub/ +interface PubSubMessage { + // Message ID + readonly mid: number; + // MQTT broker FQDN in the form mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:PORT + readonly broker: string; + // The MQTT topic the message was sent on. + readonly topic: string; + // The client ID of the client that published this message. + readonly clientId: string; + // The unique identifier (JWT ID) used by the client to authenticate, if token + // auth was used. + readonly jti?: string; + // A Unix timestamp (seconds from Jan 1, 1970), set when the Pub/Sub Broker + // received the message from the client. + readonly receivedAt: number; + // An (optional) string with the MIME type of the payload, if set by the + // client. + readonly contentType: string; + // Set to 1 when the payload is a UTF-8 string + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901063 + readonly payloadFormatIndicator: number; + // Pub/Sub (MQTT) payloads can be UTF-8 strings, or byte arrays. + // You can use payloadFormatIndicator to inspect this before decoding. + payload: string | Uint8Array; +} +// JsonWebKey extended by kid parameter +interface JsonWebKeyWithKid extends JsonWebKey { + // Key Identifier of the JWK + readonly kid: string; +} +interface RateLimitOptions { + key: string; +} +interface RateLimitOutcome { + success: boolean; +} +interface RateLimit { + /** + * Rate limit a request based on the provided options. + * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/ + * @returns A promise that resolves with the outcome of the rate limit. + */ + limit(options: RateLimitOptions): Promise; +} +// Namespace for RPC utility types. Unfortunately, we can't use a `module` here as these types need +// to referenced by `Fetcher`. This is included in the "importable" version of the types which +// strips all `module` blocks. +declare namespace Rpc { + // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s. + // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`. + // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to + // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape) + export const __RPC_STUB_BRAND: '__RPC_STUB_BRAND'; + export const __RPC_TARGET_BRAND: '__RPC_TARGET_BRAND'; + export const __WORKER_ENTRYPOINT_BRAND: '__WORKER_ENTRYPOINT_BRAND'; + export const __DURABLE_OBJECT_BRAND: '__DURABLE_OBJECT_BRAND'; + export const __WORKFLOW_ENTRYPOINT_BRAND: '__WORKFLOW_ENTRYPOINT_BRAND'; + export interface RpcTargetBranded { + [__RPC_TARGET_BRAND]: never; + } + export interface WorkerEntrypointBranded { + [__WORKER_ENTRYPOINT_BRAND]: never; + } + export interface DurableObjectBranded { + [__DURABLE_OBJECT_BRAND]: never; + } + export interface WorkflowEntrypointBranded { + [__WORKFLOW_ENTRYPOINT_BRAND]: never; + } + export type EntrypointBranded = WorkerEntrypointBranded | DurableObjectBranded | WorkflowEntrypointBranded; + // Types that can be used through `Stub`s + export type Stubable = RpcTargetBranded | ((...args: any[]) => any); + // Types that can be passed over RPC + // The reason for using a generic type here is to build a serializable subset of structured + // cloneable composite types. This allows types defined with the "interface" keyword to pass the + // serializable check as well. Otherwise, only types defined with the "type" keyword would pass. + type Serializable = + // Structured cloneables + BaseType + // Structured cloneable composites + | Map ? Serializable : never, T extends Map ? Serializable : never> | Set ? Serializable : never> | ReadonlyArray ? Serializable : never> | { + [K in keyof T]: K extends number | string ? Serializable : never; + } + // Special types + | Stub + // Serialized as stubs, see `Stubify` + | Stubable; + // Base type for all RPC stubs, including common memory management methods. + // `T` is used as a marker type for unwrapping `Stub`s later. + interface StubBase extends Disposable { + [__RPC_STUB_BRAND]: T; + dup(): this; + } + export type Stub = Provider & StubBase; + // This represents all the types that can be sent as-is over an RPC boundary + type BaseType = void | undefined | null | boolean | number | bigint | string | TypedArray | ArrayBuffer | DataView | Date | Error | RegExp | ReadableStream | WritableStream | Request | Response | Headers; + // Recursively rewrite all `Stubable` types with `Stub`s + // prettier-ignore + type Stubify = T extends Stubable ? Stub : T extends Map ? Map, Stubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: any; + } ? { + [K in keyof T]: Stubify; + } : T; + // Recursively rewrite all `Stub`s with the corresponding `T`s. + // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies: + // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`. + // prettier-ignore + type Unstubify = T extends StubBase ? V : T extends Map ? Map, Unstubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: unknown; + } ? { + [K in keyof T]: Unstubify; + } : T; + type UnstubifyAll = { + [I in keyof A]: Unstubify; + }; + // Utility type for adding `Provider`/`Disposable`s to `object` types only. + // Note `unknown & T` is equivalent to `T`. + type MaybeProvider = T extends object ? Provider : unknown; + type MaybeDisposable = T extends object ? Disposable : unknown; + // Type for method return or property on an RPC interface. + // - Stubable types are replaced by stubs. + // - Serializable types are passed by value, with stubable types replaced by stubs + // and a top-level `Disposer`. + // Everything else can't be passed over PRC. + // Technically, we use custom thenables here, but they quack like `Promise`s. + // Intersecting with `(Maybe)Provider` allows pipelining. + // prettier-ignore + type Result = R extends Stubable ? Promise> & Provider : R extends Serializable ? Promise & MaybeDisposable> & MaybeProvider : never; + // Type for method or property on an RPC interface. + // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s. + // Unwrapping `Stub`s allows calling with `Stubable` arguments. + // For properties, rewrite types to be `Result`s. + // In each case, unwrap `Promise`s. + type MethodOrProperty = V extends (...args: infer P) => infer R ? (...args: UnstubifyAll

) => Result> : Result>; + // Type for the callable part of an `Provider` if `T` is callable. + // This is intersected with methods/properties. + type MaybeCallableProvider = T extends (...args: any[]) => any ? MethodOrProperty : unknown; + // Base type for all other types providing RPC-like interfaces. + // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types. + // `Reserved` names (e.g. stub method names like `dup()`) and symbols can't be accessed over RPC. + export type Provider = MaybeCallableProvider & { + [K in Exclude>]: MethodOrProperty; + }; +} +declare namespace Cloudflare { + interface Env { + } +} +declare module 'cloudflare:workers' { + export type RpcStub = Rpc.Stub; + export const RpcStub: { + new (value: T): Rpc.Stub; + }; + export abstract class RpcTarget implements Rpc.RpcTargetBranded { + [Rpc.__RPC_TARGET_BRAND]: never; + } + // `protected` fields don't appear in `keyof`s, so can't be accessed over RPC + export abstract class WorkerEntrypoint implements Rpc.WorkerEntrypointBranded { + [Rpc.__WORKER_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + fetch?(request: Request): Response | Promise; + tail?(events: TraceItem[]): void | Promise; + trace?(traces: TraceItem[]): void | Promise; + scheduled?(controller: ScheduledController): void | Promise; + queue?(batch: MessageBatch): void | Promise; + test?(controller: TestController): void | Promise; + } + export abstract class DurableObject implements Rpc.DurableObjectBranded { + [Rpc.__DURABLE_OBJECT_BRAND]: never; + protected ctx: DurableObjectState; + protected env: Env; + constructor(ctx: DurableObjectState, env: Env); + fetch?(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; + } + export type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; + export type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number; + export type WorkflowDelayDuration = WorkflowSleepDuration; + export type WorkflowTimeoutDuration = WorkflowSleepDuration; + export type WorkflowBackoff = 'constant' | 'linear' | 'exponential'; + export type WorkflowStepConfig = { + retries?: { + limit: number; + delay: WorkflowDelayDuration | number; + backoff?: WorkflowBackoff; + }; + timeout?: WorkflowTimeoutDuration | number; + }; + export type WorkflowEvent = { + payload: Readonly; + timestamp: Date; + instanceId: string; + }; + export type WorkflowStepEvent = { + payload: Readonly; + timestamp: Date; + type: string; + }; + export abstract class WorkflowStep { + do>(name: string, callback: () => Promise): Promise; + do>(name: string, config: WorkflowStepConfig, callback: () => Promise): Promise; + sleep: (name: string, duration: WorkflowSleepDuration) => Promise; + sleepUntil: (name: string, timestamp: Date | number) => Promise; + waitForEvent>(name: string, options: { + type: string; + timeout?: WorkflowTimeoutDuration | number; + }): Promise>; + } + export abstract class WorkflowEntrypoint | unknown = unknown> implements Rpc.WorkflowEntrypointBranded { + [Rpc.__WORKFLOW_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + run(event: Readonly>, step: WorkflowStep): Promise; + } + export const env: Cloudflare.Env; +} +interface SecretsStoreSecret { + /** + * Get a secret from the Secrets Store, returning a string of the secret value + * if it exists, or throws an error if it does not exist + */ + get(): Promise; +} +declare module "cloudflare:sockets" { + function _connect(address: string | SocketAddress, options?: SocketOptions): Socket; + export { _connect as connect }; +} +declare namespace TailStream { + interface Header { + readonly name: string; + readonly value: string; + } + interface FetchEventInfo { + readonly type: "fetch"; + readonly method: string; + readonly url: string; + readonly cfJson: string; + readonly headers: Header[]; + } + interface JsRpcEventInfo { + readonly type: "jsrpc"; + readonly methodName: string; + } + interface ScheduledEventInfo { + readonly type: "scheduled"; + readonly scheduledTime: Date; + readonly cron: string; + } + interface AlarmEventInfo { + readonly type: "alarm"; + readonly scheduledTime: Date; + } + interface QueueEventInfo { + readonly type: "queue"; + readonly queueName: string; + readonly batchSize: number; + } + interface EmailEventInfo { + readonly type: "email"; + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; + } + interface TraceEventInfo { + readonly type: "trace"; + readonly traces: (string | null)[]; + } + interface HibernatableWebSocketEventInfoMessage { + readonly type: "message"; + } + interface HibernatableWebSocketEventInfoError { + readonly type: "error"; + } + interface HibernatableWebSocketEventInfoClose { + readonly type: "close"; + readonly code: number; + readonly wasClean: boolean; + } + interface HibernatableWebSocketEventInfo { + readonly type: "hibernatableWebSocket"; + readonly info: HibernatableWebSocketEventInfoClose | HibernatableWebSocketEventInfoError | HibernatableWebSocketEventInfoMessage; + } + interface Resume { + readonly type: "resume"; + readonly attachment?: any; + } + interface CustomEventInfo { + readonly type: "custom"; + } + interface FetchResponseInfo { + readonly type: "fetch"; + readonly statusCode: number; + } + type EventOutcome = "ok" | "canceled" | "exception" | "unknown" | "killSwitch" | "daemonDown" | "exceededCpu" | "exceededMemory" | "loadShed" | "responseStreamDisconnected" | "scriptNotFound"; + interface ScriptVersion { + readonly id: string; + readonly tag?: string; + readonly message?: string; + } + interface Trigger { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Onset { + readonly type: "onset"; + readonly dispatchNamespace?: string; + readonly entrypoint?: string; + readonly scriptName?: string; + readonly scriptTags?: string[]; + readonly scriptVersion?: ScriptVersion; + readonly trigger?: Trigger; + readonly info: FetchEventInfo | JsRpcEventInfo | ScheduledEventInfo | AlarmEventInfo | QueueEventInfo | EmailEventInfo | TraceEventInfo | HibernatableWebSocketEventInfo | Resume | CustomEventInfo; + } + interface Outcome { + readonly type: "outcome"; + readonly outcome: EventOutcome; + readonly cpuTime: number; + readonly wallTime: number; + } + interface Hibernate { + readonly type: "hibernate"; + } + interface SpanOpen { + readonly type: "spanOpen"; + readonly op?: string; + readonly info?: FetchEventInfo | JsRpcEventInfo | Attribute[]; + } + interface SpanClose { + readonly type: "spanClose"; + readonly outcome: EventOutcome; + } + interface DiagnosticChannelEvent { + readonly type: "diagnosticChannel"; + readonly channel: string; + readonly message: any; + } + interface Exception { + readonly type: "exception"; + readonly name: string; + readonly message: string; + readonly stack?: string; + } + interface Log { + readonly type: "log"; + readonly level: "debug" | "error" | "info" | "log" | "warn"; + readonly message: string; + } + interface Return { + readonly type: "return"; + readonly info?: FetchResponseInfo | Attribute[]; + } + interface Link { + readonly type: "link"; + readonly label?: string; + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Attribute { + readonly type: "attribute"; + readonly name: string; + readonly value: string | string[] | boolean | boolean[] | number | number[]; + } + type Mark = DiagnosticChannelEvent | Exception | Log | Return | Link | Attribute[]; + interface TailEvent { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + readonly timestamp: Date; + readonly sequence: number; + readonly event: Onset | Outcome | Hibernate | SpanOpen | SpanClose | Mark; + } + type TailEventHandler = (event: TailEvent) => void | Promise; + type TailEventHandlerName = "onset" | "outcome" | "hibernate" | "spanOpen" | "spanClose" | "diagnosticChannel" | "exception" | "log" | "return" | "link" | "attribute"; + type TailEventHandlerObject = Record; + type TailEventHandlerType = TailEventHandler | TailEventHandlerObject; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +/** + * Data types supported for holding vector metadata. + */ +type VectorizeVectorMetadataValue = string | number | boolean | string[]; +/** + * Additional information to associate with a vector. + */ +type VectorizeVectorMetadata = VectorizeVectorMetadataValue | Record; +type VectorFloatArray = Float32Array | Float64Array; +interface VectorizeError { + code?: number; + error: string; +} +/** + * Comparison logic/operation to use for metadata filtering. + * + * This list is expected to grow as support for more operations are released. + */ +type VectorizeVectorMetadataFilterOp = "$eq" | "$ne"; +/** + * Filter criteria for vector metadata used to limit the retrieved query result set. + */ +type VectorizeVectorMetadataFilter = { + [field: string]: Exclude | null | { + [Op in VectorizeVectorMetadataFilterOp]?: Exclude | null; + }; +}; +/** + * Supported distance metrics for an index. + * Distance metrics determine how other "similar" vectors are determined. + */ +type VectorizeDistanceMetric = "euclidean" | "cosine" | "dot-product"; +/** + * Metadata return levels for a Vectorize query. + * + * Default to "none". + * + * @property all Full metadata for the vector return set, including all fields (including those un-indexed) without truncation. This is a more expensive retrieval, as it requires additional fetching & reading of un-indexed data. + * @property indexed Return all metadata fields configured for indexing in the vector return set. This level of retrieval is "free" in that no additional overhead is incurred returning this data. However, note that indexed metadata is subject to truncation (especially for larger strings). + * @property none No indexed metadata will be returned. + */ +type VectorizeMetadataRetrievalLevel = "all" | "indexed" | "none"; +interface VectorizeQueryOptions { + topK?: number; + namespace?: string; + returnValues?: boolean; + returnMetadata?: boolean | VectorizeMetadataRetrievalLevel; + filter?: VectorizeVectorMetadataFilter; +} +/** + * Information about the configuration of an index. + */ +type VectorizeIndexConfig = { + dimensions: number; + metric: VectorizeDistanceMetric; +} | { + preset: string; // keep this generic, as we'll be adding more presets in the future and this is only in a read capacity +}; +/** + * Metadata about an existing index. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeIndexInfo} for its post-beta equivalent. + */ +interface VectorizeIndexDetails { + /** The unique ID of the index */ + readonly id: string; + /** The name of the index. */ + name: string; + /** (optional) A human readable description for the index. */ + description?: string; + /** The index configuration, including the dimension size and distance metric. */ + config: VectorizeIndexConfig; + /** The number of records containing vectors within the index. */ + vectorsCount: number; +} +/** + * Metadata about an existing index. + */ +interface VectorizeIndexInfo { + /** The number of records containing vectors within the index. */ + vectorCount: number; + /** Number of dimensions the index has been configured for. */ + dimensions: number; + /** ISO 8601 datetime of the last processed mutation on in the index. All changes before this mutation will be reflected in the index state. */ + processedUpToDatetime: number; + /** UUIDv4 of the last mutation processed by the index. All changes before this mutation will be reflected in the index state. */ + processedUpToMutation: number; +} +/** + * Represents a single vector value set along with its associated metadata. + */ +interface VectorizeVector { + /** The ID for the vector. This can be user-defined, and must be unique. It should uniquely identify the object, and is best set based on the ID of what the vector represents. */ + id: string; + /** The vector values */ + values: VectorFloatArray | number[]; + /** The namespace this vector belongs to. */ + namespace?: string; + /** Metadata associated with the vector. Includes the values of other fields and potentially additional details. */ + metadata?: Record; +} +/** + * Represents a matched vector for a query along with its score and (if specified) the matching vector information. + */ +type VectorizeMatch = Pick, "values"> & Omit & { + /** The score or rank for similarity, when returned as a result */ + score: number; +}; +/** + * A set of matching {@link VectorizeMatch} for a particular query. + */ +interface VectorizeMatches { + matches: VectorizeMatch[]; + count: number; +} +/** + * Results of an operation that performed a mutation on a set of vectors. + * Here, `ids` is a list of vectors that were successfully processed. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeAsyncMutation} for its post-beta equivalent. + */ +interface VectorizeVectorMutation { + /* List of ids of vectors that were successfully processed. */ + ids: string[]; + /* Total count of the number of processed vectors. */ + count: number; +} +/** + * Result type indicating a mutation on the Vectorize Index. + * Actual mutations are processed async where the `mutationId` is the unique identifier for the operation. + */ +interface VectorizeAsyncMutation { + /** The unique identifier for the async mutation operation containing the changeset. */ + mutationId: string; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link Vectorize} for its new implementation. + */ +declare abstract class VectorizeIndex { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with the ids & count of records that were successfully processed (and thus deleted). + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * Mutations in this version are async, returning a mutation id. + */ +declare abstract class Vectorize { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Use the provided vector-id to perform a similarity search across the index. + * @param vectorId Id for a vector in the index against which the index should be queried. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public queryById(vectorId: string, options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the insert changeset. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the upsert changeset. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with a unique identifier of a mutation containing the delete changeset. + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * The interface for "version_metadata" binding + * providing metadata about the Worker Version using this binding. + */ +type WorkerVersionMetadata = { + /** The ID of the Worker Version using this binding */ + id: string; + /** The tag of the Worker Version using this binding */ + tag: string; + /** The timestamp of when the Worker Version was uploaded */ + timestamp: string; +}; +interface DynamicDispatchLimits { + /** + * Limit CPU time in milliseconds. + */ + cpuMs?: number; + /** + * Limit number of subrequests. + */ + subRequests?: number; +} +interface DynamicDispatchOptions { + /** + * Limit resources of invoked Worker script. + */ + limits?: DynamicDispatchLimits; + /** + * Arguments for outbound Worker script, if configured. + */ + outbound?: { + [key: string]: any; + }; +} +interface DispatchNamespace { + /** + * @param name Name of the Worker script. + * @param args Arguments to Worker script. + * @param options Options for Dynamic Dispatch invocation. + * @returns A Fetcher object that allows you to send requests to the Worker script. + * @throws If the Worker script does not exist in this dispatch namespace, an error will be thrown. + */ + get(name: string, args?: { + [key: string]: any; + }, options?: DynamicDispatchOptions): Fetcher; +} +declare module 'cloudflare:workflows' { + /** + * NonRetryableError allows for a user to throw a fatal error + * that makes a Workflow instance fail immediately without triggering a retry + */ + export class NonRetryableError extends Error { + public constructor(message: string, name?: string); + } +} +declare abstract class Workflow { + /** + * Get a handle to an existing instance of the Workflow. + * @param id Id for the instance of this Workflow + * @returns A promise that resolves with a handle for the Instance + */ + public get(id: string): Promise; + /** + * Create a new instance and return a handle to it. If a provided id exists, an error will be thrown. + * @param options Options when creating an instance including id and params + * @returns A promise that resolves with a handle for the Instance + */ + public create(options?: WorkflowInstanceCreateOptions): Promise; + /** + * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. + * @param batch List of Options when creating an instance including name and params + * @returns A promise that resolves with a list of handles for the created instances. + */ + public createBatch(batch: WorkflowInstanceCreateOptions[]): Promise; +} +interface WorkflowInstanceCreateOptions { + /** + * An id for your Workflow instance. Must be unique within the Workflow. + */ + id?: string; + /** + * The event payload the Workflow instance is triggered with + */ + params?: PARAMS; +} +type InstanceStatus = { + status: 'queued' // means that instance is waiting to be started (see concurrency limits) + | 'running' | 'paused' | 'errored' | 'terminated' // user terminated the instance while it was running + | 'complete' | 'waiting' // instance is hibernating and waiting for sleep or event to finish + | 'waitingForPause' // instance is finishing the current work to pause + | 'unknown'; + error?: string; + output?: object; +}; +interface WorkflowError { + code?: number; + message: string; +} +declare abstract class WorkflowInstance { + public id: string; + /** + * Pause the instance. + */ + public pause(): Promise; + /** + * Resume the instance. If it is already running, an error will be thrown. + */ + public resume(): Promise; + /** + * Terminate the instance. If it is errored, terminated or complete, an error will be thrown. + */ + public terminate(): Promise; + /** + * Restart the instance. + */ + public restart(): Promise; + /** + * Returns the current status of the instance. + */ + public status(): Promise; + /** + * Send an event to this instance. + */ + public sendEvent({ type, payload, }: { + type: string; + payload: unknown; + }): Promise; +} diff --git a/apps/graphql/wrangler.jsonc b/apps/graphql/wrangler.jsonc new file mode 100644 index 00000000..73098c74 --- /dev/null +++ b/apps/graphql/wrangler.jsonc @@ -0,0 +1,132 @@ +/** + * For more details on how to configure Wrangler, refer to: + * https://developers.cloudflare.com/workers/wrangler/configuration/ + */ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": "src/graphql.app.ts", + "compatibility_date": "2025-03-10", + "compatibility_flags": ["nodejs_compat"], + "name": "mcp-cloudflare-graphql-dev", + "migrations": [ + { + "new_sqlite_classes": ["GraphQLMCP"], + "tag": "v1" + } + ], + "observability": { + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } + }, + "durable_objects": { + "bindings": [ + { + "class_name": "GraphQLMCP", + "name": "MCP_OBJECT" + }, + { + "class_name": "UserDetails", + "name": "USER_DETAILS" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "DEV_KV" + } + ], + "vars": { + "ENVIRONMENT": "development", + "MCP_SERVER_NAME": "", + "MCP_SERVER_VERSION": "", + "CLOUDFLARE_CLIENT_ID": "", + "CLOUDFLARE_CLIENT_SECRET": "" + }, + "dev": { + "port": 8976 + }, + "workers_dev": false, + "preview_urls": false, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-dev" + } + ], + "env": { + "staging": { + "name": "mcp-cloudflare-graphql-staging", + "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", + "routes": [{ "pattern": "graphql-staging.mcp.cloudflare.com", "custom_domain": true }], + "durable_objects": { + "bindings": [ + { + "class_name": "GraphQLMCP", + "name": "MCP_OBJECT" + }, + { + "class_name": "UserDetails", + "name": "USER_DETAILS", + "script_name": "mcp-cloudflare-workers-observability-staging" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "1b00683b8c7443bc85ee67bcdca311b5" + } + ], + "vars": { + "ENVIRONMENT": "staging", + "MCP_SERVER_NAME": "graphql-staging", + "MCP_SERVER_VERSION": "1.0.0" + }, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-staging" + } + ] + }, + "production": { + "name": "mcp-cloudflare-graphql-production", + "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", + "routes": [{ "pattern": "graphql.mcp.cloudflare.com", "custom_domain": true }], + "durable_objects": { + "bindings": [ + { + "class_name": "GraphQLMCP", + "name": "MCP_OBJECT" + }, + { + "class_name": "UserDetails", + "name": "USER_DETAILS", + "script_name": "mcp-cloudflare-workers-observability-production" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "84e15e4639ff40c59c6ffe41a68cdd54" + } + ], + "vars": { + "ENVIRONMENT": "production", + "MCP_SERVER_NAME": "graphql", + "MCP_SERVER_VERSION": "1.0.0" + }, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-production" + } + ] + } + } +} diff --git a/apps/logpush/.dev.vars.example b/apps/logpush/.dev.vars.example index c087f669..d2d3cd20 100644 --- a/apps/logpush/.dev.vars.example +++ b/apps/logpush/.dev.vars.example @@ -2,4 +2,3 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file diff --git a/apps/logpush/CHANGELOG.md b/apps/logpush/CHANGELOG.md new file mode 100644 index 00000000..9a1ca04e --- /dev/null +++ b/apps/logpush/CHANGELOG.md @@ -0,0 +1,125 @@ +# logpush + +## 0.1.10 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.8 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.7 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.6 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.5 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/logpush/README.md b/apps/logpush/README.md index 09b38224..fbe97472 100644 --- a/apps/logpush/README.md +++ b/apps/logpush/README.md @@ -25,7 +25,7 @@ This MCP server is still a work in progress, and we plan to add more tools in th If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://logs.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -34,7 +34,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://logs.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://logs.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/logpush/package.json b/apps/logpush/package.json index 09611611..c8b6a1b1 100644 --- a/apps/logpush/package.json +++ b/apps/logpush/package.json @@ -1,23 +1,23 @@ { "name": "logpush", - "version": "0.0.1", + "version": "0.1.10", "private": true, "scripts": { "check:lint": "run-eslint-workers", "check:types": "run-tsc", - "deploy": "wrangler deploy", + "deploy": "run-wrangler-deploy", "dev": "wrangler dev", "start": "wrangler dev", "types": "wrangler types --include-env=false", "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/logpush/src/index.ts b/apps/logpush/src/logpush.app.ts similarity index 76% rename from apps/logpush/src/index.ts rename to apps/logpush/src/logpush.app.ts index 7b619c24..4f8999de 100644 --- a/apps/logpush/src/index.ts +++ b/apps/logpush/src/logpush.app.ts @@ -1,22 +1,23 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerLogsTools } from './tools/logs' +import { registerLogsTools } from './tools/logpush.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './logpush.context' const env = getEnv() @@ -50,8 +51,12 @@ export class LogsMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -67,9 +72,14 @@ export class LogsMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -79,7 +89,12 @@ export class LogsMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -96,8 +111,8 @@ const LogPushScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(LogsMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(LogsMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/logpush/src/context.ts b/apps/logpush/src/logpush.context.ts similarity index 83% rename from apps/logpush/src/context.ts rename to apps/logpush/src/logpush.context.ts index 4d9ce5fa..0e9f349e 100644 --- a/apps/logpush/src/context.ts +++ b/apps/logpush/src/logpush.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { LogsMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { LogsMCP } from './logpush.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string diff --git a/apps/logpush/src/tools/logs.ts b/apps/logpush/src/tools/logpush.tools.ts similarity index 96% rename from apps/logpush/src/tools/logs.ts rename to apps/logpush/src/tools/logpush.tools.ts index 008a9f09..ef93a020 100644 --- a/apps/logpush/src/tools/logs.ts +++ b/apps/logpush/src/tools/logpush.tools.ts @@ -1,8 +1,9 @@ import { z } from 'zod' import { fetchCloudflareApi } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' -import type { LogsMCP } from '../index' +import type { LogsMCP } from '../logpush.app' const zJobIdentifier = z.number().int().min(1).optional().describe('Unique id of the job.') const zEnabled = z.boolean().optional().describe('Flag that indicates if the job is enabled.') @@ -104,7 +105,7 @@ export function registerLogsTools(agent: LogsMCP) { agent.server.tool( 'logpush_jobs_by_account_id', `All Logpush jobs by Account ID. - + You should use this tool when: - You have questions or wish to request information about their Cloudflare Logpush jobs by account - You want a condensed version for the output results of your account's Cloudflare Logpush job @@ -125,7 +126,8 @@ export function registerLogsTools(agent: LogsMCP) { } } try { - const result = await handleGetAccountLogPushJobs(accountId, agent.props.accessToken) + const props = getProps(agent) + const result = await handleGetAccountLogPushJobs(accountId, props.accessToken) return { content: [ { diff --git a/apps/logpush/vitest.config.ts b/apps/logpush/vitest.config.ts index c201c144..ec58869b 100644 --- a/apps/logpush/vitest.config.ts +++ b/apps/logpush/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/logpush.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/logpush/wrangler.jsonc b/apps/logpush/wrangler.jsonc index 36bdbf86..b83188bb 100644 --- a/apps/logpush/wrangler.jsonc +++ b/apps/logpush/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/logpush.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-logpush-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/radar/.dev.vars.example b/apps/radar/.dev.vars.example index 860dc82f..5950b164 100644 --- a/apps/radar/.dev.vars.example +++ b/apps/radar/.dev.vars.example @@ -2,4 +2,4 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= + diff --git a/apps/radar/CHANGELOG.md b/apps/radar/CHANGELOG.md new file mode 100644 index 00000000..9c251215 --- /dev/null +++ b/apps/radar/CHANGELOG.md @@ -0,0 +1,119 @@ +# cloudflare-radar-mcp-server + +## 0.1.9 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/radar/CONTRIBUTING.md b/apps/radar/CONTRIBUTING.md index 20e6cf61..797f595a 100644 --- a/apps/radar/CONTRIBUTING.md +++ b/apps/radar/CONTRIBUTING.md @@ -17,7 +17,6 @@ If you'd like to iterate and test your MCP server, you can do so in local develo ``` DEV_DISABLE_OAUTH=true - DEV_CLOUDFLARE_EMAIL=your_cloudflare_email # This is your global api token DEV_CLOUDFLARE_API_TOKEN=your_development_api_token ``` @@ -28,7 +27,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/radar/README.md b/apps/radar/README.md index 739cdf0c..3eef520e 100644 --- a/apps/radar/README.md +++ b/apps/radar/README.md @@ -4,46 +4,179 @@ This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introdu connections, with Cloudflare OAuth built-in. It integrates tools powered by the [Cloudflare Radar API](https://developers.cloudflare.com/radar/) to provide global -Internet traffic insights, trends and other utilities. +Internet traffic insights, trends and other utilities. Explore the data visually at [radar.cloudflare.com](https://radar.cloudflare.com). ## 🔨 Available Tools Currently available tools: -| **Category** | **Tool** | **Description** | -| ---------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -| **AI** | `get_ai_data` | Retrieves AI-related data, including traffic from AI user agents, as well as popular models and model tasks | -| **Autonomous Systems** | `list_autonomous_systems` | Lists ASes; filter by location and sort by population size | -| | `get_as_details` | Retrieves detailed info for a specific ASN | -| **Domains** | `get_domains_ranking` | Gets top or trending domains | -| | `get_domain_rank_details` | Gets domain rank details | -| **DNS** | `get_dns_data` | Retrieves DNS query data to 1.1.1.1, including timeseries, summaries, and breakdowns by dimensions like `queryType` | -| **Email Routing** | `get_email_routing_data` | Retrieves Email Routing data, including timeseries, and breakdowns by dimensions like `encrypted` | -| **Email Security** | `get_email_security_data` | Retrieves Email Security data, including timeseries, and breakdowns by dimensions like `threatCategory` | -| **HTTP** | `get_http_data` | Retrieves HTTP request data, including timeseries, and breakdowns by dimensions like `deviceType` | -| **IP Addresses** | `get_ip_details` | Provides details about a specific IP address | -| **Internet Services** | `get_internet_services_ranking` | Gets top Internet services | -| **Internet Quality** | `get_internet_quality_data` | Retrieves a summary or time series of bandwidth, latency, or DNS response time from the Radar Internet Quality Index | -| **Internet Speed** | `get_internet_speed_data` | Retrieves summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test | -| **Layer 3 Attacks** | `get_l3_attack_data` | Retrieves L3 attack data, including timeseries, top attacks, and breakdowns by dimensions like `protocol` | -| **Layer 7 Attacks** | `get_l7_attack_data` | Retrieves L7 attack data, including timeseries, top attacks, and breakdowns by dimensions like `mitigationProduct` | -| **Traffic Anomalies** | `get_traffic_anomalies` | Lists traffic anomalies and outages; filter by AS, location, start date, and end date | -| **URL Scanner** | `scan_url` | Scans a URL via [Cloudflare’s URL Scanner](https://developers.cloudflare.com/radar/investigate/url-scanner/) | - -This MCP server is still a work in progress, and we plan to add more tools in the future. +| **Category** | **Tool** | **Description** | +| --------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| **AI** | `get_ai_data` | Retrieves AI-related data, including traffic from AI user agents, as well as popular models and model tasks | +| **Events, Outages & Traffic Anomalies** | `get_annotations` | Retrieves annotations including Internet events, outages, and anomalies from various Cloudflare data sources | +| | `get_outages` | Retrieves Internet outages and anomalies with detected connectivity issues | +| | `get_outages_by_location` | Gets outage counts aggregated by location - identify which countries have the most outages | +| | `get_traffic_anomalies` | Lists traffic anomalies and outages; filter by AS, location, start date, and end date | +| | `get_traffic_anomalies_by_location` | Gets traffic anomalies aggregated by location - shows which countries have the most detected outage signals | +| **AS112** | `get_as112_data` | Retrieves AS112 DNS sink hole data for reverse DNS lookups of private IP addresses (RFC 1918) | +| **Autonomous Systems** | `list_autonomous_systems` | Lists ASes; filter by location and sort by population size | +| | `get_as_details` | Retrieves detailed info for a specific ASN | +| | `get_as_set` | Gets IRR AS-SETs that an AS is a member of | +| | `get_as_relationships` | Gets AS-level relationships (peer, upstream, downstream) | +| **BGP** | `get_bgp_hijacks` | Retrieves BGP hijack events with filtering by hijacker/victim ASN, confidence score | +| | `get_bgp_leaks` | Retrieves BGP route leak events | +| | `get_bgp_route_stats` | Retrieves BGP routing table statistics | +| | `get_bgp_timeseries` | Retrieves BGP updates time series (announcements and withdrawals) | +| | `get_bgp_top_ases` | Gets top ASes by BGP update count | +| | `get_bgp_top_prefixes` | Gets top IP prefixes by BGP update count | +| | `get_bgp_moas` | Gets Multi-Origin AS (MOAS) prefixes | +| | `get_bgp_pfx2as` | Gets prefix-to-ASN mapping | +| | `get_bgp_ip_space_timeseries` | Retrieves announced IP address space over time (IPv4 /24s and IPv6 /48s) - useful for detecting route withdrawals | +| | `get_bgp_routes_realtime` | Gets real-time BGP routes for a prefix using RouteViews and RIPE RIS collectors | +| | `get_bgp_routing_table_ases` | Lists all ASes in global routing tables with routing statistics (prefix counts, RPKI status) | +| | `get_bgp_top_ases_by_prefixes` | Gets top ASes ordered by announced prefix count - shows which networks have the largest routing footprint | +| **Bots** | `get_bots_data` | Retrieves bot traffic data by name, operator, category (AI crawlers, search engines, etc.) | +| | `list_bots` | Lists known bots with details (AI crawlers, search engines, monitoring bots) | +| | `get_bot_details` | Gets detailed information about a specific bot by slug | +| | `get_bots_crawlers_data` | Retrieves web crawler HTTP request data by client type, user agent, referrer, industry | +| **Certificate Transparency** | `get_certificate_transparency_data` | Retrieves CT log data for SSL/TLS certificate issuance trends | +| | `list_ct_authorities` | Lists Certificate Authorities tracked in CT logs | +| | `get_ct_authority_details` | Gets details for a specific CA by fingerprint | +| | `list_ct_logs` | Lists Certificate Transparency logs | +| | `get_ct_log_details` | Gets details for a specific CT log by slug | +| **Cloud Observatory** | `list_origins` | Lists cloud provider origins (AWS, GCP, Azure, OCI) | +| | `get_origin_details` | Gets details for a specific cloud provider | +| | `get_origins_data` | Retrieves cloud provider performance metrics (timeseries, summaries, grouped by region/percentile) | +| **Domains** | `get_domains_ranking` | Gets top or trending domains | +| | `get_domain_rank_details` | Gets domain rank details | +| | `get_domains_ranking_timeseries` | Gets domain ranking timeseries data to track how domains rank over time | +| **DNS** | `get_dns_queries_data` | Retrieves DNS query data to 1.1.1.1, including timeseries, summaries, and breakdowns by dimensions like `queryType` | +| **Email Routing** | `get_email_routing_data` | Retrieves Email Routing data, including timeseries, and breakdowns by dimensions like `encrypted` | +| **Email Security** | `get_email_security_data` | Retrieves Email Security data, including timeseries, and breakdowns by dimensions like `threatCategory` | +| **Geolocations** | `list_geolocations` | Lists available geolocations (ADM1 - states/provinces) with GeoNames IDs | +| | `get_geolocation_details` | Gets details for a specific geolocation by GeoNames ID | +| **HTTP** | `get_http_data` | Retrieves HTTP request data with geoId filtering for ADM1 (states/provinces) | +| **IP Addresses** | `get_ip_details` | Provides details about a specific IP address including full ASN details (name, country, population estimates) | +| **Internet Services** | `get_internet_services_ranking` | Gets top Internet services | +| | `get_internet_services_timeseries` | Tracks internet service ranking changes over time (e.g., how ChatGPT ranks over months) | +| **Internet Quality & Speed** | `get_internet_quality_data` | Retrieves a summary or time series of bandwidth, latency, or DNS response time from the Radar Internet Quality Index | +| | `get_internet_speed_data` | Retrieves summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test | +| | `get_speed_histogram` | Gets speed test histogram data showing distribution of results for bandwidth, latency, or jitter | +| **Layer 3 Attacks** | `get_l3_attack_data` | Retrieves network layer (L3/DDoS) attack data, including timeseries, top attacks, and breakdowns by dimensions | +| **Layer 7 Attacks** | `get_l7_attack_data` | Retrieves L7 attack data, including timeseries, top attacks, and breakdowns by dimensions like `mitigationProduct` | +| **Leaked Credentials** | `get_leaked_credentials_data` | Retrieves trends in HTTP auth requests and compromised credential detection | +| **NetFlows** | `get_netflows_data` | Retrieves network traffic patterns with geoId filtering for ADM1 (states/provinces) | +| **Robots.txt** | `get_robots_txt_data` | Retrieves robots.txt analysis data showing crawler access rules across domains | +| **TCP Quality** | `get_tcp_resets_timeouts_data` | Retrieves TCP connection quality metrics (resets and timeouts) | +| **TLDs** | `list_tlds` | Lists top-level domains (TLDs) including generic, country-code, and sponsored TLDs | +| | `get_tld_details` | Gets detailed information about a specific TLD | +| **URL Scanner** | `search_url_scans` | Search URL scans using ElasticSearch-like query syntax | +| | `create_url_scan` | Submit a URL to scan, returns scan UUID | +| | `get_url_scan` | Get scan results by UUID (verdicts, page info, stats) | +| | `get_url_scan_screenshot` | Get screenshot URL for a completed scan | +| | `get_url_scan_har` | Get HAR (HTTP Archive) data for a completed scan | ### Prompt Examples +**Traffic & Network Analysis** + - `What are the most used operating systems?` +- `Show me HTTP traffic trends from Lisbon, Portugal.` +- `What is the TCP reset and timeout rate globally?` +- `Show me network traffic patterns for California.` +- `Compare HTTP traffic between mobile and desktop devices.` +- `What percentage of traffic uses IPv6 vs IPv4?` + +**Autonomous Systems & BGP** + - `What are the top 5 ASes in Portugal?` - `Get information about ASN 13335.` -- `What are the details of IP address 1.1.1.1?` -- `List me traffic anomalies in Syria over the last year.` -- `Compare domain rankings in the US and UK.` -- `Give me rank details for google.com in March 2025.` -- `Scan https://example.com.` -- `Show me HTTP traffic trends from Portugal.` +- `What are the relationships (peers, upstreams) for Cloudflare's AS?` +- `Show me recent BGP hijack events with high confidence.` +- `Which prefixes have the most BGP updates?` +- `What AS announces the prefix 1.1.1.0/24?` +- `Show me IPv6 announced address space for Portugal over the last 30 days.` +- `Compare IPv4 vs IPv6 BGP address space trends for AS13335.` +- `Get real-time BGP routes for prefix 1.1.1.0/24.` +- `List the top ASes by prefix count in Germany.` +- `Which ASes have the most IPv6 address space globally?` +- `Show me BGP route leak events from the last week.` + +**Security & Attacks** + - `Show me application layer attack trends from the last 7 days.` +- `What are the top L3 attack vectors?` +- `Show me leaked credential detection trends.` +- `Scan https://example.com for security analysis.` +- `Which industries are most targeted by DDoS attacks?` + +**Bots & Crawlers** + +- `What AI crawlers are most active?` +- `List all known AI crawler bots.` +- `How are websites configuring robots.txt for AI crawlers?` +- `What percentage of sites block vs allow AI crawlers?` +- `Show me crawler traffic by industry.` + +**DNS** + +- `What are the most common DNS query types to 1.1.1.1?` +- `Show me AS112 DNS queries by protocol.` +- `What is the DNSSEC adoption rate?` +- `Show me DNS query trends by response code.` + +**Email** + +- `What are the email security threat trends?` +- `Show me email routing data by encryption status.` +- `What percentage of emails fail DMARC validation?` +- `Which TLDs send the most malicious emails?` + +**Certificate Transparency** + +- `What are the most active Certificate Authorities?` +- `List Certificate Transparency logs.` +- `Show me certificate issuance trends by validation level.` +- `Get details for the CT log argon2026h1.` +- `What is the top Certificate Authority and show me its details.` + +**Rankings & Internet Services** + +- `What are the top trending domains?` +- `Compare domain rankings in the US and UK.` +- `What are the top Internet services in the E-commerce category?` +- `Track how google.com ranks over the last 30 days.` +- `How has ChatGPT's ranking changed over the last 6 months?` + +**TLDs** + +- `List all country-code TLDs.` +- `Show me details about the .io TLD.` +- `Give me a ranking of TLDs based on DNS magnitude.` + +**Speed & Quality** + +- `Show me the bandwidth distribution histogram for the US.` +- `What is the average latency in Portugal?` +- `Which countries have the fastest internet speeds?` +- `Show me top ASes by download bandwidth.` + +**Outages & Events** + +- `Show me recent Internet outages.` +- `Which countries have the most Internet outages?` +- `Show me verified traffic anomalies by location.` + +**Cloud & Infrastructure** + +- `What are the top AWS regions by traffic?` +- `Compare latency between Azure and GCP regions.` +- `What is the connection success rate for cloud providers?` + +**IP Information** + +- `What are the details of IP address 8.8.8.8?` +- `Give me full information about IP 1.1.1.1 including ASN details.` ## Access the remote MCP server from any MCP Client @@ -58,7 +191,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://radar.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://radar.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/radar/package.json b/apps/radar/package.json index 6fda6646..32c207d2 100644 --- a/apps/radar/package.json +++ b/apps/radar/package.json @@ -1,6 +1,6 @@ { "name": "cloudflare-radar-mcp-server", - "version": "0.0.1", + "version": "0.1.9", "private": true, "scripts": { "check:lint": "run-eslint-workers", @@ -12,12 +12,12 @@ "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "zod": "3.24.2" diff --git a/apps/radar/src/context.ts b/apps/radar/src/context.ts deleted file mode 100644 index 881b4b18..00000000 --- a/apps/radar/src/context.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { RadarMCP, UserDetails } from './index' - -export interface Env { - OAUTH_KV: KVNamespace - ENVIRONMENT: 'development' | 'staging' | 'production' - MCP_SERVER_NAME: string - MCP_SERVER_VERSION: string - CLOUDFLARE_CLIENT_ID: string - CLOUDFLARE_CLIENT_SECRET: string - MCP_OBJECT: DurableObjectNamespace - USER_DETAILS: DurableObjectNamespace - MCP_METRICS: AnalyticsEngineDataset - DEV_DISABLE_OAUTH: string - DEV_CLOUDFLARE_API_TOKEN: string - DEV_CLOUDFLARE_EMAIL: string -} - -export const BASE_INSTRUCTIONS = /* markdown */ ` -# Cloudflare Radar MCP Server - -This server integrates tools powered by the Cloudflare Radar API to provide insights into global Internet traffic, -trends, and other related utilities. - -An active account is **only required** for URL Scanner-related tools (e.g., \`scan_url\`). - -For tools related to Internet trends and insights, analyze the results and, when appropriate, generate visualizations -such as line charts, pie charts, bar charts, stacked area charts, choropleth maps, treemaps, or other relevant chart types. - -### Making comparisons - -Many tools support **array-based filters** to enable comparisons across multiple criteria. -In such cases, the array index corresponds to a distinct data series. -For each data series, provide a corresponding \`dateRange\`, or alternatively a \`dateStart\` and \`dateEnd\` pair. -Example: To compare HTTP traffic between Portugal and Spain over the last 7 days: -- \`dateRange: ["7d", "7d"]\` -- \`location: ["PT", "ES"]\` - -This applies to date filters and other filters that support comparison across multiple values. -If a tool does **not** support array-based filters, you can achieve the same comparison by making multiple separate -calls to the tool. -` diff --git a/apps/radar/src/index.ts b/apps/radar/src/radar.app.ts similarity index 73% rename from apps/radar/src/index.ts rename to apps/radar/src/radar.app.ts index 765ce96c..2f9217d7 100644 --- a/apps/radar/src/index.ts +++ b/apps/radar/src/radar.app.ts @@ -1,24 +1,25 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '@repo/mcp-observability' -import { BASE_INSTRUCTIONS } from './context' -import { registerRadarTools } from './tools/radar' -import { registerUrlScannerTools } from './tools/url-scanner' +import { BASE_INSTRUCTIONS } from './radar.context' +import { registerRadarTools } from './tools/radar.tools' +import { registerUrlScannerTools } from './tools/url-scanner.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './radar.context' const env = getEnv() @@ -52,8 +53,12 @@ export class RadarMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -69,9 +74,14 @@ export class RadarMCP extends McpAgent { async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -81,7 +91,12 @@ export class RadarMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -98,8 +113,8 @@ const RadarScopes = { export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(RadarMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(RadarMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/radar/src/radar.context.ts b/apps/radar/src/radar.context.ts new file mode 100644 index 00000000..df04d381 --- /dev/null +++ b/apps/radar/src/radar.context.ts @@ -0,0 +1,113 @@ +import type { RadarMCP, UserDetails } from './radar.app' + +export interface Env { + OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string + ENVIRONMENT: 'development' | 'staging' | 'production' + MCP_SERVER_NAME: string + MCP_SERVER_VERSION: string + CLOUDFLARE_CLIENT_ID: string + CLOUDFLARE_CLIENT_SECRET: string + MCP_OBJECT: DurableObjectNamespace + USER_DETAILS: DurableObjectNamespace + MCP_METRICS: AnalyticsEngineDataset + DEV_DISABLE_OAUTH: string + DEV_CLOUDFLARE_API_TOKEN: string + DEV_CLOUDFLARE_EMAIL: string +} + +export const BASE_INSTRUCTIONS = /* markdown */ ` +# Cloudflare Radar MCP Server + +This server provides tools powered by the Cloudflare Radar API for global Internet insights. + +## Authentication + +- **URL Scanner** requires an active account (use \`set_active_account\`) +- All other Radar data tools work without account selection + +## Tool Categories + +- **Entities**: Look up ASN, IP, and location details (\`list_autonomous_systems\`, \`get_as_details\`, \`get_ip_details\`) +- **Traffic**: HTTP and DNS trends (\`get_http_data\`, \`get_dns_queries_data\`) +- **Attacks**: Layer 3/7 DDoS attack trends (\`get_l3_attack_data\`, \`get_l7_attack_data\`) +- **Email**: Routing and security trends (\`get_email_routing_data\`, \`get_email_security_data\`) +- **Quality**: Internet speed and quality metrics (\`get_internet_quality_data\`, \`get_internet_speed_data\`) +- **Rankings**: Top domains and services (\`get_domains_ranking\`, \`get_internet_services_ranking\`) +- **AI**: AI bot traffic and Workers AI usage (\`get_ai_data\`) +- **BGP**: Route hijacks, leaks, and stats (\`get_bgp_hijacks\`, \`get_bgp_leaks\`, \`get_bgp_route_stats\`) +- **Bots**: Bot traffic by category, operator, kind (\`get_bots_data\`) +- **Certificate Transparency**: SSL/TLS certificate issuance trends (\`get_certificate_transparency_data\`) +- **NetFlows**: Network traffic patterns with ADM1 filtering (\`get_netflows_data\`) +- **Cloud Observatory**: Cloud provider performance - AWS, GCP, Azure, OCI (\`list_origins\`, \`get_origin_details\`, \`get_origins_data\`) +- **URL Scanner**: Scan and analyze URLs for security threats (\`search_url_scans\`, \`create_url_scan\`, \`get_url_scan\`, \`get_url_scan_screenshot\`, \`get_url_scan_har\`) + +## Making Comparisons + +Many tools support **array-based filters** for comparisons. Filter arrays (location, asn, continent, geoId) map **positionally** to dateRange arrays - each array index corresponds to a distinct data series. + +### Cross-Location Comparison (Same Time Period) +Compare HTTP traffic between Portugal and Spain over the last 7 days: +- \`dateRange: ["7d", "7d"]\` +- \`location: ["PT", "ES"]\` + +Result: \`summary_0\` = Portugal (7d), \`summary_1\` = Spain (7d) + +### Time Period Comparison (Same Location) +Compare Portugal bandwidth this week vs last week: +- \`dateRange: ["7d", "7dcontrol"]\` +- \`location: ["PT", "PT"]\` *(repeat the location for each dateRange)* + +Result: \`summary_0\` = PT (current week), \`summary_1\` = PT (previous week) + +### IMPORTANT: Positional Mapping Behavior +Filter arrays must match the length of dateRange for consistent filtering. If you provide fewer filter values than dateRange values, unmatched periods default to worldwide/unfiltered data. + +**Correct** (comparing Portugal across time periods): +- \`dateRange: ["7d", "7dcontrol"], location: ["PT", "PT"]\` + +**Incorrect** (control period will be worldwide, not Portugal): +- \`dateRange: ["7d", "7dcontrol"], location: ["PT"]\` *(missing second location)* + +This positional mapping applies to all filter arrays: **location**, **asn**, **continent**, and **geoId**. + +## Geographic Options + +- **location**: Filter by country (alpha-2 codes like "US", "PT") +- **continent**: Filter by continent (alpha-2 codes like "EU", "NA") +- **asn**: Filter by Autonomous System Number (e.g., "13335" for Cloudflare, "15169" for Google) +- **geoId**: Filter by ADM1 region (GeoNames IDs for states/provinces) - available for HTTP and NetFlows + +## Dimension Types + +Many tools use a \`dimension\` parameter to control the response format: + +### timeseries +Returns data points over time with timestamps and values. Best for line charts showing trends. +\`\`\` +{ timestamps: ["2026-01-01T12:00:00Z", ...], values: ["0.735", "0.812", ...] } +\`\`\` + +### summary +Returns aggregated percentages or values grouped by a category. Best for pie/bar charts. +\`\`\` +{ Chrome: "31.44%", ChromeMobile: "27.89%", Safari: "12.27%", ... } +\`\`\` + +### timeseriesGroups +Combines both: time series data broken down by category. Best for stacked area/line charts. +\`\`\` +{ timestamps: [...], Chrome: ["28.15", "27.35", ...], Firefox: ["31.01", "31.23", ...] } +\`\`\` + +Use \`summary/*\` dimensions (e.g., \`summary/browser\`) for snapshots and \`timeseriesGroups/*\` (e.g., \`timeseriesGroups/browser\`) for trends over time. + +## Visualizations + +Generate charts when appropriate: +- **Line charts**: Timeseries data +- **Bar charts**: Rankings, summaries +- **Pie charts**: Distributions +- **Choropleth maps**: Geographic data +- **Stacked area charts**: Grouped timeseries +` diff --git a/apps/radar/src/tools/radar.tools.ts b/apps/radar/src/tools/radar.tools.ts new file mode 100644 index 00000000..7575b9e5 --- /dev/null +++ b/apps/radar/src/tools/radar.tools.ts @@ -0,0 +1,2976 @@ +import { z } from 'zod' + +import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' +import { getProps } from '@repo/mcp-common/src/get-props' +import { + PaginationLimitParam, + PaginationOffsetParam, +} from '@repo/mcp-common/src/types/shared.types' + +import { + AiDimensionParam, + AnnotationDataSourceParam, + AnnotationEventTypeParam, + As112DimensionParam, + As112ProtocolParam, + As112QueryTypeParam, + As112ResponseCodeParam, + AsnArrayParam, + AsnParam, + AsOrderByParam, + BgpHijackerAsnParam, + BgpInvalidOnlyParam, + BgpInvolvedAsnParam, + BgpInvolvedCountryParam, + BgpIpVersionParam, + BgpLeakAsnParam, + BgpLongestPrefixMatchParam, + BgpMaxConfidenceParam, + BgpMinConfidenceParam, + BgpOriginParam, + BgpPrefixArrayParam, + BgpPrefixParam, + BgpRoutesAsesSortByParam, + BgpRpkiStatusParam, + BgpSortByParam, + BgpSortOrderParam, + BgpUpdateTypeParam, + BgpVictimAsnParam, + BotCategoryParam, + BotKindParam, + BotNameParam, + BotOperatorParam, + BotsCrawlersDimensionParam, + BotsCrawlersFormatParam, + BotsDimensionParam, + BotVerificationStatusParam, + BucketSizeParam, + ContinentArrayParam, + CrawlerClientTypeParam, + CrawlerIndustryParam, + CrawlerVerticalParam, + CtCaOwnerParam, + CtCaParam, + CtDimensionParam, + CtDurationParam, + CtEntryTypeParam, + CtPublicKeyAlgorithmParam, + CtTldParam, + CtValidationLevelParam, + DateEndArrayParam, + DateEndParam, + DateListParam, + DateRangeArrayParam, + DateRangeParam, + DateStartArrayParam, + DateStartParam, + DnsDimensionParam, + DomainCategoryArrayParam, + DomainParam, + DomainRankingTypeParam, + DomainsArrayParam, + EmailRoutingDimensionParam, + EmailSecurityDimensionParam, + GeoIdArrayParam, + GeoIdParam, + HttpDimensionParam, + InternetQualityMetricParam, + InternetServicesCategoryParam, + InternetSpeedDimensionParam, + InternetSpeedOrderByParam, + IpParam, + L3AttackDimensionParam, + L7AttackDimensionParam, + LeakedCredentialsBotClassParam, + LeakedCredentialsCompromisedParam, + LeakedCredentialsDimensionParam, + LimitPerGroupParam, + LocationArrayParam, + LocationListParam, + LocationParam, + NetflowsDimensionParam, + NetflowsProductParam, + NormalizationParam, + OriginArrayParam, + OriginDataDimensionParam, + OriginMetricParam, + OriginNormalizationParam, + OriginRegionParam, + OriginSlugParam, + RobotsTxtDimensionParam, + RobotsTxtDirectiveParam, + RobotsTxtDomainCategoryParam, + RobotsTxtPatternParam, + RobotsTxtUserAgentCategoryParam, + Sha256FingerprintParam, + SlugParam, + SpeedHistogramMetricParam, + TcpResetsTimeoutsDimensionParam, + TldFilterParam, + TldManagerParam, + TldParam, + TldTypeParam, + TrafficAnomalyStatusParam, +} from '../types/radar' +import { resolveAndInvoke } from '../utils' + +import type { RadarMCP } from '../radar.app' + +const RADAR_API_BASE = 'https://api.cloudflare.com/client/v4/radar' + +/** + * Helper function to make authenticated requests to the Radar API + * Used for endpoints not yet available in the Cloudflare SDK + */ +async function fetchRadarApi( + accessToken: string, + endpoint: string, + params: Record = {} +): Promise { + const url = new URL(`${RADAR_API_BASE}${endpoint}`) + + // Defense-in-depth: Ensure the resolved path stays within Radar API scope + // The URL constructor normalizes the path (resolves '..' and decodes percent-encoding), + // so we check the final pathname to prevent path traversal attacks + if (!url.pathname.startsWith('/client/v4/radar/')) { + throw new Error('Invalid endpoint path: must be within the Radar API scope') + } + + for (const [key, value] of Object.entries(params)) { + if (value === undefined || value === null) continue + + if (Array.isArray(value)) { + for (const item of value) { + url.searchParams.append(key, String(item)) + } + } else { + url.searchParams.set(key, String(value)) + } + } + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + }) + + if (!response.ok) { + const errorBody = await response.text() + throw new Error(`API request failed (${response.status}): ${errorBody}`) + } + + const data = (await response.json()) as { success: boolean; result: unknown; errors?: unknown[] } + + if (!data.success) { + throw new Error(`API returned error: ${JSON.stringify(data.errors)}`) + } + + return data.result +} + +export function registerRadarTools(agent: RadarMCP) { + agent.server.tool( + 'list_autonomous_systems', + 'List Autonomous Systems', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + location: LocationParam.optional(), + orderBy: AsOrderByParam, + }, + async ({ limit, offset, location, orderBy }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.entities.asns.list({ + limit, + offset, + location, + orderBy, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r.asns, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error listing ASes: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_as_details', + 'Get Autonomous System details by ASN', + { + asn: AsnParam, + }, + async ({ asn }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.entities.asns.get(asn) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r.asn, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting AS details: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_ip_details', + 'Get IP address information including full ASN details (name, country, population estimates from APNIC).', + { + ip: IpParam, + }, + async ({ ip }) => { + try { + const props = getProps(agent) + + // Fetch both IP details and ASN details in parallel + const [ipResult, asnResult] = await Promise.all([ + fetchRadarApi(props.accessToken, '/entities/ip', { ip }), + fetchRadarApi(props.accessToken, '/entities/asns/ip', { ip }), + ]) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: { + ip: ipResult, + asn: asnResult, + }, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting IP details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_traffic_anomalies', + 'Get traffic anomalies and outages', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + asn: AsnParam.optional(), + location: LocationParam.optional(), + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + }, + async ({ limit, offset, asn, location, dateStart, dateEnd, dateRange }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.trafficAnomalies.get({ + limit, + offset, + asn, + location, + dateRange, + dateStart, + dateEnd, + status: 'VERIFIED', + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r.trafficAnomalies, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting traffic anomalies: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_internet_services_ranking', + 'Get top Internet services', + { + limit: PaginationLimitParam, + date: DateListParam.optional(), + serviceCategory: InternetServicesCategoryParam.optional(), + }, + async ({ limit, date, serviceCategory }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.ranking.internetServices.top({ + limit, + date, + serviceCategory, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting Internet services ranking: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_domains_ranking', + 'Get top or trending domains', + { + limit: PaginationLimitParam, + date: DateListParam.optional(), + location: LocationListParam.optional(), + rankingType: DomainRankingTypeParam.optional(), + }, + async ({ limit, date, location, rankingType }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.ranking.top({ + limit, + date, + location, + rankingType, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting domains ranking: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_domain_rank_details', + 'Get domain rank details', + { + domain: DomainParam, + date: DateListParam.optional(), + }, + async ({ domain, date }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.ranking.domain.get(domain, { date }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting domain ranking details: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_http_data', + 'Retrieve HTTP traffic trends.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + geoId: GeoIdArrayParam, + dimension: HttpDimensionParam, + }, + async ({ dateStart, dateEnd, dateRange, asn, location, continent, geoId, dimension }) => { + try { + const props = getProps(agent) + + const result = await fetchRadarApi(props.accessToken, `/http/${dimension}`, { + asn, + continent, + location, + geoId, + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting HTTP data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_dns_queries_data', + 'Retrieve trends in DNS queries to the 1.1.1.1 resolver.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + dimension: DnsDimensionParam, + }, + async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { + try { + const props = getProps(agent) + + const result = await fetchRadarApi(props.accessToken, `/dns/${dimension}`, { + asn, + continent, + location, + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting DNS data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_l7_attack_data', + 'Retrieve application layer (L7) attack trends.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + dimension: L7AttackDimensionParam, + }, + async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await resolveAndInvoke(client.radar.attacks.layer7, dimension, { + asn, + continent, + location, + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting L7 attack data: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_l3_attack_data', + 'Retrieve network layer (L3/DDoS) attack trends.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + dimension: L3AttackDimensionParam, + }, + async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await resolveAndInvoke(client.radar.attacks.layer3, dimension, { + asn, + continent, + location, + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting L3 attack data: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_email_routing_data', + 'Retrieve Email Routing trends.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + dimension: EmailRoutingDimensionParam, + }, + async ({ dateStart, dateEnd, dateRange, dimension }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await resolveAndInvoke(client.radar.email.routing, dimension, { + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting Email Routing data: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_email_security_data', + 'Retrieve Email Security trends.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + dimension: EmailSecurityDimensionParam, + }, + async ({ dateStart, dateEnd, dateRange, dimension }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await resolveAndInvoke(client.radar.email.security, dimension, { + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting Email Security data: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_internet_speed_data', + 'Retrieve summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test.', + { + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + dimension: InternetSpeedDimensionParam, + orderBy: InternetSpeedOrderByParam.optional(), + }, + async ({ dateEnd, asn, location, continent, dimension, orderBy }) => { + if (orderBy && dimension === 'summary') { + throw new Error('Order by is only allowed for top locations and ASes') + } + + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await resolveAndInvoke(client.radar.quality.speed, dimension, { + asn, + continent, + location, + dateEnd, + orderBy, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting Internet speed data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_internet_quality_data', + 'Retrieves a summary or time series of bandwidth, latency, or DNS response time percentiles from the Radar Internet Quality Index (IQI).', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + format: z.enum(['summary', 'timeseriesGroups']), + metric: InternetQualityMetricParam, + }, + async ({ dateRange, dateStart, dateEnd, asn, location, continent, format, metric }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.quality.iqi[format]({ + asn, + continent, + location, + dateRange, + dateStart, + dateEnd, + metric, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + result: r, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting Internet quality data: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_ai_data', + 'Retrieves AI-related data, including traffic from AI user agents, as well as popular models and model tasks specifically from Cloudflare Workers AI.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + dimension: AiDimensionParam, + }, + async ({ dateRange, dateStart, dateEnd, asn, location, continent, dimension }) => { + try { + const props = getProps(agent) + + const result = await fetchRadarApi(props.accessToken, `/ai/${dimension}`, { + asn, + continent, + location, + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting AI data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // BGP Tools + // TODO: Replace with SDK when BGP hijacks/leaks endpoints work correctly in cloudflare SDK + // ============================================================ + + agent.server.tool( + 'get_bgp_hijacks', + 'Retrieve BGP hijack events. BGP hijacks occur when an AS announces routes it does not own, potentially redirecting traffic.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + hijackerAsn: BgpHijackerAsnParam, + victimAsn: BgpVictimAsnParam, + involvedAsn: BgpInvolvedAsnParam, + involvedCountry: BgpInvolvedCountryParam, + prefix: BgpPrefixParam, + minConfidence: BgpMinConfidenceParam, + maxConfidence: BgpMaxConfidenceParam, + sortBy: BgpSortByParam, + sortOrder: BgpSortOrderParam, + }, + async ({ + limit, + offset, + dateRange, + dateStart, + dateEnd, + hijackerAsn, + victimAsn, + involvedAsn, + involvedCountry, + prefix, + minConfidence, + maxConfidence, + sortBy, + sortOrder, + }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/hijacks/events', { + page: offset ? Math.floor(offset / (limit || 10)) + 1 : 1, + per_page: limit, + dateRange, + dateStart, + dateEnd, + hijackerAsn, + victimAsn, + involvedAsn, + involvedCountry, + prefix, + minConfidence, + maxConfidence, + sortBy, + sortOrder, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting BGP hijacks: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_leaks', + 'Retrieve BGP route leak events. Route leaks occur when an AS improperly announces routes learned from one peer to another.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + leakAsn: BgpLeakAsnParam, + involvedAsn: BgpInvolvedAsnParam, + involvedCountry: BgpInvolvedCountryParam, + sortBy: BgpSortByParam, + sortOrder: BgpSortOrderParam, + }, + async ({ + limit, + offset, + dateRange, + dateStart, + dateEnd, + leakAsn, + involvedAsn, + involvedCountry, + sortBy, + sortOrder, + }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/leaks/events', { + page: offset ? Math.floor(offset / (limit || 10)) + 1 : 1, + per_page: limit, + dateRange, + dateStart, + dateEnd, + leakAsn, + involvedAsn, + involvedCountry, + sortBy, + sortOrder, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting BGP leaks: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_route_stats', + 'Retrieve BGP routing table statistics including number of routes, origin ASes, and more.', + { + asn: AsnParam.optional(), + location: LocationParam.optional(), + }, + async ({ asn, location }) => { + try { + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) + const r = await client.radar.bgp.routes.stats({ + asn, + location, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result: r }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting BGP route stats: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Bots Tools + // TODO: Replace with SDK when bots endpoints are added to cloudflare SDK + // ============================================================ + + agent.server.tool( + 'get_bots_data', + 'Retrieve bot traffic data including trends by bot name, operator, category, and kind. Covers AI crawlers, search engines, monitoring bots, and more.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + bot: BotNameParam, + botOperator: BotOperatorParam, + botCategory: BotCategoryParam, + botKind: BotKindParam, + botVerificationStatus: BotVerificationStatusParam, + dimension: BotsDimensionParam, + limitPerGroup: LimitPerGroupParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + asn, + continent, + location, + bot, + botOperator, + botCategory, + botKind, + botVerificationStatus, + dimension, + limitPerGroup, + }) => { + try { + const props = getProps(agent) + + const endpoint = dimension === 'timeseries' ? '/bots/timeseries' : `/bots/${dimension}` + + const result = await fetchRadarApi(props.accessToken, endpoint, { + asn, + continent, + location, + dateRange, + dateStart, + dateEnd, + bot, + botOperator, + botCategory, + botKind, + botVerificationStatus, + limitPerGroup: dimension !== 'timeseries' ? limitPerGroup : undefined, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting bots data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Certificate Transparency Tools + // TODO: Replace with SDK when CT endpoints are added to cloudflare SDK + // ============================================================ + + agent.server.tool( + 'get_certificate_transparency_data', + 'Retrieve Certificate Transparency (CT) log data. CT provides visibility into SSL/TLS certificates issued for domains, useful for security monitoring.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + ca: CtCaParam, + caOwner: CtCaOwnerParam, + duration: CtDurationParam, + entryType: CtEntryTypeParam, + tld: CtTldParam, + validationLevel: CtValidationLevelParam, + publicKeyAlgorithm: CtPublicKeyAlgorithmParam, + dimension: CtDimensionParam, + limitPerGroup: LimitPerGroupParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + ca, + caOwner, + duration, + entryType, + tld, + validationLevel, + publicKeyAlgorithm, + dimension, + limitPerGroup, + }) => { + try { + const props = getProps(agent) + + const result = await fetchRadarApi(props.accessToken, `/ct/${dimension}`, { + dateRange, + dateStart, + dateEnd, + ca, + caOwner, + duration, + entryType, + tld, + validationLevel, + publicKeyAlgorithm, + limitPerGroup: dimension !== 'timeseries' ? limitPerGroup : undefined, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting CT data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // NetFlows Tools + // TODO: Replace with SDK when netflows endpoints support geoId in cloudflare SDK + // ============================================================ + + agent.server.tool( + 'get_netflows_data', + 'Retrieve NetFlows traffic data showing network traffic patterns. Supports filtering by ADM1 (administrative level 1, e.g., states/provinces) via geoId.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + geoId: GeoIdArrayParam, + product: NetflowsProductParam, + normalization: NormalizationParam, + dimension: NetflowsDimensionParam, + limitPerGroup: LimitPerGroupParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + asn, + continent, + location, + geoId, + product, + normalization, + dimension, + limitPerGroup, + }) => { + try { + const props = getProps(agent) + + let endpoint: string + if (dimension === 'timeseries') { + endpoint = '/netflows/timeseries' + } else if (dimension === 'summary') { + endpoint = '/netflows/summary' + } else { + endpoint = `/netflows/${dimension}` + } + + const result = await fetchRadarApi(props.accessToken, endpoint, { + asn, + continent, + location, + geoId, + dateRange, + dateStart, + dateEnd, + product, + normalization, + limitPerGroup: !['timeseries', 'summary'].includes(dimension) ? limitPerGroup : undefined, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting NetFlows data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Cloud Observatory / Origins Tools + // TODO: Replace with SDK when origins endpoints are added to cloudflare SDK + // ============================================================ + + agent.server.tool( + 'list_origins', + 'List cloud provider origins (hyperscalers) available in Cloud Observatory. Returns Amazon (AWS), Google (GCP), Microsoft (Azure), and Oracle (OCI) with their available regions.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + }, + async ({ limit, offset }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/origins', { + limit, + offset, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error listing origins: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_origin_details', + 'Get details for a specific cloud provider origin, including all available regions.', + { + slug: OriginSlugParam, + }, + async ({ slug }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/origins/${slug}`) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting origin details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_origins_data', + 'Retrieve cloud provider (AWS, GCP, Azure, OCI) performance metrics. Supports timeseries, summaries grouped by region/success_rate/percentile, and grouped timeseries.', + { + dimension: OriginDataDimensionParam, + origin: OriginArrayParam, + metric: OriginMetricParam, + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + region: OriginRegionParam, + limitPerGroup: LimitPerGroupParam, + normalization: OriginNormalizationParam, + }, + async ({ + dimension, + origin, + metric, + dateRange, + dateStart, + dateEnd, + region, + limitPerGroup, + normalization, + }) => { + try { + const props = getProps(agent) + + let endpoint: string + if (dimension === 'timeseries') { + endpoint = '/origins/timeseries' + } else if (dimension.startsWith('summary/')) { + const groupBy = dimension.replace('summary/', '') + endpoint = `/origins/summary/${groupBy}` + } else { + const groupBy = dimension.replace('timeseriesGroups/', '') + endpoint = `/origins/timeseries_groups/${groupBy}` + } + + const result = await fetchRadarApi(props.accessToken, endpoint, { + origin, + metric, + dateRange, + dateStart, + dateEnd, + region, + limitPerGroup: dimension !== 'timeseries' ? limitPerGroup : undefined, + normalization: dimension.startsWith('timeseriesGroups/') ? normalization : undefined, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting origins data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Robots.txt Tools + // ============================================================ + + agent.server.tool( + 'get_robots_txt_data', + 'Retrieve robots.txt analysis data. Shows how websites configure crawler access rules, particularly for AI crawlers. Useful for understanding web crawler policies across domains.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + date: DateListParam.optional(), + directive: RobotsTxtDirectiveParam, + pattern: RobotsTxtPatternParam, + domainCategory: RobotsTxtDomainCategoryParam, + userAgentCategory: RobotsTxtUserAgentCategoryParam, + dimension: RobotsTxtDimensionParam, + limitPerGroup: LimitPerGroupParam, + limit: PaginationLimitParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + date, + directive, + pattern, + domainCategory, + userAgentCategory, + dimension, + limitPerGroup, + limit, + }) => { + try { + const props = getProps(agent) + + const endpoint = `/robots_txt/${dimension}` + + const result = await fetchRadarApi(props.accessToken, endpoint, { + dateRange, + dateStart, + dateEnd, + date, + directive, + pattern, + domainCategory, + userAgentCategory, + limitPerGroup, + limit, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting robots.txt data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Bots Crawlers Tools + // ============================================================ + + agent.server.tool( + 'get_bots_crawlers_data', + 'Retrieve web crawler HTTP request data. Shows crawler traffic patterns by client type, user agent, referrer, and industry. Useful for analyzing crawler behavior and traffic distribution.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + dimension: BotsCrawlersDimensionParam, + format: BotsCrawlersFormatParam, + botOperator: BotOperatorParam, + vertical: CrawlerVerticalParam, + industry: CrawlerIndustryParam, + clientType: CrawlerClientTypeParam, + limitPerGroup: LimitPerGroupParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + dimension, + format, + botOperator, + vertical, + industry, + clientType, + limitPerGroup, + }) => { + try { + const props = getProps(agent) + + const endpoint = `/bots/crawlers/${format}/${dimension}` + + const result = await fetchRadarApi(props.accessToken, endpoint, { + dateRange, + dateStart, + dateEnd, + botOperator, + vertical, + industry, + clientType, + limitPerGroup, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting bots crawlers data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'list_bots', + 'List known bots with their details. Includes AI crawlers, search engines, monitoring bots, and more. Filter by category, operator, kind, or verification status.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + botCategory: z + .enum([ + 'SEARCH_ENGINE_CRAWLER', + 'SEARCH_ENGINE_OPTIMIZATION', + 'MONITORING_AND_ANALYTICS', + 'ADVERTISING_AND_MARKETING', + 'SOCIAL_MEDIA_MARKETING', + 'PAGE_PREVIEW', + 'ACADEMIC_RESEARCH', + 'SECURITY', + 'ACCESSIBILITY', + 'WEBHOOKS', + 'FEED_FETCHER', + 'AI_CRAWLER', + 'AGGREGATOR', + 'AI_ASSISTANT', + 'AI_SEARCH', + 'ARCHIVER', + ]) + .optional() + .describe('Filter by bot category.'), + botOperator: z.string().optional().describe('Filter by bot operator name.'), + kind: z.enum(['AGENT', 'BOT']).optional().describe('Filter by bot kind.'), + botVerificationStatus: z + .enum(['VERIFIED']) + .optional() + .describe('Filter by verification status.'), + }, + async ({ limit, offset, botCategory, botOperator, kind, botVerificationStatus }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bots', { + limit, + offset, + botCategory, + botOperator, + kind, + botVerificationStatus, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error listing bots: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bot_details', + 'Get detailed information about a specific bot by its slug identifier.', + { + botSlug: SlugParam.describe('The bot slug identifier (e.g., "googlebot", "bingbot").'), + }, + async ({ botSlug }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/bots/${botSlug}`) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting bot details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Leaked Credential Checks Tools + // ============================================================ + + agent.server.tool( + 'get_leaked_credentials_data', + 'Retrieve trends in HTTP authentication requests and compromised credential detection. Shows distribution by compromised status and bot class.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + botClass: LeakedCredentialsBotClassParam, + compromised: LeakedCredentialsCompromisedParam, + dimension: LeakedCredentialsDimensionParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + asn, + continent, + location, + botClass, + compromised, + dimension, + }) => { + try { + const props = getProps(agent) + + let endpoint: string + if (dimension === 'timeseries') { + endpoint = '/leaked_credential_checks/timeseries' + } else { + endpoint = `/leaked_credential_checks/${dimension}` + } + + const result = await fetchRadarApi(props.accessToken, endpoint, { + dateRange, + dateStart, + dateEnd, + asn, + continent, + location, + botClass, + compromised, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting leaked credentials data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // AS112 Tools + // ============================================================ + + agent.server.tool( + 'get_as112_data', + 'Retrieve AS112 DNS sink hole data. AS112 handles reverse DNS lookups for private IP addresses (RFC 1918). Useful for analyzing DNS misconfiguration patterns.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + continent: ContinentArrayParam, + location: LocationArrayParam, + queryType: As112QueryTypeParam, + protocol: As112ProtocolParam, + responseCode: As112ResponseCodeParam, + dimension: As112DimensionParam, + }, + async ({ + dateRange, + dateStart, + dateEnd, + continent, + location, + queryType, + protocol, + responseCode, + dimension, + }) => { + try { + const props = getProps(agent) + + let endpoint: string + if (dimension === 'timeseries') { + endpoint = '/as112/timeseries' + } else if (dimension === 'top/locations') { + endpoint = '/as112/top/locations' + } else { + endpoint = `/as112/${dimension}` + } + + const result = await fetchRadarApi(props.accessToken, endpoint, { + dateRange, + dateStart, + dateEnd, + continent, + location, + queryType, + protocol, + responseCode, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting AS112 data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Geolocation Tools + // ============================================================ + + agent.server.tool( + 'list_geolocations', + 'List available geolocations (ADM1 - administrative divisions like states/provinces). Use this to find GeoNames IDs for filtering HTTP and NetFlows data by region.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + geoId: z.string().optional().describe('Filter by specific GeoNames ID.'), + location: LocationParam.optional(), + }, + async ({ limit, offset, geoId, location }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/geolocations', { + limit, + offset, + geoId, + location, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error listing geolocations: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_geolocation_details', + 'Get details for a specific geolocation by its GeoNames ID.', + { + geoId: GeoIdParam, + }, + async ({ geoId }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/geolocations/${geoId}`) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting geolocation details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // TCP Resets/Timeouts Tools + // ============================================================ + + agent.server.tool( + 'get_tcp_resets_timeouts_data', + 'Retrieve TCP connection quality metrics including resets and timeouts. Useful for understanding connection reliability across networks and locations.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + dimension: TcpResetsTimeoutsDimensionParam, + }, + async ({ dateRange, dateStart, dateEnd, asn, continent, location, dimension }) => { + try { + const props = getProps(agent) + + const endpoint = + dimension === 'summary' + ? '/tcp_resets_timeouts/summary' + : '/tcp_resets_timeouts/timeseries_groups' + + const result = await fetchRadarApi(props.accessToken, endpoint, { + dateRange, + dateStart, + dateEnd, + asn, + continent, + location, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting TCP resets/timeouts data: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Annotations/Outages Tools + // ============================================================ + + agent.server.tool( + 'get_annotations', + 'Retrieve annotations including Internet events, outages, and anomalies from various Cloudflare data sources.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + dataSource: AnnotationDataSourceParam, + eventType: AnnotationEventTypeParam, + asn: AsnParam.optional(), + location: LocationParam.optional(), + }, + async ({ + limit, + offset, + dateRange, + dateStart, + dateEnd, + dataSource, + eventType, + asn, + location, + }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/annotations', { + limit, + offset, + dateRange, + dateStart, + dateEnd, + dataSource, + eventType, + asn, + location, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting annotations: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_outages', + 'Retrieve Internet outages and anomalies. Provides information about detected connectivity issues across ASes and locations.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + asn: AsnParam.optional(), + location: LocationParam.optional(), + }, + async ({ limit, offset, dateRange, dateStart, dateEnd, asn, location }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/annotations/outages', { + limit, + offset, + dateRange, + dateStart, + dateEnd, + asn, + location, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting outages: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Certificate Transparency Authorities & Logs Tools + // ============================================================ + + agent.server.tool( + 'list_ct_authorities', + 'List Certificate Authorities (CAs) tracked in Certificate Transparency logs.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + }, + async ({ limit, offset }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/ct/authorities', { + limit, + offset, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error listing CT authorities: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_ct_authority_details', + 'Get details for a specific Certificate Authority by its SHA256 fingerprint.', + { + caSlug: Sha256FingerprintParam.describe( + 'The Certificate Authority SHA256 fingerprint (64 hexadecimal characters).' + ), + }, + async ({ caSlug }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/ct/authorities/${caSlug}`) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting CT authority details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'list_ct_logs', + 'List Certificate Transparency logs.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + }, + async ({ limit, offset }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/ct/logs', { + limit, + offset, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error listing CT logs: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_ct_log_details', + 'Get details for a specific Certificate Transparency log by its slug.', + { + logSlug: SlugParam.describe('The Certificate Transparency log slug identifier.'), + }, + async ({ logSlug }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/ct/logs/${logSlug}`) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting CT log details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // BGP Additional Tools + // ============================================================ + + agent.server.tool( + 'get_bgp_timeseries', + 'Retrieve BGP updates time series data. Shows BGP announcement and withdrawal patterns over time.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + prefix: BgpPrefixArrayParam, + updateType: BgpUpdateTypeParam, + }, + async ({ dateRange, dateStart, dateEnd, asn, prefix, updateType }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/timeseries', { + dateRange, + dateStart, + dateEnd, + asn, + prefix, + updateType, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP timeseries: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_top_ases', + 'Get top Autonomous Systems by BGP update count.', + { + limit: PaginationLimitParam, + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + prefix: BgpPrefixArrayParam, + updateType: BgpUpdateTypeParam, + }, + async ({ limit, dateRange, dateStart, dateEnd, asn, prefix, updateType }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/top/ases', { + limit, + dateRange, + dateStart, + dateEnd, + asn, + prefix, + updateType, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP top ASes: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_top_prefixes', + 'Get top IP prefixes by BGP update count.', + { + limit: PaginationLimitParam, + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + updateType: BgpUpdateTypeParam, + }, + async ({ limit, dateRange, dateStart, dateEnd, asn, updateType }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/top/prefixes', { + limit, + dateRange, + dateStart, + dateEnd, + asn, + updateType, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP top prefixes: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_moas', + 'Get Multi-Origin AS (MOAS) prefixes. MOAS occurs when a prefix is announced by multiple ASes, which can indicate hijacking or legitimate anycast.', + { + origin: BgpOriginParam, + prefix: BgpPrefixParam, + invalidOnly: BgpInvalidOnlyParam, + }, + async ({ origin, prefix, invalidOnly }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/routes/moas', { + origin, + prefix, + invalid_only: invalidOnly, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP MOAS: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_pfx2as', + 'Get prefix-to-ASN mapping. Useful for looking up which AS announces a given IP prefix.', + { + prefix: BgpPrefixParam, + origin: BgpOriginParam, + rpkiStatus: BgpRpkiStatusParam, + longestPrefixMatch: BgpLongestPrefixMatchParam, + }, + async ({ prefix, origin, rpkiStatus, longestPrefixMatch }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/routes/pfx2as', { + prefix, + origin, + rpkiStatus, + longestPrefixMatch, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP pfx2as: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_ip_space_timeseries', + 'Retrieve announced IP address space time series data. Shows the count of announced IPv4 /24s and IPv6 /48s over time. Essential for monitoring BGP route withdrawals, IPv6 address space changes, and detecting significant routing events by ASN or country.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + location: LocationArrayParam, + ipVersion: BgpIpVersionParam, + }, + async ({ dateRange, dateStart, dateEnd, asn, location, ipVersion }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/ips/timeseries', { + dateRange, + dateStart, + dateEnd, + asn, + location, + ipVersion, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP IP space timeseries: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_bgp_routes_realtime', + 'Get real-time BGP routes for a specific IP prefix using public route collectors (RouteViews and RIPE RIS). Shows current routing state including AS paths, RPKI validation status, and visibility across peers. Useful for troubleshooting routing issues and verifying route announcements.', + { + prefix: BgpPrefixParam, + }, + async ({ prefix }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/routes/realtime', { + prefix, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting real-time BGP routes: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // AS Sets and Relationships Tools + // ============================================================ + + agent.server.tool( + 'get_as_set', + 'Get IRR AS-SETs that an Autonomous System is a member of. AS-SETs are used in routing policies.', + { + asn: AsnParam, + }, + async ({ asn }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/entities/asns/${asn}/as_set`) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting AS set: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_as_relationships', + 'Get AS-level relationships for an Autonomous System. Shows peer, upstream, and downstream relationships with other ASes.', + { + asn: AsnParam, + asn2: z + .number() + .int() + .positive() + .optional() + .describe('Optional second ASN to check specific relationship.'), + }, + async ({ asn, asn2 }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/entities/asns/${asn}/rel`, { + asn2, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting AS relationships: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // TLD Tools + // ============================================================ + + agent.server.tool( + 'list_tlds', + 'List top-level domains (TLDs) including generic, country-code, and sponsored TLDs. Filter by type or manager.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + tldType: TldTypeParam, + manager: TldManagerParam, + tld: TldFilterParam, + }, + async ({ limit, offset, tldType, manager, tld }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/tlds', { + limit, + offset, + tldType, + manager, + tld, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error listing TLDs: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'get_tld_details', + 'Get detailed information about a specific top-level domain (TLD).', + { + tld: TldParam, + }, + async ({ tld }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, `/tlds/${tld}`) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting TLD details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Ranking Timeseries Tool + // ============================================================ + + agent.server.tool( + 'get_domains_ranking_timeseries', + 'Get domain ranking timeseries data. Track how specific domains rank over time.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + domains: DomainsArrayParam, + domainCategory: DomainCategoryArrayParam, + location: LocationArrayParam, + limit: PaginationLimitParam, + }, + async ({ dateRange, dateStart, dateEnd, domains, domainCategory, location, limit }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/ranking/timeseries_groups', { + dateRange, + dateStart, + dateEnd, + domains, + domainCategory, + location, + limit, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting domains ranking timeseries: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Speed Histogram Tool + // ============================================================ + + agent.server.tool( + 'get_speed_histogram', + 'Get speed test histogram data. Shows distribution of speed test results for bandwidth, latency, or jitter.', + { + dateEnd: DateEndArrayParam.optional(), + asn: AsnArrayParam, + continent: ContinentArrayParam, + location: LocationArrayParam, + metric: SpeedHistogramMetricParam, + bucketSize: BucketSizeParam, + }, + async ({ dateEnd, asn, continent, location, metric, bucketSize }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/quality/speed/histogram', { + dateEnd, + asn, + continent, + location, + metric, + bucketSize, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting speed histogram: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Internet Services Timeseries Tool + // ============================================================ + + agent.server.tool( + 'get_internet_services_timeseries', + 'Track internet service ranking changes over time. Useful for monitoring how services like ChatGPT, Google, etc. rank over time.', + { + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + serviceCategory: InternetServicesCategoryParam.optional(), + limit: PaginationLimitParam, + }, + async ({ dateRange, dateStart, dateEnd, serviceCategory, limit }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi( + props.accessToken, + '/ranking/internet_services/timeseries_groups', + { + dateRange, + dateStart, + dateEnd, + serviceCategory, + limit, + } + ) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting internet services timeseries: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Outages by Location Tool + // ============================================================ + + agent.server.tool( + 'get_outages_by_location', + 'Get outage counts aggregated by location. Useful for identifying which countries have the most Internet outages.', + { + limit: PaginationLimitParam, + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + }, + async ({ limit, dateRange, dateStart, dateEnd }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/annotations/outages/locations', { + limit, + dateRange, + dateStart, + dateEnd, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting outages by location: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // Traffic Anomalies by Location Tool + // ============================================================ + + agent.server.tool( + 'get_traffic_anomalies_by_location', + 'Get traffic anomalies aggregated by location. Shows which countries have the most detected outage signals, automatically detected by Radar.', + { + limit: PaginationLimitParam, + dateRange: DateRangeParam.optional(), + dateStart: DateStartParam.optional(), + dateEnd: DateEndParam.optional(), + status: TrafficAnomalyStatusParam, + }, + async ({ limit, dateRange, dateStart, dateEnd, status }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/traffic_anomalies/locations', { + limit, + dateRange, + dateStart, + dateEnd, + status, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting traffic anomalies by location: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // BGP Routing Table ASes Tool + // ============================================================ + + agent.server.tool( + 'get_bgp_routing_table_ases', + 'List all ASes in global routing tables with routing statistics (prefix counts, IPv4/IPv6 address count, RPKI validation status). Data comes from public BGP MRT archives.', + { + limit: PaginationLimitParam, + location: LocationParam.optional(), + sortBy: BgpRoutesAsesSortByParam, + sortOrder: BgpSortOrderParam, + }, + async ({ limit, location, sortBy, sortOrder }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/routes/ases', { + limit, + location, + sortBy, + sortOrder, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP routing table ASes: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // ============================================================ + // BGP Top ASes by Prefixes Tool + // ============================================================ + + agent.server.tool( + 'get_bgp_top_ases_by_prefixes', + 'Get top ASes ordered by announced prefix count. Useful for understanding which networks have the largest routing footprint. Data comes from public BGP MRT archives and updates every 2 hours.', + { + limit: PaginationLimitParam, + country: LocationParam.optional().describe('Filter by country (alpha-2 code).'), + }, + async ({ limit, country }) => { + try { + const props = getProps(agent) + const result = await fetchRadarApi(props.accessToken, '/bgp/top/ases/prefixes', { + limit, + country, + }) + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting BGP top ASes by prefixes: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) +} diff --git a/apps/radar/src/tools/radar.ts b/apps/radar/src/tools/radar.ts deleted file mode 100644 index aaaab47c..00000000 --- a/apps/radar/src/tools/radar.ts +++ /dev/null @@ -1,730 +0,0 @@ -import { z } from 'zod' - -import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' -import { PaginationLimitParam, PaginationOffsetParam } from '@repo/mcp-common/src/types/shared' - -import { - AiDimensionParam, - AsnArrayParam, - AsnParam, - AsOrderByParam, - ContinentArrayParam, - DateEndArrayParam, - DateEndParam, - DateListParam, - DateRangeArrayParam, - DateRangeParam, - DateStartArrayParam, - DateStartParam, - DnsDimensionParam, - DomainParam, - DomainRankingTypeParam, - EmailRoutingDimensionParam, - EmailSecurityDimensionParam, - HttpDimensionParam, - InternetQualityMetricParam, - InternetServicesCategoryParam, - InternetSpeedDimensionParam, - InternetSpeedOrderByParam, - IpParam, - L3AttackDimensionParam, - L7AttackDimensionParam, - LocationArrayParam, - LocationListParam, - LocationParam, -} from '../types/radar' -import { resolveAndInvoke } from '../utils' - -import type { RadarMCP } from '../index' - -export function registerRadarTools(agent: RadarMCP) { - agent.server.tool( - 'list_autonomous_systems', - 'List Autonomous Systems', - { - limit: PaginationLimitParam, - offset: PaginationOffsetParam, - location: LocationParam.optional(), - orderBy: AsOrderByParam, - }, - async ({ limit, offset, location, orderBy }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.entities.asns.list({ - limit, - offset, - location, - orderBy, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r.asns, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error listing ASes: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_as_details', - 'Get Autonomous System details by ASN', - { - asn: AsnParam, - }, - async ({ asn }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.entities.asns.get(asn) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r.asn, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting AS details: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_ip_details', - 'Get IP address information', - { - ip: IpParam, - }, - async ({ ip }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.entities.get({ ip }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r.ip, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting IP details: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_traffic_anomalies', - 'Get traffic anomalies and outages', - { - limit: PaginationLimitParam, - offset: PaginationOffsetParam, - asn: AsnParam.optional(), - location: LocationParam.optional(), - dateRange: DateRangeParam.optional(), - dateStart: DateStartParam.optional(), - dateEnd: DateEndParam.optional(), - }, - async ({ limit, offset, asn, location, dateStart, dateEnd, dateRange }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.trafficAnomalies.get({ - limit, - offset, - asn, - location, - dateRange, - dateStart, - dateEnd, - status: 'VERIFIED', - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r.trafficAnomalies, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting IP details: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_internet_services_ranking', - 'Get top Internet services', - { - limit: PaginationLimitParam, - date: DateListParam.optional(), - serviceCategory: InternetServicesCategoryParam.optional(), - }, - async ({ limit, date, serviceCategory }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.ranking.internetServices.top({ - limit, - date, - serviceCategory, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting Internet services ranking: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_domains_ranking', - 'Get top or trending domains', - { - limit: PaginationLimitParam, - date: DateListParam.optional(), - location: LocationListParam.optional(), - rankingType: DomainRankingTypeParam.optional(), - }, - async ({ limit, date, location, rankingType }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.ranking.top({ - limit, - date, - location, - rankingType, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting domains ranking: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_domain_rank_details', - 'Get domain rank details', - { - domain: DomainParam, - date: DateListParam.optional(), - }, - async ({ domain, date }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.ranking.domain.get(domain, { date }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting domain ranking details: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_http_data', - 'Retrieve HTTP traffic trends.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - dimension: HttpDimensionParam, - }, - async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.http, dimension, { - asn, - continent, - location, - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting HTTP data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_dns_queries_data', - 'Retrieve trends in DNS queries to the 1.1.1.1 resolver.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - dimension: DnsDimensionParam, - }, - async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.dns, dimension, { - asn, - continent, - location, - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting DNS data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_l7_attack_data', - 'Retrieve application layer (L7) attack trends.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - dimension: L7AttackDimensionParam, - }, - async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.attacks.layer7, dimension, { - asn, - continent, - location, - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting L7 attack data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_l3_attack_data', - 'Retrieve application layer (L3) attack trends.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - dimension: L3AttackDimensionParam, - }, - async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.attacks.layer3, dimension, { - asn, - continent, - location, - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting L3 attack data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_email_routing_data', - 'Retrieve Email Routing trends.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - dimension: EmailRoutingDimensionParam, - }, - async ({ dateStart, dateEnd, dateRange, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.email.routing, dimension, { - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting Email Routing data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_email_security_data', - 'Retrieve Email Security trends.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - dimension: EmailSecurityDimensionParam, - }, - async ({ dateStart, dateEnd, dateRange, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.email.security, dimension, { - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting Email Security data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_internet_speed_data', - 'Retrieve summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test.', - { - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - dimension: InternetSpeedDimensionParam, - orderBy: InternetSpeedOrderByParam.optional(), - }, - async ({ dateEnd, asn, location, continent, dimension, orderBy }) => { - if (orderBy && dimension === 'summary') { - throw new Error('Order by is only allowed for top locations and ASes') - } - - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.quality.speed, dimension, { - asn, - continent, - location, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting Internet speed data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_internet_quality_data', - 'Retrieves a summary or time series of bandwidth, latency, or DNS response time percentiles from the Radar Internet Quality Index (IQI).', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - format: z.enum(['summary', 'timeseriesGroups']), - metric: InternetQualityMetricParam, - }, - async ({ dateRange, dateStart, dateEnd, asn, location, continent, format, metric }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await client.radar.quality.iqi[format]({ - asn, - continent, - location, - dateRange, - dateStart, - dateEnd, - metric, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting Internet quality data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) - - agent.server.tool( - 'get_ai_data', - 'Retrieves AI-related data, including traffic from AI user agents, as well as popular models and model tasks specifically from Cloudflare Workers AI.', - { - dateRange: DateRangeArrayParam.optional(), - dateStart: DateStartArrayParam.optional(), - dateEnd: DateEndArrayParam.optional(), - asn: AsnArrayParam, - continent: ContinentArrayParam, - location: LocationArrayParam, - dimension: AiDimensionParam, - }, - async ({ dateRange, dateStart, dateEnd, asn, location, continent, dimension }) => { - try { - const client = getCloudflareClient(agent.props.accessToken) - const r = await resolveAndInvoke(client.radar.ai, dimension, { - asn, - continent, - location, - dateRange, - dateStart, - dateEnd, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: r, - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error getting AI data: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) -} diff --git a/apps/radar/src/tools/url-scanner.tools.ts b/apps/radar/src/tools/url-scanner.tools.ts new file mode 100644 index 00000000..46c71323 --- /dev/null +++ b/apps/radar/src/tools/url-scanner.tools.ts @@ -0,0 +1,324 @@ +import { z } from 'zod' + +import { getProps } from '@repo/mcp-common/src/get-props' + +import { + CreateScanResult, + ScanIdParam, + ScanVisibilityParam, + ScreenshotResolutionParam, + SearchQueryParam, + SearchSizeParam, + UrlParam, +} from '../types/url-scanner' + +import type { RadarMCP } from '../radar.app' + +const URLSCANNER_API_BASE = 'https://api.cloudflare.com/client/v4/accounts' + +type ToolResponse = { + content: Array<{ type: 'text'; text: string }> +} + +/** + * Helper to get account ID or return error response + */ +async function getAccountIdOrError( + agent: RadarMCP +): Promise<{ accountId: string } | { error: ToolResponse }> { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + error: { + content: [ + { + type: 'text' as const, + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + }, + } + } + return { accountId } +} + +export function registerUrlScannerTools(agent: RadarMCP) { + // Search URL scans + agent.server.tool( + 'search_url_scans', + "Search URL scans using ElasticSearch-like query syntax. Examples: 'page.domain:example.com', 'verdicts.malicious:true', 'page.asn:AS24940 AND hash:xxx', 'apikey:me AND date:[2025-01 TO 2025-02]'", + { + query: SearchQueryParam, + size: SearchSizeParam, + }, + async ({ query, size }) => { + const result = await getAccountIdOrError(agent) + if ('error' in result) return result.error + + try { + const props = getProps(agent) + const url = new URL(`${URLSCANNER_API_BASE}/${result.accountId}/urlscanner/v2/search`) + if (query) url.searchParams.set('q', query) + if (size) url.searchParams.set('size', String(size)) + + const res = await fetch(url.toString(), { + headers: { Authorization: `Bearer ${props.accessToken}` }, + }) + + if (!res.ok) { + const errorData = await res.json().catch(() => ({})) + throw new Error(`Search failed: ${res.status} ${JSON.stringify(errorData)}`) + } + + const data = await res.json() + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify(data), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error searching scans: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // Create URL scan + agent.server.tool( + 'create_url_scan', + 'Submit a URL to scan. Returns the scan UUID which can be used to retrieve results.', + { + url: UrlParam, + visibility: ScanVisibilityParam, + screenshotResolution: ScreenshotResolutionParam, + }, + async ({ url, visibility, screenshotResolution }) => { + const result = await getAccountIdOrError(agent) + if ('error' in result) return result.error + + try { + const props = getProps(agent) + + const body: Record = { url } + if (visibility) body.visibility = visibility + if (screenshotResolution) body.screenshotsResolutions = [screenshotResolution] + + const res = await fetch(`${URLSCANNER_API_BASE}/${result.accountId}/urlscanner/v2/scan`, { + method: 'POST', + headers: { + Authorization: `Bearer ${props.accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }) + + if (!res.ok) { + const errorData = await res.json().catch(() => ({})) + throw new Error(`Scan submission failed: ${res.status} ${JSON.stringify(errorData)}`) + } + + const scan = CreateScanResult.parse(await res.json()) + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ + message: 'Scan submitted successfully', + scanId: scan.uuid, + url, + visibility: visibility || 'Public', + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error creating scan: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // Get URL scan result + agent.server.tool( + 'get_url_scan', + 'Get the results of a URL scan by its UUID. Returns detailed information including verdicts, page info, requests, cookies, and more.', + { + scanId: ScanIdParam, + }, + async ({ scanId }) => { + const result = await getAccountIdOrError(agent) + if ('error' in result) return result.error + + try { + const props = getProps(agent) + + const res = await fetch( + `${URLSCANNER_API_BASE}/${result.accountId}/urlscanner/v2/result/${scanId}`, + { + headers: { Authorization: `Bearer ${props.accessToken}` }, + } + ) + + if (!res.ok) { + if (res.status === 404) { + throw new Error('Scan not found or still in progress') + } + const errorData = await res.json().catch(() => ({})) + throw new Error(`Failed to get scan: ${res.status} ${JSON.stringify(errorData)}`) + } + + const data = (await res.json()) as { + verdicts?: unknown + page?: unknown + stats?: unknown + lists?: unknown + } + // Return a summary of the most useful fields + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ + verdicts: data.verdicts, + page: data.page, + stats: data.stats, + lists: data.lists, + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting scan: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // Get scan screenshot + agent.server.tool( + 'get_url_scan_screenshot', + 'Get the screenshot URL for a completed scan.', + { + scanId: ScanIdParam, + resolution: z + .enum(['desktop', 'mobile', 'tablet']) + .default('desktop') + .optional() + .describe('Screenshot resolution/device type.'), + }, + async ({ scanId, resolution }) => { + const result = await getAccountIdOrError(agent) + if ('error' in result) return result.error + + try { + const props = getProps(agent) + const res = resolution || 'desktop' + + const screenshotUrl = `${URLSCANNER_API_BASE}/${result.accountId}/urlscanner/v2/screenshots/${scanId}.png` + // Verify the screenshot exists + const response = await fetch(screenshotUrl, { + method: 'HEAD', + headers: { Authorization: `Bearer ${props.accessToken}` }, + }) + + if (!response.ok) { + throw new Error('Screenshot not available. The scan may still be in progress or failed.') + } + + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify({ + screenshotUrl, + resolution: res, + note: 'Use this URL with Authorization header to download the screenshot', + }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting screenshot: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + // Get scan HAR + agent.server.tool( + 'get_url_scan_har', + 'Get the HAR (HTTP Archive) data for a completed scan. Contains detailed network request/response information.', + { + scanId: ScanIdParam, + }, + async ({ scanId }) => { + const result = await getAccountIdOrError(agent) + if ('error' in result) return result.error + + try { + const props = getProps(agent) + + const res = await fetch( + `${URLSCANNER_API_BASE}/${result.accountId}/urlscanner/v2/har/${scanId}`, + { + headers: { Authorization: `Bearer ${props.accessToken}` }, + } + ) + + if (!res.ok) { + if (res.status === 404) { + throw new Error('HAR not available. The scan may still be in progress or failed.') + } + const errorData = await res.json().catch(() => ({})) + throw new Error(`Failed to get HAR: ${res.status} ${JSON.stringify(errorData)}`) + } + + const data = await res.json() + return { + content: [ + { + type: 'text' as const, + text: JSON.stringify(data), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text' as const, + text: `Error getting HAR: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) +} diff --git a/apps/radar/src/tools/url-scanner.ts b/apps/radar/src/tools/url-scanner.ts deleted file mode 100644 index 4d029a47..00000000 --- a/apps/radar/src/tools/url-scanner.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api' -import { pollUntilReady } from '@repo/mcp-common/src/utils/poll' - -import { CreateScanResult, UrlParam } from '../types/url-scanner' - -import type { RadarMCP } from '../index' - -const MAX_WAIT_SECONDS = 30 -const INTERVAL_SECONDS = 2 - -export function registerUrlScannerTools(agent: RadarMCP) { - agent.server.tool( - 'scan_url', - 'Submit a URL to scan', - { - url: UrlParam, - }, - async ({ url }) => { - const accountId = await agent.getActiveAccountId() - if (!accountId) { - return { - content: [ - { - type: 'text', - text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', - }, - ], - } - } - - try { - const client = getCloudflareClient(agent.props.accessToken) - - // Search if there are recent scans for the URL - const scans = await client.urlScanner.scans.list({ - account_id: accountId, - q: `page.url:"${url}"`, - }) - - let scanId = scans.results.length > 0 ? scans.results[0]._id : null - - if (!scanId) { - // Submit scan - // TODO theres an issue (reported) with this method in the cloudflare TS lib - // const scan = await (client.urlScanner.scans.create({ account_id, url: "https://www.example.com" }, { headers })).withResponse() - - const res = await fetch( - `https://api.cloudflare.com/client/v4/accounts/${accountId}/urlscanner/v2/scan`, - { - method: 'POST', - headers: { - Authorization: `Bearer ${agent.props.accessToken}`, - }, - body: JSON.stringify({ url }), - } - ) - - if (!res.ok) { - throw new Error('Failed to submit scan') - } - - const scan = CreateScanResult.parse(await res.json()) - scanId = scan?.uuid - } - - const r = await pollUntilReady({ - taskFn: () => client.urlScanner.scans.get(scanId, { account_id: accountId }), - intervalSeconds: INTERVAL_SECONDS, - maxWaitSeconds: MAX_WAIT_SECONDS, - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - result: { verdicts: r.verdicts, stats: r.stats, page: r.page }, // TODO select what is more relevant, or add a param to allow the agent to select a set of metrics - }), - }, - ], - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error scanning URL: ${error instanceof Error && error.message}`, - }, - ], - } - } - } - ) -} diff --git a/apps/radar/src/types/radar.ts b/apps/radar/src/types/radar.ts index aefce133..fa283bbe 100644 --- a/apps/radar/src/types/radar.ts +++ b/apps/radar/src/types/radar.ts @@ -68,7 +68,11 @@ export const DateRangeArrayParam: z.ZodType = .describe( 'Filters results by date range. ' + 'For example, use `7d` and `7dcontrol` to compare this week with the previous week. ' + - 'Use this parameter or set specific start and end dates (`dateStart` and `dateEnd` parameters).' + 'Use this parameter or set specific start and end dates (`dateStart` and `dateEnd` parameters). ' + + 'IMPORTANT: When using multiple dateRange values for comparison, filter array parameters (location, asn, ' + + 'continent, geoId) map positionally to each dateRange element. For example, with dateRange: ["7d", "7dcontrol"] ' + + 'and location: ["PT", "PT"], the first period uses PT and the control period also uses PT. If you only provide ' + + 'location: ["PT"], only the first period is filtered by PT while the control period defaults to worldwide data.' ) export const DateStartParam = z @@ -124,7 +128,12 @@ export const LocationArrayParam: z.ZodType = z .optional() .describe( 'Filters results by location. Provide an array of alpha-2 country codes (e.g., "US", "PT"). ' + - 'Prefix a code with `-` to exclude it (e.g., ["-US", "PT"] excludes the US and includes Portugal).' + 'Prefix a code with `-` to exclude it (e.g., ["-US", "PT"] excludes the US and includes Portugal). ' + + 'IMPORTANT: When using multiple dateRange values (e.g., ["7d", "7dcontrol"]), each array element ' + + 'maps positionally to each dateRange. To compare the same location across time periods, repeat the ' + + 'location code (e.g., location: ["PT", "PT"] with dateRange: ["7d", "7dcontrol"]). Using a single ' + + 'location with multiple dateRange values will filter only the first period, with subsequent periods ' + + 'defaulting to worldwide data.' ) export const ContinentArrayParam: z.ZodType = z @@ -136,7 +145,11 @@ export const ContinentArrayParam: z.ZodType = .optional() .describe( 'Filters results by continent. Provide an array of alpha-2 continent codes (e.g., "EU", "NA"). ' + - 'Prefix a code with `-` to exclude it (e.g., ["-EU", "NA"] excludes Europe and includes North America).' + 'Prefix a code with `-` to exclude it (e.g., ["-EU", "NA"] excludes Europe and includes North America). ' + + 'IMPORTANT: When using multiple dateRange values, each array element maps positionally to each dateRange. ' + + 'To compare the same continent across time periods, repeat the continent code (e.g., continent: ["EU", "EU"] ' + + 'with dateRange: ["7d", "7dcontrol"]). Using a single continent with multiple dateRange values will filter ' + + 'only the first period, with subsequent periods defaulting to worldwide data.' ) export const AsnArrayParam: z.ZodType = z @@ -144,7 +157,11 @@ export const AsnArrayParam: z.ZodType = z .optional() .describe( 'Filters results by ASN. Provide an array of ASN strings. ' + - 'Prefix with `-` to exclude (e.g., ["-174", "3356"] excludes AS174 and includes AS3356). ' + 'Prefix with `-` to exclude (e.g., ["-174", "3356"] excludes AS174 and includes AS3356). ' + + 'IMPORTANT: When using multiple dateRange values, each array element maps positionally to each dateRange. ' + + 'To compare the same ASN across time periods, repeat the ASN (e.g., asn: ["13335", "13335"] with ' + + 'dateRange: ["7d", "7dcontrol"]). Using a single ASN with multiple dateRange values will filter only ' + + 'the first period, with subsequent periods defaulting to all ASNs.' ) export const AsOrderByParam: z.ZodType = z @@ -155,26 +172,31 @@ export const AsOrderByParam: z.ZodType = z export const HttpDimensionParam = z .enum([ 'timeseries', - 'summary/deviceType', - 'summary/httpProtocol', - 'summary/httpVersion', - 'summary/botClass', - 'summary/ipVersion', - 'summary/tlsVersion', + // Summary dimensions (new unified endpoint) + 'summary/adm1', + 'summary/bot_class', + 'summary/browser', + 'summary/browser_family', + 'summary/device_type', + 'summary/http_protocol', + 'summary/http_version', + 'summary/ip_version', 'summary/os', - 'summary/postQuantum', - 'top/browser', // TODO replace with "summary/browser" and "summary/browserFamily" once available on the lib - 'top/browserFamily', - 'timeseriesGroups/deviceType', - 'timeseriesGroups/httpProtocol', - 'timeseriesGroups/httpVersion', - 'timeseriesGroups/botClass', - 'timeseriesGroups/ipVersion', - 'timeseriesGroups/tlsVersion', - 'timeseriesGroups/os', - 'timeseriesGroups/postQuantum', - 'timeseriesGroups/browser', - 'timeseriesGroups/browserFamily', + 'summary/post_quantum', + 'summary/tls_version', + // Timeseries groups dimensions (new unified endpoint) + 'timeseries_groups/adm1', + 'timeseries_groups/bot_class', + 'timeseries_groups/browser', + 'timeseries_groups/browser_family', + 'timeseries_groups/device_type', + 'timeseries_groups/http_protocol', + 'timeseries_groups/http_version', + 'timeseries_groups/ip_version', + 'timeseries_groups/os', + 'timeseries_groups/post_quantum', + 'timeseries_groups/tls_version', + // Top endpoints 'top/locations', 'top/ases', ]) @@ -183,23 +205,29 @@ export const HttpDimensionParam = z export const DnsDimensionParam = z .enum([ 'timeseries', - 'summary/ipVersion', - 'summary/cacheHit', + 'summary/ip_version', + 'summary/cache_hit', 'summary/dnssec', - 'summary/dnssecAware', - 'summary/matchingAnswer', + 'summary/dnssec_aware', + 'summary/dnssec_e2e', + 'summary/matching_answer', 'summary/protocol', - 'summary/queryType', - 'summary/responseCode', - 'summary/responseTTL', - 'timeseriesGroups/ipVersion', - 'timeseriesGroups/cacheHit', - 'timeseriesGroups/dnssecAware', - 'timeseriesGroups/matchingAnswer', - 'timeseriesGroups/protocol', - 'timeseriesGroups/queryType', - 'timeseriesGroups/responseCode', - 'timeseriesGroups/responseTTL', + 'summary/query_type', + 'summary/response_code', + 'summary/response_ttl', + 'summary/tld', + 'summary/tld_dns_magnitude', + 'timeseries_groups/ip_version', + 'timeseries_groups/cache_hit', + 'timeseries_groups/dnssec', + 'timeseries_groups/dnssec_aware', + 'timeseries_groups/dnssec_e2e', + 'timeseries_groups/matching_answer', + 'timeseries_groups/protocol', + 'timeseries_groups/query_type', + 'timeseries_groups/response_code', + 'timeseries_groups/response_ttl', + 'timeseries_groups/tld', 'top/locations', 'top/ases', ]) @@ -295,12 +323,19 @@ export const EmailSecurityDimensionParam = z export const AiDimensionParam = z .enum([ - 'bots/summary/userAgent', - 'bots/timeseriesGroups/userAgent', + 'bots/timeseries', + 'bots/summary/user_agent', + 'bots/summary/crawl_purpose', + 'bots/summary/industry', + 'bots/summary/vertical', + 'bots/timeseries_groups/user_agent', + 'bots/timeseries_groups/crawl_purpose', + 'bots/timeseries_groups/industry', + 'bots/timeseries_groups/vertical', 'inference/summary/model', 'inference/summary/task', - 'inference/timeseriesGroups/model', - 'inference/timeseriesGroups/task', + 'inference/timeseries_groups/model', + 'inference/timeseries_groups/task', ]) .describe('Dimension indicating the type and format of AI data to retrieve.') @@ -322,3 +357,637 @@ export const InternetSpeedOrderByParam = z export const InternetQualityMetricParam = z .enum(['BANDWIDTH', 'DNS', 'LATENCY']) .describe('Specifies which metric to return (bandwidth, latency, or DNS response time).') + +// GeoId filter for ADM1 (administrative level 1) filtering +export const GeoIdArrayParam = z + .array(z.string()) + .optional() + .describe( + 'Filters results by Geolocation (ADM1 - administrative level 1, e.g., states/provinces). ' + + 'Provide an array of GeoNames IDs. Prefix with `-` to exclude. ' + + 'Example: ["2267056", "-360689"] includes Lisbon area but excludes another region. ' + + 'IMPORTANT: When using multiple dateRange values, each array element maps positionally to each dateRange. ' + + 'To compare the same region across time periods, repeat the GeoID (e.g., geoId: ["2267056", "2267056"] ' + + 'with dateRange: ["7d", "7dcontrol"]). Using a single geoId with multiple dateRange values will filter ' + + 'only the first period, with subsequent periods defaulting to all regions.' + ) + +// BGP Parameters +export const BgpHijackerAsnParam = z + .number() + .int() + .positive() + .optional() + .describe('Filter by the potential hijacker AS of a BGP hijack event.') + +export const BgpVictimAsnParam = z + .number() + .int() + .positive() + .optional() + .describe('Filter by the potential victim AS of a BGP hijack event.') + +export const BgpInvolvedAsnParam = z + .number() + .positive() + .optional() + .describe('Filter by ASN involved (as hijacker or victim) in a BGP event.') + +export const BgpInvolvedCountryParam = z + .string() + .regex(/^[a-zA-Z]{2}$/) + .optional() + .describe('Filter by country code of the involved AS in a BGP event.') + +export const BgpLeakAsnParam = z + .number() + .positive() + .optional() + .describe('Filter by the leaking AS of a route leak event.') + +export const BgpPrefixParam = z + .string() + .optional() + .describe('Filter by IP prefix (e.g., "1.1.1.0/24").') + +export const BgpMinConfidenceParam = z + .number() + .int() + .min(1) + .max(10) + .optional() + .describe('Filter by minimum confidence score (1-4 low, 5-7 mid, 8+ high).') + +export const BgpMaxConfidenceParam = z + .number() + .int() + .min(1) + .max(10) + .optional() + .describe('Filter by maximum confidence score (1-4 low, 5-7 mid, 8+ high).') + +export const BgpSortByParam = z + .enum(['TIME', 'CONFIDENCE', 'ID']) + .optional() + .describe('Sort results by specified field.') + +export const BgpSortOrderParam = z + .enum(['ASC', 'DESC']) + .optional() + .describe('Sort order (ascending or descending).') + +// Bots Parameters +export const BotsDimensionParam = z + .enum([ + 'timeseries', + 'summary/bot', + 'summary/bot_kind', + 'summary/bot_operator', + 'summary/bot_category', + 'timeseries_groups/bot', + 'timeseries_groups/bot_kind', + 'timeseries_groups/bot_operator', + 'timeseries_groups/bot_category', + ]) + .describe('Dimension indicating the type and format of bot data to retrieve.') + +export const BotNameParam = z + .array(z.string().max(100)) + .optional() + .describe('Filter results by bot name.') + +export const BotOperatorParam = z + .array(z.string().max(100)) + .optional() + .describe('Filter results by bot operator (e.g., Google, Microsoft, OpenAI).') + +export const BotCategoryParam = z + .array( + z.enum([ + 'SEARCH_ENGINE_CRAWLER', + 'SEARCH_ENGINE_OPTIMIZATION', + 'MONITORING_AND_ANALYTICS', + 'ADVERTISING_AND_MARKETING', + 'SOCIAL_MEDIA_MARKETING', + 'PAGE_PREVIEW', + 'ACADEMIC_RESEARCH', + 'SECURITY', + 'ACCESSIBILITY', + 'WEBHOOKS', + 'FEED_FETCHER', + 'AI_CRAWLER', + 'AGGREGATOR', + 'AI_ASSISTANT', + 'AI_SEARCH', + 'ARCHIVER', + ]) + ) + .optional() + .describe('Filter results by bot category.') + +export const BotKindParam = z + .array(z.enum(['AGENT', 'BOT'])) + .optional() + .describe('Filter results by bot kind (AGENT or BOT).') + +export const BotVerificationStatusParam = z + .array(z.enum(['VERIFIED'])) + .optional() + .describe('Filter results by bot verification status.') + +// Certificate Transparency Parameters +export const CtDimensionParam = z + .enum([ + 'timeseries', + 'summary/ca', + 'summary/ca_owner', + 'summary/duration', + 'summary/entry_type', + 'summary/expiration_status', + 'summary/has_ips', + 'summary/has_wildcards', + 'summary/log_api', + 'summary/public_key_algorithm', + 'summary/signature_algorithm', + 'summary/validation_level', + 'timeseries_groups/ca', + 'timeseries_groups/ca_owner', + 'timeseries_groups/duration', + 'timeseries_groups/entry_type', + 'timeseries_groups/expiration_status', + 'timeseries_groups/has_ips', + 'timeseries_groups/has_wildcards', + 'timeseries_groups/log_api', + 'timeseries_groups/public_key_algorithm', + 'timeseries_groups/signature_algorithm', + 'timeseries_groups/validation_level', + ]) + .describe( + 'Dimension indicating the type and format of Certificate Transparency data to retrieve.' + ) + +export const CtCaParam = z + .array(z.string()) + .optional() + .describe('Filter results by certificate authority.') + +export const CtCaOwnerParam = z + .array(z.string()) + .optional() + .describe('Filter results by certificate authority owner.') + +export const CtDurationParam = z + .array( + z.enum([ + 'LTE_3D', + 'GT_3D_LTE_7D', + 'GT_7D_LTE_10D', + 'GT_10D_LTE_47D', + 'GT_47D_LTE_100D', + 'GT_100D_LTE_200D', + 'GT_200D', + ]) + ) + .optional() + .describe('Filter results by certificate duration.') + +export const CtEntryTypeParam = z + .array(z.enum(['PRECERTIFICATE', 'CERTIFICATE'])) + .optional() + .describe('Filter results by entry type (certificate vs. pre-certificate).') + +export const CtTldParam = z + .array(z.string().min(2).max(63)) + .optional() + .describe('Filter results by top-level domain (e.g., "com", "org").') + +export const CtValidationLevelParam = z + .array(z.enum(['DOMAIN', 'ORGANIZATION', 'EXTENDED'])) + .optional() + .describe('Filter results by validation level (DV, OV, EV).') + +export const CtPublicKeyAlgorithmParam = z + .array(z.enum(['DSA', 'ECDSA', 'RSA'])) + .optional() + .describe('Filter results by public key algorithm.') + +// Netflows Parameters +export const NetflowsDimensionParam = z + .enum(['timeseries', 'summary', 'summary/adm1', 'summary/product', 'top/locations', 'top/ases']) + .describe('Dimension indicating the type and format of NetFlows data to retrieve.') + +export const NetflowsProductParam = z + .array(z.enum(['HTTP', 'ALL'])) + .optional() + .describe('Filter results by network traffic product type.') + +export const NormalizationParam = z + .enum(['RAW_VALUES', 'PERCENTAGE']) + .optional() + .describe('Normalization method applied to results.') + +export const LimitPerGroupParam = z + .number() + .int() + .positive() + .optional() + .describe('Limits the number of items per group. Extra items appear grouped under "other".') + +// Origins/Cloud Observatory Parameters (used by fetch-based tools) +export const OriginSlugParam = z + .enum(['AMAZON', 'GOOGLE', 'MICROSOFT', 'ORACLE']) + .describe( + 'The cloud provider origin to query. Supported values: AMAZON (AWS), GOOGLE (GCP), MICROSOFT (Azure), ORACLE (OCI).' + ) + +export const OriginArrayParam = z + .array(OriginSlugParam) + .min(1) + .describe('Array of cloud provider origins to query. At least one origin must be specified.') + +export const OriginMetricParam = z + .enum([ + 'CONNECTION_FAILURES', + 'REQUESTS', + 'RESPONSE_HEADER_RECEIVE_DURATION', + 'TCP_HANDSHAKE_DURATION', + 'TCP_RTT', + 'TLS_HANDSHAKE_DURATION', + ]) + .describe( + 'The performance metric to retrieve. Only valid when dimension is timeseries or percentile. ' + + 'CONNECTION_FAILURES: Number of failed connections. ' + + 'REQUESTS: Total request count. ' + + 'RESPONSE_HEADER_RECEIVE_DURATION: Time to receive response headers (ms). ' + + 'TCP_HANDSHAKE_DURATION: TCP handshake time (ms). ' + + 'TCP_RTT: TCP round-trip time (ms). ' + + 'TLS_HANDSHAKE_DURATION: TLS handshake time (ms).' + ) + +export const OriginDataDimensionParam = z + .enum([ + 'timeseries', + 'summary/REGION', + 'summary/SUCCESS_RATE', + 'summary/PERCENTILE', + 'timeseriesGroups/REGION', + 'timeseriesGroups/SUCCESS_RATE', + 'timeseriesGroups/PERCENTILE', + ]) + .describe( + 'Dimension indicating the type and format of origins data to retrieve. ' + + 'timeseries: Raw time series data. Requires setting the metric parameter.' + + 'summary/*: Aggregated data grouped by dimension. ' + + 'timeseriesGroups/*: Time series grouped by dimension. ' + + 'REGION: Group by cloud provider region (e.g., us-east-1). ' + + 'SUCCESS_RATE: Group by connection success rate. ' + + 'PERCENTILE: Group by performance percentiles (p50, p90, p99). Requires setting the metric parameter.' + ) + +export const OriginRegionParam = z + .array(z.string().max(100)) + .optional() + .describe( + 'Filters results by cloud provider region. ' + + 'Example regions: us-east-1, eu-west-1, ap-southeast-1.' + ) + +export const OriginNormalizationParam = z + .enum(['PERCENTAGE', 'MIN0_MAX']) + .optional() + .describe('Normalization method for results.') + +// ============================================================ +// Robots.txt Parameters +// ============================================================ + +export const RobotsTxtDimensionParam = z + .enum([ + 'summary/user_agent', + 'timeseries_groups/user_agent', + 'top/domain_categories', + 'top/user_agents/directive', + ]) + .describe('Dimension indicating the type and format of robots.txt data to retrieve.') + +export const RobotsTxtDirectiveParam = z + .enum(['ALLOW', 'DISALLOW']) + .optional() + .describe('Filter by robots.txt directive type (ALLOW or DISALLOW).') + +export const RobotsTxtPatternParam = z + .enum(['FULLY', 'PARTIALLY']) + .optional() + .describe('Filter by pattern matching type (FULLY or PARTIALLY matched).') + +export const RobotsTxtDomainCategoryParam = z + .array(z.string()) + .optional() + .describe('Filter by domain categories.') + +export const RobotsTxtUserAgentCategoryParam = z + .enum(['AI']) + .optional() + .describe('Filter by user agent category (currently only AI is supported).') + +// ============================================================ +// Bots Crawlers Parameters +// ============================================================ + +export const BotsCrawlersDimensionParam = z + .enum(['CLIENT_TYPE', 'USER_AGENT', 'REFERER', 'CRAWL_REFER_RATIO', 'VERTICAL', 'INDUSTRY']) + .describe( + 'Dimension for crawler data. CLIENT_TYPE: crawler type, USER_AGENT: crawler user agent, ' + + 'REFERER: referrer analysis, CRAWL_REFER_RATIO: crawl to referrer ratio, ' + + 'VERTICAL: industry vertical, INDUSTRY: industry classification.' + ) + +export const BotsCrawlersFormatParam = z + .enum(['summary', 'timeseries_groups']) + .describe('Format for crawler data: summary or time series grouped data.') + +export const CrawlerVerticalParam = z + .array(z.string()) + .optional() + .describe('Filter by industry vertical.') + +export const CrawlerIndustryParam = z + .array(z.string()) + .optional() + .describe('Filter by industry classification.') + +export const CrawlerClientTypeParam = z + .array(z.string()) + .optional() + .describe('Filter by client type.') + +// ============================================================ +// Leaked Credential Checks Parameters +// ============================================================ + +export const LeakedCredentialsDimensionParam = z + .enum([ + 'timeseries', + 'summary/compromised', + 'summary/bot_class', + 'timeseries_groups/compromised', + 'timeseries_groups/bot_class', + ]) + .describe('Dimension indicating the type and format of leaked credentials data to retrieve.') + +export const LeakedCredentialsBotClassParam = z + .array(z.string()) + .optional() + .describe('Filter by bot class.') + +export const LeakedCredentialsCompromisedParam = z + .array(z.string()) + .optional() + .describe('Filter by compromised status.') + +// ============================================================ +// AS112 Parameters +// ============================================================ + +export const As112DimensionParam = z + .enum([ + 'timeseries', + 'summary/dnssec', + 'summary/edns', + 'summary/ip_version', + 'summary/protocol', + 'summary/query_type', + 'summary/response_code', + 'timeseries_groups/dnssec', + 'timeseries_groups/edns', + 'timeseries_groups/ip_version', + 'timeseries_groups/protocol', + 'timeseries_groups/query_type', + 'timeseries_groups/response_code', + 'top/locations', + ]) + .describe( + 'Dimension indicating the type and format of AS112 data to retrieve. ' + + 'AS112 is a DNS sink hole for reverse DNS lookups of private IP addresses.' + ) + +export const As112QueryTypeParam = z + .array(z.string()) + .optional() + .describe('Filter by DNS query type.') + +export const As112ProtocolParam = z + .array(z.string()) + .optional() + .describe('Filter by DNS protocol (UDP/TCP).') + +export const As112ResponseCodeParam = z + .array(z.string()) + .optional() + .describe('Filter by DNS response code.') + +// ============================================================ +// TCP Resets/Timeouts Parameters +// ============================================================ + +export const TcpResetsTimeoutsDimensionParam = z + .enum(['summary', 'timeseries_groups']) + .describe('Format for TCP resets/timeouts data: summary or time series grouped data.') + +// ============================================================ +// Annotations/Outages Parameters +// ============================================================ + +export const AnnotationDataSourceParam = z + .enum([ + 'ALL', + 'AI_BOTS', + 'AI_GATEWAY', + 'BGP', + 'BOTS', + 'CONNECTION_ANOMALY', + 'CT', + 'DNS', + 'DNS_MAGNITUDE', + 'DNS_AS112', + 'DOS', + 'EMAIL_ROUTING', + 'EMAIL_SECURITY', + 'FW', + 'FW_PG', + 'HTTP', + 'HTTP_CONTROL', + 'HTTP_CRAWLER_REFERER', + 'HTTP_ORIGINS', + 'IQI', + 'LEAKED_CREDENTIALS', + 'NET', + 'ROBOTS_TXT', + 'SPEED', + 'WORKERS_AI', + ]) + .optional() + .describe('Filter annotations by data source.') + +export const AnnotationEventTypeParam = z + .enum(['EVENT', 'GENERAL', 'OUTAGE', 'PARTIAL_PROJECTION', 'PIPELINE', 'TRAFFIC_ANOMALY']) + .optional() + .describe('Filter annotations by event type.') + +// ============================================================ +// BGP Additional Parameters +// ============================================================ + +export const BgpUpdateTypeParam = z + .array(z.enum(['ANNOUNCEMENT', 'WITHDRAWAL'])) + .optional() + .describe('Filter by BGP update type (ANNOUNCEMENT or WITHDRAWAL).') + +export const BgpPrefixArrayParam = z + .array(z.string()) + .optional() + .describe('Filter by IP prefix(es).') + +export const BgpRpkiStatusParam = z + .enum(['VALID', 'INVALID', 'UNKNOWN']) + .optional() + .describe('Filter by RPKI validation status.') + +export const BgpLongestPrefixMatchParam = z + .boolean() + .optional() + .describe('Whether to use longest prefix match.') + +export const BgpOriginParam = z + .number() + .int() + .positive() + .optional() + .describe('Filter by origin ASN.') + +export const BgpInvalidOnlyParam = z + .boolean() + .optional() + .describe('Only return invalid MOAS prefixes.') + +export const BgpIpVersionParam = z + .array(z.enum(['IPv4', 'IPv6'])) + .optional() + .describe( + 'Filters results by IP version (IPv4 vs. IPv6). Useful for monitoring IPv6 address space specifically.' + ) + +// ============================================================ +// Geolocation Parameters +// ============================================================ + +export const GeoIdParam = z + .string() + .regex(/^[0-9]+$/, { + message: 'Invalid GeoNames ID. Must be a numeric string.', + }) + .describe('GeoNames ID for the geolocation (e.g., "2267056" for Lisbon).') + +// ============================================================ +// Slug Parameters (Path Traversal Prevention) +// ============================================================ + +export const SlugParam = z + .string() + .min(1) + .max(100) + .regex(/^[a-zA-Z0-9]+$/, { + message: 'Invalid slug format. Only alphanumeric characters are allowed.', + }) + .describe('A slug identifier containing only alphanumeric characters.') + +export const Sha256FingerprintParam = z + .string() + .toUpperCase() + .regex(/^[A-F0-9]{64}$/, { + message: 'Invalid SHA256 fingerprint. Must be 64 hexadecimal characters.', + }) + .describe('A SHA256 fingerprint (64 hexadecimal characters).') + +// ============================================================ +// TLD Parameters +// ============================================================ + +export const TldTypeParam = z + .enum(['GENERIC', 'COUNTRY_CODE', 'GENERIC_RESTRICTED', 'INFRASTRUCTURE', 'SPONSORED']) + .optional() + .describe('Filters results by TLD type.') + +export const TldManagerParam = z + .string() + .max(100) + .optional() + .describe('Filters results by TLD manager (e.g., "VeriSign Global Registry Services").') + +export const TldParam = z + .string() + .min(2) + .max(63) + .regex(/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/) + .describe('Top-level domain (e.g., "com", "org", "net").') + +export const TldFilterParam = z + .string() + .optional() + .describe('Filters results by top-level domain. Specify a comma-separated list of TLDs.') + +// ============================================================ +// Ranking Timeseries Parameters +// ============================================================ + +export const DomainsArrayParam = z + .array( + z + .string() + .min(1) + .max(253) + .regex(/^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+(?:[A-Za-z]{2,63}))$/) + ) + .optional() + .describe('Filters results by domain name. Specify an array of domain names to track.') + +export const DomainCategoryArrayParam = z + .array(z.string().max(100)) + .optional() + .describe('Filters results by domain category (e.g., "News & Media", "Technology").') + +// ============================================================ +// Speed Histogram Parameters +// ============================================================ + +export const SpeedHistogramMetricParam = z + .enum(['BANDWIDTH', 'LATENCY', 'JITTER']) + .optional() + .describe('Metrics to be returned in the histogram. Defaults to BANDWIDTH.') + +export const BucketSizeParam = z + .number() + .int() + .positive() + .optional() + .describe('Specifies the width for every bucket in the histogram.') + +// ============================================================ +// BGP Routes ASes Parameters +// ============================================================ + +export const BgpRoutesAsesSortByParam = z + .enum(['cone', 'pfxs', 'ipv4', 'ipv6', 'rpki_valid', 'rpki_invalid', 'rpki_unknown']) + .optional() + .describe( + 'Sort ASes by: cone (customer cone size), pfxs (total prefixes), ipv4/ipv6 (address count), or rpki_* (RPKI validation status).' + ) + +// ============================================================ +// Traffic Anomalies Parameters +// ============================================================ + +export const TrafficAnomalyStatusParam = z + .enum(['VERIFIED', 'UNVERIFIED']) + .optional() + .describe('Filter by anomaly verification status.') diff --git a/apps/radar/src/types/url-scanner.ts b/apps/radar/src/types/url-scanner.ts index 36845101..441e943c 100644 --- a/apps/radar/src/types/url-scanner.ts +++ b/apps/radar/src/types/url-scanner.ts @@ -8,6 +8,38 @@ export const UrlParam = z .url() .describe('A valid URL including protocol (e.g., "https://example.com").') +export const ScanIdParam = z.string().uuid().describe('The UUID of the scan.') + +export const SearchQueryParam = z + .string() + .optional() + .describe( + "ElasticSearch-like query to filter scans. Examples: 'page.domain:example.com', 'verdicts.malicious:true', 'page.asn:AS24940', 'date:[2025-01 TO 2025-02]'" + ) + +export const SearchSizeParam = z + .number() + .int() + .min(1) + .max(100) + .default(10) + .optional() + .describe('Limit the number of results (1-100, default 10).') + +export const ScanVisibilityParam = z + .enum(['Public', 'Unlisted']) + .default('Public') + .optional() + .describe( + 'Scan visibility. Public scans appear in search results. Unlisted scans require the scan ID to access.' + ) + +export const ScreenshotResolutionParam = z + .enum(['desktop', 'mobile', 'tablet']) + .default('desktop') + .optional() + .describe('Screenshot resolution/device type.') + export const CreateScanResult = z .object({ uuid: z.string(), diff --git a/apps/radar/vitest.config.ts b/apps/radar/vitest.config.ts index c201c144..5f76e45c 100644 --- a/apps/radar/vitest.config.ts +++ b/apps/radar/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/radar.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/radar/wrangler.jsonc b/apps/radar/wrangler.jsonc index 32cdd477..3f89ca17 100644 --- a/apps/radar/wrangler.jsonc +++ b/apps/radar/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/radar.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-radar-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ diff --git a/apps/sandbox-container/.dev.vars.example b/apps/sandbox-container/.dev.vars.example index c087f669..d2d3cd20 100644 --- a/apps/sandbox-container/.dev.vars.example +++ b/apps/sandbox-container/.dev.vars.example @@ -2,4 +2,3 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file diff --git a/apps/sandbox-container/CHANGELOG.md b/apps/sandbox-container/CHANGELOG.md new file mode 100644 index 00000000..4382b138 --- /dev/null +++ b/apps/sandbox-container/CHANGELOG.md @@ -0,0 +1,142 @@ +# containers-mcp + +## 0.2.10 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/eval-tools@0.32.5 + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.2.9 + +### Patch Changes + +- e659dcf: fix: use blob field for binary resources to prevent context overflow (#252) + +## 0.2.8 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.2.7 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + - @repo/eval-tools@0.32.4 + +## 0.2.6 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/eval-tools@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.2.5 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/eval-tools@0.32.2 + - @repo/mcp-common@0.19.2 + +## 0.2.4 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.2.3 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.2.2 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/eval-tools@0.32.1 + - @repo/mcp-observability@0.32.1 + +## 0.2.1 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.2.0 + +### Minor Changes + +- 2621557: Use new workers:read scope instead of workers:write, as these mcp servers don't require workers write permissions + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/eval-tools@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/eval-tools@0.31.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/sandbox-container/CONTRIBUTING.md b/apps/sandbox-container/CONTRIBUTING.md index 675172a4..36928685 100644 --- a/apps/sandbox-container/CONTRIBUTING.md +++ b/apps/sandbox-container/CONTRIBUTING.md @@ -12,7 +12,7 @@ Do the following from within the sandbox-container app: 2. Get the Cloudflare client id and secret from a team member and add them to the `.dev.vars` file. 3. Run `pnpm i` then `pnpm dev` to start the MCP server. 4. Run `pnpx @modelcontextprotocol/inspector` to start the MCP inspector client. -5. Open the inspector client in your browser and connect to the server via `http://localhost:8976/sse`. +5. Open the inspector client in your browser and connect to the server via `http://localhost:8976/mcp`. Note: Temporary files created through files tool calls are stored in the workdir folder of this app. @@ -36,7 +36,7 @@ npx https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/14387504 "args": [ "mcp-remote", // this is my deployed instance - "https://container-starter-2.cmsparks.workers.dev/sse" + "https://container-starter-2.cmsparks.workers.dev/mcp" ] } } diff --git a/apps/sandbox-container/README.md b/apps/sandbox-container/README.md index 556b1eb1..8916a522 100644 --- a/apps/sandbox-container/README.md +++ b/apps/sandbox-container/README.md @@ -37,7 +37,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://containers.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://containers.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/sandbox-container/container/index.ts b/apps/sandbox-container/container/sandbox.container.app.ts similarity index 100% rename from apps/sandbox-container/container/index.ts rename to apps/sandbox-container/container/sandbox.container.app.ts diff --git a/apps/sandbox-container/package.json b/apps/sandbox-container/package.json index c312e04b..5acfee61 100644 --- a/apps/sandbox-container/package.json +++ b/apps/sandbox-container/package.json @@ -1,34 +1,33 @@ { "name": "containers-mcp", - "version": "0.0.1", + "version": "0.2.10", "private": true, "type": "module", "scripts": { "check:types": "run-tsc", "check:lint": "run-eslint-workers", - "deploy": "wrangler deploy", - "dev": "concurrently \"tsx container/index.ts\" \"wrangler dev --var \"ENVIRONMENT:dev\"\"", + "deploy": "run-wrangler-deploy", + "dev": "concurrently \"tsx container/sandbox.container.app.ts\" \"wrangler dev --var \"ENVIRONMENT:dev\"\"", "build:container": "docker build --platform linux/amd64 --tag sandbox-container:$(git rev-parse --short HEAD) -f Dockerfile ../../ && wrangler containers push sandbox-container:$(git rev-parse --short HEAD)", "start": "wrangler dev", - "start:container": "tsx container/index.ts", + "start:container": "tsx container/sandbox.container.app.ts", "postinstall": "mkdir -p workdir", "test": "vitest", "types": "wrangler types --include-env=false", "eval:dev": "start-server-and-test --expect 404 eval:server http://localhost:8976 'vitest --testTimeout=60000 --config vitest.config.evals.ts'", - "eval:server": "concurrently \"tsx container/index.ts\" \"wrangler dev --var ENVIRONMENT:test --var DEV_DISABLE_OAUTH:true --var DEV_CLOUDFLARE_EMAIL:mcp-server-eval-account@workers-for-platforms-dev.cfdata.org\"", + "eval:server": "concurrently \"tsx container/sandbox.container.app.ts\" \"wrangler dev --var ENVIRONMENT:test --var DEV_DISABLE_OAUTH:true --var DEV_CLOUDFLARE_EMAIL:mcp-server-eval-account@workers-for-platforms-dev.cfdata.org\"", "eval:ci": "start-server-and-test --expect 404 eval:server http://localhost:8976 'vitest run --testTimeout=60000 --config vitest.config.evals.ts'" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@hono/node-server": "1.13.8", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@n8n/json-schema-to-zod": "1.1.0", "@repo/eval-tools": "workspace:*", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "@types/node": "22.14.1", - "agents": "0.0.67", + "agents": "0.2.19", "cron-schedule": "5.0.4", "esbuild": "0.25.1", "hono": "4.7.6", diff --git a/apps/sandbox-container/server/containerManager.ts b/apps/sandbox-container/server/containerManager.ts index b8694bb0..ca6c9196 100644 --- a/apps/sandbox-container/server/containerManager.ts +++ b/apps/sandbox-container/server/containerManager.ts @@ -5,7 +5,7 @@ import { MetricsTracker } from '@repo/mcp-observability' import { ContainerEvent } from './metrics' -import type { Env } from './context' +import type { Env } from './sandbox.server.context' const env = getEnv() export class ContainerManager extends DurableObject { @@ -40,10 +40,11 @@ export class ContainerManager extends DurableObject { // 15m timeout for container lifetime if (now.valueOf() - time.valueOf() > 15 * 60 * 1000) { + await this.killContainer(id) + // TODO: Figure out why we were running in to invalid durable object id the id does not match this durable object class error const doId = this.env.USER_CONTAINER.idFromString(id) const stub = this.env.USER_CONTAINER.get(doId) await stub.destroyContainer() - await this.killContainer(id) } } } diff --git a/apps/sandbox-container/server/containerMcp.ts b/apps/sandbox-container/server/containerMcp.ts index e701d420..71e1a29a 100644 --- a/apps/sandbox-container/server/containerMcp.ts +++ b/apps/sandbox-container/server/containerMcp.ts @@ -1,13 +1,14 @@ import { McpAgent } from 'agents/mcp' +import { getProps } from '@repo/mcp-common/src/get-props' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' import { ExecParams, FilePathParam, FileWrite } from '../shared/schema' import { BASE_INSTRUCTIONS } from './prompts' import { stripProtocolFromFilePath } from './utils' -import type { Env } from './context' -import type { Props, UserContainer } from '.' +import type { Props, UserContainer } from './sandbox.server.app' +import type { Env } from './sandbox.server.context' export class ContainerMcpAgent extends McpAgent { _server: CloudflareMCPServer | undefined @@ -24,7 +25,12 @@ export class ContainerMcpAgent extends McpAgent { } get userContainer(): DurableObjectStub { - const userContainer = this.env.USER_CONTAINER.idFromName(this.props.user.id) + const props = getProps(this) + // TODO: Support account scoped tokens? + if (props.type === 'account_token') { + throw new Error('Container server does not currently support account scoped tokens') + } + const userContainer = this.env.USER_CONTAINER.idFromName(props.user.id) return this.env.USER_CONTAINER.get(userContainer) } @@ -37,9 +43,12 @@ export class ContainerMcpAgent extends McpAgent { } async init() { - this.props.user.id + const props = getProps(this) + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const userId = props.type === 'user_token' ? props.user.id : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, @@ -50,11 +59,24 @@ export class ContainerMcpAgent extends McpAgent { this.server.tool( 'container_initialize', - `Start or restart the container. + `Start or restart the container. Use this tool to initialize a container before running any python or node.js code that the user requests ro run.`, - // @ts-ignore async () => { - const userInBlocklist = await this.env.USER_BLOCKLIST.get(this.props.user.id) + const props = getProps(this) + if (props.type === 'account_token') { + return { + // TODO: Support account scoped tokens? + // we'll need to add support for an account blocklist in that case + content: [ + { + type: 'text', + text: 'Container server does not currently support account scoped tokens.', + }, + ], + } + } + + const userInBlocklist = await this.env.USER_BLOCKLIST.get(props.user.id) if (userInBlocklist) { return { content: [{ type: 'text', text: 'Blocked from intializing container.' }], @@ -77,8 +99,8 @@ export class ContainerMcpAgent extends McpAgent { ) this.server.tool( 'container_exec', - `Run a command in a container and return the results from stdout. - If necessary, set a timeout. To debug, stream back standard error. + `Run a command in a container and return the results from stdout. + If necessary, set a timeout. To debug, stream back standard error. If you're using python, ALWAYS use python3 alongside pip3`, { args: ExecParams }, async ({ args }) => { @@ -143,7 +165,9 @@ export class ContainerMcpAgent extends McpAgent { { type: 'resource', resource: { - text: readFile.type === 'text' ? readFile.textOutput : readFile.base64Output, + ...(readFile.type === 'text' + ? { text: readFile.textOutput } + : { blob: readFile.base64Output }), uri: `file://${path}`, mimeType: readFile.mimeType, }, diff --git a/apps/sandbox-container/server/index.ts b/apps/sandbox-container/server/sandbox.server.app.ts similarity index 82% rename from apps/sandbox-container/server/index.ts rename to apps/sandbox-container/server/sandbox.server.app.ts index 1a2a37d7..f04c382d 100644 --- a/apps/sandbox-container/server/index.ts +++ b/apps/sandbox-container/server/sandbox.server.app.ts @@ -1,11 +1,11 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { createApiHandler } from '@repo/mcp-common/src/api-handler' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' import { getEnv } from '@repo/mcp-common/src/env' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { MetricsTracker } from '@repo/mcp-observability' @@ -15,7 +15,7 @@ import { ContainerMcpAgent } from './containerMcp' import { UserContainer } from './userContainer' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './sandbox.server.context' export { ContainerManager, ContainerMcpAgent, UserContainer } @@ -33,17 +33,12 @@ export type Props = AuthProps const ContainerScopes = { ...RequiredScopes, 'account:read': 'See your account info such as account details, analytics, and memberships.', - 'workers:write': - 'See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.', } as const export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if ( - (env.ENVIRONMENT === 'dev' || env.ENVIRONMENT === 'test') && - env.DEV_DISABLE_OAUTH === 'true' - ) { - return await handleDevMode(ContainerMcpAgent, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(ContainerMcpAgent, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/sandbox-container/server/context.ts b/apps/sandbox-container/server/sandbox.server.context.ts similarity index 80% rename from apps/sandbox-container/server/context.ts rename to apps/sandbox-container/server/sandbox.server.context.ts index 6bf3323b..ce77e503 100644 --- a/apps/sandbox-container/server/context.ts +++ b/apps/sandbox-container/server/sandbox.server.context.ts @@ -1,13 +1,17 @@ -import type { ContainerManager, ContainerMcpAgent, UserContainer } from './index' +import type { ContainerManager, ContainerMcpAgent, UserContainer } from './sandbox.server.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string CLOUDFLARE_CLIENT_ID: string CLOUDFLARE_CLIENT_SECRET: string ENVIRONMENT: 'dev' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string OPENAI_API_KEY: string + AI_GATEWAY_TOKEN: string + CLOUDFLARE_ACCOUNT_ID: string + AI_GATEWAY_ID: string MCP_OBJECT: DurableObjectNamespace CONTAINER_MANAGER: DurableObjectNamespace USER_CONTAINER: DurableObjectNamespace diff --git a/apps/sandbox-container/server/userContainer.ts b/apps/sandbox-container/server/userContainer.ts index 03b61b43..03e7e087 100644 --- a/apps/sandbox-container/server/userContainer.ts +++ b/apps/sandbox-container/server/userContainer.ts @@ -6,7 +6,7 @@ import { getContainerManager } from './containerManager' import { fileToBase64 } from './utils' import type { ExecParams, FileList, FileWrite } from '../shared/schema' -import type { Env } from './context' +import type { Env } from './sandbox.server.context' export class UserContainer extends DurableObject { constructor( diff --git a/apps/sandbox-container/types.d.ts b/apps/sandbox-container/types.d.ts index 664a2d66..95b20f33 100644 --- a/apps/sandbox-container/types.d.ts +++ b/apps/sandbox-container/types.d.ts @@ -1,6 +1,9 @@ declare module 'cloudflare:test' { interface ProvidedEnv { OPENAI_API_KEY: 'TODO' + AI_GATEWAY_TOKEN: string + CLOUDFLARE_ACCOUNT_ID: string + AI_GATEWAY_ID: string AI: Ai } } diff --git a/apps/sandbox-container/wrangler.jsonc b/apps/sandbox-container/wrangler.jsonc index 484b6d7b..f84387c2 100644 --- a/apps/sandbox-container/wrangler.jsonc +++ b/apps/sandbox-container/wrangler.jsonc @@ -1,7 +1,7 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "sandbox-container-dev", - "main": "server/index.ts", + "main": "server/sandbox.server.app.ts", "compatibility_date": "2025-04-03", "compatibility_flags": ["nodejs_compat"], "containers": [ @@ -40,7 +40,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "kv_namespaces": [ { diff --git a/apps/workers-bindings/.dev.vars.example b/apps/workers-bindings/.dev.vars.example index c087f669..d2d3cd20 100644 --- a/apps/workers-bindings/.dev.vars.example +++ b/apps/workers-bindings/.dev.vars.example @@ -2,4 +2,3 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file diff --git a/apps/workers-bindings/CHANGELOG.md b/apps/workers-bindings/CHANGELOG.md new file mode 100644 index 00000000..ad55ba28 --- /dev/null +++ b/apps/workers-bindings/CHANGELOG.md @@ -0,0 +1,146 @@ +# workers-bindings + +## 0.4.4 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/eval-tools@0.32.5 + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.4.3 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.4.2 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + - @repo/eval-tools@0.32.4 + +## 0.4.1 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/eval-tools@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.4.0 + +### Minor Changes + +- dee0a7b: Updated the model for docs search to embeddinggemma-300m + +## 0.3.4 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/eval-tools@0.32.2 + - @repo/mcp-common@0.19.2 + +## 0.3.3 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.3.2 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.3.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/eval-tools@0.32.1 + - @repo/mcp-observability@0.32.1 + +## 0.3.0 + +### Minor Changes + +- f885d07: Add search docs tool to bindings and obs servers + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.2.0 + +### Minor Changes + +- 2621557: Use new workers:read scope instead of workers:write, as these mcp servers don't require workers write permissions + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/eval-tools@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.3 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.2 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.1 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/eval-tools@0.31.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/workers-bindings/CONTRIBUTING.md b/apps/workers-bindings/CONTRIBUTING.md index 20e6cf61..797f595a 100644 --- a/apps/workers-bindings/CONTRIBUTING.md +++ b/apps/workers-bindings/CONTRIBUTING.md @@ -17,7 +17,6 @@ If you'd like to iterate and test your MCP server, you can do so in local develo ``` DEV_DISABLE_OAUTH=true - DEV_CLOUDFLARE_EMAIL=your_cloudflare_email # This is your global api token DEV_CLOUDFLARE_API_TOKEN=your_development_api_token ``` @@ -28,7 +27,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/workers-bindings/README.md b/apps/workers-bindings/README.md index 096886ef..df68afab 100644 --- a/apps/workers-bindings/README.md +++ b/apps/workers-bindings/README.md @@ -19,7 +19,8 @@ Currently available tools: | | `kv_namespace_get` | Get details of a kv namespace in your Cloudflare account | | | `kv_namespace_update` | Update the title of a kv namespace in your Cloudflare account | | **Workers** | `workers_list` | List all Workers in your Cloudflare account | -| | `worker_get_worker` | Get the source code of a Cloudflare Worker | +| | `workers_get_worker` | Get the details of a Cloudflare Worker | +| | `workers_get_worker_code` | Get the source code of a Cloudflare Worker | | **R2 Buckets** | `r2_buckets_list` | List r2 buckets in your Cloudflare account | | | `r2_bucket_create` | Create a new r2 bucket in your Cloudflare account | | | `r2_bucket_get` | Get details about a specific R2 bucket | @@ -75,7 +76,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://bindings.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://bindings.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/workers-bindings/evals/hyperdrive.eval.ts b/apps/workers-bindings/evals/hyperdrive.eval.ts index 310012f7..c12929b6 100644 --- a/apps/workers-bindings/evals/hyperdrive.eval.ts +++ b/apps/workers-bindings/evals/hyperdrive.eval.ts @@ -4,7 +4,7 @@ import { describeEval } from 'vitest-evals' import { runTask } from '@repo/eval-tools/src/runTask' import { checkFactuality } from '@repo/eval-tools/src/scorers' import { eachModel } from '@repo/eval-tools/src/test-models' -import { HYPERDRIVE_TOOLS } from '@repo/mcp-common/src/tools/hyperdrive' +import { HYPERDRIVE_TOOLS } from '@repo/mcp-common/src/tools/hyperdrive.tools' import { initializeClient } from './utils' // Assuming utils.ts will exist here diff --git a/apps/workers-bindings/evals/kv_namespaces.eval.ts b/apps/workers-bindings/evals/kv_namespaces.eval.ts index 53f9cc64..f7febe34 100644 --- a/apps/workers-bindings/evals/kv_namespaces.eval.ts +++ b/apps/workers-bindings/evals/kv_namespaces.eval.ts @@ -4,7 +4,7 @@ import { describeEval } from 'vitest-evals' import { runTask } from '@repo/eval-tools/src/runTask' import { checkFactuality } from '@repo/eval-tools/src/scorers' import { eachModel } from '@repo/eval-tools/src/test-models' -import { KV_NAMESPACE_TOOLS } from '@repo/mcp-common/src/tools/kv_namespace' +import { KV_NAMESPACE_TOOLS } from '@repo/mcp-common/src/tools/kv_namespace.tools' import { initializeClient } from './utils' // Assuming utils.ts will exist here @@ -19,7 +19,6 @@ eachModel('$modelName', ({ model }) => { task: async (input: string) => { const client = await initializeClient(/* Pass necessary mocks/config */) const { promptOutput, toolCalls } = await runTask(client, model, input) - const toolCall = toolCalls.find( (call) => call.toolName === KV_NAMESPACE_TOOLS.kv_namespace_create ) @@ -41,7 +40,6 @@ eachModel('$modelName', ({ model }) => { task: async (input: string) => { const client = await initializeClient(/* Pass necessary mocks/config */) const { promptOutput, toolCalls } = await runTask(client, model, input) - const toolCall = toolCalls.find( (call) => call.toolName === KV_NAMESPACE_TOOLS.kv_namespaces_list ) @@ -56,15 +54,13 @@ eachModel('$modelName', ({ model }) => { describeEval('Rename Cloudflare KV Namespace', { data: async () => [ { - input: - 'Rename my Cloudflare KV Namespace called "my-test-namespace" to "my-new-test-namespace".', + input: 'Rename my Cloudflare KV Namespace with ID 1234 to "my-new-test-namespace".', expected: `The ${KV_NAMESPACE_TOOLS.kv_namespace_update} tool should be called to rename the kv namespace.`, }, ], task: async (input: string) => { const client = await initializeClient(/* Pass necessary mocks/config */) const { promptOutput, toolCalls } = await runTask(client, model, input) - const toolCall = toolCalls.find( (call) => call.toolName === KV_NAMESPACE_TOOLS.kv_namespace_update ) @@ -79,14 +75,13 @@ eachModel('$modelName', ({ model }) => { describeEval('Get Cloudflare KV Namespace Details', { data: async () => [ { - input: 'Get details of my Cloudflare KV Namespace called "my-new-test-namespace".', + input: 'Get details of my Cloudflare KV Namespace with ID 1234.', expected: `The ${KV_NAMESPACE_TOOLS.kv_namespace_get} tool should be called to retrieve the details of the kv namespace.`, }, ], task: async (input: string) => { const client = await initializeClient(/* Pass necessary mocks/config */) const { promptOutput, toolCalls } = await runTask(client, model, input) - const toolCall = toolCalls.find( (call) => call.toolName === KV_NAMESPACE_TOOLS.kv_namespace_get ) @@ -101,14 +96,13 @@ eachModel('$modelName', ({ model }) => { describeEval('Delete Cloudflare KV Namespace', { data: async () => [ { - input: 'Look up the id of my only KV namespace and delete it.', + input: 'Delete the kv namespace with ID 1234.', expected: `The ${KV_NAMESPACE_TOOLS.kv_namespace_delete} tool should be called to delete the kv namespace.`, }, ], task: async (input: string) => { const client = await initializeClient(/* Pass necessary mocks/config */) const { promptOutput, toolCalls } = await runTask(client, model, input) - const toolCall = toolCalls.find( (call) => call.toolName === KV_NAMESPACE_TOOLS.kv_namespace_delete ) diff --git a/apps/workers-bindings/evals/vectorize.eval.ts b/apps/workers-bindings/evals/vectorize.eval.ts index b1ec3932..62d86877 100644 --- a/apps/workers-bindings/evals/vectorize.eval.ts +++ b/apps/workers-bindings/evals/vectorize.eval.ts @@ -4,7 +4,7 @@ import { describeEval } from 'vitest-evals' import { runTask } from '@repo/eval-tools/src/runTask' import { checkFactuality } from '@repo/eval-tools/src/scorers' import { eachModel } from '@repo/eval-tools/src/test-models' -import { VECTORIZE_TOOLS } from '@repo/mcp-common/src/tools/vectorize' +import { VECTORIZE_TOOLS } from '@repo/mcp-common/src/tools/vectorize.tools' import { initializeClient } from './utils' // Assuming utils.ts will exist here diff --git a/apps/workers-bindings/package.json b/apps/workers-bindings/package.json index 85652757..37f61880 100644 --- a/apps/workers-bindings/package.json +++ b/apps/workers-bindings/package.json @@ -1,11 +1,11 @@ { "name": "workers-bindings", - "version": "0.0.0", + "version": "0.4.4", "private": true, "scripts": { "check:lint": "run-eslint-workers", "check:types": "run-tsc", - "deploy": "wrangler deploy", + "deploy": "run-wrangler-deploy", "deploy:staging": "wrangler deploy --env staging", "deploy:production": "wrangler deploy --env production", "eval:dev": "start-server-and-test --expect 404 eval:server http://localhost:8977 'vitest --testTimeout=60000 --config vitest.config.evals.ts'", @@ -25,13 +25,13 @@ "wrangler": "4.10.0" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", - "@modelcontextprotocol/sdk": "1.10.2", + "@cloudflare/workers-oauth-provider": "0.0.13", + "@modelcontextprotocol/sdk": "1.20.2", "@n8n/json-schema-to-zod": "1.1.0", "@repo/eval-tools": "workspace:*", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "ai": "4.3.10", "concurrently": "9.1.2", "hono": "4.7.6", diff --git a/apps/workers-bindings/src/index.ts b/apps/workers-bindings/src/bindings.app.ts similarity index 72% rename from apps/workers-bindings/src/index.ts rename to apps/workers-bindings/src/bindings.app.ts index a21c4d68..6750a878 100644 --- a/apps/workers-bindings/src/index.ts +++ b/apps/workers-bindings/src/bindings.app.ts @@ -1,26 +1,29 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-ai-search.prompts' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' -import { registerD1Tools } from '@repo/mcp-common/src/tools/d1' -import { registerHyperdriveTools } from '@repo/mcp-common/src/tools/hyperdrive' -import { registerKVTools } from '@repo/mcp-common/src/tools/kv_namespace' -import { registerR2BucketTools } from '@repo/mcp-common/src/tools/r2_bucket' -import { registerVectorizeTools } from '@repo/mcp-common/src/tools/vectorize' -import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' +import { registerD1Tools } from '@repo/mcp-common/src/tools/d1.tools' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-ai-search.tools' +import { registerHyperdriveTools } from '@repo/mcp-common/src/tools/hyperdrive.tools' +import { registerKVTools } from '@repo/mcp-common/src/tools/kv_namespace.tools' +import { registerR2BucketTools } from '@repo/mcp-common/src/tools/r2_bucket.tools' +import { registerVectorizeTools } from '@repo/mcp-common/src/tools/vectorize.tools' +import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker.tools' import { MetricsTracker } from '@repo/mcp-observability' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './bindings.context' export { UserDetails } @@ -60,8 +63,12 @@ export class WorkersBindingsMCP extends McpAgent { - if ( - (env.ENVIRONMENT === 'development' || env.ENVIRONMENT === 'test') && - env.DEV_DISABLE_OAUTH === 'true' - ) { - return await handleDevMode(WorkersBindingsMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + console.log('is token mode') + return await handleApiTokenMode(WorkersBindingsMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/workers-bindings/src/context.ts b/apps/workers-bindings/src/bindings.context.ts similarity index 72% rename from apps/workers-bindings/src/context.ts rename to apps/workers-bindings/src/bindings.context.ts index c0368328..001c7cc1 100644 --- a/apps/workers-bindings/src/context.ts +++ b/apps/workers-bindings/src/bindings.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { WorkersBindingsMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { WorkersBindingsMCP } from './bindings.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' | 'test' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string @@ -11,10 +12,14 @@ export interface Env { MCP_OBJECT: DurableObjectNamespace USER_DETAILS: DurableObjectNamespace MCP_METRICS: AnalyticsEngineDataset - DEV_DISABLE_OAUTH: string - DEV_CLOUDFLARE_API_TOKEN: string - DEV_CLOUDFLARE_EMAIL: string CLOUDFLARE_API_TOKEN: string OPENAI_API_KEY: string + AI_GATEWAY_TOKEN: string + CLOUDFLARE_ACCOUNT_ID: string + AI_GATEWAY_ID: string AI: Ai + VECTORIZE: VectorizeIndex + DEV_DISABLE_OAUTH: string + DEV_CLOUDFLARE_API_TOKEN: string + DEV_CLOUDFLARE_EMAIL: string } diff --git a/apps/workers-bindings/vitest.config.ts b/apps/workers-bindings/vitest.config.ts index c201c144..9efac6e2 100644 --- a/apps/workers-bindings/vitest.config.ts +++ b/apps/workers-bindings/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/bindings.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/workers-bindings/wrangler.jsonc b/apps/workers-bindings/wrangler.jsonc index a892e6a1..dce4a313 100644 --- a/apps/workers-bindings/wrangler.jsonc +++ b/apps/workers-bindings/wrangler.jsonc @@ -5,7 +5,7 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "workers-bindings-dev", - "main": "src/index.ts", + "main": "src/bindings.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "migrations": [ @@ -33,7 +33,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "dev": { "port": 8976 @@ -45,6 +49,15 @@ "CLOUDFLARE_CLIENT_ID": "", "CLOUDFLARE_CLIENT_SECRET": "" }, + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-embeddinggemma-v1" + } + ], "workers_dev": false, "preview_urls": false, "analytics_engine_datasets": [ @@ -77,6 +90,15 @@ "id": "4258ce2dee98428db6c9870eb5097f26" } ], + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-embeddinggemma-v1" + } + ], "vars": { "ENVIRONMENT": "staging", "MCP_SERVER_NAME": "workers-bindings-staging", @@ -117,6 +139,15 @@ "MCP_SERVER_NAME": "workers-bindings", "MCP_SERVER_VERSION": "1.0.0" }, + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-embeddinggemma-v1" + } + ], "analytics_engine_datasets": [ { "binding": "MCP_METRICS", diff --git a/apps/workers-builds/.dev.vars.example b/apps/workers-builds/.dev.vars.example new file mode 100644 index 00000000..232698b5 --- /dev/null +++ b/apps/workers-builds/.dev.vars.example @@ -0,0 +1,6 @@ +CLOUDFLARE_CLIENT_ID= +CLOUDFLARE_CLIENT_SECRET= +# OR +DEV_DISABLE_OAUTH= +DEV_CLOUDFLARE_API_TOKEN= + diff --git a/apps/workers-builds/.eslintrc.cjs b/apps/workers-builds/.eslintrc.cjs new file mode 100644 index 00000000..f6bf291a --- /dev/null +++ b/apps/workers-builds/.eslintrc.cjs @@ -0,0 +1,5 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ['@repo/eslint-config/default.cjs'], +} diff --git a/apps/workers-builds/CHANGELOG.md b/apps/workers-builds/CHANGELOG.md new file mode 100644 index 00000000..593c119a --- /dev/null +++ b/apps/workers-builds/CHANGELOG.md @@ -0,0 +1,125 @@ +# workers-builds + +## 0.1.10 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.1.9 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.1.8 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.1.7 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.1.6 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.1.5 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.1.4 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.1.3 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/workers-builds/CONTRIBUTING.md b/apps/workers-builds/CONTRIBUTING.md new file mode 100644 index 00000000..3024be9f --- /dev/null +++ b/apps/workers-builds/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# Setup + +If you'd like to iterate and test your MCP server, you can do so in local development. + +## Local Development + +1. Create a `.dev.vars` file in your project root: + + If you're a Cloudflare employee: + + ``` + CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id + CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret + ``` + + If you're an external contributor, you can provide a development API token: + + ``` + DEV_DISABLE_OAUTH=true + # This is your global api token + DEV_CLOUDFLARE_API_TOKEN=your_development_api_token + ``` + +2. Start the local development server: + +```bash +pnpm dev +``` + +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. + Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. + +## Deploying the Worker ( Cloudflare employees only ) + +Set secrets via Wrangler: + +```bash +npx wrangler secret put CLOUDFLARE_CLIENT_ID -e +npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e +``` + +## Set up a KV namespace + +Create the KV namespace: + +```bash +npx wrangler kv namespace create "OAUTH_KV" +``` + +Then, update the Wrangler file with the generated KV namespace ID. + +## Deploy & Test + +Deploy the MCP server to make it available on your workers.dev domain: + +```bash +npx wrangler deploy -e +``` + +Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): + +```bash +npx @modelcontextprotocol/inspector@latest +``` diff --git a/apps/workers-builds/README.md b/apps/workers-builds/README.md new file mode 100644 index 00000000..ef8b978a --- /dev/null +++ b/apps/workers-builds/README.md @@ -0,0 +1,50 @@ +# Workers Builds MCP Server 🔭 + +This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP +connections, with Cloudflare OAuth built-in. + +It integrates tools to provide insights and management capabilities for your Cloudflare Workers Builds. + +## 🔨 Available Tools + +Currently available tools: + +| **Category** | **Tool** | **Description** | +| ------------------ | ---------------------------------- | ---------------------------------------------------------------------------------------- | +| **Workers Builds** | `workers_builds_set_active_worker` | Sets the active Worker ID for subsequent calls. | +| **Workers Builds** | `workers_builds_list_builds` | Lists builds for a Cloudflare Worker. | +| **Workers Builds** | `workers_builds_get_build` | Retrieves details for a specific build by its UUID, including build and deploy commands. | +| **Workers Builds** | `workers_builds_get_build_logs` | Fetches the logs for a Cloudflare Workers build by its UUID. | + +This MCP server is still a work in progress, and we plan to add more tools in the future. + +### Prompt Examples + +- `Set my-worker as the active worker.` +- `List the last 5 builds for my worker 'my-ci-worker'.` +- `What were the details for build 'xxxx-xxxx-xxxx-xxxx'?` +- `Show me the logs for build my latest build.` +- `Did the latest build for worker frontend-app succeed?` + +## Access the remote MCP server from from any MCP Client + +If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://builds.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). + +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. + +Replace the content with the following configuration: + +```json +{ + "mcpServers": { + "cloudflare": { + "command": "npx", + "args": ["mcp-remote@latest", "https://builds.mcp.cloudflare.com/mcp"] + } + } +} +``` + +Once you've set up your configuration file, restart MCP client and a browser window will open showing your OAuth login page. Proceed through the authentication flow to grant the client access to your MCP server. After you grant access, the tools will become available for you to use. + +Interested in contributing, and running this server locally? See [CONTRIBUTING.md](CONTRIBUTING.md) to get started. diff --git a/apps/workers-builds/package.json b/apps/workers-builds/package.json new file mode 100644 index 00000000..37de03f7 --- /dev/null +++ b/apps/workers-builds/package.json @@ -0,0 +1,35 @@ +{ + "name": "workers-builds", + "version": "0.1.10", + "private": true, + "scripts": { + "check:lint": "run-eslint-workers", + "check:types": "run-tsc", + "deploy": "run-wrangler-deploy", + "dev": "vite dev", + "start": "vite dev", + "types": "wrangler types --include-env=false", + "test": "vitest run" + }, + "dependencies": { + "@cloudflare/workers-oauth-provider": "0.0.13", + "@hono/zod-validator": "0.4.3", + "@modelcontextprotocol/sdk": "1.20.2", + "@repo/mcp-common": "workspace:*", + "@repo/mcp-observability": "workspace:*", + "agents": "0.2.19", + "cloudflare": "4.2.0", + "hono": "4.7.6", + "zod": "3.24.2" + }, + "devDependencies": { + "@cloudflare/vite-plugin": "1.1.0", + "@cloudflare/vitest-pool-workers": "0.8.14", + "@types/node": "22.14.1", + "prettier": "3.5.3", + "typescript": "5.5.4", + "vite": "6.3.4", + "vitest": "3.0.9", + "wrangler": "4.10.0" + } +} diff --git a/apps/workers-builds/src/tools/workers-builds.tools.ts b/apps/workers-builds/src/tools/workers-builds.tools.ts new file mode 100644 index 00000000..fc9b9ffd --- /dev/null +++ b/apps/workers-builds/src/tools/workers-builds.tools.ts @@ -0,0 +1,282 @@ +import { z } from 'zod' + +import { getBuild, getBuildLogs, listBuilds } from '@repo/mcp-common/src/api/workers-builds.api' +import { fmt } from '@repo/mcp-common/src/format' +import { getProps } from '@repo/mcp-common/src/get-props' + +import type { BuildsMCP } from '../workers-builds.app' + +/** + * Registers the Workers Builds tools with the MCP server + * @param server The MCP server instance + * @param accountId Cloudflare account ID + * @param apiToken Cloudflare API token + */ +export function registerBuildsTools(agent: BuildsMCP) { + agent.server.tool( + 'workers_builds_set_active_worker', + fmt.trim(` + Set the active Worker ID for subsequent calls. + Use this tool to set the active worker for subsequent calls. + + Worker IDs are formatted similar to: db6a6421c2b046679a9daada1537088b + If you are given a Worker name or script name, you can use workers_get_worker to get the Worker ID. + `), + { + workerId: z.string().describe('The Worker ID to set as active.'), + }, + async ({ workerId }) => { + await agent.setActiveWorkerId(workerId) + return { + content: [ + { + type: 'text', + text: `Active worker set to ${workerId}`, + }, + ], + } + } + ) + + agent.server.tool( + 'workers_builds_list_builds', + fmt.trim(` + Use the Workers Builds API to list builds for a Cloudflare Worker. + + MUST provide a workerId or call workers_builds_set_active_worker first. + `), + { + workerId: z.string().optional().describe('The Worker ID to list builds for.'), + page: z.number().optional().default(1).describe('The page number to return.'), + perPage: z.number().optional().default(10).describe('The number of builds per page.'), + }, + async ({ workerId, page, perPage }) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: fmt.oneLine(` + No currently active accountId. Try listing your accounts (accounts_list) + and then setting an active account (set_active_account) + `), + }, + ], + } + } + + if (!workerId) { + const activeWorkerId = await agent.getActiveWorkerId() + if (activeWorkerId) { + workerId = activeWorkerId + } else { + return { + content: [ + { + type: 'text', + text: fmt.oneLine(` + No workerId provided and no active workerId. + Either provide a workerId or call workers_builds_set_active_worker first. + `), + }, + ], + } + } + } + + try { + const props = getProps(agent) + const res = await listBuilds({ + apiToken: props.accessToken, + accountId, + workerId, + page, + perPage, + }) + + if (!res.result) { + return { + content: [ + { + type: 'text', + text: 'No builds found', + }, + ], + } + } + + const buildsFormatted = res.result + .sort((a, b) => b.created_on.getTime() - a.created_on.getTime()) + .map((build) => ({ + buildUUID: build.build_uuid, + createdOn: build.created_on.toISOString(), + status: build.status, + buildOutcome: build.build_outcome, + branch: build.build_trigger_metadata.branch, + commitHash: build.build_trigger_metadata.commit_hash, + commitMessage: build.build_trigger_metadata.commit_message, + commitAuthor: build.build_trigger_metadata.author, + })) + + return { + content: [ + { + type: 'text', + text: 'pagination_info:', + }, + { + type: 'text', + text: await fmt.asTSV([res.result_info]), + }, + { + type: 'text', + text: 'builds:', + }, + { + type: 'text', + text: await fmt.asTSV(buildsFormatted), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error: listing builds failed: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'workers_builds_get_build', + fmt.trim(` + Get details for a specific build by its UUID. + Includes build and deploy commands for the build (useful for debugging build failures). + `), + { + buildUUID: z.string().describe('The build UUID to get details for.'), + }, + async ({ buildUUID }) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Set an active account first.', + }, + ], + } + } + + try { + const props = getProps(agent) + const { result: build } = await getBuild({ + apiToken: props.accessToken, + accountId, + buildUUID, + }) + + if (!build) { + return { + content: [ + { + type: 'text', + text: 'Build not found', + }, + ], + } + } + + const buildFormatted = { + buildUUID: build.build_uuid, + createdOn: build.created_on.toISOString(), + status: build.status, + buildOutcome: build.build_outcome, + branch: build.build_trigger_metadata.branch, + commitHash: build.build_trigger_metadata.commit_hash, + commitMessage: build.build_trigger_metadata.commit_message, + commitAuthor: build.build_trigger_metadata.author, + buildCommand: build.build_trigger_metadata.build_command, + deployCommand: build.build_trigger_metadata.deploy_command, + } + + return { + content: [ + { + type: 'text', + text: await fmt.asTSV([buildFormatted]), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error: getting build failed: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) + + agent.server.tool( + 'workers_builds_get_build_logs', + fmt.trim(` + Get logs for a Cloudflare Workers build. + `), + { + buildUUID: z.string().describe('The build UUID to get logs for.'), + }, + async ({ buildUUID }) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Set an active account first.', + }, + ], + } + } + + try { + const props = getProps(agent) + const logs = await getBuildLogs({ + apiToken: props.accessToken, + accountId, + buildUUID, + }) + const logsFormatted = logs.map((log) => ({ + timestamp: `${log[0].getUTCHours()}:${log[0].getUTCMinutes()}:${log[0].getUTCSeconds()}`, + message: log[1], + })) + return { + content: [ + { + type: 'text', + text: await fmt.asTSV(logsFormatted), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error: getting build logs failed: ${error instanceof Error && error.message}`, + }, + ], + } + } + } + ) +} diff --git a/apps/workers-builds/src/workers-builds.app.ts b/apps/workers-builds/src/workers-builds.app.ts new file mode 100644 index 00000000..2c804ecb --- /dev/null +++ b/apps/workers-builds/src/workers-builds.app.ts @@ -0,0 +1,203 @@ +import OAuthProvider from '@cloudflare/workers-oauth-provider' +import { McpAgent } from 'agents/mcp' + +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' +import { + createAuthHandlers, + handleTokenExchangeCallback, +} from '@repo/mcp-common/src/cloudflare-oauth-handler' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import { getEnv } from '@repo/mcp-common/src/env' +import { fmt } from '@repo/mcp-common/src/format' +import { getProps } from '@repo/mcp-common/src/get-props' +import { RequiredScopes } from '@repo/mcp-common/src/scopes' +import { initSentryWithUser } from '@repo/mcp-common/src/sentry' +import { CloudflareMCPServer } from '@repo/mcp-common/src/server' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' +import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker.tools' + +import { MetricsTracker } from '../../../packages/mcp-observability/src' +import { registerBuildsTools } from './tools/workers-builds.tools' + +import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' +import type { Env } from './workers-builds.context' + +export { UserDetails } + +const env = getEnv() + +const metrics = new MetricsTracker(env.MCP_METRICS, { + name: env.MCP_SERVER_NAME, + version: env.MCP_SERVER_VERSION, +}) + +// Context from the auth process, encrypted & stored in the auth token +// and provided to the DurableMCP as this.props +type Props = AuthProps + +type State = { + activeAccountId: string | null + activeBuildUUID: string | null + activeWorkerId: string | null +} + +export class BuildsMCP extends McpAgent { + _server: CloudflareMCPServer | undefined + set server(server: CloudflareMCPServer) { + this._server = server + } + get server(): CloudflareMCPServer { + if (!this._server) { + throw new Error('Tried to access server before it was initialized') + } + return this._server + } + + async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + const sentry = + props.type === 'user_token' ? initSentryWithUser(env, this.ctx, props.user.id) : undefined + + this.server = new CloudflareMCPServer({ + userId, + wae: this.env.MCP_METRICS, + serverInfo: { + name: this.env.MCP_SERVER_NAME, + version: this.env.MCP_SERVER_VERSION, + }, + sentry, + options: { + instructions: fmt.trim(` + # Cloudflare Workers Builds Tool + * A Cloudflare Worker is a serverless function + * Workers Builds is a CI/CD system for building and deploying your Worker whenever you push code to GitHub/GitLab. + + This server allows you to view and debug Cloudflare Workers Builds for your Workers (NOT Cloudflare Pages). + + To get started, you can list your accounts (accounts_list) and then set an active account (set_active_account). + Once you have an active account, you can list your Workers (workers_list) and set an active Worker (workers_builds_set_active_worker). + You can then list the builds for your Worker (workers_builds_list_builds) and set an active build (workers_builds_set_active_build). + Once you have an active build, you can view the logs (workers_builds_get_build_logs). + `), + }, + }) + + registerAccountTools(this) + + // Register Cloudflare Workers tools + registerWorkersTools(this) + + // Register Cloudflare Workers logs tools + registerBuildsTools(this) + } + + async getActiveAccountId() { + try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } + // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it + // we do this so we can persist activeAccountId across sessions + const userDetails = getUserDetails(env, props.user.id) + return await userDetails.getActiveAccountId() + } catch (e) { + this.server.recordError(e) + return null + } + } + + async setActiveAccountId(accountId: string) { + try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) + await userDetails.setActiveAccountId(accountId) + } catch (e) { + this.server.recordError(e) + } + } + + async getActiveBuildUUID(): Promise { + try { + return this.state.activeBuildUUID + } catch (e) { + this.server.recordError(e) + return null + } + } + + async setActiveBuildUUID(buildUUID: string | null): Promise { + try { + this.setState({ + ...this.state, + activeBuildUUID: buildUUID, + }) + } catch (e) { + this.server.recordError(e) + } + } + + async getActiveWorkerId(): Promise { + try { + return this.state.activeWorkerId + } catch (e) { + this.server.recordError(e) + return null + } + } + + async setActiveWorkerId(workerId: string | null): Promise { + try { + this.setState({ + ...this.state, + activeWorkerId: workerId, + }) + } catch (e) { + this.server.recordError(e) + } + } +} + +const BuildsScopes = { + ...RequiredScopes, + 'account:read': 'See your account info such as account details, analytics, and memberships.', + 'workers:read': + 'See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.', + 'workers_builds:read': + 'See and change Cloudflare Workers Builds data such as builds, build configuration, and logs.', +} as const + +export default { + fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(BuildsMCP, req, env, ctx) + } + + return new OAuthProvider({ + apiHandlers: { + '/mcp': BuildsMCP.serve('/mcp'), + '/sse': BuildsMCP.serveSSE('/sse'), + }, + // @ts-expect-error + defaultHandler: createAuthHandlers({ scopes: BuildsScopes, metrics }), + authorizeEndpoint: '/oauth/authorize', + tokenEndpoint: '/token', + tokenExchangeCallback: (options) => + handleTokenExchangeCallback( + options, + env.CLOUDFLARE_CLIENT_ID, + env.CLOUDFLARE_CLIENT_SECRET + ), + // Cloudflare access token TTL + accessTokenTTL: 3600, + clientRegistrationEndpoint: '/register', + }).fetch(req, env, ctx) + }, +} diff --git a/apps/workers-builds/src/workers-builds.context.ts b/apps/workers-builds/src/workers-builds.context.ts new file mode 100644 index 00000000..35646fa5 --- /dev/null +++ b/apps/workers-builds/src/workers-builds.context.ts @@ -0,0 +1,22 @@ +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { BuildsMCP } from './workers-builds.app' + +export interface Env { + OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string + ENVIRONMENT: 'development' | 'staging' | 'production' + MCP_SERVER_NAME: string + MCP_SERVER_VERSION: string + CLOUDFLARE_CLIENT_ID: string + CLOUDFLARE_CLIENT_SECRET: string + MCP_OBJECT: DurableObjectNamespace + USER_DETAILS: DurableObjectNamespace + MCP_METRICS: AnalyticsEngineDataset + SENTRY_ACCESS_CLIENT_ID: string + SENTRY_ACCESS_CLIENT_SECRET: string + GIT_HASH: string + SENTRY_DSN: string + DEV_DISABLE_OAUTH: string + DEV_CLOUDFLARE_API_TOKEN: string + DEV_CLOUDFLARE_EMAIL: string +} diff --git a/apps/workers-builds/tsconfig.json b/apps/workers-builds/tsconfig.json new file mode 100644 index 00000000..9dcfd4e8 --- /dev/null +++ b/apps/workers-builds/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@repo/typescript-config/workers.json", + "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] +} diff --git a/apps/workers-builds/types.d.ts b/apps/workers-builds/types.d.ts new file mode 100644 index 00000000..e090f9a7 --- /dev/null +++ b/apps/workers-builds/types.d.ts @@ -0,0 +1,5 @@ +import type { TestEnv } from './vitest.config' + +declare module 'cloudflare:test' { + interface ProvidedEnv extends TestEnv {} +} diff --git a/apps/workers-builds/vite.config.mts b/apps/workers-builds/vite.config.mts new file mode 100644 index 00000000..da8d7d4b --- /dev/null +++ b/apps/workers-builds/vite.config.mts @@ -0,0 +1,9 @@ +import { cloudflare } from '@cloudflare/vite-plugin' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [cloudflare()], + server: { + port: 8976, + }, +}) diff --git a/apps/workers-builds/vitest.config.ts b/apps/workers-builds/vitest.config.ts new file mode 100644 index 00000000..67fa736c --- /dev/null +++ b/apps/workers-builds/vitest.config.ts @@ -0,0 +1,24 @@ +import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' + +import type { Env } from './src/workers-builds.context' + +export interface TestEnv extends Env { + CLOUDFLARE_MOCK_ACCOUNT_ID: string + CLOUDFLARE_MOCK_API_TOKEN: string +} + +export default defineWorkersConfig({ + test: { + poolOptions: { + workers: { + wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, + miniflare: { + bindings: { + CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', + CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', + } satisfies Partial, + }, + }, + }, + }, +}) diff --git a/apps/workers-builds/worker-configuration.d.ts b/apps/workers-builds/worker-configuration.d.ts new file mode 100644 index 00000000..305dc71b --- /dev/null +++ b/apps/workers-builds/worker-configuration.d.ts @@ -0,0 +1,5694 @@ +// Runtime types generated with workerd@1.20250409.0 2025-03-10 nodejs_compat +// Begin runtime types +/*! ***************************************************************************** +Copyright (c) Cloudflare. All rights reserved. +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +/* eslint-disable */ +// noinspection JSUnusedGlobalSymbols +declare var onmessage: never; +/** + * An abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException) + */ +declare class DOMException extends Error { + constructor(message?: string, name?: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/message) */ + readonly message: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ + readonly name: string; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMException/code) + */ + readonly code: number; + static readonly INDEX_SIZE_ERR: number; + static readonly DOMSTRING_SIZE_ERR: number; + static readonly HIERARCHY_REQUEST_ERR: number; + static readonly WRONG_DOCUMENT_ERR: number; + static readonly INVALID_CHARACTER_ERR: number; + static readonly NO_DATA_ALLOWED_ERR: number; + static readonly NO_MODIFICATION_ALLOWED_ERR: number; + static readonly NOT_FOUND_ERR: number; + static readonly NOT_SUPPORTED_ERR: number; + static readonly INUSE_ATTRIBUTE_ERR: number; + static readonly INVALID_STATE_ERR: number; + static readonly SYNTAX_ERR: number; + static readonly INVALID_MODIFICATION_ERR: number; + static readonly NAMESPACE_ERR: number; + static readonly INVALID_ACCESS_ERR: number; + static readonly VALIDATION_ERR: number; + static readonly TYPE_MISMATCH_ERR: number; + static readonly SECURITY_ERR: number; + static readonly NETWORK_ERR: number; + static readonly ABORT_ERR: number; + static readonly URL_MISMATCH_ERR: number; + static readonly QUOTA_EXCEEDED_ERR: number; + static readonly TIMEOUT_ERR: number; + static readonly INVALID_NODE_TYPE_ERR: number; + static readonly DATA_CLONE_ERR: number; + get stack(): any; + set stack(value: any); +} +type WorkerGlobalScopeEventMap = { + fetch: FetchEvent; + scheduled: ScheduledEvent; + queue: QueueEvent; + unhandledrejection: PromiseRejectionEvent; + rejectionhandled: PromiseRejectionEvent; +}; +declare abstract class WorkerGlobalScope extends EventTarget { + EventTarget: typeof EventTarget; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console) */ +interface Console { + "assert"(condition?: boolean, ...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/clear_static) */ + clear(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/count_static) */ + count(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/countreset_static) */ + countReset(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/debug_static) */ + debug(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dir_static) */ + dir(item?: any, options?: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/dirxml_static) */ + dirxml(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/error_static) */ + error(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/group_static) */ + group(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupcollapsed_static) */ + groupCollapsed(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/groupend_static) */ + groupEnd(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/info_static) */ + info(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static) */ + log(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/table_static) */ + table(tabularData?: any, properties?: string[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/time_static) */ + time(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timeend_static) */ + timeEnd(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/timelog_static) */ + timeLog(label?: string, ...data: any[]): void; + timeStamp(label?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/trace_static) */ + trace(...data: any[]): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/console/warn_static) */ + warn(...data: any[]): void; +} +declare const console: Console; +type BufferSource = ArrayBufferView | ArrayBuffer; +type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array; +declare namespace WebAssembly { + class CompileError extends Error { + constructor(message?: string); + } + class RuntimeError extends Error { + constructor(message?: string); + } + type ValueType = "anyfunc" | "externref" | "f32" | "f64" | "i32" | "i64" | "v128"; + interface GlobalDescriptor { + value: ValueType; + mutable?: boolean; + } + class Global { + constructor(descriptor: GlobalDescriptor, value?: any); + value: any; + valueOf(): any; + } + type ImportValue = ExportValue | number; + type ModuleImports = Record; + type Imports = Record; + type ExportValue = Function | Global | Memory | Table; + type Exports = Record; + class Instance { + constructor(module: Module, imports?: Imports); + readonly exports: Exports; + } + interface MemoryDescriptor { + initial: number; + maximum?: number; + shared?: boolean; + } + class Memory { + constructor(descriptor: MemoryDescriptor); + readonly buffer: ArrayBuffer; + grow(delta: number): number; + } + type ImportExportKind = "function" | "global" | "memory" | "table"; + interface ModuleExportDescriptor { + kind: ImportExportKind; + name: string; + } + interface ModuleImportDescriptor { + kind: ImportExportKind; + module: string; + name: string; + } + abstract class Module { + static customSections(module: Module, sectionName: string): ArrayBuffer[]; + static exports(module: Module): ModuleExportDescriptor[]; + static imports(module: Module): ModuleImportDescriptor[]; + } + type TableKind = "anyfunc" | "externref"; + interface TableDescriptor { + element: TableKind; + initial: number; + maximum?: number; + } + class Table { + constructor(descriptor: TableDescriptor, value?: any); + readonly length: number; + get(index: number): any; + grow(delta: number, value?: any): number; + set(index: number, value?: any): void; + } + function instantiate(module: Module, imports?: Imports): Promise; + function validate(bytes: BufferSource): boolean; +} +/** + * This ServiceWorker API interface represents the global execution context of a service worker. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope) + */ +interface ServiceWorkerGlobalScope extends WorkerGlobalScope { + DOMException: typeof DOMException; + WorkerGlobalScope: typeof WorkerGlobalScope; + btoa(data: string): string; + atob(data: string): string; + setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; + setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearTimeout(timeoutId: number | null): void; + setInterval(callback: (...args: any[]) => void, msDelay?: number): number; + setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; + clearInterval(timeoutId: number | null): void; + queueMicrotask(task: Function): void; + structuredClone(value: T, options?: StructuredSerializeOptions): T; + reportError(error: any): void; + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + self: ServiceWorkerGlobalScope; + crypto: Crypto; + caches: CacheStorage; + scheduler: Scheduler; + performance: Performance; + Cloudflare: Cloudflare; + readonly origin: string; + Event: typeof Event; + ExtendableEvent: typeof ExtendableEvent; + CustomEvent: typeof CustomEvent; + PromiseRejectionEvent: typeof PromiseRejectionEvent; + FetchEvent: typeof FetchEvent; + TailEvent: typeof TailEvent; + TraceEvent: typeof TailEvent; + ScheduledEvent: typeof ScheduledEvent; + MessageEvent: typeof MessageEvent; + CloseEvent: typeof CloseEvent; + ReadableStreamDefaultReader: typeof ReadableStreamDefaultReader; + ReadableStreamBYOBReader: typeof ReadableStreamBYOBReader; + ReadableStream: typeof ReadableStream; + WritableStream: typeof WritableStream; + WritableStreamDefaultWriter: typeof WritableStreamDefaultWriter; + TransformStream: typeof TransformStream; + ByteLengthQueuingStrategy: typeof ByteLengthQueuingStrategy; + CountQueuingStrategy: typeof CountQueuingStrategy; + ErrorEvent: typeof ErrorEvent; + EventSource: typeof EventSource; + ReadableStreamBYOBRequest: typeof ReadableStreamBYOBRequest; + ReadableStreamDefaultController: typeof ReadableStreamDefaultController; + ReadableByteStreamController: typeof ReadableByteStreamController; + WritableStreamDefaultController: typeof WritableStreamDefaultController; + TransformStreamDefaultController: typeof TransformStreamDefaultController; + CompressionStream: typeof CompressionStream; + DecompressionStream: typeof DecompressionStream; + TextEncoderStream: typeof TextEncoderStream; + TextDecoderStream: typeof TextDecoderStream; + Headers: typeof Headers; + Body: typeof Body; + Request: typeof Request; + Response: typeof Response; + WebSocket: typeof WebSocket; + WebSocketPair: typeof WebSocketPair; + WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair; + AbortController: typeof AbortController; + AbortSignal: typeof AbortSignal; + TextDecoder: typeof TextDecoder; + TextEncoder: typeof TextEncoder; + navigator: Navigator; + Navigator: typeof Navigator; + URL: typeof URL; + URLSearchParams: typeof URLSearchParams; + URLPattern: typeof URLPattern; + Blob: typeof Blob; + File: typeof File; + FormData: typeof FormData; + Crypto: typeof Crypto; + SubtleCrypto: typeof SubtleCrypto; + CryptoKey: typeof CryptoKey; + CacheStorage: typeof CacheStorage; + Cache: typeof Cache; + FixedLengthStream: typeof FixedLengthStream; + IdentityTransformStream: typeof IdentityTransformStream; + HTMLRewriter: typeof HTMLRewriter; +} +declare function addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; +declare function removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; +/** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ +declare function dispatchEvent(event: WorkerGlobalScopeEventMap[keyof WorkerGlobalScopeEventMap]): boolean; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/btoa) */ +declare function btoa(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/atob) */ +declare function atob(data: string): string; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout) */ +declare function setTimeout(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearTimeout) */ +declare function clearTimeout(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: any[]) => void, msDelay?: number): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/setInterval) */ +declare function setInterval(callback: (...args: Args) => void, msDelay?: number, ...args: Args): number; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/clearInterval) */ +declare function clearInterval(timeoutId: number | null): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/queueMicrotask) */ +declare function queueMicrotask(task: Function): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/structuredClone) */ +declare function structuredClone(value: T, options?: StructuredSerializeOptions): T; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/reportError) */ +declare function reportError(error: any): void; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/fetch) */ +declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise; +declare const self: ServiceWorkerGlobalScope; +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare const crypto: Crypto; +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare const caches: CacheStorage; +declare const scheduler: Scheduler; +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +declare const performance: Performance; +declare const Cloudflare: Cloudflare; +declare const origin: string; +declare const navigator: Navigator; +interface TestController { +} +interface ExecutionContext { + waitUntil(promise: Promise): void; + passThroughOnException(): void; + props: any; +} +type ExportedHandlerFetchHandler = (request: Request>, env: Env, ctx: ExecutionContext) => Response | Promise; +type ExportedHandlerTailHandler = (events: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTraceHandler = (traces: TraceItem[], env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTailStreamHandler = (event: TailStream.TailEvent, env: Env, ctx: ExecutionContext) => TailStream.TailEventHandlerType | Promise; +type ExportedHandlerScheduledHandler = (controller: ScheduledController, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerQueueHandler = (batch: MessageBatch, env: Env, ctx: ExecutionContext) => void | Promise; +type ExportedHandlerTestHandler = (controller: TestController, env: Env, ctx: ExecutionContext) => void | Promise; +interface ExportedHandler { + fetch?: ExportedHandlerFetchHandler; + tail?: ExportedHandlerTailHandler; + trace?: ExportedHandlerTraceHandler; + tailStream?: ExportedHandlerTailStreamHandler; + scheduled?: ExportedHandlerScheduledHandler; + test?: ExportedHandlerTestHandler; + email?: EmailExportedHandler; + queue?: ExportedHandlerQueueHandler; +} +interface StructuredSerializeOptions { + transfer?: any[]; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent) */ +declare abstract class PromiseRejectionEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/promise) */ + readonly promise: Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PromiseRejectionEvent/reason) */ + readonly reason: any; +} +declare abstract class Navigator { + sendBeacon(url: string, body?: (ReadableStream | string | (ArrayBuffer | ArrayBufferView) | Blob | FormData | URLSearchParams | URLSearchParams)): boolean; + readonly userAgent: string; + readonly hardwareConcurrency: number; +} +/** +* The Workers runtime supports a subset of the Performance API, used to measure timing and performance, +* as well as timing of subrequests and other operations. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/) +*/ +interface Performance { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */ + readonly timeOrigin: number; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancenow) */ + now(): number; +} +interface AlarmInvocationInfo { + readonly isRetry: boolean; + readonly retryCount: number; +} +interface Cloudflare { + readonly compatibilityFlags: Record; +} +interface DurableObject { + fetch(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; +} +type DurableObjectStub = Fetcher & { + readonly id: DurableObjectId; + readonly name?: string; +}; +interface DurableObjectId { + toString(): string; + equals(other: DurableObjectId): boolean; + readonly name?: string; +} +interface DurableObjectNamespace { + newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions): DurableObjectId; + idFromName(name: string): DurableObjectId; + idFromString(id: string): DurableObjectId; + get(id: DurableObjectId, options?: DurableObjectNamespaceGetDurableObjectOptions): DurableObjectStub; + jurisdiction(jurisdiction: DurableObjectJurisdiction): DurableObjectNamespace; +} +type DurableObjectJurisdiction = "eu" | "fedramp"; +interface DurableObjectNamespaceNewUniqueIdOptions { + jurisdiction?: DurableObjectJurisdiction; +} +type DurableObjectLocationHint = "wnam" | "enam" | "sam" | "weur" | "eeur" | "apac" | "oc" | "afr" | "me"; +interface DurableObjectNamespaceGetDurableObjectOptions { + locationHint?: DurableObjectLocationHint; +} +interface DurableObjectState { + waitUntil(promise: Promise): void; + readonly id: DurableObjectId; + readonly storage: DurableObjectStorage; + container?: Container; + blockConcurrencyWhile(callback: () => Promise): Promise; + acceptWebSocket(ws: WebSocket, tags?: string[]): void; + getWebSockets(tag?: string): WebSocket[]; + setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void; + getWebSocketAutoResponse(): WebSocketRequestResponsePair | null; + getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null; + setHibernatableWebSocketEventTimeout(timeoutMs?: number): void; + getHibernatableWebSocketEventTimeout(): number | null; + getTags(ws: WebSocket): string[]; + abort(reason?: string): void; +} +interface DurableObjectTransaction { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + rollback(): void; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; +} +interface DurableObjectStorage { + get(key: string, options?: DurableObjectGetOptions): Promise; + get(keys: string[], options?: DurableObjectGetOptions): Promise>; + list(options?: DurableObjectListOptions): Promise>; + put(key: string, value: T, options?: DurableObjectPutOptions): Promise; + put(entries: Record, options?: DurableObjectPutOptions): Promise; + delete(key: string, options?: DurableObjectPutOptions): Promise; + delete(keys: string[], options?: DurableObjectPutOptions): Promise; + deleteAll(options?: DurableObjectPutOptions): Promise; + transaction(closure: (txn: DurableObjectTransaction) => Promise): Promise; + getAlarm(options?: DurableObjectGetAlarmOptions): Promise; + setAlarm(scheduledTime: number | Date, options?: DurableObjectSetAlarmOptions): Promise; + deleteAlarm(options?: DurableObjectSetAlarmOptions): Promise; + sync(): Promise; + sql: SqlStorage; + transactionSync(closure: () => T): T; + getCurrentBookmark(): Promise; + getBookmarkForTime(timestamp: number | Date): Promise; + onNextSessionRestoreBookmark(bookmark: string): Promise; +} +interface DurableObjectListOptions { + start?: string; + startAfter?: string; + end?: string; + prefix?: string; + reverse?: boolean; + limit?: number; + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetOptions { + allowConcurrency?: boolean; + noCache?: boolean; +} +interface DurableObjectGetAlarmOptions { + allowConcurrency?: boolean; +} +interface DurableObjectPutOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; + noCache?: boolean; +} +interface DurableObjectSetAlarmOptions { + allowConcurrency?: boolean; + allowUnconfirmed?: boolean; +} +declare class WebSocketRequestResponsePair { + constructor(request: string, response: string); + get request(): string; + get response(): string; +} +interface AnalyticsEngineDataset { + writeDataPoint(event?: AnalyticsEngineDataPoint): void; +} +interface AnalyticsEngineDataPoint { + indexes?: ((ArrayBuffer | string) | null)[]; + doubles?: number[]; + blobs?: ((ArrayBuffer | string) | null)[]; +} +/** + * An event which takes place in the DOM. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event) + */ +declare class Event { + constructor(type: string, init?: EventInit); + /** + * Returns the type of event, e.g. "click", "hashchange", or "submit". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/type) + */ + get type(): string; + /** + * Returns the event's phase, which is one of NONE, CAPTURING_PHASE, AT_TARGET, and BUBBLING_PHASE. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/eventPhase) + */ + get eventPhase(): number; + /** + * Returns true or false depending on how event was initialized. True if event invokes listeners past a ShadowRoot node that is the root of its target, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composed) + */ + get composed(): boolean; + /** + * Returns true or false depending on how event was initialized. True if event goes through its target's ancestors in reverse tree order, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/bubbles) + */ + get bubbles(): boolean; + /** + * Returns true or false depending on how event was initialized. Its return value does not always carry meaning, but true can indicate that part of the operation during which event was dispatched, can be canceled by invoking the preventDefault() method. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelable) + */ + get cancelable(): boolean; + /** + * Returns true if preventDefault() was invoked successfully to indicate cancelation, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/defaultPrevented) + */ + get defaultPrevented(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/returnValue) + */ + get returnValue(): boolean; + /** + * Returns the object whose event listener's callback is currently being invoked. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget) + */ + get currentTarget(): EventTarget | undefined; + /** + * Returns the object to which event is dispatched (its target). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target) + */ + get target(): EventTarget | undefined; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/srcElement) + */ + get srcElement(): EventTarget | undefined; + /** + * Returns the event's timestamp as the number of milliseconds measured relative to the time origin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/timeStamp) + */ + get timeStamp(): number; + /** + * Returns true if event was dispatched by the user agent, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/isTrusted) + */ + get isTrusted(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + get cancelBubble(): boolean; + /** + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/cancelBubble) + */ + set cancelBubble(value: boolean); + /** + * Invoking this method prevents event from reaching any registered event listeners after the current one finishes running and, when dispatched in a tree, also prevents event from reaching any other objects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopImmediatePropagation) + */ + stopImmediatePropagation(): void; + /** + * If invoked when the cancelable attribute value is true, and while executing a listener for the event with passive set to false, signals to the operation that caused event to be dispatched that it needs to be canceled. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) + */ + preventDefault(): void; + /** + * When dispatched in a tree, invoking this method prevents event from reaching any objects other than the current object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) + */ + stopPropagation(): void; + /** + * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/composedPath) + */ + composedPath(): EventTarget[]; + static readonly NONE: number; + static readonly CAPTURING_PHASE: number; + static readonly AT_TARGET: number; + static readonly BUBBLING_PHASE: number; +} +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} +type EventListener = (event: EventType) => void; +interface EventListenerObject { + handleEvent(event: EventType): void; +} +type EventListenerOrEventListenerObject = EventListener | EventListenerObject; +/** + * EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget) + */ +declare class EventTarget = Record> { + constructor(); + /** + * Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched. + * + * The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options's capture. + * + * When set to true, options's capture prevents callback from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event's eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event's eventPhase attribute value is AT_TARGET. + * + * When set to true, options's passive indicates that the callback will not cancel the event by invoking preventDefault(). This is used to enable performance optimizations described in § 2.8 Observing event listeners. + * + * When set to true, options's once indicates that the callback will only be invoked once after which the event listener will be removed. + * + * If an AbortSignal is passed for options's signal, then the event listener will be removed when signal is aborted. + * + * The event listener is appended to target's event listener list and is not appended if it has the same type, callback, and capture. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) + */ + addEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetAddEventListenerOptions | boolean): void; + /** + * Removes the event listener in target's event listener list with the same type, callback, and options. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener) + */ + removeEventListener(type: Type, handler: EventListenerOrEventListenerObject, options?: EventTargetEventListenerOptions | boolean): void; + /** + * Dispatches a synthetic event event to target and returns true if either event's cancelable attribute value is false or its preventDefault() method was not invoked, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventTarget/dispatchEvent) + */ + dispatchEvent(event: EventMap[keyof EventMap]): boolean; +} +interface EventTargetEventListenerOptions { + capture?: boolean; +} +interface EventTargetAddEventListenerOptions { + capture?: boolean; + passive?: boolean; + once?: boolean; + signal?: AbortSignal; +} +interface EventTargetHandlerObject { + handleEvent: (event: Event) => any | undefined; +} +/** + * A controller object that allows you to abort one or more DOM requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +declare class AbortController { + constructor(); + /** + * Returns the AbortSignal object associated with this object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + get signal(): AbortSignal; + /** + * Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} +/** + * A signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +declare abstract class AbortSignal extends EventTarget { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) */ + static abort(reason?: any): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) */ + static timeout(delay: number): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) */ + static any(signals: AbortSignal[]): AbortSignal; + /** + * Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + get aborted(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) */ + get reason(): any; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + get onabort(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + set onabort(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) */ + throwIfAborted(): void; +} +interface Scheduler { + wait(delay: number, maybeOptions?: SchedulerWaitOptions): Promise; +} +interface SchedulerWaitOptions { + signal?: AbortSignal; +} +/** + * Extends the lifetime of the install and activate events dispatched on the global scope as part of the service worker lifecycle. This ensures that any functional events (like FetchEvent) are not dispatched until it upgrades database schemas and deletes the outdated cache entries. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent) + */ +declare abstract class ExtendableEvent extends Event { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) */ + waitUntil(promise: Promise): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent) */ +declare class CustomEvent extends Event { + constructor(type: string, init?: CustomEventCustomEventInit); + /** + * Returns any custom data event was created with. Typically used for synthetic events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail) + */ + get detail(): T; +} +interface CustomEventCustomEventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; + detail?: any; +} +/** + * A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob) + */ +declare class Blob { + constructor(type?: ((ArrayBuffer | ArrayBufferView) | string | Blob)[], options?: BlobOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */ + get size(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */ + get type(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */ + slice(start?: number, end?: number, type?: string): Blob; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/stream) */ + stream(): ReadableStream; +} +interface BlobOptions { + type?: string; +} +/** + * Provides information about files and allows JavaScript in a web page to access their content. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/File) + */ +declare class File extends Blob { + constructor(bits: ((ArrayBuffer | ArrayBufferView) | string | Blob)[] | undefined, name: string, options?: FileOptions); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */ + get name(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */ + get lastModified(): number; +} +interface FileOptions { + type?: string; + lastModified?: number; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class CacheStorage { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CacheStorage/open) */ + open(cacheName: string): Promise; + readonly default: Cache; +} +/** +* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/) +*/ +declare abstract class Cache { + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */ + delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#match) */ + match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + /* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#put) */ + put(request: RequestInfo | URL, response: Response): Promise; +} +interface CacheQueryOptions { + ignoreMethod?: boolean; +} +/** +* The Web Crypto API provides a set of low-level functions for common cryptographic tasks. +* The Workers runtime implements the full surface of this API, but with some differences in +* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms) +* compared to those implemented in most browsers. +* +* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/) +*/ +declare abstract class Crypto { + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/subtle) + */ + get subtle(): SubtleCrypto; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */ + getRandomValues(buffer: T): T; + /** + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID) + */ + randomUUID(): string; + DigestStream: typeof DigestStream; +} +/** + * This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto) + */ +declare abstract class SubtleCrypto { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/encrypt) */ + encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, plainText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/decrypt) */ + decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm, key: CryptoKey, cipherText: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/sign) */ + sign(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/verify) */ + verify(algorithm: string | SubtleCryptoSignAlgorithm, key: CryptoKey, signature: ArrayBuffer | ArrayBufferView, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/digest) */ + digest(algorithm: string | SubtleCryptoHashAlgorithm, data: ArrayBuffer | ArrayBufferView): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) */ + generateKey(algorithm: string | SubtleCryptoGenerateKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveKey) */ + deriveKey(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, derivedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/deriveBits) */ + deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm, baseKey: CryptoKey, length?: number | null): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) */ + importKey(format: string, keyData: (ArrayBuffer | ArrayBufferView) | JsonWebKey, algorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/exportKey) */ + exportKey(format: string, key: CryptoKey): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/wrapKey) */ + wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/unwrapKey) */ + unwrapKey(format: string, wrappedKey: ArrayBuffer | ArrayBufferView, unwrappingKey: CryptoKey, unwrapAlgorithm: string | SubtleCryptoEncryptAlgorithm, unwrappedKeyAlgorithm: string | SubtleCryptoImportKeyAlgorithm, extractable: boolean, keyUsages: string[]): Promise; + timingSafeEqual(a: ArrayBuffer | ArrayBufferView, b: ArrayBuffer | ArrayBufferView): boolean; +} +/** + * The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. + * Available only in secure contexts. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey) + */ +declare abstract class CryptoKey { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ + readonly type: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/extractable) */ + readonly extractable: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/algorithm) */ + readonly algorithm: CryptoKeyKeyAlgorithm | CryptoKeyAesKeyAlgorithm | CryptoKeyHmacKeyAlgorithm | CryptoKeyRsaKeyAlgorithm | CryptoKeyEllipticKeyAlgorithm | CryptoKeyArbitraryKeyAlgorithm; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CryptoKey/usages) */ + readonly usages: string[]; +} +interface CryptoKeyPair { + publicKey: CryptoKey; + privateKey: CryptoKey; +} +interface JsonWebKey { + kty: string; + use?: string; + key_ops?: string[]; + alg?: string; + ext?: boolean; + crv?: string; + x?: string; + y?: string; + d?: string; + n?: string; + e?: string; + p?: string; + q?: string; + dp?: string; + dq?: string; + qi?: string; + oth?: RsaOtherPrimesInfo[]; + k?: string; +} +interface RsaOtherPrimesInfo { + r?: string; + d?: string; + t?: string; +} +interface SubtleCryptoDeriveKeyAlgorithm { + name: string; + salt?: (ArrayBuffer | ArrayBufferView); + iterations?: number; + hash?: (string | SubtleCryptoHashAlgorithm); + $public?: CryptoKey; + info?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoEncryptAlgorithm { + name: string; + iv?: (ArrayBuffer | ArrayBufferView); + additionalData?: (ArrayBuffer | ArrayBufferView); + tagLength?: number; + counter?: (ArrayBuffer | ArrayBufferView); + length?: number; + label?: (ArrayBuffer | ArrayBufferView); +} +interface SubtleCryptoGenerateKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + modulusLength?: number; + publicExponent?: (ArrayBuffer | ArrayBufferView); + length?: number; + namedCurve?: string; +} +interface SubtleCryptoHashAlgorithm { + name: string; +} +interface SubtleCryptoImportKeyAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + length?: number; + namedCurve?: string; + compressed?: boolean; +} +interface SubtleCryptoSignAlgorithm { + name: string; + hash?: (string | SubtleCryptoHashAlgorithm); + dataLength?: number; + saltLength?: number; +} +interface CryptoKeyKeyAlgorithm { + name: string; +} +interface CryptoKeyAesKeyAlgorithm { + name: string; + length: number; +} +interface CryptoKeyHmacKeyAlgorithm { + name: string; + hash: CryptoKeyKeyAlgorithm; + length: number; +} +interface CryptoKeyRsaKeyAlgorithm { + name: string; + modulusLength: number; + publicExponent: ArrayBuffer | ArrayBufferView; + hash?: CryptoKeyKeyAlgorithm; +} +interface CryptoKeyEllipticKeyAlgorithm { + name: string; + namedCurve: string; +} +interface CryptoKeyArbitraryKeyAlgorithm { + name: string; + hash?: CryptoKeyKeyAlgorithm; + namedCurve?: string; + length?: number; +} +declare class DigestStream extends WritableStream { + constructor(algorithm: string | SubtleCryptoHashAlgorithm); + readonly digest: Promise; + get bytesWritten(): number | bigint; +} +/** + * A decoder for a specific method, that is a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261, gbk, etc. A decoder takes a stream of bytes as input and emits a stream of code points. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder) + */ +declare class TextDecoder { + constructor(label?: string, options?: TextDecoderConstructorOptions); + /** + * Returns the result of running encoding's decoder. The method can be invoked zero or more times with options's stream set to true, and then once without options's stream (or set to false), to process a fragmented input. If the invocation without options's stream (or set to false) has no input, it's clearest to omit both arguments. + * + * ``` + * var string = "", decoder = new TextDecoder(encoding), buffer; + * while(buffer = next_chunk()) { + * string += decoder.decode(buffer, {stream:true}); + * } + * string += decoder.decode(); // end-of-queue + * ``` + * + * If the error mode is "fatal" and encoding's decoder returns error, throws a TypeError. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode) + */ + decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string; + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +/** + * TextEncoder takes a stream of code points as input and emits a stream of bytes. For a more scalable, non-native library, see StringView – a C-like representation of strings based on typed arrays. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder) + */ +declare class TextEncoder { + constructor(); + /** + * Returns the result of running UTF-8's encoder. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encode) + */ + encode(input?: string): Uint8Array; + /** + * Runs the UTF-8 encoder on source, stores the result of that operation into destination, and returns the progress made as an object wherein read is the number of converted code units of source and written is the number of bytes modified in destination. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoder/encodeInto) + */ + encodeInto(input: string, buffer: ArrayBuffer | ArrayBufferView): TextEncoderEncodeIntoResult; + get encoding(): string; +} +interface TextDecoderConstructorOptions { + fatal: boolean; + ignoreBOM: boolean; +} +interface TextDecoderDecodeOptions { + stream: boolean; +} +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} +/** + * Events providing information related to errors in scripts or in files. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ErrorEvent) + */ +declare class ErrorEvent extends Event { + constructor(type: string, init?: ErrorEventErrorEventInit); + get filename(): string; + get message(): string; + get lineno(): number; + get colno(): number; + get error(): any; +} +interface ErrorEventErrorEventInit { + message?: string; + filename?: string; + lineno?: number; + colno?: number; + error?: any; +} +/** + * Provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data". + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData) + */ +declare class FormData { + constructor(); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */ + append(name: string, value: Blob, filename?: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/delete) */ + delete(name: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get) */ + get(name: string): (File | string) | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/getAll) */ + getAll(name: string): (File | string)[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/set) */ + set(name: string, value: Blob, filename?: string): void; + /* Returns an array of key, value pairs for every entry in the list. */ + entries(): IterableIterator<[ + key: string, + value: File | string + ]>; + /* Returns a list of keys in the list. */ + keys(): IterableIterator; + /* Returns a list of values in the list. */ + values(): IterableIterator<(File | string)>; + forEach(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: File | string + ]>; +} +interface ContentOptions { + html?: boolean; +} +declare class HTMLRewriter { + constructor(); + on(selector: string, handlers: HTMLRewriterElementContentHandlers): HTMLRewriter; + onDocument(handlers: HTMLRewriterDocumentContentHandlers): HTMLRewriter; + transform(response: Response): Response; +} +interface HTMLRewriterElementContentHandlers { + element?(element: Element): void | Promise; + comments?(comment: Comment): void | Promise; + text?(element: Text): void | Promise; +} +interface HTMLRewriterDocumentContentHandlers { + doctype?(doctype: Doctype): void | Promise; + comments?(comment: Comment): void | Promise; + text?(text: Text): void | Promise; + end?(end: DocumentEnd): void | Promise; +} +interface Doctype { + readonly name: string | null; + readonly publicId: string | null; + readonly systemId: string | null; +} +interface Element { + tagName: string; + readonly attributes: IterableIterator; + readonly removed: boolean; + readonly namespaceURI: string; + getAttribute(name: string): string | null; + hasAttribute(name: string): boolean; + setAttribute(name: string, value: string): Element; + removeAttribute(name: string): Element; + before(content: string | ReadableStream | Response, options?: ContentOptions): Element; + after(content: string | ReadableStream | Response, options?: ContentOptions): Element; + prepend(content: string | ReadableStream | Response, options?: ContentOptions): Element; + append(content: string | ReadableStream | Response, options?: ContentOptions): Element; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Element; + remove(): Element; + removeAndKeepContent(): Element; + setInnerContent(content: string | ReadableStream | Response, options?: ContentOptions): Element; + onEndTag(handler: (tag: EndTag) => void | Promise): void; +} +interface EndTag { + name: string; + before(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + after(content: string | ReadableStream | Response, options?: ContentOptions): EndTag; + remove(): EndTag; +} +interface Comment { + text: string; + readonly removed: boolean; + before(content: string, options?: ContentOptions): Comment; + after(content: string, options?: ContentOptions): Comment; + replace(content: string, options?: ContentOptions): Comment; + remove(): Comment; +} +interface Text { + readonly text: string; + readonly lastInTextNode: boolean; + readonly removed: boolean; + before(content: string | ReadableStream | Response, options?: ContentOptions): Text; + after(content: string | ReadableStream | Response, options?: ContentOptions): Text; + replace(content: string | ReadableStream | Response, options?: ContentOptions): Text; + remove(): Text; +} +interface DocumentEnd { + append(content: string, options?: ContentOptions): DocumentEnd; +} +/** + * This is the event type for fetch events dispatched on the service worker global scope. It contains information about the fetch, including the request and how the receiver will treat the response. It provides the event.respondWith() method, which allows us to provide a response to this fetch. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent) + */ +declare abstract class FetchEvent extends ExtendableEvent { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/request) */ + readonly request: Request; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/FetchEvent/respondWith) */ + respondWith(promise: Response | Promise): void; + passThroughOnException(): void; +} +type HeadersInit = Headers | Iterable> | Record; +/** + * This Fetch API interface allows you to perform various actions on HTTP request and response headers. These actions include retrieving, setting, adding to, and removing. A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs.  You can add to this using methods like append() (see Examples.) In all methods of this interface, header names are matched by case-insensitive byte sequence. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers) + */ +declare class Headers { + constructor(init?: HeadersInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get) */ + get(name: string): string | null; + getAll(name: string): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie) */ + getSetCookie(): string[]; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has) */ + has(name: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set) */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append) */ + append(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete) */ + delete(name: string): void; + forEach(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void; + /* Returns an iterator allowing to go through all key/value pairs contained in this object. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */ + keys(): IterableIterator; + /* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */ + values(): IterableIterator; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +type BodyInit = ReadableStream | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData; +declare abstract class Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/body) */ + get body(): ReadableStream | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bodyUsed) */ + get bodyUsed(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/arrayBuffer) */ + arrayBuffer(): Promise; + bytes(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/text) */ + text(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ + json(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ + formData(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ + blob(): Promise; +} +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +declare var Response: { + prototype: Response; + new (body?: BodyInit | null, init?: ResponseInit): Response; + error(): Response; + redirect(url: string, status?: number): Response; + json(any: any, maybeInit?: (ResponseInit | Response)): Response; +}; +/** + * This Fetch API interface represents the response to a request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response) + */ +interface Response extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/clone) */ + clone(): Response; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/status) */ + status: number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/statusText) */ + statusText: string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/headers) */ + headers: Headers; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/ok) */ + ok: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/redirected) */ + redirected: boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/url) */ + url: string; + webSocket: WebSocket | null; + cf: any | undefined; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Response/type) */ + type: "default" | "error"; +} +interface ResponseInit { + status?: number; + statusText?: string; + headers?: HeadersInit; + cf?: any; + webSocket?: (WebSocket | null); + encodeBody?: "automatic" | "manual"; +} +type RequestInfo> = Request | string; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +declare var Request: { + prototype: Request; + new >(input: RequestInfo | URL, init?: RequestInit): Request; +}; +/** + * This Fetch API interface represents a resource request. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request) + */ +interface Request> extends Body { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/clone) */ + clone(): Request; + /** + * Returns request's HTTP method, which is "GET" by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/method) + */ + method: string; + /** + * Returns the URL of request as a string. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/url) + */ + url: string; + /** + * Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/headers) + */ + headers: Headers; + /** + * Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/redirect) + */ + redirect: string; + fetcher: Fetcher | null; + /** + * Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/signal) + */ + signal: AbortSignal; + cf: Cf | undefined; + /** + * Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI] + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/integrity) + */ + integrity: string; + /* Returns a boolean indicating whether or not request can outlive the global in which it was created. */ + keepalive: boolean; + /** + * Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/cache) + */ + cache?: "no-store"; +} +interface RequestInit { + /* A string to set request's method. */ + method?: string; + /* A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /* A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: string; + fetcher?: (Fetcher | null); + cf?: Cf; + /* A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: "no-store"; + /* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /* An AbortSignal to set request's signal. */ + signal?: (AbortSignal | null); + encodeResponseBody?: "automatic" | "manual"; +} +type Service = Fetcher; +type Fetcher = (T extends Rpc.EntrypointBranded ? Rpc.Provider : unknown) & { + fetch(input: RequestInfo | URL, init?: RequestInit): Promise; + connect(address: SocketAddress | string, options?: SocketOptions): Socket; +}; +interface KVNamespaceListKey { + name: Key; + expiration?: number; + metadata?: Metadata; +} +type KVNamespaceListResult = { + list_complete: false; + keys: KVNamespaceListKey[]; + cursor: string; + cacheStatus: string | null; +} | { + list_complete: true; + keys: KVNamespaceListKey[]; + cacheStatus: string | null; +}; +interface KVNamespace { + get(key: Key, options?: Partial>): Promise; + get(key: Key, type: "text"): Promise; + get(key: Key, type: "json"): Promise; + get(key: Key, type: "arrayBuffer"): Promise; + get(key: Key, type: "stream"): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"text">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"json">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"arrayBuffer">): Promise; + get(key: Key, options?: KVNamespaceGetOptions<"stream">): Promise; + get(key: Array, type: "text"): Promise>; + get(key: Array, type: "json"): Promise>; + get(key: Array, options?: Partial>): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>; + get(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>; + list(options?: KVNamespaceListOptions): Promise>; + put(key: Key, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVNamespacePutOptions): Promise; + getWithMetadata(key: Key, options?: Partial>): Promise>; + getWithMetadata(key: Key, type: "text"): Promise>; + getWithMetadata(key: Key, type: "json"): Promise>; + getWithMetadata(key: Key, type: "arrayBuffer"): Promise>; + getWithMetadata(key: Key, type: "stream"): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"text">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"json">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"arrayBuffer">): Promise>; + getWithMetadata(key: Key, options: KVNamespaceGetOptions<"stream">): Promise>; + getWithMetadata(key: Array, type: "text"): Promise>>; + getWithMetadata(key: Array, type: "json"): Promise>>; + getWithMetadata(key: Array, options?: Partial>): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"text">): Promise>>; + getWithMetadata(key: Array, options?: KVNamespaceGetOptions<"json">): Promise>>; + delete(key: Key): Promise; +} +interface KVNamespaceListOptions { + limit?: number; + prefix?: (string | null); + cursor?: (string | null); +} +interface KVNamespaceGetOptions { + type: Type; + cacheTtl?: number; +} +interface KVNamespacePutOptions { + expiration?: number; + expirationTtl?: number; + metadata?: (any | null); +} +interface KVNamespaceGetWithMetadataResult { + value: Value | null; + metadata: Metadata | null; + cacheStatus: string | null; +} +type QueueContentType = "text" | "bytes" | "json" | "v8"; +interface Queue { + send(message: Body, options?: QueueSendOptions): Promise; + sendBatch(messages: Iterable>, options?: QueueSendBatchOptions): Promise; +} +interface QueueSendOptions { + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueSendBatchOptions { + delaySeconds?: number; +} +interface MessageSendRequest { + body: Body; + contentType?: QueueContentType; + delaySeconds?: number; +} +interface QueueRetryOptions { + delaySeconds?: number; +} +interface Message { + readonly id: string; + readonly timestamp: Date; + readonly body: Body; + readonly attempts: number; + retry(options?: QueueRetryOptions): void; + ack(): void; +} +interface QueueEvent extends ExtendableEvent { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface MessageBatch { + readonly messages: readonly Message[]; + readonly queue: string; + retryAll(options?: QueueRetryOptions): void; + ackAll(): void; +} +interface R2Error extends Error { + readonly name: string; + readonly code: number; + readonly message: string; + readonly action: string; + readonly stack: any; +} +interface R2ListOptions { + limit?: number; + prefix?: string; + cursor?: string; + delimiter?: string; + startAfter?: string; + include?: ("httpMetadata" | "customMetadata")[]; +} +declare abstract class R2Bucket { + head(key: string): Promise; + get(key: string, options: R2GetOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + get(key: string, options?: R2GetOptions): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + }): Promise; + put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise; + createMultipartUpload(key: string, options?: R2MultipartOptions): Promise; + resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload; + delete(keys: string | string[]): Promise; + list(options?: R2ListOptions): Promise; +} +interface R2MultipartUpload { + readonly key: string; + readonly uploadId: string; + uploadPart(partNumber: number, value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, options?: R2UploadPartOptions): Promise; + abort(): Promise; + complete(uploadedParts: R2UploadedPart[]): Promise; +} +interface R2UploadedPart { + partNumber: number; + etag: string; +} +declare abstract class R2Object { + readonly key: string; + readonly version: string; + readonly size: number; + readonly etag: string; + readonly httpEtag: string; + readonly checksums: R2Checksums; + readonly uploaded: Date; + readonly httpMetadata?: R2HTTPMetadata; + readonly customMetadata?: Record; + readonly range?: R2Range; + readonly storageClass: string; + readonly ssecKeyMd5?: string; + writeHttpMetadata(headers: Headers): void; +} +interface R2ObjectBody extends R2Object { + get body(): ReadableStream; + get bodyUsed(): boolean; + arrayBuffer(): Promise; + text(): Promise; + json(): Promise; + blob(): Promise; +} +type R2Range = { + offset: number; + length?: number; +} | { + offset?: number; + length: number; +} | { + suffix: number; +}; +interface R2Conditional { + etagMatches?: string; + etagDoesNotMatch?: string; + uploadedBefore?: Date; + uploadedAfter?: Date; + secondsGranularity?: boolean; +} +interface R2GetOptions { + onlyIf?: (R2Conditional | Headers); + range?: (R2Range | Headers); + ssecKey?: (ArrayBuffer | string); +} +interface R2PutOptions { + onlyIf?: (R2Conditional | Headers); + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + md5?: (ArrayBuffer | string); + sha1?: (ArrayBuffer | string); + sha256?: (ArrayBuffer | string); + sha384?: (ArrayBuffer | string); + sha512?: (ArrayBuffer | string); + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2MultipartOptions { + httpMetadata?: (R2HTTPMetadata | Headers); + customMetadata?: Record; + storageClass?: string; + ssecKey?: (ArrayBuffer | string); +} +interface R2Checksums { + readonly md5?: ArrayBuffer; + readonly sha1?: ArrayBuffer; + readonly sha256?: ArrayBuffer; + readonly sha384?: ArrayBuffer; + readonly sha512?: ArrayBuffer; + toJSON(): R2StringChecksums; +} +interface R2StringChecksums { + md5?: string; + sha1?: string; + sha256?: string; + sha384?: string; + sha512?: string; +} +interface R2HTTPMetadata { + contentType?: string; + contentLanguage?: string; + contentDisposition?: string; + contentEncoding?: string; + cacheControl?: string; + cacheExpiry?: Date; +} +type R2Objects = { + objects: R2Object[]; + delimitedPrefixes: string[]; +} & ({ + truncated: true; + cursor: string; +} | { + truncated: false; +}); +interface R2UploadPartOptions { + ssecKey?: (ArrayBuffer | string); +} +declare abstract class ScheduledEvent extends ExtendableEvent { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface ScheduledController { + readonly scheduledTime: number; + readonly cron: string; + noRetry(): void; +} +interface QueuingStrategy { + highWaterMark?: (number | bigint); + size?: (chunk: T) => number | bigint; +} +interface UnderlyingSink { + type?: string; + start?: (controller: WritableStreamDefaultController) => void | Promise; + write?: (chunk: W, controller: WritableStreamDefaultController) => void | Promise; + abort?: (reason: any) => void | Promise; + close?: () => void | Promise; +} +interface UnderlyingByteSource { + type: "bytes"; + autoAllocateChunkSize?: number; + start?: (controller: ReadableByteStreamController) => void | Promise; + pull?: (controller: ReadableByteStreamController) => void | Promise; + cancel?: (reason: any) => void | Promise; +} +interface UnderlyingSource { + type?: "" | undefined; + start?: (controller: ReadableStreamDefaultController) => void | Promise; + pull?: (controller: ReadableStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: (number | bigint); +} +interface Transformer { + readableType?: string; + writableType?: string; + start?: (controller: TransformStreamDefaultController) => void | Promise; + transform?: (chunk: I, controller: TransformStreamDefaultController) => void | Promise; + flush?: (controller: TransformStreamDefaultController) => void | Promise; + cancel?: (reason: any) => void | Promise; + expectedLength?: number; +} +interface StreamPipeOptions { + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + preventAbort?: boolean; + preventCancel?: boolean; + signal?: AbortSignal; +} +type ReadableStreamReadResult = { + done: false; + value: R; +} | { + done: true; + value?: undefined; +}; +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +interface ReadableStream { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/cancel) */ + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(): ReadableStreamDefaultReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/getReader) */ + getReader(options: ReadableStreamGetReaderOptions): ReadableStreamBYOBReader; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeThrough) */ + pipeThrough(transform: ReadableWritablePair, options?: StreamPipeOptions): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/pipeTo) */ + pipeTo(destination: WritableStream, options?: StreamPipeOptions): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee) */ + tee(): [ + ReadableStream, + ReadableStream + ]; + values(options?: ReadableStreamValuesOptions): AsyncIterableIterator; + [Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator; +} +/** + * This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream) + */ +declare const ReadableStream: { + prototype: ReadableStream; + new (underlyingSource: UnderlyingByteSource, strategy?: QueuingStrategy): ReadableStream; + new (underlyingSource?: UnderlyingSource, strategy?: QueuingStrategy): ReadableStream; +}; +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader) */ +declare class ReadableStreamDefaultReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/read) */ + read(): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultReader/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader) */ +declare class ReadableStreamBYOBReader { + constructor(stream: ReadableStream); + get closed(): Promise; + cancel(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/read) */ + read(view: T): Promise>; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader/releaseLock) */ + releaseLock(): void; + readAtLeast(minElements: number, view: T): Promise>; +} +interface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions { + min?: number; +} +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode: "byob"; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest) */ +declare abstract class ReadableStreamBYOBRequest { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/view) */ + get view(): Uint8Array | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respond) */ + respond(bytesWritten: number): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBRequest/respondWithNewView) */ + respondWithNewView(view: ArrayBuffer | ArrayBufferView): void; + get atLeast(): number | null; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController) */ +declare abstract class ReadableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/enqueue) */ + enqueue(chunk?: R): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamDefaultController/error) */ + error(reason: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController) */ +declare abstract class ReadableByteStreamController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/byobRequest) */ + get byobRequest(): ReadableStreamBYOBRequest | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/close) */ + close(): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/enqueue) */ + enqueue(chunk: ArrayBuffer | ArrayBufferView): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableByteStreamController/error) */ + error(reason: any): void; +} +/** + * This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController) + */ +declare abstract class WritableStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/signal) */ + get signal(): AbortSignal; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultController/error) */ + error(reason?: any): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController) */ +declare abstract class TransformStreamDefaultController { + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/enqueue) */ + enqueue(chunk?: O): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/error) */ + error(reason: any): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStreamDefaultController/terminate) */ + terminate(): void; +} +interface ReadableWritablePair { + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; + readable: ReadableStream; +} +/** + * This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream) + */ +declare class WritableStream { + constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ + get locked(): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStream/getWriter) */ + getWriter(): WritableStreamDefaultWriter; +} +/** + * This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter) + */ +declare class WritableStreamDefaultWriter { + constructor(stream: WritableStream); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/closed) */ + get closed(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/ready) */ + get ready(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/desiredSize) */ + get desiredSize(): number | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/abort) */ + abort(reason?: any): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/close) */ + close(): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/write) */ + write(chunk?: W): Promise; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WritableStreamDefaultWriter/releaseLock) */ + releaseLock(): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream) */ +declare class TransformStream { + constructor(transformer?: Transformer, writableStrategy?: QueuingStrategy, readableStrategy?: QueuingStrategy); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/readable) */ + get readable(): ReadableStream; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TransformStream/writable) */ + get writable(): WritableStream; +} +declare class FixedLengthStream extends IdentityTransformStream { + constructor(expectedLength: number | bigint, queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +declare class IdentityTransformStream extends TransformStream { + constructor(queuingStrategy?: IdentityTransformStreamQueuingStrategy); +} +interface IdentityTransformStreamQueuingStrategy { + highWaterMark?: (number | bigint); +} +interface ReadableStreamValuesOptions { + preventCancel?: boolean; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CompressionStream) */ +declare class CompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DecompressionStream) */ +declare class DecompressionStream extends TransformStream { + constructor(format: "gzip" | "deflate" | "deflate-raw"); +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextEncoderStream) */ +declare class TextEncoderStream extends TransformStream { + constructor(); + get encoding(): string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoderStream) */ +declare class TextDecoderStream extends TransformStream { + constructor(label?: string, options?: TextDecoderStreamTextDecoderStreamInit); + get encoding(): string; + get fatal(): boolean; + get ignoreBOM(): boolean; +} +interface TextDecoderStreamTextDecoderStreamInit { + fatal?: boolean; + ignoreBOM?: boolean; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy) + */ +declare class ByteLengthQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ByteLengthQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +/** + * This Streams API interface provides a built-in byte length queuing strategy that can be used when constructing streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy) + */ +declare class CountQueuingStrategy implements QueuingStrategy { + constructor(init: QueuingStrategyInit); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/highWaterMark) */ + get highWaterMark(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/CountQueuingStrategy/size) */ + get size(): (chunk?: any) => number; +} +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} +interface ScriptVersion { + id?: string; + tag?: string; + message?: string; +} +declare abstract class TailEvent extends ExtendableEvent { + readonly events: TraceItem[]; + readonly traces: TraceItem[]; +} +interface TraceItem { + readonly event: (TraceItemFetchEventInfo | TraceItemJsRpcEventInfo | TraceItemScheduledEventInfo | TraceItemAlarmEventInfo | TraceItemQueueEventInfo | TraceItemEmailEventInfo | TraceItemTailEventInfo | TraceItemCustomEventInfo | TraceItemHibernatableWebSocketEventInfo) | null; + readonly eventTimestamp: number | null; + readonly logs: TraceLog[]; + readonly exceptions: TraceException[]; + readonly diagnosticsChannelEvents: TraceDiagnosticChannelEvent[]; + readonly scriptName: string | null; + readonly entrypoint?: string; + readonly scriptVersion?: ScriptVersion; + readonly dispatchNamespace?: string; + readonly scriptTags?: string[]; + readonly outcome: string; + readonly executionModel: string; + readonly truncated: boolean; + readonly cpuTime: number; + readonly wallTime: number; +} +interface TraceItemAlarmEventInfo { + readonly scheduledTime: Date; +} +interface TraceItemCustomEventInfo { +} +interface TraceItemScheduledEventInfo { + readonly scheduledTime: number; + readonly cron: string; +} +interface TraceItemQueueEventInfo { + readonly queue: string; + readonly batchSize: number; +} +interface TraceItemEmailEventInfo { + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; +} +interface TraceItemTailEventInfo { + readonly consumedEvents: TraceItemTailEventInfoTailItem[]; +} +interface TraceItemTailEventInfoTailItem { + readonly scriptName: string | null; +} +interface TraceItemFetchEventInfo { + readonly response?: TraceItemFetchEventInfoResponse; + readonly request: TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoRequest { + readonly cf?: any; + readonly headers: Record; + readonly method: string; + readonly url: string; + getUnredacted(): TraceItemFetchEventInfoRequest; +} +interface TraceItemFetchEventInfoResponse { + readonly status: number; +} +interface TraceItemJsRpcEventInfo { + readonly rpcMethod: string; +} +interface TraceItemHibernatableWebSocketEventInfo { + readonly getWebSocketEvent: TraceItemHibernatableWebSocketEventInfoMessage | TraceItemHibernatableWebSocketEventInfoClose | TraceItemHibernatableWebSocketEventInfoError; +} +interface TraceItemHibernatableWebSocketEventInfoMessage { + readonly webSocketEventType: string; +} +interface TraceItemHibernatableWebSocketEventInfoClose { + readonly webSocketEventType: string; + readonly code: number; + readonly wasClean: boolean; +} +interface TraceItemHibernatableWebSocketEventInfoError { + readonly webSocketEventType: string; +} +interface TraceLog { + readonly timestamp: number; + readonly level: string; + readonly message: any; +} +interface TraceException { + readonly timestamp: number; + readonly message: string; + readonly name: string; + readonly stack?: string; +} +interface TraceDiagnosticChannelEvent { + readonly timestamp: number; + readonly channel: string; + readonly message: any; +} +interface TraceMetrics { + readonly cpuTime: number; + readonly wallTime: number; +} +interface UnsafeTraceMetrics { + fromTrace(item: TraceItem): TraceMetrics; +} +/** + * The URL interface represents an object providing static methods used for creating object URLs. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL) + */ +declare class URL { + constructor(url: string | URL, base?: string | URL); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/origin) */ + get origin(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + get href(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/href) */ + set href(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + get protocol(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/protocol) */ + set protocol(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + get username(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/username) */ + set username(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + get password(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/password) */ + set password(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + get host(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/host) */ + set host(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + get hostname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hostname) */ + set hostname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + get port(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/port) */ + set port(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + get pathname(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/pathname) */ + set pathname(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + get search(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/search) */ + set search(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + get hash(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/hash) */ + set hash(value: string); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/searchParams) */ + get searchParams(): URLSearchParams; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/toJSON) */ + toJSON(): string; + /*function toString() { [native code] }*/ + toString(): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/canParse_static) */ + static canParse(url: string, base?: string): boolean; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/parse_static) */ + static parse(url: string, base?: string): URL | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static) */ + static createObjectURL(object: File | Blob): string; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static) */ + static revokeObjectURL(object_url: string): void; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams) */ +declare class URLSearchParams { + constructor(init?: (Iterable> | Record | string)); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/size) */ + get size(): number; + /** + * Appends a specified key/value pair as a new search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append) + */ + append(name: string, value: string): void; + /** + * Deletes the given search parameter, and its associated value, from the list of all search parameters. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete) + */ + delete(name: string, value?: string): void; + /** + * Returns the first value associated to the given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) + */ + get(name: string): string | null; + /** + * Returns all the values association with a given search parameter. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) + */ + getAll(name: string): string[]; + /** + * Returns a Boolean indicating if such a search parameter exists. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has) + */ + has(name: string, value?: string): boolean; + /** + * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set) + */ + set(name: string, value: string): void; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/sort) */ + sort(): void; + /* Returns an array of key, value pairs for every entry in the search params. */ + entries(): IterableIterator<[ + key: string, + value: string + ]>; + /* Returns a list of keys in the search params. */ + keys(): IterableIterator; + /* Returns a list of values in the search params. */ + values(): IterableIterator; + forEach(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void; + /*function toString() { [native code] } Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */ + toString(): string; + [Symbol.iterator](): IterableIterator<[ + key: string, + value: string + ]>; +} +declare class URLPattern { + constructor(input?: (string | URLPatternURLPatternInit), baseURL?: (string | URLPatternURLPatternOptions), patternOptions?: URLPatternURLPatternOptions); + get protocol(): string; + get username(): string; + get password(): string; + get hostname(): string; + get port(): string; + get pathname(): string; + get search(): string; + get hash(): string; + test(input?: (string | URLPatternURLPatternInit), baseURL?: string): boolean; + exec(input?: (string | URLPatternURLPatternInit), baseURL?: string): URLPatternURLPatternResult | null; +} +interface URLPatternURLPatternInit { + protocol?: string; + username?: string; + password?: string; + hostname?: string; + port?: string; + pathname?: string; + search?: string; + hash?: string; + baseURL?: string; +} +interface URLPatternURLPatternComponentResult { + input: string; + groups: Record; +} +interface URLPatternURLPatternResult { + inputs: (string | URLPatternURLPatternInit)[]; + protocol: URLPatternURLPatternComponentResult; + username: URLPatternURLPatternComponentResult; + password: URLPatternURLPatternComponentResult; + hostname: URLPatternURLPatternComponentResult; + port: URLPatternURLPatternComponentResult; + pathname: URLPatternURLPatternComponentResult; + search: URLPatternURLPatternComponentResult; + hash: URLPatternURLPatternComponentResult; +} +interface URLPatternURLPatternOptions { + ignoreCase?: boolean; +} +/** + * A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent) + */ +declare class CloseEvent extends Event { + constructor(type: string, initializer?: CloseEventInit); + /** + * Returns the WebSocket connection close code provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/code) + */ + readonly code: number; + /** + * Returns the WebSocket connection close reason provided by the server. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/reason) + */ + readonly reason: string; + /** + * Returns true if the connection closed cleanly; false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CloseEvent/wasClean) + */ + readonly wasClean: boolean; +} +interface CloseEventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} +/** + * A message received by a target object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent) + */ +declare class MessageEvent extends Event { + constructor(type: string, initializer: MessageEventInit); + /** + * Returns the data of the message. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessageEvent/data) + */ + readonly data: ArrayBuffer | string; +} +interface MessageEventInit { + data: ArrayBuffer | string; +} +type WebSocketEventMap = { + close: CloseEvent; + message: MessageEvent; + open: Event; + error: ErrorEvent; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +declare var WebSocket: { + prototype: WebSocket; + new (url: string, protocols?: (string[] | string)): WebSocket; + readonly READY_STATE_CONNECTING: number; + readonly CONNECTING: number; + readonly READY_STATE_OPEN: number; + readonly OPEN: number; + readonly READY_STATE_CLOSING: number; + readonly CLOSING: number; + readonly READY_STATE_CLOSED: number; + readonly CLOSED: number; +}; +/** + * Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket) + */ +interface WebSocket extends EventTarget { + accept(): void; + /** + * Transmits data using the WebSocket connection. data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/send) + */ + send(message: (ArrayBuffer | ArrayBufferView) | string): void; + /** + * Closes the WebSocket connection, optionally using code as the the WebSocket connection close code and reason as the the WebSocket connection close reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/close) + */ + close(code?: number, reason?: string): void; + serializeAttachment(attachment: any): void; + deserializeAttachment(): any | null; + /** + * Returns the state of the WebSocket object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/readyState) + */ + readyState: number; + /** + * Returns the URL that was used to establish the WebSocket connection. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/url) + */ + url: string | null; + /** + * Returns the subprotocol selected by the server, if any. It can be used in conjunction with the array form of the constructor's second argument to perform subprotocol negotiation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/protocol) + */ + protocol: string | null; + /** + * Returns the extensions selected by the server, if any. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebSocket/extensions) + */ + extensions: string | null; +} +declare const WebSocketPair: { + new (): { + 0: WebSocket; + 1: WebSocket; + }; +}; +interface SqlStorage { + exec>(query: string, ...bindings: any[]): SqlStorageCursor; + get databaseSize(): number; + Cursor: typeof SqlStorageCursor; + Statement: typeof SqlStorageStatement; +} +declare abstract class SqlStorageStatement { +} +type SqlStorageValue = ArrayBuffer | string | number | null; +declare abstract class SqlStorageCursor> { + next(): { + done?: false; + value: T; + } | { + done: true; + value?: never; + }; + toArray(): T[]; + one(): T; + raw(): IterableIterator; + columnNames: string[]; + get rowsRead(): number; + get rowsWritten(): number; + [Symbol.iterator](): IterableIterator; +} +interface Socket { + get readable(): ReadableStream; + get writable(): WritableStream; + get closed(): Promise; + get opened(): Promise; + close(): Promise; + startTls(options?: TlsOptions): Socket; +} +interface SocketOptions { + secureTransport?: string; + allowHalfOpen: boolean; + highWaterMark?: (number | bigint); +} +interface SocketAddress { + hostname: string; + port: number; +} +interface TlsOptions { + expectedServerHostname?: string; +} +interface SocketInfo { + remoteAddress?: string; + localAddress?: string; +} +/* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource) */ +declare class EventSource extends EventTarget { + constructor(url: string, init?: EventSourceEventSourceInit); + /** + * Aborts any instances of the fetch algorithm started for this EventSource object, and sets the readyState attribute to CLOSED. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/close) + */ + close(): void; + /** + * Returns the URL providing the event stream. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/url) + */ + get url(): string; + /** + * Returns true if the credentials mode for connection requests to the URL providing the event stream is set to "include", and false otherwise. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/withCredentials) + */ + get withCredentials(): boolean; + /** + * Returns the state of this EventSource object's connection. It can have the values described below. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/readyState) + */ + get readyState(): number; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + get onopen(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/open_event) */ + set onopen(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + get onmessage(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/message_event) */ + set onmessage(value: any | null); + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + get onerror(): any | null; + /* [MDN Reference](https://developer.mozilla.org/docs/Web/API/EventSource/error_event) */ + set onerror(value: any | null); + static readonly CONNECTING: number; + static readonly OPEN: number; + static readonly CLOSED: number; + static from(stream: ReadableStream): EventSource; +} +interface EventSourceEventSourceInit { + withCredentials?: boolean; + fetcher?: Fetcher; +} +interface Container { + get running(): boolean; + start(options?: ContainerStartupOptions): void; + monitor(): Promise; + destroy(error?: any): Promise; + signal(signo: number): void; + getTcpPort(port: number): Fetcher; +} +interface ContainerStartupOptions { + entrypoint?: string[]; + enableInternet: boolean; + env?: Record; +} +type AiImageClassificationInput = { + image: number[]; +}; +type AiImageClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiImageClassification { + inputs: AiImageClassificationInput; + postProcessedOutputs: AiImageClassificationOutput; +} +type AiImageToTextInput = { + image: number[]; + prompt?: string; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageToText { + inputs: AiImageToTextInput; + postProcessedOutputs: AiImageToTextOutput; +} +type AiImageTextToTextInput = { + image: string; + prompt?: string; + max_tokens?: number; + temperature?: number; + ignore_eos?: boolean; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + raw?: boolean; + messages?: RoleScopedChatInput[]; +}; +type AiImageTextToTextOutput = { + description: string; +}; +declare abstract class BaseAiImageTextToText { + inputs: AiImageTextToTextInput; + postProcessedOutputs: AiImageTextToTextOutput; +} +type AiObjectDetectionInput = { + image: number[]; +}; +type AiObjectDetectionOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiObjectDetection { + inputs: AiObjectDetectionInput; + postProcessedOutputs: AiObjectDetectionOutput; +} +type AiSentenceSimilarityInput = { + source: string; + sentences: string[]; +}; +type AiSentenceSimilarityOutput = number[]; +declare abstract class BaseAiSentenceSimilarity { + inputs: AiSentenceSimilarityInput; + postProcessedOutputs: AiSentenceSimilarityOutput; +} +type AiAutomaticSpeechRecognitionInput = { + audio: number[]; +}; +type AiAutomaticSpeechRecognitionOutput = { + text?: string; + words?: { + word: string; + start: number; + end: number; + }[]; + vtt?: string; +}; +declare abstract class BaseAiAutomaticSpeechRecognition { + inputs: AiAutomaticSpeechRecognitionInput; + postProcessedOutputs: AiAutomaticSpeechRecognitionOutput; +} +type AiSummarizationInput = { + input_text: string; + max_length?: number; +}; +type AiSummarizationOutput = { + summary: string; +}; +declare abstract class BaseAiSummarization { + inputs: AiSummarizationInput; + postProcessedOutputs: AiSummarizationOutput; +} +type AiTextClassificationInput = { + text: string; +}; +type AiTextClassificationOutput = { + score?: number; + label?: string; +}[]; +declare abstract class BaseAiTextClassification { + inputs: AiTextClassificationInput; + postProcessedOutputs: AiTextClassificationOutput; +} +type AiTextEmbeddingsInput = { + text: string | string[]; +}; +type AiTextEmbeddingsOutput = { + shape: number[]; + data: number[][]; +}; +declare abstract class BaseAiTextEmbeddings { + inputs: AiTextEmbeddingsInput; + postProcessedOutputs: AiTextEmbeddingsOutput; +} +type RoleScopedChatInput = { + role: "user" | "assistant" | "system" | "tool" | (string & NonNullable); + content: string; + name?: string; +}; +type AiTextGenerationToolLegacyInput = { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; +}; +type AiTextGenerationToolInput = { + type: "function" | (string & NonNullable); + function: { + name: string; + description: string; + parameters?: { + type: "object" | (string & NonNullable); + properties: { + [key: string]: { + type: string; + description?: string; + }; + }; + required: string[]; + }; + }; +}; +type AiTextGenerationFunctionsInput = { + name: string; + code: string; +}; +type AiTextGenerationResponseFormat = { + type: string; + json_schema?: any; +}; +type AiTextGenerationInput = { + prompt?: string; + raw?: boolean; + stream?: boolean; + max_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + seed?: number; + repetition_penalty?: number; + frequency_penalty?: number; + presence_penalty?: number; + messages?: RoleScopedChatInput[]; + response_format?: AiTextGenerationResponseFormat; + tools?: AiTextGenerationToolInput[] | AiTextGenerationToolLegacyInput[] | (object & NonNullable); + functions?: AiTextGenerationFunctionsInput[]; +}; +type AiTextGenerationOutput = { + response?: string; + tool_calls?: { + name: string; + arguments: unknown; + }[]; +} | ReadableStream; +declare abstract class BaseAiTextGeneration { + inputs: AiTextGenerationInput; + postProcessedOutputs: AiTextGenerationOutput; +} +type AiTextToSpeechInput = { + prompt: string; + lang?: string; +}; +type AiTextToSpeechOutput = Uint8Array | { + audio: string; +}; +declare abstract class BaseAiTextToSpeech { + inputs: AiTextToSpeechInput; + postProcessedOutputs: AiTextToSpeechOutput; +} +type AiTextToImageInput = { + prompt: string; + negative_prompt?: string; + height?: number; + width?: number; + image?: number[]; + image_b64?: string; + mask?: number[]; + num_steps?: number; + strength?: number; + guidance?: number; + seed?: number; +}; +type AiTextToImageOutput = ReadableStream; +declare abstract class BaseAiTextToImage { + inputs: AiTextToImageInput; + postProcessedOutputs: AiTextToImageOutput; +} +type AiTranslationInput = { + text: string; + target_lang: string; + source_lang?: string; +}; +type AiTranslationOutput = { + translated_text?: string; +}; +declare abstract class BaseAiTranslation { + inputs: AiTranslationInput; + postProcessedOutputs: AiTranslationOutput; +} +type Ai_Cf_Openai_Whisper_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper { + inputs: Ai_Cf_Openai_Whisper_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Output; +} +type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | { + /** + * The input text prompt for the model to generate a response. + */ + prompt?: string; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + image: number[] | (string & NonNullable); + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; +}; +interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output { + description?: string; +} +declare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M { + inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input; + postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output; +} +type Ai_Cf_Openai_Whisper_Tiny_En_Input = string | { + /** + * An array of integers that represent the audio data constrained to 8-bit unsigned integer values + */ + audio: number[]; +}; +interface Ai_Cf_Openai_Whisper_Tiny_En_Output { + /** + * The transcription + */ + text: string; + word_count?: number; + words?: { + word?: string; + /** + * The second this word begins in the recording + */ + start?: number; + /** + * The ending second when the word completes + */ + end?: number; + }[]; + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En { + inputs: Ai_Cf_Openai_Whisper_Tiny_En_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input { + /** + * Base64 encoded value of the audio data. + */ + audio: string; + /** + * Supported tasks are 'translate' or 'transcribe'. + */ + task?: string; + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * Preprocess the audio with a voice activity detection model. + */ + vad_filter?: string; + /** + * A text prompt to help provide context to the model on the contents of the audio. + */ + initial_prompt?: string; + /** + * The prefix it appended the the beginning of the output of the transcription and can guide the transcription result. + */ + prefix?: string; +} +interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output { + transcription_info?: { + /** + * The language of the audio being transcribed or translated. + */ + language?: string; + /** + * The confidence level or probability of the detected language being accurate, represented as a decimal between 0 and 1. + */ + language_probability?: number; + /** + * The total duration of the original audio file, in seconds. + */ + duration?: number; + /** + * The duration of the audio after applying Voice Activity Detection (VAD) to remove silent or irrelevant sections, in seconds. + */ + duration_after_vad?: number; + }; + /** + * The complete transcription of the audio. + */ + text: string; + /** + * The total number of words in the transcription. + */ + word_count?: number; + segments?: { + /** + * The starting time of the segment within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the segment within the audio, in seconds. + */ + end?: number; + /** + * The transcription of the segment. + */ + text?: string; + /** + * The temperature used in the decoding process, controlling randomness in predictions. Lower values result in more deterministic outputs. + */ + temperature?: number; + /** + * The average log probability of the predictions for the words in this segment, indicating overall confidence. + */ + avg_logprob?: number; + /** + * The compression ratio of the input to the output, measuring how much the text was compressed during the transcription process. + */ + compression_ratio?: number; + /** + * The probability that the segment contains no speech, represented as a decimal between 0 and 1. + */ + no_speech_prob?: number; + words?: { + /** + * The individual word transcribed from the audio. + */ + word?: string; + /** + * The starting time of the word within the audio, in seconds. + */ + start?: number; + /** + * The ending time of the word within the audio, in seconds. + */ + end?: number; + }[]; + }[]; + /** + * The transcription in WebVTT format, which includes timing and text information for use in subtitles. + */ + vtt?: string; +} +declare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo { + inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input; + postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output; +} +type Ai_Cf_Baai_Bge_M3_Input = BGEM3InputQueryAndContexts | BGEM3InputEmbedding; +interface BGEM3InputQueryAndContexts { + /** + * A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts + */ + query?: string; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +interface BGEM3InputEmbedding { + text: string | string[]; + /** + * When provided with too long context should the model error out or truncate the context to fit? + */ + truncate_inputs?: boolean; +} +type Ai_Cf_Baai_Bge_M3_Output = BGEM3OuputQuery | BGEM3OutputEmbeddingForContexts | BGEM3OuputEmbedding; +interface BGEM3OuputQuery { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +interface BGEM3OutputEmbeddingForContexts { + response?: number[][]; + shape?: number[]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +interface BGEM3OuputEmbedding { + shape?: number[]; + /** + * Embeddings of the requested text values + */ + data?: number[][]; + /** + * The pooling method used in the embedding process. + */ + pooling?: "mean" | "cls"; +} +declare abstract class Base_Ai_Cf_Baai_Bge_M3 { + inputs: Ai_Cf_Baai_Bge_M3_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_M3_Output; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input { + /** + * A text description of the image you want to generate. + */ + prompt: string; + /** + * The number of diffusion steps; higher values can improve quality but take longer. + */ + steps?: number; +} +interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output { + /** + * The generated image in Base64 format. + */ + image?: string; +} +declare abstract class Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell { + inputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input; + postProcessedOutputs: Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Prompt | Messages; +interface Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + image?: number[] | (string & NonNullable); + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; + /** + * Name of the LoRA (Low-Rank Adaptation) model to fine-tune the base model. + */ + lora?: string; +} +interface Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role: string; + /** + * The content of the message as a string. + */ + content: string; + }[]; + image?: number[] | string; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * If true, the response will be streamed back incrementally. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output = { + /** + * The generated text response from the model + */ + response?: string; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | ReadableStream; +declare abstract class Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct { + inputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Input { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender must alternate between 'user' and 'assistant'. + */ + role: "user" | "assistant"; + /** + * The content of the message as a string. + */ + content: string; + }[]; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Dictate the output format of the generated response. + */ + response_format?: { + /** + * Set to json_object to process and output generated text as JSON. + */ + type?: string; + }; +} +interface Ai_Cf_Meta_Llama_Guard_3_8B_Output { + response?: string | { + /** + * Whether the conversation is safe or not. + */ + safe?: boolean; + /** + * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe. + */ + categories?: string[]; + }; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; +} +declare abstract class Base_Ai_Cf_Meta_Llama_Guard_3_8B { + inputs: Ai_Cf_Meta_Llama_Guard_3_8B_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_Guard_3_8B_Output; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Input { + /** + * A query you wish to perform against the provided contexts. + */ + /** + * Number of returned results starting with the best score. + */ + top_k?: number; + /** + * List of provided contexts. Note that the index in this array is important, as the response will refer to it. + */ + contexts: { + /** + * One of the provided context content + */ + text?: string; + }[]; +} +interface Ai_Cf_Baai_Bge_Reranker_Base_Output { + response?: { + /** + * Index of the context in the request + */ + id?: number; + /** + * Score of the context under the index. + */ + score?: number; + }[]; +} +declare abstract class Base_Ai_Cf_Baai_Bge_Reranker_Base { + inputs: Ai_Cf_Baai_Bge_Reranker_Base_Input; + postProcessedOutputs: Ai_Cf_Baai_Bge_Reranker_Base_Output; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input = Ai_Cf_Meta_Llama_4_Prompt | Ai_Cf_Meta_Llama_4_Messages; +interface Ai_Cf_Meta_Llama_4_Prompt { + /** + * The input text prompt for the model to generate a response. + */ + prompt: string; + /** + * JSON schema that should be fulfilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +interface Ai_Cf_Meta_Llama_4_Messages { + /** + * An array of message objects representing the conversation history. + */ + messages: { + /** + * The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool'). + */ + role?: string; + /** + * The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001 + */ + tool_call_id?: string; + content?: string | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }[] | { + /** + * Type of the content provided + */ + type?: string; + text?: string; + image_url?: { + /** + * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted + */ + url?: string; + }; + }; + }[]; + functions?: { + name: string; + code: string; + }[]; + /** + * A list of tools available for the assistant to use. + */ + tools?: ({ + /** + * The name of the tool. More descriptive the better. + */ + name: string; + /** + * A brief description of what the tool does. + */ + description: string; + /** + * Schema defining the parameters accepted by the tool. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + } | { + /** + * Specifies the type of tool (e.g., 'function'). + */ + type: string; + /** + * Details of the function tool. + */ + function: { + /** + * The name of the function. + */ + name: string; + /** + * A brief description of what the function does. + */ + description: string; + /** + * Schema defining the parameters accepted by the function. + */ + parameters: { + /** + * The type of the parameters object (usually 'object'). + */ + type: string; + /** + * List of required parameter names. + */ + required?: string[]; + /** + * Definitions of each parameter. + */ + properties: { + [k: string]: { + /** + * The data type of the parameter. + */ + type: string; + /** + * A description of the expected parameter. + */ + description: string; + }; + }; + }; + }; + })[]; + /** + * JSON schema that should be fufilled for the response. + */ + guided_json?: object; + /** + * If true, a chat template is not applied and you must adhere to the specific model's expected formatting. + */ + raw?: boolean; + /** + * If true, the response will be streamed back incrementally using SSE, Server Sent Events. + */ + stream?: boolean; + /** + * The maximum number of tokens to generate in the response. + */ + max_tokens?: number; + /** + * Controls the randomness of the output; higher values produce more random results. + */ + temperature?: number; + /** + * Adjusts the creativity of the AI's responses by controlling how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses. + */ + top_p?: number; + /** + * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises. + */ + top_k?: number; + /** + * Random seed for reproducibility of the generation. + */ + seed?: number; + /** + * Penalty for repeated tokens; higher values discourage repetition. + */ + repetition_penalty?: number; + /** + * Decreases the likelihood of the model repeating the same lines verbatim. + */ + frequency_penalty?: number; + /** + * Increases the likelihood of the model introducing new topics. + */ + presence_penalty?: number; +} +type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output = { + /** + * The generated text response from the model + */ + response: string; + /** + * Usage statistics for the inference request + */ + usage?: { + /** + * Total number of tokens in input + */ + prompt_tokens?: number; + /** + * Total number of tokens in output + */ + completion_tokens?: number; + /** + * Total number of input and output tokens + */ + total_tokens?: number; + }; + /** + * An array of tool calls requests made during the response generation + */ + tool_calls?: { + /** + * The arguments passed to be passed to the tool call request + */ + arguments?: object; + /** + * The name of the tool to be called + */ + name?: string; + }[]; +} | string; +declare abstract class Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct { + inputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input; + postProcessedOutputs: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output; +} +interface AiModels { + "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; + "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-inpainting": BaseAiTextToImage; + "@cf/runwayml/stable-diffusion-v1-5-img2img": BaseAiTextToImage; + "@cf/lykon/dreamshaper-8-lcm": BaseAiTextToImage; + "@cf/bytedance/stable-diffusion-xl-lightning": BaseAiTextToImage; + "@cf/myshell-ai/melotts": BaseAiTextToSpeech; + "@cf/baai/bge-base-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-small-en-v1.5": BaseAiTextEmbeddings; + "@cf/baai/bge-large-en-v1.5": BaseAiTextEmbeddings; + "@cf/microsoft/resnet-50": BaseAiImageClassification; + "@cf/facebook/detr-resnet-50": BaseAiObjectDetection; + "@cf/meta/llama-2-7b-chat-int8": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.1": BaseAiTextGeneration; + "@cf/meta/llama-2-7b-chat-fp16": BaseAiTextGeneration; + "@hf/thebloke/llama-2-13b-chat-awq": BaseAiTextGeneration; + "@hf/thebloke/mistral-7b-instruct-v0.1-awq": BaseAiTextGeneration; + "@hf/thebloke/zephyr-7b-beta-awq": BaseAiTextGeneration; + "@hf/thebloke/openhermes-2.5-mistral-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/neural-chat-7b-v3-1-awq": BaseAiTextGeneration; + "@hf/thebloke/llamaguard-7b-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-base-awq": BaseAiTextGeneration; + "@hf/thebloke/deepseek-coder-6.7b-instruct-awq": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-math-7b-instruct": BaseAiTextGeneration; + "@cf/defog/sqlcoder-7b-2": BaseAiTextGeneration; + "@cf/openchat/openchat-3.5-0106": BaseAiTextGeneration; + "@cf/tiiuae/falcon-7b-instruct": BaseAiTextGeneration; + "@cf/thebloke/discolm-german-7b-v1-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-0.5b-chat": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-7b-chat-awq": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-14b-chat-awq": BaseAiTextGeneration; + "@cf/tinyllama/tinyllama-1.1b-chat-v1.0": BaseAiTextGeneration; + "@cf/microsoft/phi-2": BaseAiTextGeneration; + "@cf/qwen/qwen1.5-1.8b-chat": BaseAiTextGeneration; + "@cf/mistral/mistral-7b-instruct-v0.2-lora": BaseAiTextGeneration; + "@hf/nousresearch/hermes-2-pro-mistral-7b": BaseAiTextGeneration; + "@hf/nexusflow/starling-lm-7b-beta": BaseAiTextGeneration; + "@hf/google/gemma-7b-it": BaseAiTextGeneration; + "@cf/meta-llama/llama-2-7b-chat-hf-lora": BaseAiTextGeneration; + "@cf/google/gemma-2b-it-lora": BaseAiTextGeneration; + "@cf/google/gemma-7b-it-lora": BaseAiTextGeneration; + "@hf/mistral/mistral-7b-instruct-v0.2": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/fblgit/una-cybertron-7b-v2-bf16": BaseAiTextGeneration; + "@cf/meta/llama-3-8b-instruct-awq": BaseAiTextGeneration; + "@hf/meta-llama/meta-llama-3-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-fp8": BaseAiTextGeneration; + "@cf/meta/llama-3.1-8b-instruct-awq": BaseAiTextGeneration; + "@cf/meta/llama-3.2-3b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.2-1b-instruct": BaseAiTextGeneration; + "@cf/meta/llama-3.3-70b-instruct-fp8-fast": BaseAiTextGeneration; + "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b": BaseAiTextGeneration; + "@cf/meta/m2m100-1.2b": BaseAiTranslation; + "@cf/facebook/bart-large-cnn": BaseAiSummarization; + "@cf/llava-hf/llava-1.5-7b-hf": BaseAiImageToText; + "@cf/openai/whisper": Base_Ai_Cf_Openai_Whisper; + "@cf/unum/uform-gen2-qwen-500m": Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M; + "@cf/openai/whisper-tiny-en": Base_Ai_Cf_Openai_Whisper_Tiny_En; + "@cf/openai/whisper-large-v3-turbo": Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo; + "@cf/baai/bge-m3": Base_Ai_Cf_Baai_Bge_M3; + "@cf/black-forest-labs/flux-1-schnell": Base_Ai_Cf_Black_Forest_Labs_Flux_1_Schnell; + "@cf/meta/llama-3.2-11b-vision-instruct": Base_Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct; + "@cf/meta/llama-guard-3-8b": Base_Ai_Cf_Meta_Llama_Guard_3_8B; + "@cf/baai/bge-reranker-base": Base_Ai_Cf_Baai_Bge_Reranker_Base; + "@cf/meta/llama-4-scout-17b-16e-instruct": Base_Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct; +} +type AiOptions = { + gateway?: GatewayOptions; + returnRawResponse?: boolean; + prefix?: string; + extraHeaders?: object; +}; +type ConversionResponse = { + name: string; + mimeType: string; + format: "markdown"; + tokens: number; + data: string; +}; +type AiModelsSearchParams = { + author?: string; + hide_experimental?: boolean; + page?: number; + per_page?: number; + search?: string; + source?: number; + task?: string; +}; +type AiModelsSearchObject = { + id: string; + source: number; + name: string; + description: string; + task: { + id: string; + name: string; + description: string; + }; + tags: string[]; + properties: { + property_id: string; + value: string; + }[]; +}; +interface InferenceUpstreamError extends Error { +} +interface AiInternalError extends Error { +} +type AiModelListType = Record; +declare abstract class Ai { + aiGatewayLogId: string | null; + gateway(gatewayId: string): AiGateway; + autorag(autoragId: string): AutoRAG; + run(model: Name, inputs: AiModelList[Name]["inputs"], options?: Options): Promise; + models(params?: AiModelsSearchParams): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }[], options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; + toMarkdown(files: { + name: string; + blob: Blob; + }, options?: { + gateway?: GatewayOptions; + extraHeaders?: object; + }): Promise; +} +type GatewayOptions = { + id: string; + cacheKey?: string; + cacheTtl?: number; + skipCache?: boolean; + metadata?: Record; + collectLog?: boolean; +}; +type AiGatewayPatchLog = { + score?: number | null; + feedback?: -1 | 1 | null; + metadata?: Record | null; +}; +type AiGatewayLog = { + id: string; + provider: string; + model: string; + model_type?: string; + path: string; + duration: number; + request_type?: string; + request_content_type?: string; + status_code: number; + response_content_type?: string; + success: boolean; + cached: boolean; + tokens_in?: number; + tokens_out?: number; + metadata?: Record; + step?: number; + cost?: number; + custom_cost?: boolean; + request_size: number; + request_head?: string; + request_head_complete: boolean; + response_size: number; + response_head?: string; + response_head_complete: boolean; + created_at: Date; +}; +type AIGatewayProviders = "workers-ai" | "anthropic" | "aws-bedrock" | "azure-openai" | "google-vertex-ai" | "huggingface" | "openai" | "perplexity-ai" | "replicate" | "groq" | "cohere" | "google-ai-studio" | "mistral" | "grok" | "openrouter" | "deepseek" | "cerebras" | "cartesia" | "elevenlabs" | "adobe-firefly"; +type AIGatewayHeaders = { + "cf-aig-metadata": Record | string; + "cf-aig-custom-cost": { + per_token_in?: number; + per_token_out?: number; + } | { + total_cost?: number; + } | string; + "cf-aig-cache-ttl": number | string; + "cf-aig-skip-cache": boolean | string; + "cf-aig-cache-key": string; + "cf-aig-collect-log": boolean | string; + Authorization: string; + "Content-Type": string; + [key: string]: string | number | boolean | object; +}; +type AIGatewayUniversalRequest = { + provider: AIGatewayProviders | string; // eslint-disable-line + endpoint: string; + headers: Partial; + query: unknown; +}; +interface AiGatewayInternalError extends Error { +} +interface AiGatewayLogNotFound extends Error { +} +declare abstract class AiGateway { + patchLog(logId: string, data: AiGatewayPatchLog): Promise; + getLog(logId: string): Promise; + run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[]): Promise; + getUrl(provider?: AIGatewayProviders | string): Promise; // eslint-disable-line +} +interface AutoRAGInternalError extends Error { +} +interface AutoRAGNotFoundError extends Error { +} +interface AutoRAGUnauthorizedError extends Error { +} +type AutoRagSearchRequest = { + query: string; + max_num_results?: number; + ranking_options?: { + ranker?: string; + score_threshold?: number; + }; + rewrite_query?: boolean; +}; +type AutoRagSearchResponse = { + object: "vector_store.search_results.page"; + search_query: string; + data: { + file_id: string; + filename: string; + score: number; + attributes: Record; + content: { + type: "text"; + text: string; + }[]; + }[]; + has_more: boolean; + next_page: string | null; +}; +type AutoRagAiSearchResponse = AutoRagSearchResponse & { + response: string; +}; +declare abstract class AutoRAG { + search(params: AutoRagSearchRequest): Promise; + aiSearch(params: AutoRagSearchRequest): Promise; +} +interface BasicImageTransformations { + /** + * Maximum width in image pixels. The value must be an integer. + */ + width?: number; + /** + * Maximum height in image pixels. The value must be an integer. + */ + height?: number; + /** + * Resizing mode as a string. It affects interpretation of width and height + * options: + * - scale-down: Similar to contain, but the image is never enlarged. If + * the image is larger than given width or height, it will be resized. + * Otherwise its original size will be kept. + * - contain: Resizes to maximum size that fits within the given width and + * height. If only a single dimension is given (e.g. only width), the + * image will be shrunk or enlarged to exactly match that dimension. + * Aspect ratio is always preserved. + * - cover: Resizes (shrinks or enlarges) to fill the entire area of width + * and height. If the image has an aspect ratio different from the ratio + * of width and height, it will be cropped to fit. + * - crop: The image will be shrunk and cropped to fit within the area + * specified by width and height. The image will not be enlarged. For images + * smaller than the given dimensions it's the same as scale-down. For + * images larger than the given dimensions, it's the same as cover. + * See also trim. + * - pad: Resizes to the maximum size that fits within the given width and + * height, and then fills the remaining area with a background color + * (white by default). Use of this mode is not recommended, as the same + * effect can be more efficiently achieved with the contain mode and the + * CSS object-fit: contain property. + * - squeeze: Stretches and deforms to the width and height given, even if it + * breaks aspect ratio + */ + fit?: "scale-down" | "contain" | "cover" | "crop" | "pad" | "squeeze"; + /** + * When cropping with fit: "cover", this defines the side or point that should + * be left uncropped. The value is either a string + * "left", "right", "top", "bottom", "auto", or "center" (the default), + * or an object {x, y} containing focal point coordinates in the original + * image expressed as fractions ranging from 0.0 (top or left) to 1.0 + * (bottom or right), 0.5 being the center. {fit: "cover", gravity: "top"} will + * crop bottom or left and right sides as necessary, but won’t crop anything + * from the top. {fit: "cover", gravity: {x:0.5, y:0.2}} will crop each side to + * preserve as much as possible around a point at 20% of the height of the + * source image. + */ + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | BasicImageTransformationsGravityCoordinates; + /** + * Background color to add underneath the image. Applies only to images with + * transparency (such as PNG). Accepts any CSS color (#RRGGBB, rgba(…), + * hsl(…), etc.) + */ + background?: string; + /** + * Number of degrees (90, 180, 270) to rotate the image by. width and height + * options refer to axes after rotation. + */ + rotate?: 0 | 90 | 180 | 270 | 360; +} +interface BasicImageTransformationsGravityCoordinates { + x?: number; + y?: number; + mode?: 'remainder' | 'box-center'; +} +/** + * In addition to the properties you can set in the RequestInit dict + * that you pass as an argument to the Request constructor, you can + * set certain properties of a `cf` object to control how Cloudflare + * features are applied to that new Request. + * + * Note: Currently, these properties cannot be tested in the + * playground. + */ +interface RequestInitCfProperties extends Record { + cacheEverything?: boolean; + /** + * A request's cache key is what determines if two requests are + * "the same" for caching purposes. If a request has the same cache key + * as some previous request, then we can serve the same cached response for + * both. (e.g. 'some-key') + * + * Only available for Enterprise customers. + */ + cacheKey?: string; + /** + * This allows you to append additional Cache-Tag response headers + * to the origin response without modifications to the origin server. + * This will allow for greater control over the Purge by Cache Tag feature + * utilizing changes only in the Workers process. + * + * Only available for Enterprise customers. + */ + cacheTags?: string[]; + /** + * Force response to be cached for a given number of seconds. (e.g. 300) + */ + cacheTtl?: number; + /** + * Force response to be cached for a given number of seconds based on the Origin status code. + * (e.g. { '200-299': 86400, '404': 1, '500-599': 0 }) + */ + cacheTtlByStatus?: Record; + scrapeShield?: boolean; + apps?: boolean; + image?: RequestInitCfPropertiesImage; + minify?: RequestInitCfPropertiesImageMinify; + mirage?: boolean; + polish?: "lossy" | "lossless" | "off"; + r2?: RequestInitCfPropertiesR2; + /** + * Redirects the request to an alternate origin server. You can use this, + * for example, to implement load balancing across several origins. + * (e.g.us-east.example.com) + * + * Note - For security reasons, the hostname set in resolveOverride must + * be proxied on the same Cloudflare zone of the incoming request. + * Otherwise, the setting is ignored. CNAME hosts are allowed, so to + * resolve to a host under a different domain or a DNS only domain first + * declare a CNAME record within your own zone’s DNS mapping to the + * external hostname, set proxy on Cloudflare, then set resolveOverride + * to point to that CNAME record. + */ + resolveOverride?: string; +} +interface RequestInitCfPropertiesImageDraw extends BasicImageTransformations { + /** + * Absolute URL of the image file to use for the drawing. It can be any of + * the supported file formats. For drawing of watermarks or non-rectangular + * overlays we recommend using PNG or WebP images. + */ + url: string; + /** + * Floating-point number between 0 (transparent) and 1 (opaque). + * For example, opacity: 0.5 makes overlay semitransparent. + */ + opacity?: number; + /** + * - If set to true, the overlay image will be tiled to cover the entire + * area. This is useful for stock-photo-like watermarks. + * - If set to "x", the overlay image will be tiled horizontally only + * (form a line). + * - If set to "y", the overlay image will be tiled vertically only + * (form a line). + */ + repeat?: true | "x" | "y"; + /** + * Position of the overlay image relative to a given edge. Each property is + * an offset in pixels. 0 aligns exactly to the edge. For example, left: 10 + * positions left side of the overlay 10 pixels from the left edge of the + * image it's drawn over. bottom: 0 aligns bottom of the overlay with bottom + * of the background image. + * + * Setting both left & right, or both top & bottom is an error. + * + * If no position is specified, the image will be centered. + */ + top?: number; + left?: number; + bottom?: number; + right?: number; +} +interface RequestInitCfPropertiesImage extends BasicImageTransformations { + /** + * Device Pixel Ratio. Default 1. Multiplier for width/height that makes it + * easier to specify higher-DPI sizes in . + */ + dpr?: number; + /** + * Allows you to trim your image. Takes dpr into account and is performed before + * resizing or rotation. + * + * It can be used as: + * - left, top, right, bottom - it will specify the number of pixels to cut + * off each side + * - width, height - the width/height you'd like to end up with - can be used + * in combination with the properties above + * - border - this will automatically trim the surroundings of an image based on + * it's color. It consists of three properties: + * - color: rgb or hex representation of the color you wish to trim (todo: verify the rgba bit) + * - tolerance: difference from color to treat as color + * - keep: the number of pixels of border to keep + */ + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; + /** + * Quality setting from 1-100 (useful values are in 60-90 range). Lower values + * make images look worse, but load faster. The default is 85. It applies only + * to JPEG and WebP images. It doesn’t have any effect on PNG. + */ + quality?: number | "low" | "medium-low" | "medium-high" | "high"; + /** + * Output format to generate. It can be: + * - avif: generate images in AVIF format. + * - webp: generate images in Google WebP format. Set quality to 100 to get + * the WebP-lossless format. + * - json: instead of generating an image, outputs information about the + * image, in JSON format. The JSON object will contain image size + * (before and after resizing), source image’s MIME type, file size, etc. + * - jpeg: generate images in JPEG format. + * - png: generate images in PNG format. + */ + format?: "avif" | "webp" | "json" | "jpeg" | "png" | "baseline-jpeg" | "png-force" | "svg"; + /** + * Whether to preserve animation frames from input files. Default is true. + * Setting it to false reduces animations to still images. This setting is + * recommended when enlarging images or processing arbitrary user content, + * because large GIF animations can weigh tens or even hundreds of megabytes. + * It is also useful to set anim:false when using format:"json" to get the + * response quicker without the number of frames. + */ + anim?: boolean; + /** + * What EXIF data should be preserved in the output image. Note that EXIF + * rotation and embedded color profiles are always applied ("baked in" into + * the image), and aren't affected by this option. Note that if the Polish + * feature is enabled, all metadata may have been removed already and this + * option may have no effect. + * - keep: Preserve most of EXIF metadata, including GPS location if there's + * any. + * - copyright: Only keep the copyright tag, and discard everything else. + * This is the default behavior for JPEG files. + * - none: Discard all invisible EXIF metadata. Currently WebP and PNG + * output formats always discard metadata. + */ + metadata?: "keep" | "copyright" | "none"; + /** + * Strength of sharpening filter to apply to the image. Floating-point + * number between 0 (no sharpening, default) and 10 (maximum). 1.0 is a + * recommended value for downscaled images. + */ + sharpen?: number; + /** + * Radius of a blur filter (approximate gaussian). Maximum supported radius + * is 250. + */ + blur?: number; + /** + * Overlays are drawn in the order they appear in the array (last array + * entry is the topmost layer). + */ + draw?: RequestInitCfPropertiesImageDraw[]; + /** + * Fetching image from authenticated origin. Setting this property will + * pass authentication headers (Authorization, Cookie, etc.) through to + * the origin. + */ + "origin-auth"?: "share-publicly"; + /** + * Adds a border around the image. The border is added after resizing. Border + * width takes dpr into account, and can be specified either using a single + * width property, or individually for each side. + */ + border?: { + color: string; + width: number; + } | { + color: string; + top: number; + right: number; + bottom: number; + left: number; + }; + /** + * Increase brightness by a factor. A value of 1.0 equals no change, a value + * of 0.5 equals half brightness, and a value of 2.0 equals twice as bright. + * 0 is ignored. + */ + brightness?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + contrast?: number; + /** + * Increase exposure by a factor. A value of 1.0 equals no change, a value of + * 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored. + */ + gamma?: number; + /** + * Increase contrast by a factor. A value of 1.0 equals no change, a value of + * 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is + * ignored. + */ + saturation?: number; + /** + * Flips the images horizontally, vertically, or both. Flipping is applied before + * rotation, so if you apply flip=h,rotate=90 then the image will be flipped + * horizontally, then rotated by 90 degrees. + */ + flip?: 'h' | 'v' | 'hv'; + /** + * Slightly reduces latency on a cache miss by selecting a + * quickest-to-compress file format, at a cost of increased file size and + * lower image quality. It will usually override the format option and choose + * JPEG over WebP or AVIF. We do not recommend using this option, except in + * unusual circumstances like resizing uncacheable dynamically-generated + * images. + */ + compression?: "fast"; +} +interface RequestInitCfPropertiesImageMinify { + javascript?: boolean; + css?: boolean; + html?: boolean; +} +interface RequestInitCfPropertiesR2 { + /** + * Colo id of bucket that an object is stored in + */ + bucketColoId?: number; +} +/** + * Request metadata provided by Cloudflare's edge. + */ +type IncomingRequestCfProperties = IncomingRequestCfPropertiesBase & IncomingRequestCfPropertiesBotManagementEnterprise & IncomingRequestCfPropertiesCloudflareForSaaSEnterprise & IncomingRequestCfPropertiesGeographicInformation & IncomingRequestCfPropertiesCloudflareAccessOrApiShield; +interface IncomingRequestCfPropertiesBase extends Record { + /** + * [ASN](https://www.iana.org/assignments/as-numbers/as-numbers.xhtml) of the incoming request. + * + * @example 395747 + */ + asn: number; + /** + * The organization which owns the ASN of the incoming request. + * + * @example "Google Cloud" + */ + asOrganization: string; + /** + * The original value of the `Accept-Encoding` header if Cloudflare modified it. + * + * @example "gzip, deflate, br" + */ + clientAcceptEncoding?: string; + /** + * The number of milliseconds it took for the request to reach your worker. + * + * @example 22 + */ + clientTcpRtt?: number; + /** + * The three-letter [IATA](https://en.wikipedia.org/wiki/IATA_airport_code) + * airport code of the data center that the request hit. + * + * @example "DFW" + */ + colo: string; + /** + * Represents the upstream's response to a + * [TCP `keepalive` message](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) + * from cloudflare. + * + * For workers with no upstream, this will always be `1`. + * + * @example 3 + */ + edgeRequestKeepAliveStatus: IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus; + /** + * The HTTP Protocol the request used. + * + * @example "HTTP/2" + */ + httpProtocol: string; + /** + * The browser-requested prioritization information in the request object. + * + * If no information was set, defaults to the empty string `""` + * + * @example "weight=192;exclusive=0;group=3;group-weight=127" + * @default "" + */ + requestPriority: string; + /** + * The TLS version of the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "TLSv1.3" + */ + tlsVersion: string; + /** + * The cipher for the connection to Cloudflare. + * In requests served over plaintext (without TLS), this property is the empty string `""`. + * + * @example "AEAD-AES128-GCM-SHA256" + */ + tlsCipher: string; + /** + * Metadata containing the [`HELLO`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2) and [`FINISHED`](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9) messages from this request's TLS handshake. + * + * If the incoming request was served over plaintext (without TLS) this field is undefined. + */ + tlsExportedAuthenticator?: IncomingRequestCfPropertiesExportedAuthenticatorMetadata; +} +interface IncomingRequestCfPropertiesBotManagementBase { + /** + * Cloudflare’s [level of certainty](https://developers.cloudflare.com/bots/concepts/bot-score/) that a request comes from a bot, + * represented as an integer percentage between `1` (almost certainly a bot) and `99` (almost certainly human). + * + * @example 54 + */ + score: number; + /** + * A boolean value that is true if the request comes from a good bot, like Google or Bing. + * Most customers choose to allow this traffic. For more details, see [Traffic from known bots](https://developers.cloudflare.com/firewall/known-issues-and-faq/#how-does-firewall-rules-handle-traffic-from-known-bots). + */ + verifiedBot: boolean; + /** + * A boolean value that is true if the request originates from a + * Cloudflare-verified proxy service. + */ + corporateProxy: boolean; + /** + * A boolean value that's true if the request matches [file extensions](https://developers.cloudflare.com/bots/reference/static-resources/) for many types of static resources. + */ + staticResource: boolean; + /** + * List of IDs that correlate to the Bot Management heuristic detections made on a request (you can have multiple heuristic detections on the same request). + */ + detectionIds: number[]; +} +interface IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase; + /** + * Duplicate of `botManagement.score`. + * + * @deprecated + */ + clientTrustScore: number; +} +interface IncomingRequestCfPropertiesBotManagementEnterprise extends IncomingRequestCfPropertiesBotManagement { + /** + * Results of Cloudflare's Bot Management analysis + */ + botManagement: IncomingRequestCfPropertiesBotManagementBase & { + /** + * A [JA3 Fingerprint](https://developers.cloudflare.com/bots/concepts/ja3-fingerprint/) to help profile specific SSL/TLS clients + * across different destination IPs, Ports, and X509 certificates. + */ + ja3Hash: string; + }; +} +interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise { + /** + * Custom metadata set per-host in [Cloudflare for SaaS](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/). + * + * This field is only present if you have Cloudflare for SaaS enabled on your account + * and you have followed the [required steps to enable it]((https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/custom-metadata/)). + */ + hostMetadata: HostMetadata; +} +interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield { + /** + * Information about the client certificate presented to Cloudflare. + * + * This is populated when the incoming request is served over TLS using + * either Cloudflare Access or API Shield (mTLS) + * and the presented SSL certificate has a valid + * [Certificate Serial Number](https://ldapwiki.com/wiki/Certificate%20Serial%20Number) + * (i.e., not `null` or `""`). + * + * Otherwise, a set of placeholder values are used. + * + * The property `certPresented` will be set to `"1"` when + * the object is populated (i.e. the above conditions were met). + */ + tlsClientAuth: IncomingRequestCfPropertiesTLSClientAuth | IncomingRequestCfPropertiesTLSClientAuthPlaceholder; +} +/** + * Metadata about the request's TLS handshake + */ +interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata { + /** + * The client's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + clientHandshake: string; + /** + * The server's [`HELLO` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.2), encoded in hexadecimal + * + * @example "44372ba35fa1270921d318f34c12f155dc87b682cf36a790cfaa3ba8737a1b5d" + */ + serverHandshake: string; + /** + * The client's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + clientFinished: string; + /** + * The server's [`FINISHED` message](https://www.rfc-editor.org/rfc/rfc5246#section-7.4.9), encoded in hexadecimal + * + * @example "084ee802fe1348f688220e2a6040a05b2199a761f33cf753abb1b006792d3f8b" + */ + serverFinished: string; +} +/** + * Geographic data about the request's origin. + */ +interface IncomingRequestCfPropertiesGeographicInformation { + /** + * The [ISO 3166-1 Alpha 2](https://www.iso.org/iso-3166-country-codes.html) country code the request originated from. + * + * If your worker is [configured to accept TOR connections](https://support.cloudflare.com/hc/en-us/articles/203306930-Understanding-Cloudflare-Tor-support-and-Onion-Routing), this may also be `"T1"`, indicating a request that originated over TOR. + * + * If Cloudflare is unable to determine where the request originated this property is omitted. + * + * The country code `"T1"` is used for requests originating on TOR. + * + * @example "GB" + */ + country?: Iso3166Alpha2Code | "T1"; + /** + * If present, this property indicates that the request originated in the EU + * + * @example "1" + */ + isEUCountry?: "1"; + /** + * A two-letter code indicating the continent the request originated from. + * + * @example "AN" + */ + continent?: ContinentCode; + /** + * The city the request originated from + * + * @example "Austin" + */ + city?: string; + /** + * Postal code of the incoming request + * + * @example "78701" + */ + postalCode?: string; + /** + * Latitude of the incoming request + * + * @example "30.27130" + */ + latitude?: string; + /** + * Longitude of the incoming request + * + * @example "-97.74260" + */ + longitude?: string; + /** + * Timezone of the incoming request + * + * @example "America/Chicago" + */ + timezone?: string; + /** + * If known, the ISO 3166-2 name for the first level region associated with + * the IP address of the incoming request + * + * @example "Texas" + */ + region?: string; + /** + * If known, the ISO 3166-2 code for the first-level region associated with + * the IP address of the incoming request + * + * @example "TX" + */ + regionCode?: string; + /** + * Metro code (DMA) of the incoming request + * + * @example "635" + */ + metroCode?: string; +} +/** Data about the incoming request's TLS certificate */ +interface IncomingRequestCfPropertiesTLSClientAuth { + /** Always `"1"`, indicating that the certificate was presented */ + certPresented: "1"; + /** + * Result of certificate verification. + * + * @example "FAILED:self signed certificate" + */ + certVerified: Exclude; + /** The presented certificate's revokation status. + * + * - A value of `"1"` indicates the certificate has been revoked + * - A value of `"0"` indicates the certificate has not been revoked + */ + certRevoked: "1" | "0"; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDN: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDN: string; + /** + * The certificate issuer's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certIssuerDNRFC2253: string; + /** + * The certificate subject's [distinguished name](https://knowledge.digicert.com/generalinformation/INFO1745.html) ([RFC 2253](https://www.rfc-editor.org/rfc/rfc2253.html) formatted) + * + * @example "CN=*.cloudflareaccess.com, C=US, ST=Texas, L=Austin, O=Cloudflare" + */ + certSubjectDNRFC2253: string; + /** The certificate issuer's distinguished name (legacy policies) */ + certIssuerDNLegacy: string; + /** The certificate subject's distinguished name (legacy policies) */ + certSubjectDNLegacy: string; + /** + * The certificate's serial number + * + * @example "00936EACBE07F201DF" + */ + certSerial: string; + /** + * The certificate issuer's serial number + * + * @example "2489002934BDFEA34" + */ + certIssuerSerial: string; + /** + * The certificate's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certSKI: string; + /** + * The certificate issuer's Subject Key Identifier + * + * @example "BB:AF:7E:02:3D:FA:A6:F1:3C:84:8E:AD:EE:38:98:EC:D9:32:32:D4" + */ + certIssuerSKI: string; + /** + * The certificate's SHA-1 fingerprint + * + * @example "6b9109f323999e52259cda7373ff0b4d26bd232e" + */ + certFingerprintSHA1: string; + /** + * The certificate's SHA-256 fingerprint + * + * @example "acf77cf37b4156a2708e34c4eb755f9b5dbbe5ebb55adfec8f11493438d19e6ad3f157f81fa3b98278453d5652b0c1fd1d71e5695ae4d709803a4d3f39de9dea" + */ + certFingerprintSHA256: string; + /** + * The effective starting date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotBefore: string; + /** + * The effective expiration date of the certificate + * + * @example "Dec 22 19:39:00 2018 GMT" + */ + certNotAfter: string; +} +/** Placeholder values for TLS Client Authorization */ +interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder { + certPresented: "0"; + certVerified: "NONE"; + certRevoked: "0"; + certIssuerDN: ""; + certSubjectDN: ""; + certIssuerDNRFC2253: ""; + certSubjectDNRFC2253: ""; + certIssuerDNLegacy: ""; + certSubjectDNLegacy: ""; + certSerial: ""; + certIssuerSerial: ""; + certSKI: ""; + certIssuerSKI: ""; + certFingerprintSHA1: ""; + certFingerprintSHA256: ""; + certNotBefore: ""; + certNotAfter: ""; +} +/** Possible outcomes of TLS verification */ +declare type CertVerificationStatus = +/** Authentication succeeded */ +"SUCCESS" +/** No certificate was presented */ + | "NONE" +/** Failed because the certificate was self-signed */ + | "FAILED:self signed certificate" +/** Failed because the certificate failed a trust chain check */ + | "FAILED:unable to verify the first certificate" +/** Failed because the certificate not yet valid */ + | "FAILED:certificate is not yet valid" +/** Failed because the certificate is expired */ + | "FAILED:certificate has expired" +/** Failed for another unspecified reason */ + | "FAILED"; +/** + * An upstream endpoint's response to a TCP `keepalive` message from Cloudflare. + */ +declare type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus = 0 /** Unknown */ | 1 /** no keepalives (not found) */ | 2 /** no connection re-use, opening keepalive connection failed */ | 3 /** no connection re-use, keepalive accepted and saved */ | 4 /** connection re-use, refused by the origin server (`TCP FIN`) */ | 5; /** connection re-use, accepted by the origin server */ +/** ISO 3166-1 Alpha-2 codes */ +declare type Iso3166Alpha2Code = "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AO" | "AQ" | "AR" | "AS" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "FI" | "FJ" | "FK" | "FM" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GS" | "GT" | "GU" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MH" | "MK" | "ML" | "MM" | "MN" | "MO" | "MP" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PR" | "PS" | "PT" | "PW" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VI" | "VN" | "VU" | "WF" | "WS" | "YE" | "YT" | "ZA" | "ZM" | "ZW"; +/** The 2-letter continent codes Cloudflare uses */ +declare type ContinentCode = "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; +type CfProperties = IncomingRequestCfProperties | RequestInitCfProperties; +interface D1Meta { + duration: number; + size_after: number; + rows_read: number; + rows_written: number; + last_row_id: number; + changed_db: boolean; + changes: number; + /** + * The region of the database instance that executed the query. + */ + served_by_region?: string; + /** + * True if-and-only-if the database instance that executed the query was the primary. + */ + served_by_primary?: boolean; + timings?: { + /** + * The duration of the SQL query execution by the database instance. It doesn't include any network time. + */ + sql_duration_ms: number; + }; +} +interface D1Response { + success: true; + meta: D1Meta & Record; + error?: never; +} +type D1Result = D1Response & { + results: T[]; +}; +interface D1ExecResult { + count: number; + duration: number; +} +type D1SessionConstraint = +// Indicates that the first query should go to the primary, and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). +"first-primary" +// Indicates that the first query can go anywhere (primary or replica), and the rest queries +// using the same D1DatabaseSession will go to any replica that is consistent with +// the bookmark maintained by the session (returned by the first query). + | "first-unconstrained"; +type D1SessionBookmark = string; +declare abstract class D1Database { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + exec(query: string): Promise; + /** + * Creates a new D1 Session anchored at the given constraint or the bookmark. + * All queries executed using the created session will have sequential consistency, + * meaning that all writes done through the session will be visible in subsequent reads. + * + * @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session. + */ + withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession; + /** + * @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases. + */ + dump(): Promise; +} +declare abstract class D1DatabaseSession { + prepare(query: string): D1PreparedStatement; + batch(statements: D1PreparedStatement[]): Promise[]>; + /** + * @returns The latest session bookmark across all executed queries on the session. + * If no query has been executed yet, `null` is returned. + */ + getBookmark(): D1SessionBookmark | null; +} +declare abstract class D1PreparedStatement { + bind(...values: unknown[]): D1PreparedStatement; + first(colName: string): Promise; + first>(): Promise; + run>(): Promise>; + all>(): Promise>; + raw(options: { + columnNames: true; + }): Promise<[ + string[], + ...T[] + ]>; + raw(options?: { + columnNames?: false; + }): Promise; +} +// `Disposable` was added to TypeScript's standard lib types in version 5.2. +// To support older TypeScript versions, define an empty `Disposable` interface. +// Users won't be able to use `using`/`Symbol.dispose` without upgrading to 5.2, +// but this will ensure type checking on older versions still passes. +// TypeScript's interface merging will ensure our empty interface is effectively +// ignored when `Disposable` is included in the standard lib. +interface Disposable { +} +/** + * An email message that can be sent from a Worker. + */ +interface EmailMessage { + /** + * Envelope From attribute of the email message. + */ + readonly from: string; + /** + * Envelope To attribute of the email message. + */ + readonly to: string; +} +/** + * An email message that is sent to a consumer Worker and can be rejected/forwarded. + */ +interface ForwardableEmailMessage extends EmailMessage { + /** + * Stream of the email message content. + */ + readonly raw: ReadableStream; + /** + * An [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + */ + readonly headers: Headers; + /** + * Size of the email message content. + */ + readonly rawSize: number; + /** + * Reject this email message by returning a permanent SMTP error back to the connecting client including the given reason. + * @param reason The reject reason. + * @returns void + */ + setReject(reason: string): void; + /** + * Forward this email message to a verified destination address of the account. + * @param rcptTo Verified destination address. + * @param headers A [Headers object](https://developer.mozilla.org/en-US/docs/Web/API/Headers). + * @returns A promise that resolves when the email message is forwarded. + */ + forward(rcptTo: string, headers?: Headers): Promise; + /** + * Reply to the sender of this email message with a new EmailMessage object. + * @param message The reply message. + * @returns A promise that resolves when the email message is replied. + */ + reply(message: EmailMessage): Promise; +} +/** + * A binding that allows a Worker to send email messages. + */ +interface SendEmail { + send(message: EmailMessage): Promise; +} +declare abstract class EmailEvent extends ExtendableEvent { + readonly message: ForwardableEmailMessage; +} +declare type EmailExportedHandler = (message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) => void | Promise; +declare module "cloudflare:email" { + let _EmailMessage: { + prototype: EmailMessage; + new (from: string, to: string, raw: ReadableStream | string): EmailMessage; + }; + export { _EmailMessage as EmailMessage }; +} +interface Hyperdrive { + /** + * Connect directly to Hyperdrive as if it's your database, returning a TCP socket. + * + * Calling this method returns an idential socket to if you call + * `connect("host:port")` using the `host` and `port` fields from this object. + * Pick whichever approach works better with your preferred DB client library. + * + * Note that this socket is not yet authenticated -- it's expected that your + * code (or preferably, the client library of your choice) will authenticate + * using the information in this class's readonly fields. + */ + connect(): Socket; + /** + * A valid DB connection string that can be passed straight into the typical + * client library/driver/ORM. This will typically be the easiest way to use + * Hyperdrive. + */ + readonly connectionString: string; + /* + * A randomly generated hostname that is only valid within the context of the + * currently running Worker which, when passed into `connect()` function from + * the "cloudflare:sockets" module, will connect to the Hyperdrive instance + * for your database. + */ + readonly host: string; + /* + * The port that must be paired the the host field when connecting. + */ + readonly port: number; + /* + * The username to use when authenticating to your database via Hyperdrive. + * Unlike the host and password, this will be the same every time + */ + readonly user: string; + /* + * The randomly generated password to use when authenticating to your + * database via Hyperdrive. Like the host field, this password is only valid + * within the context of the currently running Worker instance from which + * it's read. + */ + readonly password: string; + /* + * The name of the database to connect to. + */ + readonly database: string; +} +// Copyright (c) 2024 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +type ImageInfoResponse = { + format: 'image/svg+xml'; +} | { + format: string; + fileSize: number; + width: number; + height: number; +}; +type ImageTransform = { + width?: number; + height?: number; + background?: string; + blur?: number; + border?: { + color?: string; + width?: number; + } | { + top?: number; + bottom?: number; + left?: number; + right?: number; + }; + brightness?: number; + contrast?: number; + fit?: 'scale-down' | 'contain' | 'pad' | 'squeeze' | 'cover' | 'crop'; + flip?: 'h' | 'v' | 'hv'; + gamma?: number; + gravity?: 'left' | 'right' | 'top' | 'bottom' | 'center' | 'auto' | 'entropy' | { + x?: number; + y?: number; + mode: 'remainder' | 'box-center'; + }; + rotate?: 0 | 90 | 180 | 270; + saturation?: number; + sharpen?: number; + trim?: "border" | { + top?: number; + bottom?: number; + left?: number; + right?: number; + width?: number; + height?: number; + border?: boolean | { + color?: string; + tolerance?: number; + keep?: number; + }; + }; +}; +type ImageDrawOptions = { + opacity?: number; + repeat?: boolean | string; + top?: number; + left?: number; + bottom?: number; + right?: number; +}; +type ImageOutputOptions = { + format: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/avif' | 'rgb' | 'rgba'; + quality?: number; + background?: string; +}; +interface ImagesBinding { + /** + * Get image metadata (type, width and height) + * @throws {@link ImagesError} with code 9412 if input is not an image + * @param stream The image bytes + */ + info(stream: ReadableStream): Promise; + /** + * Begin applying a series of transformations to an image + * @param stream The image bytes + * @returns A transform handle + */ + input(stream: ReadableStream): ImageTransformer; +} +interface ImageTransformer { + /** + * Apply transform next, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param transform + */ + transform(transform: ImageTransform): ImageTransformer; + /** + * Draw an image on this transformer, returning a transform handle. + * You can then apply more transformations, draw, or retrieve the output. + * @param image The image (or transformer that will give the image) to draw + * @param options The options configuring how to draw the image + */ + draw(image: ReadableStream | ImageTransformer, options?: ImageDrawOptions): ImageTransformer; + /** + * Retrieve the image that results from applying the transforms to the + * provided input + * @param options Options that apply to the output e.g. output format + */ + output(options: ImageOutputOptions): Promise; +} +interface ImageTransformationResult { + /** + * The image as a response, ready to store in cache or return to users + */ + response(): Response; + /** + * The content type of the returned image + */ + contentType(): string; + /** + * The bytes of the response + */ + image(): ReadableStream; +} +interface ImagesError extends Error { + readonly code: number; + readonly message: string; + readonly stack?: string; +} +type Params

= Record; +type EventContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; +}; +type PagesFunction = Record> = (context: EventContext) => Response | Promise; +type EventPluginContext = { + request: Request>; + functionPath: string; + waitUntil: (promise: Promise) => void; + passThroughOnException: () => void; + next: (input?: Request | string, init?: RequestInit) => Promise; + env: Env & { + ASSETS: { + fetch: typeof fetch; + }; + }; + params: Params

; + data: Data; + pluginArgs: PluginArgs; +}; +type PagesPluginFunction = Record, PluginArgs = unknown> = (context: EventPluginContext) => Response | Promise; +declare module "assets:*" { + export const onRequest: PagesFunction; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +declare module "cloudflare:pipelines" { + export abstract class PipelineTransformationEntrypoint { + protected env: Env; + protected ctx: ExecutionContext; + constructor(ctx: ExecutionContext, env: Env); + /** + * run recieves an array of PipelineRecord which can be + * transformed and returned to the pipeline + * @param records Incoming records from the pipeline to be transformed + * @param metadata Information about the specific pipeline calling the transformation entrypoint + * @returns A promise containing the transformed PipelineRecord array + */ + public run(records: I[], metadata: PipelineBatchMetadata): Promise; + } + export type PipelineRecord = Record; + export type PipelineBatchMetadata = { + pipelineId: string; + pipelineName: string; + }; + export interface Pipeline { + /** + * The Pipeline interface represents the type of a binding to a Pipeline + * + * @param records The records to send to the pipeline + */ + send(records: T[]): Promise; + } +} +// PubSubMessage represents an incoming PubSub message. +// The message includes metadata about the broker, the client, and the payload +// itself. +// https://developers.cloudflare.com/pub-sub/ +interface PubSubMessage { + // Message ID + readonly mid: number; + // MQTT broker FQDN in the form mqtts://BROKER.NAMESPACE.cloudflarepubsub.com:PORT + readonly broker: string; + // The MQTT topic the message was sent on. + readonly topic: string; + // The client ID of the client that published this message. + readonly clientId: string; + // The unique identifier (JWT ID) used by the client to authenticate, if token + // auth was used. + readonly jti?: string; + // A Unix timestamp (seconds from Jan 1, 1970), set when the Pub/Sub Broker + // received the message from the client. + readonly receivedAt: number; + // An (optional) string with the MIME type of the payload, if set by the + // client. + readonly contentType: string; + // Set to 1 when the payload is a UTF-8 string + // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901063 + readonly payloadFormatIndicator: number; + // Pub/Sub (MQTT) payloads can be UTF-8 strings, or byte arrays. + // You can use payloadFormatIndicator to inspect this before decoding. + payload: string | Uint8Array; +} +// JsonWebKey extended by kid parameter +interface JsonWebKeyWithKid extends JsonWebKey { + // Key Identifier of the JWK + readonly kid: string; +} +interface RateLimitOptions { + key: string; +} +interface RateLimitOutcome { + success: boolean; +} +interface RateLimit { + /** + * Rate limit a request based on the provided options. + * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/ + * @returns A promise that resolves with the outcome of the rate limit. + */ + limit(options: RateLimitOptions): Promise; +} +// Namespace for RPC utility types. Unfortunately, we can't use a `module` here as these types need +// to referenced by `Fetcher`. This is included in the "importable" version of the types which +// strips all `module` blocks. +declare namespace Rpc { + // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s. + // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`. + // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to + // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape) + export const __RPC_STUB_BRAND: '__RPC_STUB_BRAND'; + export const __RPC_TARGET_BRAND: '__RPC_TARGET_BRAND'; + export const __WORKER_ENTRYPOINT_BRAND: '__WORKER_ENTRYPOINT_BRAND'; + export const __DURABLE_OBJECT_BRAND: '__DURABLE_OBJECT_BRAND'; + export const __WORKFLOW_ENTRYPOINT_BRAND: '__WORKFLOW_ENTRYPOINT_BRAND'; + export interface RpcTargetBranded { + [__RPC_TARGET_BRAND]: never; + } + export interface WorkerEntrypointBranded { + [__WORKER_ENTRYPOINT_BRAND]: never; + } + export interface DurableObjectBranded { + [__DURABLE_OBJECT_BRAND]: never; + } + export interface WorkflowEntrypointBranded { + [__WORKFLOW_ENTRYPOINT_BRAND]: never; + } + export type EntrypointBranded = WorkerEntrypointBranded | DurableObjectBranded | WorkflowEntrypointBranded; + // Types that can be used through `Stub`s + export type Stubable = RpcTargetBranded | ((...args: any[]) => any); + // Types that can be passed over RPC + // The reason for using a generic type here is to build a serializable subset of structured + // cloneable composite types. This allows types defined with the "interface" keyword to pass the + // serializable check as well. Otherwise, only types defined with the "type" keyword would pass. + type Serializable = + // Structured cloneables + BaseType + // Structured cloneable composites + | Map ? Serializable : never, T extends Map ? Serializable : never> | Set ? Serializable : never> | ReadonlyArray ? Serializable : never> | { + [K in keyof T]: K extends number | string ? Serializable : never; + } + // Special types + | Stub + // Serialized as stubs, see `Stubify` + | Stubable; + // Base type for all RPC stubs, including common memory management methods. + // `T` is used as a marker type for unwrapping `Stub`s later. + interface StubBase extends Disposable { + [__RPC_STUB_BRAND]: T; + dup(): this; + } + export type Stub = Provider & StubBase; + // This represents all the types that can be sent as-is over an RPC boundary + type BaseType = void | undefined | null | boolean | number | bigint | string | TypedArray | ArrayBuffer | DataView | Date | Error | RegExp | ReadableStream | WritableStream | Request | Response | Headers; + // Recursively rewrite all `Stubable` types with `Stub`s + // prettier-ignore + type Stubify = T extends Stubable ? Stub : T extends Map ? Map, Stubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: any; + } ? { + [K in keyof T]: Stubify; + } : T; + // Recursively rewrite all `Stub`s with the corresponding `T`s. + // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies: + // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`. + // prettier-ignore + type Unstubify = T extends StubBase ? V : T extends Map ? Map, Unstubify> : T extends Set ? Set> : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends BaseType ? T : T extends { + [key: string | number]: unknown; + } ? { + [K in keyof T]: Unstubify; + } : T; + type UnstubifyAll = { + [I in keyof A]: Unstubify; + }; + // Utility type for adding `Provider`/`Disposable`s to `object` types only. + // Note `unknown & T` is equivalent to `T`. + type MaybeProvider = T extends object ? Provider : unknown; + type MaybeDisposable = T extends object ? Disposable : unknown; + // Type for method return or property on an RPC interface. + // - Stubable types are replaced by stubs. + // - Serializable types are passed by value, with stubable types replaced by stubs + // and a top-level `Disposer`. + // Everything else can't be passed over PRC. + // Technically, we use custom thenables here, but they quack like `Promise`s. + // Intersecting with `(Maybe)Provider` allows pipelining. + // prettier-ignore + type Result = R extends Stubable ? Promise> & Provider : R extends Serializable ? Promise & MaybeDisposable> & MaybeProvider : never; + // Type for method or property on an RPC interface. + // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s. + // Unwrapping `Stub`s allows calling with `Stubable` arguments. + // For properties, rewrite types to be `Result`s. + // In each case, unwrap `Promise`s. + type MethodOrProperty = V extends (...args: infer P) => infer R ? (...args: UnstubifyAll

) => Result> : Result>; + // Type for the callable part of an `Provider` if `T` is callable. + // This is intersected with methods/properties. + type MaybeCallableProvider = T extends (...args: any[]) => any ? MethodOrProperty : unknown; + // Base type for all other types providing RPC-like interfaces. + // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types. + // `Reserved` names (e.g. stub method names like `dup()`) and symbols can't be accessed over RPC. + export type Provider = MaybeCallableProvider & { + [K in Exclude>]: MethodOrProperty; + }; +} +declare namespace Cloudflare { + interface Env { + } +} +declare module 'cloudflare:workers' { + export type RpcStub = Rpc.Stub; + export const RpcStub: { + new (value: T): Rpc.Stub; + }; + export abstract class RpcTarget implements Rpc.RpcTargetBranded { + [Rpc.__RPC_TARGET_BRAND]: never; + } + // `protected` fields don't appear in `keyof`s, so can't be accessed over RPC + export abstract class WorkerEntrypoint implements Rpc.WorkerEntrypointBranded { + [Rpc.__WORKER_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + fetch?(request: Request): Response | Promise; + tail?(events: TraceItem[]): void | Promise; + trace?(traces: TraceItem[]): void | Promise; + scheduled?(controller: ScheduledController): void | Promise; + queue?(batch: MessageBatch): void | Promise; + test?(controller: TestController): void | Promise; + } + export abstract class DurableObject implements Rpc.DurableObjectBranded { + [Rpc.__DURABLE_OBJECT_BRAND]: never; + protected ctx: DurableObjectState; + protected env: Env; + constructor(ctx: DurableObjectState, env: Env); + fetch?(request: Request): Response | Promise; + alarm?(alarmInfo?: AlarmInvocationInfo): void | Promise; + webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise; + webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise; + webSocketError?(ws: WebSocket, error: unknown): void | Promise; + } + export type WorkflowDurationLabel = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; + export type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ''}` | number; + export type WorkflowDelayDuration = WorkflowSleepDuration; + export type WorkflowTimeoutDuration = WorkflowSleepDuration; + export type WorkflowBackoff = 'constant' | 'linear' | 'exponential'; + export type WorkflowStepConfig = { + retries?: { + limit: number; + delay: WorkflowDelayDuration | number; + backoff?: WorkflowBackoff; + }; + timeout?: WorkflowTimeoutDuration | number; + }; + export type WorkflowEvent = { + payload: Readonly; + timestamp: Date; + instanceId: string; + }; + export type WorkflowStepEvent = { + payload: Readonly; + timestamp: Date; + type: string; + }; + export abstract class WorkflowStep { + do>(name: string, callback: () => Promise): Promise; + do>(name: string, config: WorkflowStepConfig, callback: () => Promise): Promise; + sleep: (name: string, duration: WorkflowSleepDuration) => Promise; + sleepUntil: (name: string, timestamp: Date | number) => Promise; + waitForEvent>(name: string, options: { + type: string; + timeout?: WorkflowTimeoutDuration | number; + }): Promise>; + } + export abstract class WorkflowEntrypoint | unknown = unknown> implements Rpc.WorkflowEntrypointBranded { + [Rpc.__WORKFLOW_ENTRYPOINT_BRAND]: never; + protected ctx: ExecutionContext; + protected env: Env; + constructor(ctx: ExecutionContext, env: Env); + run(event: Readonly>, step: WorkflowStep): Promise; + } + export const env: Cloudflare.Env; +} +interface SecretsStoreSecret { + /** + * Get a secret from the Secrets Store, returning a string of the secret value + * if it exists, or throws an error if it does not exist + */ + get(): Promise; +} +declare module "cloudflare:sockets" { + function _connect(address: string | SocketAddress, options?: SocketOptions): Socket; + export { _connect as connect }; +} +declare namespace TailStream { + interface Header { + readonly name: string; + readonly value: string; + } + interface FetchEventInfo { + readonly type: "fetch"; + readonly method: string; + readonly url: string; + readonly cfJson: string; + readonly headers: Header[]; + } + interface JsRpcEventInfo { + readonly type: "jsrpc"; + readonly methodName: string; + } + interface ScheduledEventInfo { + readonly type: "scheduled"; + readonly scheduledTime: Date; + readonly cron: string; + } + interface AlarmEventInfo { + readonly type: "alarm"; + readonly scheduledTime: Date; + } + interface QueueEventInfo { + readonly type: "queue"; + readonly queueName: string; + readonly batchSize: number; + } + interface EmailEventInfo { + readonly type: "email"; + readonly mailFrom: string; + readonly rcptTo: string; + readonly rawSize: number; + } + interface TraceEventInfo { + readonly type: "trace"; + readonly traces: (string | null)[]; + } + interface HibernatableWebSocketEventInfoMessage { + readonly type: "message"; + } + interface HibernatableWebSocketEventInfoError { + readonly type: "error"; + } + interface HibernatableWebSocketEventInfoClose { + readonly type: "close"; + readonly code: number; + readonly wasClean: boolean; + } + interface HibernatableWebSocketEventInfo { + readonly type: "hibernatableWebSocket"; + readonly info: HibernatableWebSocketEventInfoClose | HibernatableWebSocketEventInfoError | HibernatableWebSocketEventInfoMessage; + } + interface Resume { + readonly type: "resume"; + readonly attachment?: any; + } + interface CustomEventInfo { + readonly type: "custom"; + } + interface FetchResponseInfo { + readonly type: "fetch"; + readonly statusCode: number; + } + type EventOutcome = "ok" | "canceled" | "exception" | "unknown" | "killSwitch" | "daemonDown" | "exceededCpu" | "exceededMemory" | "loadShed" | "responseStreamDisconnected" | "scriptNotFound"; + interface ScriptVersion { + readonly id: string; + readonly tag?: string; + readonly message?: string; + } + interface Trigger { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Onset { + readonly type: "onset"; + readonly dispatchNamespace?: string; + readonly entrypoint?: string; + readonly scriptName?: string; + readonly scriptTags?: string[]; + readonly scriptVersion?: ScriptVersion; + readonly trigger?: Trigger; + readonly info: FetchEventInfo | JsRpcEventInfo | ScheduledEventInfo | AlarmEventInfo | QueueEventInfo | EmailEventInfo | TraceEventInfo | HibernatableWebSocketEventInfo | Resume | CustomEventInfo; + } + interface Outcome { + readonly type: "outcome"; + readonly outcome: EventOutcome; + readonly cpuTime: number; + readonly wallTime: number; + } + interface Hibernate { + readonly type: "hibernate"; + } + interface SpanOpen { + readonly type: "spanOpen"; + readonly op?: string; + readonly info?: FetchEventInfo | JsRpcEventInfo | Attribute[]; + } + interface SpanClose { + readonly type: "spanClose"; + readonly outcome: EventOutcome; + } + interface DiagnosticChannelEvent { + readonly type: "diagnosticChannel"; + readonly channel: string; + readonly message: any; + } + interface Exception { + readonly type: "exception"; + readonly name: string; + readonly message: string; + readonly stack?: string; + } + interface Log { + readonly type: "log"; + readonly level: "debug" | "error" | "info" | "log" | "warn"; + readonly message: string; + } + interface Return { + readonly type: "return"; + readonly info?: FetchResponseInfo | Attribute[]; + } + interface Link { + readonly type: "link"; + readonly label?: string; + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + } + interface Attribute { + readonly type: "attribute"; + readonly name: string; + readonly value: string | string[] | boolean | boolean[] | number | number[]; + } + type Mark = DiagnosticChannelEvent | Exception | Log | Return | Link | Attribute[]; + interface TailEvent { + readonly traceId: string; + readonly invocationId: string; + readonly spanId: string; + readonly timestamp: Date; + readonly sequence: number; + readonly event: Onset | Outcome | Hibernate | SpanOpen | SpanClose | Mark; + } + type TailEventHandler = (event: TailEvent) => void | Promise; + type TailEventHandlerName = "onset" | "outcome" | "hibernate" | "spanOpen" | "spanClose" | "diagnosticChannel" | "exception" | "log" | "return" | "link" | "attribute"; + type TailEventHandlerObject = Record; + type TailEventHandlerType = TailEventHandler | TailEventHandlerObject; +} +// Copyright (c) 2022-2023 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +/** + * Data types supported for holding vector metadata. + */ +type VectorizeVectorMetadataValue = string | number | boolean | string[]; +/** + * Additional information to associate with a vector. + */ +type VectorizeVectorMetadata = VectorizeVectorMetadataValue | Record; +type VectorFloatArray = Float32Array | Float64Array; +interface VectorizeError { + code?: number; + error: string; +} +/** + * Comparison logic/operation to use for metadata filtering. + * + * This list is expected to grow as support for more operations are released. + */ +type VectorizeVectorMetadataFilterOp = "$eq" | "$ne"; +/** + * Filter criteria for vector metadata used to limit the retrieved query result set. + */ +type VectorizeVectorMetadataFilter = { + [field: string]: Exclude | null | { + [Op in VectorizeVectorMetadataFilterOp]?: Exclude | null; + }; +}; +/** + * Supported distance metrics for an index. + * Distance metrics determine how other "similar" vectors are determined. + */ +type VectorizeDistanceMetric = "euclidean" | "cosine" | "dot-product"; +/** + * Metadata return levels for a Vectorize query. + * + * Default to "none". + * + * @property all Full metadata for the vector return set, including all fields (including those un-indexed) without truncation. This is a more expensive retrieval, as it requires additional fetching & reading of un-indexed data. + * @property indexed Return all metadata fields configured for indexing in the vector return set. This level of retrieval is "free" in that no additional overhead is incurred returning this data. However, note that indexed metadata is subject to truncation (especially for larger strings). + * @property none No indexed metadata will be returned. + */ +type VectorizeMetadataRetrievalLevel = "all" | "indexed" | "none"; +interface VectorizeQueryOptions { + topK?: number; + namespace?: string; + returnValues?: boolean; + returnMetadata?: boolean | VectorizeMetadataRetrievalLevel; + filter?: VectorizeVectorMetadataFilter; +} +/** + * Information about the configuration of an index. + */ +type VectorizeIndexConfig = { + dimensions: number; + metric: VectorizeDistanceMetric; +} | { + preset: string; // keep this generic, as we'll be adding more presets in the future and this is only in a read capacity +}; +/** + * Metadata about an existing index. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeIndexInfo} for its post-beta equivalent. + */ +interface VectorizeIndexDetails { + /** The unique ID of the index */ + readonly id: string; + /** The name of the index. */ + name: string; + /** (optional) A human readable description for the index. */ + description?: string; + /** The index configuration, including the dimension size and distance metric. */ + config: VectorizeIndexConfig; + /** The number of records containing vectors within the index. */ + vectorsCount: number; +} +/** + * Metadata about an existing index. + */ +interface VectorizeIndexInfo { + /** The number of records containing vectors within the index. */ + vectorCount: number; + /** Number of dimensions the index has been configured for. */ + dimensions: number; + /** ISO 8601 datetime of the last processed mutation on in the index. All changes before this mutation will be reflected in the index state. */ + processedUpToDatetime: number; + /** UUIDv4 of the last mutation processed by the index. All changes before this mutation will be reflected in the index state. */ + processedUpToMutation: number; +} +/** + * Represents a single vector value set along with its associated metadata. + */ +interface VectorizeVector { + /** The ID for the vector. This can be user-defined, and must be unique. It should uniquely identify the object, and is best set based on the ID of what the vector represents. */ + id: string; + /** The vector values */ + values: VectorFloatArray | number[]; + /** The namespace this vector belongs to. */ + namespace?: string; + /** Metadata associated with the vector. Includes the values of other fields and potentially additional details. */ + metadata?: Record; +} +/** + * Represents a matched vector for a query along with its score and (if specified) the matching vector information. + */ +type VectorizeMatch = Pick, "values"> & Omit & { + /** The score or rank for similarity, when returned as a result */ + score: number; +}; +/** + * A set of matching {@link VectorizeMatch} for a particular query. + */ +interface VectorizeMatches { + matches: VectorizeMatch[]; + count: number; +} +/** + * Results of an operation that performed a mutation on a set of vectors. + * Here, `ids` is a list of vectors that were successfully processed. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link VectorizeAsyncMutation} for its post-beta equivalent. + */ +interface VectorizeVectorMutation { + /* List of ids of vectors that were successfully processed. */ + ids: string[]; + /* Total count of the number of processed vectors. */ + count: number; +} +/** + * Result type indicating a mutation on the Vectorize Index. + * Actual mutations are processed async where the `mutationId` is the unique identifier for the operation. + */ +interface VectorizeAsyncMutation { + /** The unique identifier for the async mutation operation containing the changeset. */ + mutationId: string; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * This type is exclusively for the Vectorize **beta** and will be deprecated once Vectorize RC is released. + * See {@link Vectorize} for its new implementation. + */ +declare abstract class VectorizeIndex { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with the ids & count of records that were successfully processed. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with the ids & count of records that were successfully processed (and thus deleted). + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * A Vectorize Vector Search Index for querying vectors/embeddings. + * + * Mutations in this version are async, returning a mutation id. + */ +declare abstract class Vectorize { + /** + * Get information about the currently bound index. + * @returns A promise that resolves with information about the current index. + */ + public describe(): Promise; + /** + * Use the provided vector to perform a similarity search across the index. + * @param vector Input vector that will be used to drive the similarity search. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public query(vector: VectorFloatArray | number[], options?: VectorizeQueryOptions): Promise; + /** + * Use the provided vector-id to perform a similarity search across the index. + * @param vectorId Id for a vector in the index against which the index should be queried. + * @param options Configuration options to massage the returned data. + * @returns A promise that resolves with matched and scored vectors. + */ + public queryById(vectorId: string, options?: VectorizeQueryOptions): Promise; + /** + * Insert a list of vectors into the index dataset. If a provided id exists, an error will be thrown. + * @param vectors List of vectors that will be inserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the insert changeset. + */ + public insert(vectors: VectorizeVector[]): Promise; + /** + * Upsert a list of vectors into the index dataset. If a provided id exists, it will be replaced with the new values. + * @param vectors List of vectors that will be upserted. + * @returns A promise that resolves with a unique identifier of a mutation containing the upsert changeset. + */ + public upsert(vectors: VectorizeVector[]): Promise; + /** + * Delete a list of vectors with a matching id. + * @param ids List of vector ids that should be deleted. + * @returns A promise that resolves with a unique identifier of a mutation containing the delete changeset. + */ + public deleteByIds(ids: string[]): Promise; + /** + * Get a list of vectors with a matching id. + * @param ids List of vector ids that should be returned. + * @returns A promise that resolves with the raw unscored vectors matching the id set. + */ + public getByIds(ids: string[]): Promise; +} +/** + * The interface for "version_metadata" binding + * providing metadata about the Worker Version using this binding. + */ +type WorkerVersionMetadata = { + /** The ID of the Worker Version using this binding */ + id: string; + /** The tag of the Worker Version using this binding */ + tag: string; + /** The timestamp of when the Worker Version was uploaded */ + timestamp: string; +}; +interface DynamicDispatchLimits { + /** + * Limit CPU time in milliseconds. + */ + cpuMs?: number; + /** + * Limit number of subrequests. + */ + subRequests?: number; +} +interface DynamicDispatchOptions { + /** + * Limit resources of invoked Worker script. + */ + limits?: DynamicDispatchLimits; + /** + * Arguments for outbound Worker script, if configured. + */ + outbound?: { + [key: string]: any; + }; +} +interface DispatchNamespace { + /** + * @param name Name of the Worker script. + * @param args Arguments to Worker script. + * @param options Options for Dynamic Dispatch invocation. + * @returns A Fetcher object that allows you to send requests to the Worker script. + * @throws If the Worker script does not exist in this dispatch namespace, an error will be thrown. + */ + get(name: string, args?: { + [key: string]: any; + }, options?: DynamicDispatchOptions): Fetcher; +} +declare module 'cloudflare:workflows' { + /** + * NonRetryableError allows for a user to throw a fatal error + * that makes a Workflow instance fail immediately without triggering a retry + */ + export class NonRetryableError extends Error { + public constructor(message: string, name?: string); + } +} +declare abstract class Workflow { + /** + * Get a handle to an existing instance of the Workflow. + * @param id Id for the instance of this Workflow + * @returns A promise that resolves with a handle for the Instance + */ + public get(id: string): Promise; + /** + * Create a new instance and return a handle to it. If a provided id exists, an error will be thrown. + * @param options Options when creating an instance including id and params + * @returns A promise that resolves with a handle for the Instance + */ + public create(options?: WorkflowInstanceCreateOptions): Promise; + /** + * Create a batch of instances and return handle for all of them. If a provided id exists, an error will be thrown. + * `createBatch` is limited at 100 instances at a time or when the RPC limit for the batch (1MiB) is reached. + * @param batch List of Options when creating an instance including name and params + * @returns A promise that resolves with a list of handles for the created instances. + */ + public createBatch(batch: WorkflowInstanceCreateOptions[]): Promise; +} +interface WorkflowInstanceCreateOptions { + /** + * An id for your Workflow instance. Must be unique within the Workflow. + */ + id?: string; + /** + * The event payload the Workflow instance is triggered with + */ + params?: PARAMS; +} +type InstanceStatus = { + status: 'queued' // means that instance is waiting to be started (see concurrency limits) + | 'running' | 'paused' | 'errored' | 'terminated' // user terminated the instance while it was running + | 'complete' | 'waiting' // instance is hibernating and waiting for sleep or event to finish + | 'waitingForPause' // instance is finishing the current work to pause + | 'unknown'; + error?: string; + output?: object; +}; +interface WorkflowError { + code?: number; + message: string; +} +declare abstract class WorkflowInstance { + public id: string; + /** + * Pause the instance. + */ + public pause(): Promise; + /** + * Resume the instance. If it is already running, an error will be thrown. + */ + public resume(): Promise; + /** + * Terminate the instance. If it is errored, terminated or complete, an error will be thrown. + */ + public terminate(): Promise; + /** + * Restart the instance. + */ + public restart(): Promise; + /** + * Returns the current status of the instance. + */ + public status(): Promise; + /** + * Send an event to this instance. + */ + public sendEvent({ type, payload, }: { + type: string; + payload: unknown; + }): Promise; +} diff --git a/apps/workers-builds/wrangler.jsonc b/apps/workers-builds/wrangler.jsonc new file mode 100644 index 00000000..3f76311a --- /dev/null +++ b/apps/workers-builds/wrangler.jsonc @@ -0,0 +1,134 @@ +/** + * For more details on how to configure Wrangler, refer to: + * https://developers.cloudflare.com/workers/wrangler/configuration/ + */ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": "src/workers-builds.app.ts", + "compatibility_date": "2025-03-10", + "compatibility_flags": ["nodejs_compat"], + "name": "mcp-cloudflare-workers-builds-dev", + "migrations": [ + { + "new_sqlite_classes": ["UserDetails", "BuildsMCP"], + "tag": "v1" + } + ], + "observability": { + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } + }, + "durable_objects": { + "bindings": [ + { + "class_name": "BuildsMCP", + "name": "MCP_OBJECT" + }, + { + "class_name": "UserDetails", + "name": "USER_DETAILS" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "DEV_KV" + } + ], + "vars": { + "ENVIRONMENT": "development", + "MCP_SERVER_NAME": "PLACEHOLDER", + "MCP_SERVER_VERSION": "PLACEHOLDER", + "CLOUDFLARE_CLIENT_ID": "PLACEHOLDER", + "CLOUDFLARE_CLIENT_SECRET": "PLACEHOLDER" + }, + "dev": { + "port": 8976 + }, + "workers_dev": false, + "preview_urls": false, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-dev" + } + ], + "env": { + "staging": { + "name": "mcp-cloudflare-workers-builds-staging", + "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", + "routes": [{ "pattern": "builds-staging.mcp.cloudflare.com", "custom_domain": true }], + "durable_objects": { + "bindings": [ + { + "class_name": "BuildsMCP", + "name": "MCP_OBJECT" + }, + { + "class_name": "UserDetails", + "name": "USER_DETAILS" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "a6ad24203a244d248f2fe1acfeb7b3a3" + } + ], + "vars": { + "ENVIRONMENT": "staging", + "GIT_HASH": "OVERRIDEN_DURING_DEPLOYMENT", + "SENTRY_DSN": "https://2cc5007c2ad5f33e9be007d4b40208ef@sentry10.cfdata.org/1793", + "MCP_SERVER_NAME": "workers-builds-staging", + "MCP_SERVER_VERSION": "1.0.0" + }, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-staging" + } + ] + }, + "production": { + "name": "mcp-cloudflare-workers-builds-production", + "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", + "routes": [{ "pattern": "builds.mcp.cloudflare.com", "custom_domain": true }], + "durable_objects": { + "bindings": [ + { + "class_name": "BuildsMCP", + "name": "MCP_OBJECT" + }, + { + "class_name": "UserDetails", + "name": "USER_DETAILS" + } + ] + }, + "kv_namespaces": [ + { + "binding": "OAUTH_KV", + "id": "753f27a19ef94d7dbd49de05588ca890" + } + ], + "vars": { + "ENVIRONMENT": "production", + "GIT_HASH": "OVERRIDEN_DURING_DEPLOYMENT", + "SENTRY_DSN": "https://2cc5007c2ad5f33e9be007d4b40208ef@sentry10.cfdata.org/1793", + "MCP_SERVER_NAME": "workers-builds", + "MCP_SERVER_VERSION": "1.0.0" + }, + "analytics_engine_datasets": [ + { + "binding": "MCP_METRICS", + "dataset": "mcp-metrics-production" + } + ] + } + } +} diff --git a/apps/workers-observability/.dev.vars.example b/apps/workers-observability/.dev.vars.example index c087f669..5950b164 100644 --- a/apps/workers-observability/.dev.vars.example +++ b/apps/workers-observability/.dev.vars.example @@ -2,4 +2,4 @@ CLOUDFLARE_CLIENT_ID= CLOUDFLARE_CLIENT_SECRET= DEV_DISABLE_OAUTH= DEV_CLOUDFLARE_API_TOKEN= -DEV_CLOUDFLARE_EMAIL= \ No newline at end of file + diff --git a/apps/workers-observability/CHANGELOG.md b/apps/workers-observability/CHANGELOG.md new file mode 100644 index 00000000..4dfe2f71 --- /dev/null +++ b/apps/workers-observability/CHANGELOG.md @@ -0,0 +1,140 @@ +# workers-observability + +## 0.4.4 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-common@0.20.2 + - @repo/mcp-observability@0.32.5 + +## 0.4.3 + +### Patch Changes + +- Updated dependencies [7fc3f18] + - @repo/mcp-common@0.20.1 + +## 0.4.2 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [f9f0bb6] +- Updated dependencies [847fc1f] + - @repo/mcp-common@0.20.0 + - @repo/mcp-observability@0.32.4 + +## 0.4.1 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + - @repo/mcp-common@0.19.3 + +## 0.4.0 + +### Minor Changes + +- dee0a7b: Updated the model for docs search to embeddinggemma-300m + +## 0.3.4 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools +- Updated dependencies [24dd872] + - @repo/mcp-common@0.19.2 + +## 0.3.3 + +### Patch Changes + +- dffbd36: Use proper wrangler deploy in all servers so we get the name and version + +## 0.3.2 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + - @repo/mcp-common@0.19.1 + +## 0.3.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [1833c6d] +- Updated dependencies [cc6d41f] + - @repo/mcp-common@0.19.0 + - @repo/mcp-observability@0.32.1 + +## 0.3.0 + +### Minor Changes + +- f885d07: Add search docs tool to bindings and obs servers + +### Patch Changes + +- Updated dependencies [f885d07] + - @repo/mcp-common@0.18.0 + +## 0.2.0 + +### Minor Changes + +- 2621557: Use new workers:read scope instead of workers:write, as these mcp servers don't require workers write permissions + +### Patch Changes + +- Updated dependencies [83e2d19] + - @repo/mcp-common@0.17.1 + +## 0.1.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + - @repo/mcp-common@0.17.0 + +## 0.0.4 + +### Patch Changes + +- 3677a18: Remove extraneous log +- Updated dependencies [3677a18] + - @repo/mcp-common@0.16.3 + +## 0.0.3 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth +- Updated dependencies [86c2e4f] + - @repo/mcp-common@0.16.2 + +## 0.0.2 + +### Patch Changes + +- b190e97: fix: set correct entrypoint in wrangler.jsonc +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + +- Updated dependencies [cf3771b] + - @repo/mcp-common@0.16.1 + - @repo/mcp-observability@0.31.1 diff --git a/apps/workers-observability/CONTRIBUTING.md b/apps/workers-observability/CONTRIBUTING.md index 20e6cf61..f5b2752b 100644 --- a/apps/workers-observability/CONTRIBUTING.md +++ b/apps/workers-observability/CONTRIBUTING.md @@ -17,8 +17,6 @@ If you'd like to iterate and test your MCP server, you can do so in local develo ``` DEV_DISABLE_OAUTH=true - DEV_CLOUDFLARE_EMAIL=your_cloudflare_email - # This is your global api token DEV_CLOUDFLARE_API_TOKEN=your_development_api_token ``` @@ -28,7 +26,7 @@ If you'd like to iterate and test your MCP server, you can do so in local develo npx wrangler dev ``` -3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. +3. To test locally, open Inspector, and connect to `http://localhost:8976/mcp`. Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. ## Deploying the Worker ( Cloudflare employees only ) diff --git a/apps/workers-observability/README.md b/apps/workers-observability/README.md index 1b1ae73f..e1f1f17a 100644 --- a/apps/workers-observability/README.md +++ b/apps/workers-observability/README.md @@ -30,7 +30,7 @@ This MCP server is still a work in progress, and we plan to add more tools in th If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://observability.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). -If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. +If your client does not yet support remote MCP servers, you will need to set up its respective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. Replace the content with the following configuration: @@ -39,7 +39,7 @@ Replace the content with the following configuration: "mcpServers": { "cloudflare": { "command": "npx", - "args": ["mcp-remote", "https://observability.mcp.cloudflare.com/sse"] + "args": ["mcp-remote", "https://observability.mcp.cloudflare.com/mcp"] } } } diff --git a/apps/workers-observability/package.json b/apps/workers-observability/package.json index 0cf5c7f6..0c046273 100644 --- a/apps/workers-observability/package.json +++ b/apps/workers-observability/package.json @@ -1,26 +1,27 @@ { "name": "workers-observability", - "version": "0.0.1", + "version": "0.4.4", "private": true, "scripts": { "check:lint": "run-eslint-workers", "check:types": "run-tsc", - "deploy": "wrangler deploy", + "deploy": "run-wrangler-deploy", "dev": "wrangler dev", "start": "wrangler dev", "types": "wrangler types --include-env=false", "test": "vitest run" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", "@fast-csv/format": "5.0.2", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-common": "workspace:*", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", + "workers-tagged-logger": "0.13.5", "zod": "3.24.2" }, "devDependencies": { diff --git a/apps/workers-observability/src/tools/observability.ts b/apps/workers-observability/src/tools/workers-observability.tools.ts similarity index 85% rename from apps/workers-observability/src/tools/observability.ts rename to apps/workers-observability/src/tools/workers-observability.tools.ts index a50f2fef..9412f4c3 100644 --- a/apps/workers-observability/src/tools/observability.ts +++ b/apps/workers-observability/src/tools/workers-observability.tools.ts @@ -1,18 +1,24 @@ import { writeToString } from '@fast-csv/format' +import { WorkersLogger } from 'workers-tagged-logger' import { handleWorkerLogsKeys, handleWorkerLogsValues, queryWorkersObservability, -} from '@repo/mcp-common/src/api/workers-observability' +} from '@repo/mcp-common/src/api/workers-observability.api' +import { getProps } from '@repo/mcp-common/src/get-props' import { zKeysRequest, zQueryRunRequest, zValuesRequest, -} from '@repo/mcp-common/src/types/workers-logs-schemas' +} from '@repo/mcp-common/src/types/workers-logs.types' -import type { ObservabilityMCP } from '../index' +import type { ObservabilityMCP } from '../workers-observability.app' +type Tags = { + toolName?: string +} +const logger = new WorkersLogger() /** * Registers the logs analysis tool with the MCP server * @param server The MCP server instance @@ -48,9 +54,15 @@ This tool provides three primary views of your Worker data: { query: zQueryRunRequest, }, - async ({ query }) => { + async ({ query }, req) => { + logger.setTags({ userAgent: req.requestInfo?.headers?.['mcp-protocol-version'] }) + logger.setTags({ mcpSessionId: req.requestInfo?.headers?.['mcp-session-id'] }) + logger.setTags({ userAgent: req.requestInfo?.headers?.['sec-ch-ua'] }) + logger.setTags({ toolName: 'query_worker_observability' }) const accountId = await agent.getActiveAccountId() + logger.setTags({ hasAccount: !!accountId }) if (!accountId) { + logger.warn('Ran Workers Observability Tool') return { content: [ { @@ -61,8 +73,12 @@ This tool provides three primary views of your Worker data: } } try { - const response = await queryWorkersObservability(agent.props.accessToken, accountId, query) + const props = getProps(agent) + logger.setTags({ datasets: query.parameters?.datasets }) + logger.setTags({ view: query.view }) + const response = await queryWorkersObservability(props.accessToken, accountId, query) + logger.info('Ran Workers Observability Query') if (query.view === 'calculations') { let data = '' for (const calculation of response?.calculations || []) { @@ -205,7 +221,8 @@ This tool provides three primary views of your Worker data: } } try { - const result = await handleWorkerLogsKeys(agent.props.accessToken, accountId, keysQuery) + const props = getProps(agent) + const result = await handleWorkerLogsKeys(props.accessToken, accountId, keysQuery) const tsv = await writeToString( result.map((key) => ({ type: key.type, key: key.key })), @@ -255,7 +272,8 @@ This tool provides three primary views of your Worker data: } } try { - const result = await handleWorkerLogsValues(agent.props.accessToken, accountId, valuesQuery) + const props = getProps(agent) + const result = await handleWorkerLogsValues(props.accessToken, accountId, valuesQuery) const tsv = await writeToString( result?.map((value) => ({ type: value.type, value: value.value })) || [], { headers: true, delimiter: '\t' } diff --git a/apps/workers-observability/src/index.ts b/apps/workers-observability/src/workers-observability.app.ts similarity index 71% rename from apps/workers-observability/src/index.ts rename to apps/workers-observability/src/workers-observability.app.ts index 9b4ee2e1..2aede050 100644 --- a/apps/workers-observability/src/index.ts +++ b/apps/workers-observability/src/workers-observability.app.ts @@ -1,24 +1,27 @@ import OAuthProvider from '@cloudflare/workers-oauth-provider' import { McpAgent } from 'agents/mcp' +import { handleApiTokenMode, isApiTokenRequest } from '@repo/mcp-common/src/api-token-mode' import { createAuthHandlers, handleTokenExchangeCallback, } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import { handleDevMode } from '@repo/mcp-common/src/dev-mode' -import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' +import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { getProps } from '@repo/mcp-common/src/get-props' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-ai-search.prompts' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { initSentryWithUser } from '@repo/mcp-common/src/sentry' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' -import { registerAccountTools } from '@repo/mcp-common/src/tools/account' -import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker' +import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-ai-search.tools' +import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' -import { registerObservabilityTools } from './tools/observability' +import { registerObservabilityTools } from './tools/workers-observability.tools' import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler' -import type { Env } from './context' +import type { Env } from './workers-observability.context' export { UserDetails } @@ -49,14 +52,20 @@ export class ObservabilityMCP extends McpAgent { } async init() { + // TODO: Probably we'll want to track account tokens usage through an account identifier at some point + const props = getProps(this) + const userId = props.type === 'user_token' ? props.user.id : undefined + const sentry = + props.type === 'user_token' ? initSentryWithUser(env, this.ctx, props.user.id) : undefined + this.server = new CloudflareMCPServer({ - userId: this.props.user.id, + userId, wae: this.env.MCP_METRICS, serverInfo: { name: this.env.MCP_SERVER_NAME, version: this.env.MCP_SERVER_VERSION, }, - sentry: initSentryWithUser(env, this.ctx, this.props.user.id), + sentry, options: { instructions: `# Cloudflare Workers Observability Tool * A cloudflare worker is a serverless function @@ -76,13 +85,22 @@ export class ObservabilityMCP extends McpAgent { // Register Cloudflare Workers logs tools registerObservabilityTools(this) + + // Add docs tools + registerDocsTools(this.server, this.env) + registerPrompts(this.server) } async getActiveAccountId() { try { + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return props.account.id + } // Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it // we do this so we can persist activeAccountId across sessions - const userDetails = getUserDetails(env, this.props.user.id) + const userDetails = getUserDetails(env, props.user.id) return await userDetails.getActiveAccountId() } catch (e) { this.server.recordError(e) @@ -92,7 +110,12 @@ export class ObservabilityMCP extends McpAgent { async setActiveAccountId(accountId: string) { try { - const userDetails = getUserDetails(env, this.props.user.id) + const props = getProps(this) + // account tokens are scoped to one account + if (props.type === 'account_token') { + return + } + const userDetails = getUserDetails(env, props.user.id) await userDetails.setActiveAccountId(accountId) } catch (e) { this.server.recordError(e) @@ -103,15 +126,15 @@ export class ObservabilityMCP extends McpAgent { const ObservabilityScopes = { ...RequiredScopes, 'account:read': 'See your account info such as account details, analytics, and memberships.', - 'workers:write': + 'workers:read': 'See and change Cloudflare Workers data such as zones, KV storage, namespaces, scripts, and routes.', 'workers_observability:read': 'See observability logs for your account', } as const export default { fetch: async (req: Request, env: Env, ctx: ExecutionContext) => { - if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') { - return await handleDevMode(ObservabilityMCP, req, env, ctx) + if (await isApiTokenRequest(req, env)) { + return await handleApiTokenMode(ObservabilityMCP, req, env, ctx) } return new OAuthProvider({ diff --git a/apps/workers-observability/src/context.ts b/apps/workers-observability/src/workers-observability.context.ts similarity index 79% rename from apps/workers-observability/src/context.ts rename to apps/workers-observability/src/workers-observability.context.ts index 52c15885..bbb86716 100644 --- a/apps/workers-observability/src/context.ts +++ b/apps/workers-observability/src/workers-observability.context.ts @@ -1,8 +1,9 @@ -import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details' -import type { ObservabilityMCP } from './index' +import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' +import type { ObservabilityMCP } from './workers-observability.app' export interface Env { OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string ENVIRONMENT: 'development' | 'staging' | 'production' MCP_SERVER_NAME: string MCP_SERVER_VERSION: string @@ -15,6 +16,8 @@ export interface Env { SENTRY_ACCESS_CLIENT_SECRET: string GIT_HASH: string SENTRY_DSN: string + AI: Ai + VECTORIZE: VectorizeIndex DEV_DISABLE_OAUTH: string DEV_CLOUDFLARE_API_TOKEN: string DEV_CLOUDFLARE_EMAIL: string diff --git a/apps/workers-observability/vitest.config.ts b/apps/workers-observability/vitest.config.ts index c201c144..8026ccbc 100644 --- a/apps/workers-observability/vitest.config.ts +++ b/apps/workers-observability/vitest.config.ts @@ -1,6 +1,6 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' -import type { Env } from './src/context' +import type { Env } from './src/workers-observability.context' export interface TestEnv extends Env { CLOUDFLARE_MOCK_ACCOUNT_ID: string diff --git a/apps/workers-observability/wrangler.jsonc b/apps/workers-observability/wrangler.jsonc index 078f34ce..00297b26 100644 --- a/apps/workers-observability/wrangler.jsonc +++ b/apps/workers-observability/wrangler.jsonc @@ -4,7 +4,7 @@ */ { "$schema": "node_modules/wrangler/config-schema.json", - "main": "src/index.ts", + "main": "src/workers-observability.app.ts", "compatibility_date": "2025-03-10", "compatibility_flags": ["nodejs_compat"], "name": "mcp-cloudflare-workers-observability-dev", @@ -15,7 +15,11 @@ } ], "observability": { - "enabled": true + "enabled": true, + "traces": { + "enabled": true, + "head_sampling_rate": 0.1 + } }, "durable_objects": { "bindings": [ @@ -35,6 +39,15 @@ "id": "DEV_KV" } ], + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-embeddinggemma-v1" + } + ], "vars": { "ENVIRONMENT": "development", "MCP_SERVER_NAME": "PLACEHOLDER", @@ -83,6 +96,15 @@ "MCP_SERVER_NAME": "workers-observability-staging", "MCP_SERVER_VERSION": "1.0.0" }, + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-embeddinggemma-v1" + } + ], "analytics_engine_datasets": [ { "binding": "MCP_METRICS", @@ -112,6 +134,15 @@ "id": "753f27a19ef94d7dbd49de05588ca890" } ], + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-embeddinggemma-v1" + } + ], "vars": { "ENVIRONMENT": "production", "GIT_HASH": "OVERRIDEN_DURING_DEPLOYMENT", diff --git a/implementation-guides/tools.md b/implementation-guides/tools.md index b47a22b3..e2a841db 100644 --- a/implementation-guides/tools.md +++ b/implementation-guides/tools.md @@ -112,7 +112,7 @@ export function registerMyServiceTools(agent: CloudflareMcpAgent) { - **Perform Action:** Interact with the relevant service (Cloudflare SDK, database, vector store, etc.). - **Format Response:** Return an object with a `content` property, which is an array of `ContentBlock` objects (usually `type: 'text'` or `type: 'resource'`). - For simple success/failure or structured data, `JSON.stringify` the result in a text block. - - For richer data like search results, use `EmbeddedResource` (`type: 'resource'`) as seen in `docs.ts`. + - For richer data like search results, use `EmbeddedResource` (`type: 'resource'`) as seen in `docs-autorag.tools.ts`. - Return clear error messages in the `text` property of a content block upon failure. ## Best Practices diff --git a/implementation-guides/type-validators.md b/implementation-guides/type-validators.md index ceaddc32..66069e63 100644 --- a/implementation-guides/type-validators.md +++ b/implementation-guides/type-validators.md @@ -22,7 +22,7 @@ When a tool parameter corresponds directly to a parameter in the Cloudflare Node - **Detect SDK Changes:** If the underlying SDK type changes (e.g., type alias renamed, property added/removed/renamed, type changed from `string` to `string | null`), your Zod schema definition will likely cause a TypeScript error during compilation. This immediately flags the need to update the validator and potentially the tool logic, preventing runtime errors caused by SDK misalignment. - **Accuracy:** Ensures your validator accurately reflects the type expected by the SDK function you intend to call. -**Example (`hyperdrive.ts`):** +**Example (`hyperdrive.types.ts`):** ```typescript import { z } from 'zod' @@ -54,7 +54,7 @@ Define a separate, named Zod schema for **each individual field** that a tool mi - **Reusability:** Individual field schemas (like `HyperdriveConfigIdSchema`, `HyperdriveConfigNameSchema`) can be reused across different tools (e.g., `hyperdrive_create`, `hyperdrive_update`, `hyperdrive_get`). - **Modularity:** Easier to manage, update, and test individual validation rules. -**Example (`hyperdrive.ts` Structure):** +**Example (`hyperdrive.types.ts` Structure):** ```typescript // --- Base Field Schemas --- @@ -102,7 +102,7 @@ Add a clear, concise `.describe('...')` call to **every** Zod schema you define. - **LLM Context:** The description is often extracted and provided to the LLM as part of the tool's definition, helping it understand the purpose and constraints of each parameter. - **Developer Documentation:** Serves as inline documentation for developers working with the code. -**Example (`hyperdrive.ts`):** +**Example (`hyperdrive.types.ts`):** ```typescript /** Zod schema for the list page number. */ @@ -133,7 +133,7 @@ Use a consistent naming convention for your validator schemas. A recommended pat ## Location -Place validators related to a specific service or concept in dedicated files within the `packages/mcp-common/src/types/` directory (e.g., `hyperdrive.ts`, `kv.ts`). +Place validators related to a specific service or concept in dedicated files within the `packages/mcp-common/src/types/` directory (e.g., `hyperdrive.types.ts`, `kv.ts`). ## Summary diff --git a/package.json b/package.json index 9c846c17..a1e50868 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,28 @@ { "name": "@cloudflare/mcp-server-cloudflare", "version": "1.0.0", - "description": "MCP server for interacting with Cloudflare API", + "description": "Monorepo for Cloudflare MCP servers", + "private": true, "license": "Apache-2.0", "author": "Cloudflare, Inc. (https://cloudflare.com)", "homepage": "https://github.com/cloudflare/mcp-server-cloudflare", "bugs": "https://github.com/cloudflare/mcp-server-cloudflare/issues", "type": "module", - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist", - "README.md" - ], - "access": "public", "sideEffects": false, "scripts": { "changeset:new": "run-changeset-new", - "check:deps": "pnpm exec syncpack lint", + "check:deps": "syncpack lint", "check:format": "prettier . --check --cache --ignore-unknown", "check:turbo": "run-turbo check", "types": "run-turbo types", "test:ci": "run-vitest-ci", "test": "vitest run --passWithNoTests", - "fix:format": "prettier . --write", + "fix:format": "prettier . --write --cache --ignore-unknown", + "fix:deps": "run-fix-deps", "test:watch": "vitest", - "eval:ci": "run-turbo eval:ci" + "eval:ci": "run-turbo eval:ci", + "eval:dev": "run-turbo eval:dev", + "update-deps": "syncpack update" }, "devDependencies": { "@changesets/cli": "2.28.1", diff --git a/packages/eslint-config/CHANGELOG.md b/packages/eslint-config/CHANGELOG.md new file mode 100644 index 00000000..de61d42a --- /dev/null +++ b/packages/eslint-config/CHANGELOG.md @@ -0,0 +1,13 @@ +# @repo/eslint-config + +## 0.1.37 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search + +## 0.1.36 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index dc96b547..6e80ff03 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/eslint-config", - "version": "0.1.35", + "version": "0.1.37", "private": true, "sideEffects": false, "files": [ diff --git a/packages/eval-tools/CHANGELOG.md b/packages/eval-tools/CHANGELOG.md new file mode 100644 index 00000000..4c870f38 --- /dev/null +++ b/packages/eval-tools/CHANGELOG.md @@ -0,0 +1,41 @@ +# @repo/eval-tools + +## 0.32.5 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search + +## 0.32.4 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler + +## 0.32.3 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps + +## 0.32.2 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools + +## 0.32.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol + +## 0.32.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies diff --git a/packages/eval-tools/package.json b/packages/eval-tools/package.json index ddc15ac0..18ba678c 100644 --- a/packages/eval-tools/package.json +++ b/packages/eval-tools/package.json @@ -1,6 +1,6 @@ { "name": "@repo/eval-tools", - "version": "0.31.1", + "version": "0.32.5", "private": true, "sideEffects": false, "type": "module", @@ -11,10 +11,13 @@ "bin": "bin" }, "dependencies": { + "@ai-sdk/anthropic": "1.2.11", + "@ai-sdk/google": "1.2.17", "@ai-sdk/openai": "1.3.20", "@cloudflare/vitest-pool-workers": "0.8.14", - "agents": "0.0.67", + "agents": "0.2.19", "ai": "4.3.10", + "ai-gateway-provider": "0.0.6", "workers-ai-provider": "0.3.0", "wrangler": "4.10.0", "zod": "3.24.2" diff --git a/packages/eval-tools/src/runTask.ts b/packages/eval-tools/src/runTask.ts index c0fe59e1..290f8978 100644 --- a/packages/eval-tools/src/runTask.ts +++ b/packages/eval-tools/src/runTask.ts @@ -1,8 +1,8 @@ import { type MCPClientManager } from 'agents/mcp/client' -import { jsonSchema, streamText, tool } from 'ai' +import { generateText, jsonSchema, tool } from 'ai' import { z } from 'zod' -import type { LanguageModelV1, StreamTextResult, ToolCallPart, ToolSet } from 'ai' +import type { GenerateTextResult, LanguageModelV1, ToolCallPart, ToolSet } from 'ai' export async function runTask( clientManager: MCPClientManager, @@ -10,7 +10,7 @@ export async function runTask( input: string ): Promise<{ promptOutput: string - fullResult: StreamTextResult + fullResult: GenerateTextResult toolCalls: ToolCallPart[] }> { const tools = clientManager.listTools() @@ -43,7 +43,7 @@ export async function runTask( return acc }, {} as ToolSet) - const res = streamText({ + const res = await generateText({ model, system: "You are an assistant responsible for evaluating the results of calling various tools. Given the user's query, use the tools available to you to answer the question.", @@ -53,15 +53,10 @@ export async function runTask( maxSteps: 10, }) - // we need to consume the fill stream, so this is empty - // eslint-disable-next-line no-empty - for await (const _ of res.fullStream) { - } - // convert into an LLM readable result so our factuality checker can validate tool calls let messagesWithTools = '' const toolCalls: ToolCallPart[] = [] - const response = await res.response + const response = res.response const messages = response.messages for (const message of messages) { diff --git a/packages/eval-tools/src/test-models.ts b/packages/eval-tools/src/test-models.ts index c1a6222e..9a1b4d21 100644 --- a/packages/eval-tools/src/test-models.ts +++ b/packages/eval-tools/src/test-models.ts @@ -1,4 +1,10 @@ +import { createAnthropic } from '@ai-sdk/anthropic' +import { AnthropicMessagesModelId } from '@ai-sdk/anthropic/internal' +import { createGoogleGenerativeAI } from '@ai-sdk/google' +import { GoogleGenerativeAILanguageModel } from '@ai-sdk/google/internal' import { createOpenAI } from '@ai-sdk/openai' +import { OpenAIChatModelId } from '@ai-sdk/openai/internal' +import { createAiGateway } from 'ai-gateway-provider' import { env } from 'cloudflare:test' import { describe } from 'vitest' import { createWorkersAI } from 'workers-ai-provider' @@ -13,15 +19,56 @@ type AiTextGenerationModels = Exclude< value2key > -function getOpenAiModel(modelName: string) { - if (!env.OPENAI_API_KEY) { - throw new Error('No API token set!') +function getOpenAiModel(modelName: OpenAIChatModelId) { + if (!env.CLOUDFLARE_ACCOUNT_ID || !env.AI_GATEWAY_ID || !env.AI_GATEWAY_TOKEN) { + throw new Error('No AI gateway credentials set!') } + + const aigateway = createAiGateway({ + accountId: env.CLOUDFLARE_ACCOUNT_ID, + gateway: env.AI_GATEWAY_ID, + apiKey: env.AI_GATEWAY_TOKEN, + }) + const ai = createOpenAI({ - apiKey: env.OPENAI_API_KEY, + apiKey: '', }) - const model = ai(modelName) + const model = aigateway([ai(modelName)]) + + return { modelName, model, ai } +} + +function getAnthropicModel(modelName: AnthropicMessagesModelId) { + const aigateway = createAiGateway({ + accountId: env.CLOUDFLARE_ACCOUNT_ID, + gateway: env.AI_GATEWAY_ID, + apiKey: env.AI_GATEWAY_TOKEN, + }) + + const ai = createAnthropic({ + apiKey: '', + }) + + const model = aigateway([ai(modelName)]) + + return { modelName, model, ai } +} + +function getGeminiModel(modelName: GoogleGenerativeAILanguageModel['modelId']) { + if (!env.CLOUDFLARE_ACCOUNT_ID || !env.AI_GATEWAY_ID || !env.AI_GATEWAY_TOKEN) { + throw new Error('No AI gateway credentials set!') + } + + const aigateway = createAiGateway({ + accountId: env.CLOUDFLARE_ACCOUNT_ID, + gateway: env.AI_GATEWAY_ID, + apiKey: env.AI_GATEWAY_TOKEN, + }) + + const ai = createGoogleGenerativeAI({ apiKey: '' }) + + const model = aigateway([ai(modelName)]) return { modelName, model, ai } } @@ -40,11 +87,10 @@ function getWorkersAiModel(modelName: AiTextGenerationModels) { export const eachModel = describe.each([ getOpenAiModel('gpt-4o'), getOpenAiModel('gpt-4o-mini'), - + // getAnthropicModel('claude-3-5-sonnet-20241022'), TODO: The evals pass with anthropic, but our rate limit is so low with AI wholesaling that we can't use it in CI because it's impossible to get a complete run with the current limits + getGeminiModel('gemini-2.0-flash'), // llama 3 is somewhat inconsistent //getWorkersAiModel("@cf/meta/llama-3.3-70b-instruct-fp8-fast") // Currently llama 4 is having issues with tool calling //getWorkersAiModel("@cf/meta/llama-4-scout-17b-16e-instruct") - - // TODO: add Claude, Gemini, new OpenAI models via AI gateway ]) diff --git a/packages/eval-tools/wrangler.json b/packages/eval-tools/wrangler.json index 99dd0e4e..71588e44 100644 --- a/packages/eval-tools/wrangler.json +++ b/packages/eval-tools/wrangler.json @@ -2,7 +2,10 @@ "name": "stub-worker", "compatibility_date": "2025-04-14", "vars": { - "OPENAI_API_KEY": "TODO" + "OPENAI_API_KEY": "TODO", + "AI_GATEWAY_TOKEN": "TODO", + "CLOUDFLARE_ACCOUNT_ID": "TODO", + "AI_GATEWAY_ID": "TODO" }, "ai": { "binding": "AI" diff --git a/packages/mcp-common/CHANGELOG.md b/packages/mcp-common/CHANGELOG.md new file mode 100644 index 00000000..94f8a76a --- /dev/null +++ b/packages/mcp-common/CHANGELOG.md @@ -0,0 +1,108 @@ +# @repo/mcp-common + +## 0.20.2 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search +- Updated dependencies [99e2282] + - @repo/mcp-observability@0.32.5 + +## 0.20.1 + +### Patch Changes + +- 7fc3f18: Update cloudflare oauth handler 2 + +## 0.20.0 + +### Minor Changes + +- f9f0bb6: Be explicit in oauth error messages + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler +- Updated dependencies [847fc1f] + - @repo/mcp-observability@0.32.4 + +## 0.19.3 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps +- Updated dependencies [43f493d] + - @repo/mcp-observability@0.32.3 + +## 0.19.2 + +### Patch Changes + +- 24dd872: feat: Add MCP tool titles and hints to all Cloudflare tools + +## 0.19.1 + +### Patch Changes + +- 7422e71: Update MCP sdk +- Updated dependencies [7422e71] + - @repo/mcp-observability@0.32.2 + +## 0.19.0 + +### Minor Changes + +- 1833c6d: add relative timeframe support for querying + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol +- Updated dependencies [cc6d41f] + - @repo/mcp-observability@0.32.1 + +## 0.18.0 + +### Minor Changes + +- f885d07: Add search docs tool to bindings and obs servers + +## 0.17.1 + +### Patch Changes + +- 83e2d19: Pass in type user_token in props during oauth flow + +## 0.17.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies +- Updated dependencies [6cf52a6] +- Updated dependencies [0fc4439] + - @repo/mcp-observability@0.32.0 + +## 0.16.3 + +### Patch Changes + +- 3677a18: Remove extraneous log + +## 0.16.2 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth + +## 0.16.1 + +### Patch Changes + +- cf3771b: chore: add suffixes to common files in apps and packages + + It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. + + - @repo/mcp-observability@0.31.1 diff --git a/packages/mcp-common/package.json b/packages/mcp-common/package.json index 021b9d4b..1c6760ca 100644 --- a/packages/mcp-common/package.json +++ b/packages/mcp-common/package.json @@ -1,6 +1,6 @@ { "name": "@repo/mcp-common", - "version": "0.16.0", + "version": "0.20.2", "private": true, "sideEffects": false, "main": "./src/index.ts", @@ -11,11 +11,12 @@ "test:coverage": "run-vitest-coverage" }, "dependencies": { - "@cloudflare/workers-oauth-provider": "0.0.5", + "@cloudflare/workers-oauth-provider": "0.0.13", + "@fast-csv/format": "5.0.2", "@hono/zod-validator": "0.4.3", - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "@repo/mcp-observability": "workspace:*", - "agents": "0.0.67", + "agents": "0.2.19", "cloudflare": "4.2.0", "hono": "4.7.6", "toucan-js": "4.1.1", @@ -26,8 +27,8 @@ "@repo/eslint-config": "workspace:*", "@repo/tools": "workspace:*", "@repo/typescript-config": "workspace:*", - "@types/node": "22.14.1", "@sentry/types": "8.9.2", + "@types/node": "22.14.1", "@vitest/ui": "3.0.9", "vitest": "3.0.9", "wrangler": "4.10.0" diff --git a/packages/mcp-common/src/api-token-mode.ts b/packages/mcp-common/src/api-token-mode.ts new file mode 100644 index 00000000..a13ac6ad --- /dev/null +++ b/packages/mcp-common/src/api-token-mode.ts @@ -0,0 +1,75 @@ +import { getUserAndAccounts } from './cloudflare-oauth-handler' + +import type { McpAgent } from 'agents/mcp' +import type { AuthProps } from './cloudflare-oauth-handler' + +interface RequiredEnv { + DEV_CLOUDFLARE_API_TOKEN: string + DEV_CLOUDFLARE_EMAIL: string + DEV_DISABLE_OAUTH: string +} + +export async function isApiTokenRequest(req: Request, env: RequiredEnv) { + // shortcircuit for dev + if (env.DEV_CLOUDFLARE_API_TOKEN && env.DEV_DISABLE_OAUTH === 'true') { + return true + } + + const authHeader = req.headers.get('Authorization') + if (!authHeader) return false + + const [type, token] = authHeader.split(' ') + if (type !== 'Bearer') return false + + // Return true only if the token was issued by the OAuthProvider. + // A token provisioned by the OAuthProvider has 3 parts, split by colons. + const codeParts = token.split(':') + return codeParts.length !== 3 +} + +export async function handleApiTokenMode< + T extends typeof McpAgent>, +>(agent: T, req: Request, env: RequiredEnv, ctx: ExecutionContext) { + // Handle global API token case + let opts, token + // dev mode + if (env.DEV_CLOUDFLARE_API_TOKEN && env.DEV_DISABLE_OAUTH === 'true') { + opts = { + Authorization: `Bearer ${env.DEV_CLOUDFLARE_API_TOKEN}`, + } + token = env.DEV_CLOUDFLARE_API_TOKEN + // header mode + } else { + const authHeader = req.headers.get('Authorization') + if (!authHeader) { + throw new Error('Authorization header is required') + } + + const [type, tokenStr] = authHeader.split(' ') + if (type !== 'Bearer') { + throw new Error('Invalid authorization type, must be Bearer') + } + token = tokenStr + } + + const { user, accounts } = await getUserAndAccounts(token, opts) + + // If user is null, handle API token mode + if (user === null) { + ctx.props = { + type: 'account_token', + accessToken: token, + // we always select the first account from the response, + // this assumes that account owned tokens can only access one account + account: accounts[0], + } satisfies AuthProps + } else { + ctx.props = { + type: 'user_token', + accessToken: token, + user, + accounts, + } satisfies AuthProps + } + return agent.serve('/mcp').fetch(req, env, ctx) +} diff --git a/packages/mcp-common/src/api/account.ts b/packages/mcp-common/src/api/account.api.ts similarity index 87% rename from packages/mcp-common/src/api/account.ts rename to packages/mcp-common/src/api/account.api.ts index 50bbcda2..6d97ca11 100644 --- a/packages/mcp-common/src/api/account.ts +++ b/packages/mcp-common/src/api/account.api.ts @@ -1,7 +1,9 @@ +import { getProps } from '../get-props' + import type { Cloudflare } from 'cloudflare' import type { Account } from 'cloudflare/resources/accounts/accounts.mjs' -import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' -import type { ToolHandler } from '../types/tools' +import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' +import type { ToolHandler } from '../types/tools.types' export async function handleAccountsList({ client }: { client: Cloudflare }): Promise { // Currently limited to 50 accounts @@ -27,10 +29,11 @@ export const withAccountCheck = >( } try { + const props = getProps(agent) const result = await handler({ ...params, accountId, - apiToken: agent.props.accessToken || '', + apiToken: props.accessToken || '', }) return { content: [{ type: 'text' as const, text: JSON.stringify(result) }], diff --git a/packages/mcp-common/src/api/cf1-integration.ts b/packages/mcp-common/src/api/cf1-integration.api.ts similarity index 98% rename from packages/mcp-common/src/api/cf1-integration.ts rename to packages/mcp-common/src/api/cf1-integration.api.ts index 55a4b660..7c35c190 100644 --- a/packages/mcp-common/src/api/cf1-integration.ts +++ b/packages/mcp-common/src/api/cf1-integration.api.ts @@ -5,7 +5,7 @@ import { AssetsResponse, IntegrationResponse, IntegrationsResponse, -} from '../schemas/cf1-integrations' +} from '../types/cf1-integrations.types' import { V4Schema } from '../v4-api' import type { z } from 'zod' @@ -14,7 +14,7 @@ import type { zReturnedAssetsResult, zReturnedIntegrationResult, zReturnedIntegrationsResult, -} from '../schemas/cf1-integrations' +} from '../types/cf1-integrations.types' interface BaseParams { accountId: string diff --git a/packages/mcp-common/src/api/workers-builds.api.ts b/packages/mcp-common/src/api/workers-builds.api.ts new file mode 100644 index 00000000..12fa650a --- /dev/null +++ b/packages/mcp-common/src/api/workers-builds.api.ts @@ -0,0 +1,88 @@ +import { fetchCloudflareApi } from '../cloudflare-api' +import { + GetBuildLogsResult, + GetBuildResult, + ListBuildsByScriptResult, + ListBuildsByScriptResultInfo, +} from '../types/workers-builds.types' +import { V4Schema } from '../v4-api' + +import type { LogLine } from '../types/workers-builds.types' + +export async function listBuilds({ + accountId, + workerId, + page = 1, + perPage = 10, + apiToken, +}: { + accountId: string + workerId: string + page?: number + perPage?: number + apiToken: string +}) { + return fetchCloudflareApi({ + endpoint: `/builds/workers/${workerId}/builds?page=${page}&per_page=${perPage}`, + accountId, + apiToken, + responseSchema: V4Schema(ListBuildsByScriptResult, ListBuildsByScriptResultInfo), + }) +} + +export async function getBuild({ + accountId, + buildUUID, + apiToken, +}: { + accountId: string + buildUUID: string + apiToken: string +}) { + return fetchCloudflareApi({ + endpoint: `/builds/builds/${buildUUID}`, + accountId, + apiToken, + responseSchema: V4Schema(GetBuildResult), + }) +} + +export async function getBuildLogs({ + accountId, + buildUUID, + apiToken, +}: { + accountId: string + buildUUID: string + apiToken: string +}) { + const allLogs: LogLine[] = [] + let cursor: string | undefined = undefined + let hasMore = true + + while (hasMore) { + let endpoint = `/builds/builds/${buildUUID}/logs` + if (cursor) { + endpoint += `?cursor=${cursor}` + } + + const res = await fetchCloudflareApi({ + endpoint, + accountId, + apiToken, + responseSchema: V4Schema(GetBuildLogsResult), + }) + + if (res.result) { + allLogs.push(...res.result.lines) + + if (res.result.cursor && res.result.truncated) { + cursor = res.result.cursor + } else { + hasMore = false + } + } + } + + return allLogs +} diff --git a/packages/mcp-common/src/api/workers-observability.ts b/packages/mcp-common/src/api/workers-observability.api.ts similarity index 96% rename from packages/mcp-common/src/api/workers-observability.ts rename to packages/mcp-common/src/api/workers-observability.api.ts index 6b41e105..2c155a89 100644 --- a/packages/mcp-common/src/api/workers-observability.ts +++ b/packages/mcp-common/src/api/workers-observability.api.ts @@ -5,11 +5,11 @@ import { zKeysResponse, zReturnedQueryRunResult, zValuesResponse, -} from '../types/workers-logs-schemas' +} from '../types/workers-logs.types' import { V4Schema } from '../v4-api' import type { z } from 'zod' -import type { zKeysRequest, zQueryRunRequest, zValuesRequest } from '../types/workers-logs-schemas' +import type { zKeysRequest, zQueryRunRequest, zValuesRequest } from '../types/workers-logs.types' type QueryRunRequest = z.infer diff --git a/packages/mcp-common/src/api/workers.ts b/packages/mcp-common/src/api/workers.api.ts similarity index 57% rename from packages/mcp-common/src/api/workers.ts rename to packages/mcp-common/src/api/workers.api.ts index 4e095bf2..a2350235 100644 --- a/packages/mcp-common/src/api/workers.ts +++ b/packages/mcp-common/src/api/workers.api.ts @@ -1,3 +1,7 @@ +import { fetchCloudflareApi } from '../cloudflare-api' +import { WorkersService } from '../types/workers.types' +import { V4Schema } from '../v4-api' + import type { Cloudflare } from 'cloudflare' /** @@ -16,6 +20,30 @@ export async function handleWorkersList({ return (await client.workers.scripts.list({ account_id: accountId })).result } +/** + * Get details of a worker script from Cloudflare API + * @param client Cloudflare API Client + * @param scriptName Name of the worker script to download + * @param accountId Cloudflare account ID + * @returns The script name and id + */ +export async function handleGetWorkersService({ + apiToken, + scriptName, + accountId, +}: { + apiToken: string + scriptName: string + accountId: string +}) { + return await fetchCloudflareApi({ + endpoint: `/workers/services/${scriptName}`, + accountId, + apiToken, + responseSchema: V4Schema(WorkersService), + }) +} + /** * Downloads a specific worker script from Cloudflare API * @param client Cloudflare API Client diff --git a/packages/mcp-common/src/api/zone.ts b/packages/mcp-common/src/api/zone.api.ts similarity index 100% rename from packages/mcp-common/src/api/zone.ts rename to packages/mcp-common/src/api/zone.api.ts diff --git a/packages/mcp-common/src/cloudflare-api.ts b/packages/mcp-common/src/cloudflare-api.ts index 17e2d303..447e411f 100644 --- a/packages/mcp-common/src/cloudflare-api.ts +++ b/packages/mcp-common/src/cloudflare-api.ts @@ -8,9 +8,7 @@ export function getCloudflareClient(apiToken: string) { if (env.DEV_DISABLE_OAUTH) { return new Cloudflare({ // @ts-expect-error We don't have actual env in this package, but we know this is defined because the initial Oauth handshake will fail without it - apiEmail: env.DEV_CLOUDFLARE_EMAIL, - // @ts-expect-error We don't have actual env in this package, but we know this is defined because the initial Oauth handshake will fail without it - apiKey: env.DEV_CLOUDFLARE_API_TOKEN, + apiToken: env.DEV_CLOUDFLARE_API_TOKEN, }) } @@ -45,9 +43,7 @@ export async function fetchCloudflareApi({ options.headers = { ...options.headers, // @ts-expect-error We don't have actual env in this package - 'X-Auth-Email': env.DEV_CLOUDFLARE_EMAIL, - // @ts-expect-error We don't have actual env in this package - 'X-Auth-Key': env.DEV_CLOUDFLARE_API_TOKEN, + Authorization: `Bearer ${env.DEV_CLOUDFLARE_API_TOKEN}`, } } const response = await fetch(url, { diff --git a/packages/mcp-common/src/cloudflare-auth.ts b/packages/mcp-common/src/cloudflare-auth.ts index e77c798d..37d27c82 100644 --- a/packages/mcp-common/src/cloudflare-auth.ts +++ b/packages/mcp-common/src/cloudflare-auth.ts @@ -19,7 +19,7 @@ interface PKCECodes { codeChallenge: string codeVerifier: string } -async function generatePKCECodes(): Promise { +export async function generatePKCECodes(): Promise { const output = new Uint32Array(RECOMMENDED_CODE_VERIFIER_LENGTH) crypto.getRandomValues(output) const codeVerifier = base64urlEncode( @@ -80,23 +80,22 @@ export async function getAuthorizationURL({ redirect_uri, state, scopes, + codeChallenge, }: { client_id: string redirect_uri: string state: AuthRequest scopes: Record -}): Promise<{ authUrl: string; codeVerifier: string }> { - const { codeChallenge, codeVerifier } = await generatePKCECodes() - + codeChallenge: string +}): Promise<{ authUrl: string }> { return { authUrl: generateAuthUrl({ client_id, redirect_uri, - state: btoa(JSON.stringify({ ...state, codeVerifier })), + state: btoa(JSON.stringify(state)), code_challenge: codeChallenge, scopes, }), - codeVerifier: codeVerifier, } } diff --git a/packages/mcp-common/src/cloudflare-oauth-handler.ts b/packages/mcp-common/src/cloudflare-oauth-handler.ts index 404d2ff0..22e823ab 100644 --- a/packages/mcp-common/src/cloudflare-oauth-handler.ts +++ b/packages/mcp-common/src/cloudflare-oauth-handler.ts @@ -3,11 +3,28 @@ import { Hono } from 'hono' import { z } from 'zod' import { AuthUser } from '../../mcp-observability/src' -import { getAuthorizationURL, getAuthToken, refreshAuthToken } from './cloudflare-auth' +import { + generatePKCECodes, + getAuthorizationURL, + getAuthToken, + refreshAuthToken, +} from './cloudflare-auth' import { McpError } from './mcp-error' import { useSentry } from './sentry' +import { V4Schema } from './v4-api' +import { + bindStateToSession, + clientIdAlreadyApproved, + createOAuthState, + generateCSRFProtection, + OAuthError, + parseRedirectApproval, + renderApprovalDialog, + validateOAuthState, +} from './workers-oauth-utils' import type { + AuthRequest, OAuthHelpers, TokenExchangeCallbackOptions, TokenExchangeCallbackResult, @@ -19,66 +36,59 @@ import type { BaseHonoContext } from './sentry' type AuthContext = { Bindings: { OAUTH_PROVIDER: OAuthHelpers + OAUTH_KV: KVNamespace + MCP_COOKIE_ENCRYPTION_KEY: string CLOUDFLARE_CLIENT_ID: string CLOUDFLARE_CLIENT_SECRET: string + MCP_SERVER_NAME?: string + MCP_SERVER_DESCRIPTION?: string } } & BaseHonoContext -const AuthRequestSchema = z.object({ - responseType: z.string(), - clientId: z.string(), - redirectUri: z.string(), - scope: z.array(z.string()), - state: z.string(), - codeChallenge: z.string().optional(), - codeChallengeMethod: z.string().optional(), -}) - -// AuthRequest but with extra params that we use in our authentication logic -export const AuthRequestSchemaWithExtraParams = AuthRequestSchema.merge( - z.object({ codeVerifier: z.string() }) -) - -export const AuthQuery = z.object({ +const AuthQuery = z.object({ code: z.string().describe('OAuth code from CF dash'), state: z.string().describe('Value of the OAuth state'), scope: z.string().describe('OAuth scopes granted'), }) -export type UserSchema = z.infer -const UserResponseSchema = z.object({ - result: z.object({ - id: z.string(), - email: z.string(), - }), +type UserSchema = z.infer +const UserSchema = z.object({ + id: z.string(), + email: z.string(), }) - -export type AccountSchema = z.infer -const AccountResponseSchema = z.object({ - result: z.array( - z.object({ - name: z.string(), - id: z.string(), - }) - ), +const AccountSchema = z.object({ + name: z.string(), + id: z.string(), }) +type AccountsSchema = z.infer +const AccountsSchema = z.array(AccountSchema) -export type AuthProps = { - accessToken: string - user: UserSchema['result'] - accounts: AccountSchema['result'] -} +const AccountAuthProps = z.object({ + type: z.literal('account_token'), + accessToken: z.string(), + account: AccountSchema, +}) +const UserAuthProps = z.object({ + type: z.literal('user_token'), + accessToken: z.string(), + user: UserSchema, + accounts: AccountsSchema, + refreshToken: z.string().optional(), +}) +export type AuthProps = z.infer +const AuthProps = z.discriminatedUnion('type', [AccountAuthProps, UserAuthProps]) export async function getUserAndAccounts( accessToken: string, devModeHeaders?: HeadersInit -): Promise<{ user: UserSchema['result']; accounts: AccountSchema['result'] }> { +): Promise<{ user: UserSchema | null; accounts: AccountsSchema }> { const headers = devModeHeaders ? devModeHeaders : { Authorization: `Bearer ${accessToken}`, } + // Fetch the user & accounts info from Cloudflare const [userResponse, accountsResponse] = await Promise.all([ fetch('https://api.cloudflare.com/client/v4/user', { headers, @@ -88,22 +98,32 @@ export async function getUserAndAccounts( }), ]) - if (!userResponse.ok) { - console.log(await userResponse.text()) + const { result: user } = V4Schema(UserSchema).parse(await userResponse.json()) + const { result: accounts } = V4Schema(AccountsSchema).parse(await accountsResponse.json()) + if (!user || !userResponse.ok) { + // If accounts is present, then assume that we have an account scoped token + if (accounts !== null) { + return { user: null, accounts } + } + console.log(user) throw new McpError('Failed to fetch user', 500, { reportToSentry: true }) } - if (!accountsResponse.ok) { - console.log(await accountsResponse.text()) + if (!accounts || !accountsResponse.ok) { + console.log(accounts) throw new McpError('Failed to fetch accounts', 500, { reportToSentry: true }) } - // Fetch the user & accounts info from Cloudflare - const { result: user } = UserResponseSchema.parse(await userResponse.json()) - const { result: accounts } = AccountResponseSchema.parse(await accountsResponse.json()) - return { user, accounts } } +/** + * Exchanges an OAuth authorization code for access and refresh tokens, then fetches user and account details. + * + * @param c - Hono context containing OAuth environment variables (client ID/secret) + * @param code - OAuth authorization code received from the authorization server + * @param code_verifier - PKCE code verifier used to validate the authorization request + * @returns Promise resolving to an object containing access token, refresh token, user profile, and accounts + */ async function getTokenAndUserDetails( c: Context, code: string, @@ -111,8 +131,8 @@ async function getTokenAndUserDetails( ): Promise<{ accessToken: string refreshToken: string - user: UserSchema['result'] - accounts: AccountSchema['result'] + user: UserSchema + accounts: AccountsSchema }> { // Exchange the code for an access token const { access_token: accessToken, refresh_token: refreshToken } = await getAuthToken({ @@ -124,6 +144,10 @@ async function getTokenAndUserDetails( }) const { user, accounts } = await getUserAndAccounts(accessToken) + // User cannot be null for OAuth flow + if (user === null) { + throw new McpError('Failed to fetch user', 500, { reportToSentry: true }) + } return { accessToken, refreshToken, user, accounts } } @@ -135,6 +159,15 @@ export async function handleTokenExchangeCallback( ): Promise { // options.props contains the current props if (options.grantType === 'refresh_token') { + const props = AuthProps.parse(options.props) + if (props.type === 'account_token') { + // Refreshing an account_token should not be possible, as we only do this for user tokens + throw new McpError('Internal Server Error', 500) + } + if (!props.refreshToken) { + throw new McpError('Missing refreshToken', 500) + } + // handle token refreshes const { access_token: accessToken, @@ -143,7 +176,7 @@ export async function handleTokenExchangeCallback( } = await refreshAuthToken({ client_id: clientId, client_secret: clientSecret, - refresh_token: options.props.refreshToken, + refresh_token: props.refreshToken, }) return { @@ -151,12 +184,51 @@ export async function handleTokenExchangeCallback( ...options.props, accessToken, refreshToken, - }, + } satisfies AuthProps, accessTokenTTL: expires_in, } } } +/** + * Helper function to redirect to Cloudflare OAuth + * + * Note: We pass the stateToken as a simple string in the URL. + * The existing getAuthorizationURL function will wrap it with the oauthReqInfo + * before base64-encoding. + * On callback, we extract the stateToken, look up the original oauthReqInfo in KV. + */ +async function redirectToCloudflare( + c: Context, + oauthReqInfo: AuthRequest, + stateToken: string, + codeChallenge: string, + scopes: Record, + additionalHeaders: Record = {} +): Promise { + // Create a modified oauthReqInfo that includes our stateToken + const stateWithToken: AuthRequest = { + ...oauthReqInfo, + state: stateToken, // embed our KV state token + } + + const { authUrl } = await getAuthorizationURL({ + client_id: c.env.CLOUDFLARE_CLIENT_ID, + redirect_uri: new URL('/oauth/callback', c.req.url).href, + state: stateWithToken, + scopes, + codeChallenge, + }) + + return new Response(null, { + status: 302, + headers: { + ...additionalHeaders, + Location: authUrl, + }, + }) +} + /** * Creates a Hono app with OAuth routes for a specific Cloudflare worker * @@ -171,129 +243,233 @@ export function createAuthHandlers({ scopes: Record metrics: MetricsTracker }) { - { - const app = new Hono() - app.use(useSentry) - // TODO: Add useOnError middleware rather than handling errors in each handler - // app.onError(useOnError) - /** - * OAuth Authorization Endpoint - * - * This route initiates the Cloudflare OAuth flow when a user wants to log in. - * It creates a random state parameter to prevent CSRF attacks and stores the - * original OAuth request information in KV storage for later retrieval. - * Then it redirects the user to Cloudflare's authorization page with the appropriate - * parameters so the user can authenticate and grant permissions. - */ - app.get(`/oauth/authorize`, async (c) => { - try { - const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw) - oauthReqInfo.scope = Object.keys(scopes) - if (!oauthReqInfo.clientId) { - return c.text('Invalid request', 400) - } - const res = await getAuthorizationURL({ - client_id: c.env.CLOUDFLARE_CLIENT_ID, - redirect_uri: new URL('/oauth/callback', c.req.url).href, - state: oauthReqInfo, - scopes, + const app = new Hono() + app.use(useSentry) + + /** + * GET /oauth/authorize - Show consent dialog or redirect if approved + */ + app.get(`/oauth/authorize`, async (c) => { + try { + const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw) + oauthReqInfo.scope = Object.keys(scopes) + + if (!oauthReqInfo.clientId) { + return new OAuthError('invalid_request', 'Missing client_id parameter', 400).toResponse() + } + + // Check if client was previously approved (skip consent if so) + if ( + await clientIdAlreadyApproved( + c.req.raw, + oauthReqInfo.clientId, + c.env.MCP_COOKIE_ENCRYPTION_KEY + ) + ) { + // Client already approved - create state and redirect immediately + const { codeChallenge, codeVerifier } = await generatePKCECodes() + const stateToken = await createOAuthState(oauthReqInfo, c.env.OAUTH_KV, codeVerifier) + const { setCookie: sessionCookie } = await bindStateToSession(stateToken) + + return redirectToCloudflare(c, oauthReqInfo, stateToken, codeChallenge, scopes, { + 'Set-Cookie': sessionCookie, }) + } - return Response.redirect(res.authUrl, 302) - } catch (e) { - c.var.sentry?.recordError(e) - if (e instanceof Error) { - metrics.logEvent( - new AuthUser({ - errorMessage: `Authorize Error: ${e.name}: ${e.message}`, - }) - ) - } - if (e instanceof McpError) { - return c.text(e.message, { status: e.code }) - } - console.error(e) - return c.text('Internal Error', 500) + // Client not approved - show consent dialog + const { token: csrfToken, setCookie: csrfCookie } = generateCSRFProtection() + + // Render approval dialog + const response = renderApprovalDialog(c.req.raw, { + client: await c.env.OAUTH_PROVIDER.lookupClient(oauthReqInfo.clientId), + server: { + name: c.env.MCP_SERVER_NAME || 'Cloudflare MCP Server', + logo: 'https://images.mcp.cloudflare.com/mcp.svg', + description: + c.env.MCP_SERVER_DESCRIPTION || 'This server uses Cloudflare for authentication.', + }, + state: { + oauthReqInfo, + }, + csrfToken, + setCookie: csrfCookie, + }) + + return response + } catch (e) { + c.var.sentry?.recordError(e) + let message: string | undefined + if (e instanceof Error) { + message = `${e.name}: ${e.message}` + } else if (typeof e === 'string') { + message = e + } else { + message = 'Unknown error' } - }) + metrics.logEvent( + new AuthUser({ + errorMessage: `Authorize Error: ${message}`, + }) + ) + if (e instanceof OAuthError) { + return e.toResponse() + } + if (e instanceof McpError) { + return c.text(e.message, { status: e.code }) + } + console.error(e) + return c.text('Internal Error', 500) + } + }) - /** - * OAuth Callback Endpoint - * - * This route handles the callback from Cloudflare after user authentication. - * It exchanges the temporary code for an access token, then stores some - * user metadata & the auth token as part of the 'props' on the token passed - * down to the client. It ends by redirecting the client back to _its_ callback URL - */ - app.get(`/oauth/callback`, zValidator('query', AuthQuery), async (c) => { - try { - const { state, code } = c.req.valid('query') - const oauthReqInfo = AuthRequestSchemaWithExtraParams.parse(JSON.parse(atob(state))) - // Get the oathReqInfo out of KV - if (!oauthReqInfo.clientId) { - throw new McpError('Invalid State', 400) - } - - const [{ accessToken, refreshToken, user, accounts }] = await Promise.all([ - getTokenAndUserDetails(c, code, oauthReqInfo.codeVerifier), - c.env.OAUTH_PROVIDER.createClient({ - clientId: oauthReqInfo.clientId, - tokenEndpointAuthMethod: 'none', - }), - ]) - - // TODO: Implement auth restriction in staging - // if ( - // !user.email.endsWith("@cloudflare.com") && - // !(c.env.PERMITTED_USERS ?? []).includes(user.email) - // ) { - // throw new McpError( - // `This user ${user.email} is not allowed to access this restricted MCP server`, - // 401, - // ); - // } - - // Return back to the MCP client a new token - const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({ - request: oauthReqInfo, - userId: user.id, - metadata: { - label: user.email, - }, - scope: oauthReqInfo.scope, - // This will be available on this.props inside CASBMCP - props: { - user, - accounts, - accessToken, - refreshToken, - }, + /** + * POST /oauth/authorize - Handle consent form submission + */ + app.post(`/oauth/authorize`, async (c) => { + try { + // Validates CSRF token, extracts state, and generates approved client cookie + const { state, headers } = await parseRedirectApproval( + c.req.raw, + c.env.MCP_COOKIE_ENCRYPTION_KEY + ) + + if (!state.oauthReqInfo) { + return new OAuthError( + 'invalid_request', + 'Missing OAuth request info in state', + 400 + ).toResponse() + } + + const oauthReqInfo = state.oauthReqInfo as AuthRequest + + // Create OAuth state in KV and bind to session + const { codeChallenge, codeVerifier } = await generatePKCECodes() + const stateToken = await createOAuthState(oauthReqInfo, c.env.OAUTH_KV, codeVerifier) + const { setCookie: sessionCookie } = await bindStateToSession(stateToken) + + // Build redirect response + const redirectResponse = await redirectToCloudflare( + c, + oauthReqInfo, + stateToken, + codeChallenge, + scopes + ) + + // Add both cookies: approved client cookie (if present) and session binding cookie + // Note: We must use append() for multiple Set-Cookie headers, not combine with commas + if (headers['Set-Cookie']) { + redirectResponse.headers.append('Set-Cookie', headers['Set-Cookie']) + } + redirectResponse.headers.append('Set-Cookie', sessionCookie) + + return redirectResponse + } catch (e) { + c.var.sentry?.recordError(e) + let message: string | undefined + if (e instanceof Error) { + message = `${e.name}: ${e.message}` + } else if (typeof e === 'string') { + message = e + } else { + message = 'Unknown error' + } + metrics.logEvent( + new AuthUser({ + errorMessage: `Authorize POST Error: ${message}`, }) + ) + if (e instanceof OAuthError) { + return e.toResponse() + } + console.error(e) + return c.text('Internal Error', 500) + } + }) - metrics.logEvent( - new AuthUser({ - userId: user.id, - }) - ) + /** + * GET /oauth/callback - Handle OAuth callback from Cloudflare + */ + app.get(`/oauth/callback`, zValidator('query', AuthQuery), async (c) => { + try { + const { code } = c.req.valid('query') + + // Validate state using dual validation (KV + session cookie) + const { oauthReqInfo, codeVerifier, clearCookie } = await validateOAuthState( + c.req.raw, + c.env.OAUTH_KV + ) - return Response.redirect(redirectTo, 302) - } catch (e) { - c.var.sentry?.recordError(e) - if (e instanceof Error) { - console.error(e) - metrics.logEvent( - new AuthUser({ - errorMessage: `Callback Error: ${e.name}: ${e.message}`, - }) - ) - } - if (e instanceof McpError) { - return c.text(e.message, { status: e.code }) - } - return c.text('Internal Error', 500) + if (!oauthReqInfo.clientId) { + return new OAuthError('invalid_request', 'Invalid OAuth request info', 400).toResponse() } - }) - return app - } + // Exchange code for tokens and get user details + const [{ accessToken, refreshToken, user, accounts }] = await Promise.all([ + getTokenAndUserDetails(c, code, codeVerifier), // use codeVerifier from KV + c.env.OAUTH_PROVIDER.createClient({ + clientId: oauthReqInfo.clientId, + tokenEndpointAuthMethod: 'none', + }), + ]) + + // Complete authorization and issue token to MCP client + const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({ + request: oauthReqInfo, + userId: user.id, + metadata: { + label: user.email, + }, + scope: oauthReqInfo.scope, + props: { + type: 'user_token', + user, + accounts, + accessToken, + refreshToken, + } satisfies AuthProps, + }) + + metrics.logEvent( + new AuthUser({ + userId: user.id, + }) + ) + + // Redirect back to MCP client with cleared session cookie + return new Response(null, { + status: 302, + headers: { + Location: redirectTo, + 'Set-Cookie': clearCookie, + }, + }) + } catch (e) { + c.var.sentry?.recordError(e) + let message: string | undefined + if (e instanceof Error) { + console.error(e) + message = `${e.name}: ${e.message}` + } else if (typeof e === 'string') { + message = e + } else { + message = 'Unknown error' + } + metrics.logEvent( + new AuthUser({ + errorMessage: `Callback Error: ${message}`, + }) + ) + if (e instanceof OAuthError) { + return e.toResponse() + } + if (e instanceof McpError) { + return c.text(e.message, { status: e.code }) + } + return c.text('Internal Error', 500) + } + }) + + return app } diff --git a/packages/mcp-common/src/dev-mode.ts b/packages/mcp-common/src/dev-mode.ts deleted file mode 100644 index d176ad92..00000000 --- a/packages/mcp-common/src/dev-mode.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getUserAndAccounts } from './cloudflare-oauth-handler' - -import type { McpAgent } from 'agents/mcp' -import type { AuthProps } from './cloudflare-oauth-handler' - -interface RequiredEnv { - DEV_CLOUDFLARE_EMAIL: string - DEV_CLOUDFLARE_API_TOKEN: string -} - -export async function handleDevMode< - T extends typeof McpAgent>, ->(agent: T, req: Request, env: RequiredEnv, ctx: ExecutionContext) { - const { user, accounts } = await getUserAndAccounts(env.DEV_CLOUDFLARE_API_TOKEN, { - 'X-Auth-Email': env.DEV_CLOUDFLARE_EMAIL, - 'X-Auth-Key': env.DEV_CLOUDFLARE_API_TOKEN, - }) - ctx.props = { - accessToken: env.DEV_CLOUDFLARE_API_TOKEN, - user, - accounts, - } as AuthProps - return agent.mount('/sse').fetch(req, env, ctx) -} diff --git a/packages/mcp-common/src/durable-objects/user_details.ts b/packages/mcp-common/src/durable-objects/user_details.do.ts similarity index 100% rename from packages/mcp-common/src/durable-objects/user_details.ts rename to packages/mcp-common/src/durable-objects/user_details.do.ts diff --git a/packages/mcp-common/src/format.spec.ts b/packages/mcp-common/src/format.spec.ts new file mode 100644 index 00000000..00421510 --- /dev/null +++ b/packages/mcp-common/src/format.spec.ts @@ -0,0 +1,126 @@ +import { describe, expect, it } from 'vitest' + +import { fmt } from './format' + +describe('fmt', () => { + describe('trim()', () => { + it('should return an empty string for an empty input', () => { + expect(fmt.trim('')).toBe('') + }) + + it('should trim leading and trailing spaces', () => { + expect(fmt.trim(' hello ')).toBe('hello') + }) + + it('should trim leading and trailing newlines', () => { + expect(fmt.trim('\n\nhello\n\n')).toBe('hello') + }) + + it('should trim leading/trailing spaces and newlines from each line but not remove empty lines', () => { + const input = ` + line1 + line2 + + line3 + ` + const expected = `line1 +line2 + +line3` + expect(fmt.trim(input)).toBe(expected) + }) + + it('should handle a string that is already trimmed', () => { + expect(fmt.trim('hello\nworld')).toBe('hello\nworld') + }) + + it('should handle a string with only spaces', () => { + expect(fmt.trim(' ')).toBe('') + }) + + it('should handle a string with only newlines', () => { + expect(fmt.trim('\n\n\n')).toBe('') + }) + + it('should preserve empty lines from the middle', () => { + expect(fmt.trim('hello\n\nworld')).toBe('hello\n\nworld') + }) + }) + + describe('oneLine()', () => { + it('should return an empty string for an empty input', () => { + expect(fmt.oneLine('')).toBe('') + }) + + it('should convert a multi-line string to a single line', () => { + expect(fmt.oneLine('hello\nworld')).toBe('hello world') + }) + + it('should trim leading/trailing spaces and newlines before joining', () => { + expect(fmt.oneLine(' hello \n world \n')).toBe('hello world') + }) + + it('should remove empty lines before joining', () => { + expect(fmt.oneLine('hello\n\nworld')).toBe('hello world') + }) + + it('should handle a string that is already a single line', () => { + expect(fmt.oneLine('hello world')).toBe('hello world') + }) + + it('should handle a string with only spaces and newlines', () => { + expect(fmt.oneLine(' \n \n ')).toBe('') + }) + }) + + describe('asTSV()', () => { + it('should convert an empty array to an empty string', async () => { + expect(await fmt.asTSV([])).toBe('') + }) + + it('should convert an array of one object to a TSV string', async () => { + const data = [{ a: 1, b: 'hello' }] + expect(await fmt.asTSV(data)).toBe('a\tb\n1\thello') + }) + + it('should convert an array of multiple objects to a TSV string', async () => { + const data = [ + { a: 1, b: 'hello' }, + { a: 2, b: 'world' }, + ] + expect(await fmt.asTSV(data)).toBe('a\tb\n1\thello\n2\tworld') + }) + + it('should handle objects with different keys (using keys from the first object as headers)', async () => { + const data = [ + { a: 1, b: 'hello' }, + { a: 2, c: 'world' }, + ] + expect(await fmt.asTSV(data)).toBe('a\tb\n1\thello\n2\t') + expect(await fmt.asTSV(data)).toMatchInlineSnapshot(` + "a b + 1 hello + 2 " + `) + }) + + it('should handle values with tabs and newlines (fast-csv should quote them)', async () => { + const data = [{ name: 'John\tDoe', description: 'Line1\nLine2' }] + expect(await fmt.asTSV(data)).toBe('name\tdescription\n"John\tDoe"\t"Line1\nLine2"') + expect(await fmt.asTSV(data)).toMatchInlineSnapshot(` + "name description + "John Doe" "Line1 + Line2"" + `) + }) + + it('should handle values with quotes (fast-csv should escape them)', async () => { + const data = [{ name: 'James "Jim" Raynor' }] + expect(await fmt.asTSV(data)).toBe('name\n"James ""Jim"" Raynor"') + expect(await fmt.asTSV(data)).toMatchInlineSnapshot(` + "name + "James ""Jim"" Raynor"" + `) + }) + }) +}) diff --git a/packages/mcp-common/src/format.ts b/packages/mcp-common/src/format.ts new file mode 100644 index 00000000..a7c8e7fa --- /dev/null +++ b/packages/mcp-common/src/format.ts @@ -0,0 +1,35 @@ +import { writeToString } from '@fast-csv/format' + +/** + * A collection of formatting functions (think of it like Golang's `fmt` package) + */ +export const fmt = { + /** + * Trims all lines of a string. + * Useful for formatting tool instructions. + */ + trim: (str: string): string => + str + .trim() + .split('\n') + .map((line) => line.trim()) + .join('\n'), + + /** + * Converts a multi-line string into a single line. + * Useful for formatting tool instructions. + */ + oneLine: (str: string): string => + str + .trim() + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0) + .join(' '), + + /** + * Convert an array of objects to a string of tab-separated values (TSV). + * This is better than JSON for returning data to the model because it uses fewer tokens + */ + asTSV: (data: any[]): Promise => writeToString(data, { headers: true, delimiter: '\t' }), +} as const diff --git a/packages/mcp-common/src/get-props.ts b/packages/mcp-common/src/get-props.ts new file mode 100644 index 00000000..e69160fa --- /dev/null +++ b/packages/mcp-common/src/get-props.ts @@ -0,0 +1,9 @@ +/** + * Gets props from agent or throws if undefined + */ +export function getProps(agent: { props?: T }): T { + if (!agent.props) { + throw new Error('Props required') + } + return agent.props +} diff --git a/packages/mcp-common/src/utils/poll.ts b/packages/mcp-common/src/poll.ts similarity index 100% rename from packages/mcp-common/src/utils/poll.ts rename to packages/mcp-common/src/poll.ts diff --git a/packages/mcp-common/src/prompts/docs-ai-search.prompts.ts b/packages/mcp-common/src/prompts/docs-ai-search.prompts.ts new file mode 100644 index 00000000..594e071b --- /dev/null +++ b/packages/mcp-common/src/prompts/docs-ai-search.prompts.ts @@ -0,0 +1,27 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' + +/** + * Registers developer-platform-related prompts with the MCP server + * @param server The MCP server instance + */ +export function registerPrompts(server: McpServer) { + server.prompt( + 'workers-prompt-full', + 'Detailed prompt for generating Cloudflare Workers code (and other developer platform products) from https://developers.cloudflare.com/workers/prompt.txt', + async () => ({ + messages: [ + { + role: 'user', + content: { + type: 'text', + text: await ( + await fetch('https://developers.cloudflare.com/workers/prompt.txt', { + cf: { cacheEverything: true, cacheTtl: 3600 }, + }) + ).text(), + }, + }, + ], + }) + ) +} diff --git a/packages/mcp-common/src/prompts/docs-vectorize.prompts.ts b/packages/mcp-common/src/prompts/docs-vectorize.prompts.ts new file mode 100644 index 00000000..594e071b --- /dev/null +++ b/packages/mcp-common/src/prompts/docs-vectorize.prompts.ts @@ -0,0 +1,27 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' + +/** + * Registers developer-platform-related prompts with the MCP server + * @param server The MCP server instance + */ +export function registerPrompts(server: McpServer) { + server.prompt( + 'workers-prompt-full', + 'Detailed prompt for generating Cloudflare Workers code (and other developer platform products) from https://developers.cloudflare.com/workers/prompt.txt', + async () => ({ + messages: [ + { + role: 'user', + content: { + type: 'text', + text: await ( + await fetch('https://developers.cloudflare.com/workers/prompt.txt', { + cf: { cacheEverything: true, cacheTtl: 3600 }, + }) + ).text(), + }, + }, + ], + }) + ) +} diff --git a/packages/mcp-common/src/sentry.ts b/packages/mcp-common/src/sentry.ts index 64662c3c..543a1b2b 100644 --- a/packages/mcp-common/src/sentry.ts +++ b/packages/mcp-common/src/sentry.ts @@ -54,7 +54,7 @@ export interface BaseHonoContext { } } -function initSentry( +export function initSentry( env: T, ctx: SentryContext, req?: Request diff --git a/packages/mcp-common/src/tools/account.tools.ts b/packages/mcp-common/src/tools/account.tools.ts new file mode 100644 index 00000000..e144c7b1 --- /dev/null +++ b/packages/mcp-common/src/tools/account.tools.ts @@ -0,0 +1,118 @@ +import { z } from 'zod' + +import { handleAccountsList } from '../api/account.api' +import { getCloudflareClient } from '../cloudflare-api' +import { getProps } from '../get-props' + +import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' + +export function registerAccountTools(agent: CloudflareMcpAgent) { + // Tool to list all accounts + agent.server.tool( + 'accounts_list', + 'List all accounts in your Cloudflare account', + {}, + { + title: 'List accounts', + annotations: { + readOnlyHint: true, + }, + }, + async () => { + try { + const props = getProps(agent) + const results = await handleAccountsList({ + client: getCloudflareClient(props.accessToken), + }) + // Sort accounts by created_on date (newest first) + const accounts = results + // order by created_on desc ( newest first ) + .sort((a, b) => { + if (!a.created_on) return 1 + if (!b.created_on) return -1 + return new Date(b.created_on).getTime() - new Date(a.created_on).getTime() + }) + // Remove fields not needed by the LLM + .map((account) => { + return { + id: account.id, + name: account.name, + created_on: account.created_on, + } + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + accounts, + count: accounts.length, + }), + }, + ], + } + } catch (e) { + agent.server.recordError(e) + return { + content: [ + { + type: 'text', + text: `Error listing accounts: ${e instanceof Error && e.message}`, + }, + ], + } + } + } + ) + + // Only register set_active_account tool when user token is provided, as it doesn't make sense to expose + // this tool for account scoped tokens, given that they're scoped to a single account + if (getProps(agent).type === 'user_token') { + const activeAccountIdParam = z + .string() + .describe( + 'The accountId present in the users Cloudflare account, that should be the active accountId.' + ) + agent.server.tool( + 'set_active_account', + 'Set active account to be used for tool calls that require accountId', + { + activeAccountIdParam, + }, + { + title: 'Set active account', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, + async (params) => { + try { + const { activeAccountIdParam: activeAccountId } = params + await agent.setActiveAccountId(activeAccountId) + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + activeAccountId, + }), + }, + ], + } + } catch (e) { + agent.server.recordError(e) + return { + content: [ + { + type: 'text', + text: `Error setting activeAccountID: ${e instanceof Error && e.message}`, + }, + ], + } + } + } + ) + } +} diff --git a/packages/mcp-common/src/tools/account.ts b/packages/mcp-common/src/tools/account.ts deleted file mode 100644 index 24c7f925..00000000 --- a/packages/mcp-common/src/tools/account.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { z } from 'zod' - -import { handleAccountsList } from '../api/account' -import { getCloudflareClient } from '../cloudflare-api' - -import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' - -export function registerAccountTools(agent: CloudflareMcpAgent) { - // Tool to list all accounts - agent.server.tool('accounts_list', 'List all accounts in your Cloudflare account', async () => { - try { - const results = await handleAccountsList({ - client: getCloudflareClient(agent.props.accessToken), - }) - // Sort accounts by created_on date (newest first) - const accounts = results - // order by created_on desc ( newest first ) - .sort((a, b) => { - if (!a.created_on) return 1 - if (!b.created_on) return -1 - return new Date(b.created_on).getTime() - new Date(a.created_on).getTime() - }) - // Remove fields not needed by the LLM - .map((account) => { - return { - id: account.id, - name: account.name, - created_on: account.created_on, - } - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - accounts, - count: accounts.length, - }), - }, - ], - } - } catch (e) { - agent.server.recordError(e) - return { - content: [ - { - type: 'text', - text: `Error listing accounts: ${e instanceof Error && e.message}`, - }, - ], - } - } - }) - - const activeAccountIdParam = z - .string() - .describe( - 'The accountId present in the users Cloudflare account, that should be the active accountId.' - ) - agent.server.tool( - 'set_active_account', - 'Set active account to be used for tool calls that require accountId', - { - activeAccountIdParam, - }, - async (params) => { - try { - const { activeAccountIdParam: activeAccountId } = params - await agent.setActiveAccountId(activeAccountId) - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - activeAccountId, - }), - }, - ], - } - } catch (e) { - agent.server.recordError(e) - return { - content: [ - { - type: 'text', - text: `Error setting activeAccountID: ${e instanceof Error && e.message}`, - }, - ], - } - } - } - ) -} diff --git a/packages/mcp-common/src/tools/d1.ts b/packages/mcp-common/src/tools/d1.tools.ts similarity index 80% rename from packages/mcp-common/src/tools/d1.ts rename to packages/mcp-common/src/tools/d1.tools.ts index 0e8878fd..4af822a9 100644 --- a/packages/mcp-common/src/tools/d1.ts +++ b/packages/mcp-common/src/tools/d1.tools.ts @@ -2,14 +2,15 @@ import { z } from 'zod' import { getCloudflareClient } from '../cloudflare-api' import { MISSING_ACCOUNT_ID_RESPONSE } from '../constants' -import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' +import { getProps } from '../get-props' +import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' import { D1DatabaseNameParam, D1DatabasePrimaryLocationHintParam, D1DatabaseQueryParamsParam, D1DatabaseQuerySqlParam, -} from '../types/d1' -import { PaginationPageParam, PaginationPerPageParam } from '../types/shared' +} from '../types/d1.types' +import { PaginationPageParam, PaginationPerPageParam } from '../types/shared.types' export function registerD1Tools(agent: CloudflareMcpAgent) { agent.server.tool( @@ -20,13 +21,20 @@ export function registerD1Tools(agent: CloudflareMcpAgent) { page: PaginationPageParam, per_page: PaginationPerPageParam, }, + { + title: 'List D1 databases', + annotations: { + readOnlyHint: true, + }, + }, async ({ name, page, per_page }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const listResponse = await client.d1.database.list({ account_id, name: name ?? undefined, @@ -65,13 +73,21 @@ export function registerD1Tools(agent: CloudflareMcpAgent) { name: D1DatabaseNameParam, primary_location_hint: D1DatabasePrimaryLocationHintParam.nullable().optional(), }, + { + title: 'Create D1 database', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, async ({ name, primary_location_hint }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const d1Database = await client.d1.database.create({ account_id, name, @@ -103,13 +119,21 @@ export function registerD1Tools(agent: CloudflareMcpAgent) { 'd1_database_delete', 'Delete a d1 database in your Cloudflare account', { database_id: z.string() }, + { + title: 'Delete D1 database', + annotations: { + readOnlyHint: false, + destructiveHint: true, + }, + }, async ({ database_id }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const deleteResponse = await client.d1.database.delete(database_id, { account_id, }) @@ -138,13 +162,20 @@ export function registerD1Tools(agent: CloudflareMcpAgent) { 'd1_database_get', 'Get a D1 database in your Cloudflare account', { database_id: z.string() }, + { + title: 'Get D1 database', + annotations: { + readOnlyHint: true, + }, + }, async ({ database_id }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const d1Database = await client.d1.database.get(database_id, { account_id, }) @@ -178,13 +209,21 @@ export function registerD1Tools(agent: CloudflareMcpAgent) { sql: D1DatabaseQuerySqlParam, params: D1DatabaseQueryParamsParam.nullable(), }, + { + title: 'Query D1 database', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, async ({ database_id, sql, params }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const queryResult = await client.d1.database.query(database_id, { account_id, sql, diff --git a/packages/mcp-common/src/tools/docs-ai-search.tools.ts b/packages/mcp-common/src/tools/docs-ai-search.tools.ts new file mode 100644 index 00000000..1f882a96 --- /dev/null +++ b/packages/mcp-common/src/tools/docs-ai-search.tools.ts @@ -0,0 +1,218 @@ +import { z } from 'zod' + +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' + +interface RequiredEnv { + AI: Ai +} + +// Zod schema for AI Search response validation +const AiSearchResponseSchema = z.object({ + object: z.string(), + search_query: z.string(), + data: z.array( + z.object({ + file_id: z.string(), + filename: z.string(), + score: z.number(), + attributes: z + .object({ + modified_date: z.number().optional(), + folder: z.string().optional(), + }) + .catchall(z.any()), + content: z.array( + z.object({ + id: z.string(), + type: z.string(), + text: z.string(), + }) + ), + }) + ), + has_more: z.boolean(), + next_page: z.string().nullable(), +}) + +/** + * Registers the docs search tool with the MCP server using AI Search + * @param server The MCP server instance + */ +export function registerDocsTools(server: McpServer, env: RequiredEnv) { + server.tool( + 'search_cloudflare_documentation', + `Search the Cloudflare documentation. + + This tool should be used to answer any question about Cloudflare products or features, including: + - Workers, Pages, R2, Images, Stream, D1, Durable Objects, KV, Workflows, Hyperdrive, Queues + - AI Search, Workers AI, Vectorize, AI Gateway, Browser Rendering + - Zero Trust, Access, Tunnel, Gateway, Browser Isolation, WARP, DDOS, Magic Transit, Magic WAN + - CDN, Cache, DNS, Zaraz, Argo, Rulesets, Terraform, Account and Billing + + Results are returned as semantically similar chunks to the query. + `, + { + query: z.string(), + }, + { + title: 'Search Cloudflare docs', + annotations: { + readOnlyHint: true, + }, + }, + async ({ query }) => { + const results = await queryAiSearch(env.AI, query) + const resultsAsXml = results + .map((result) => { + return ` +${result.url} +${result.title} + +${result.text} + +` + }) + .join('\n') + return { + content: [{ type: 'text', text: resultsAsXml }], + } + } + ) + + // Note: this is a tool instead of a prompt because + // prompt support is much less common than tools. + server.tool( + 'migrate_pages_to_workers_guide', + `ALWAYS read this guide before migrating Pages projects to Workers.`, + {}, + { + title: 'Get Pages migration guide', + annotations: { + readOnlyHint: true, + }, + }, + async () => { + const res = await fetch( + 'https://developers.cloudflare.com/workers/prompts/pages-to-workers.txt', + { + cf: { cacheEverything: true, cacheTtl: 3600 }, + } + ) + + if (!res.ok) { + return { + content: [{ type: 'text', text: 'Error: Failed to fetch guide. Please try again.' }], + } + } + + return { + content: [ + { + type: 'text', + text: await res.text(), + }, + ], + } + } + ) +} + +async function queryAiSearch(ai: Ai, query: string) { + const rawResponse = await doWithRetries(() => + ai.autorag('docs-mcp-rag').search({ + query, + }) + ) + + // Parse and validate the response using Zod + const response = AiSearchResponseSchema.parse(rawResponse) + + return response.data.map((item) => ({ + similarity: item.score, + id: item.file_id, + url: sourceToUrl(item.filename), + title: extractTitle(item.filename), + text: item.content.map((c) => c.text).join('\n'), + })) +} + +function sourceToUrl(filename: string): string { + // Convert filename to URL format + // Example: "workers/configuration/index.md" -> "https://developers.cloudflare.com/workers/configuration/" + return ( + 'https://developers.cloudflare.com/' + + filename.replace(/index\.mdx?$/, '').replace(/\.mdx?$/, '') + ) +} + +function extractTitle(filename: string): string { + // Extract a reasonable title from the filename + // Example: "workers/configuration/index.md" -> "Configuration" + const parts = filename.replace(/\.mdx?$/, '').split('/') + const lastPart = parts[parts.length - 1] + + if (lastPart === 'index') { + // Use the parent directory name if filename is index + return parts[parts.length - 2] || 'Documentation' + } + + // Convert kebab-case or snake_case to title case + return lastPart.replace(/[-_]/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()) +} + +/** + * Retries an action with exponential backoff, only for retryable errors + * @template T + * @param {() => Promise} action + */ +async function doWithRetries(action: () => Promise) { + const NUM_RETRIES = 5 + const INIT_RETRY_MS = 100 + + for (let i = 0; i <= NUM_RETRIES; i++) { + try { + return await action() + } catch (e) { + // Check if error is retryable (system errors, not user errors) + const isRetryable = isRetryableError(e) + + console.error(`AI Search attempt ${i + 1} failed:`, e) + + if (!isRetryable || i === NUM_RETRIES) { + throw e + } + + // Exponential backoff with jitter + const delay = Math.random() * INIT_RETRY_MS * Math.pow(2, i) + await scheduler.wait(delay) + } + } + // Should never reach here – last loop iteration should throw + throw new Error('An unknown error occurred') +} + +/** + * Determines if an error is retryable based on error type and status + */ +function isRetryableError(error: unknown): boolean { + // Handle HTTP errors from fetch-like responses + if (error && typeof error === 'object' && 'status' in error) { + const status = (error as { status: number }).status + // Retry server errors (5xx) and rate limits (429), not client errors (4xx) + return status >= 500 || status === 429 + } + + // Handle network errors, timeouts, etc. + if (error instanceof Error) { + const errorMessage = error.message.toLowerCase() + return ( + errorMessage.includes('timeout') || + errorMessage.includes('network') || + errorMessage.includes('connection') || + errorMessage.includes('fetch') + ) + } + + // Default to retryable for unknown errors (conservative approach) + return true +} diff --git a/apps/docs-vectorize/src/tools/docs.ts b/packages/mcp-common/src/tools/docs-vectorize.tools.ts similarity index 57% rename from apps/docs-vectorize/src/tools/docs.ts rename to packages/mcp-common/src/tools/docs-vectorize.tools.ts index 717e0b91..3444d2dd 100644 --- a/apps/docs-vectorize/src/tools/docs.ts +++ b/packages/mcp-common/src/tools/docs-vectorize.tools.ts @@ -1,23 +1,27 @@ import { z } from 'zod' -import type { CloudflareDocumentationMCP } from '../index' +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' + +interface RequiredEnv { + AI: Ai + VECTORIZE: VectorizeIndex +} // Always return 10 results for simplicity, don't make it configurable const TOP_K = 10 /** * Registers the docs search tool with the MCP server - * @param agent The MCP server instance + * @param server The MCP server instance */ -export function registerDocsTools(agent: CloudflareDocumentationMCP) { - // Register the worker logs analysis tool by worker name - agent.server.tool( +export function registerDocsTools(server: McpServer, env: RequiredEnv) { + server.tool( 'search_cloudflare_documentation', `Search the Cloudflare documentation. This tool should be used to answer any question about Cloudflare products or features, including: - Workers, Pages, R2, Images, Stream, D1, Durable Objects, KV, Workflows, Hyperdrive, Queues - - AutoRAG, Workers AI, Vectorize, AI Gateway, Browser Rendering + - AI Search, Workers AI, Vectorize, AI Gateway, Browser Rendering - Zero Trust, Access, Tunnel, Gateway, Browser Isolation, WARP, DDOS, Magic Transit, Magic WAN - CDN, Cache, DNS, Zaraz, Argo, Rulesets, Terraform, Account and Billing @@ -26,12 +30,19 @@ export function registerDocsTools(agent: CloudflareDocumentationMCP) { { query: z.string(), }, + { + title: 'Search Cloudflare docs', + annotations: { + readOnlyHint: true, + }, + }, async ({ query }) => { - const results = await queryVectorize(agent.env.AI, agent.env.VECTORIZE, query, TOP_K) + const results = await queryVectorize(env.AI, env.VECTORIZE, query, TOP_K) const resultsAsXml = results .map((result) => { return ` ${result.url} +${result.title} ${result.text} @@ -43,13 +54,48 @@ ${result.text} } } ) + + // Note: this is a tool instead of a prompt because + // prompt support is much less common than tools. + server.tool( + 'migrate_pages_to_workers_guide', + `ALWAYS read this guide before migrating Pages projects to Workers.`, + {}, + { + title: 'Get Pages migration guide', + annotations: { + readOnlyHint: true, + }, + }, + async () => { + const res = await fetch( + 'https://developers.cloudflare.com/workers/prompts/pages-to-workers.txt', + { + cf: { cacheEverything: true, cacheTtl: 3600 }, + } + ) + + if (!res.ok) { + return { + content: [{ type: 'text', text: 'Error: Failed to fetch guide. Please try again.' }], + } + } + + return { + content: [ + { + type: 'text', + text: await res.text(), + }, + ], + } + } + ) } async function queryVectorize(ai: Ai, vectorizeIndex: VectorizeIndex, query: string, topK: number) { - // Recommendation from: https://huggingface.co/BAAI/bge-base-en-v1.5#model-list - const [queryEmbedding] = await getEmbeddings(ai, [ - 'Represent this sentence for searching relevant passages: ' + query, - ]) + // Recommendation from: https://ai.google.dev/gemma/docs/embeddinggemma/model_card#prompt_instructions + const [queryEmbedding] = await getEmbeddings(ai, ['task: search result | query: ' + query]) const { matches } = await vectorizeIndex.query(queryEmbedding, { topK, @@ -61,6 +107,7 @@ async function queryVectorize(ai: Ai, vectorizeIndex: VectorizeIndex, query: str similarity: Math.min(match.score, 1), id: match.id, url: sourceToUrl(String(match.metadata?.filePath ?? '')), + title: String(match.metadata?.title ?? ''), text: String(match.metadata?.text ?? ''), })) } @@ -76,15 +123,15 @@ function sourceToUrl(path: string) { ) } -async function getEmbeddings(ai: Ai, strings: string[]) { +async function getEmbeddings(ai: Ai, strings: string[]): Promise { const response = await doWithRetries(() => - ai.run('@cf/baai/bge-base-en-v1.5', { + // @ts-expect-error embeddinggemma not in types yet + ai.run('@cf/google/embeddinggemma-300m', { text: strings, - // @ts-expect-error pooling not in types yet - pooling: 'cls', }) ) + // @ts-expect-error embeddinggemma not in types yet return response.data } diff --git a/packages/mcp-common/src/tools/hyperdrive.ts b/packages/mcp-common/src/tools/hyperdrive.tools.ts similarity index 90% rename from packages/mcp-common/src/tools/hyperdrive.ts rename to packages/mcp-common/src/tools/hyperdrive.tools.ts index 14d3baa8..eaab83d8 100644 --- a/packages/mcp-common/src/tools/hyperdrive.ts +++ b/packages/mcp-common/src/tools/hyperdrive.tools.ts @@ -1,6 +1,7 @@ import { getCloudflareClient } from '../cloudflare-api' import { MISSING_ACCOUNT_ID_RESPONSE } from '../constants' -import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' +import { getProps } from '../get-props' +import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' import { HyperdriveCachingDisabledSchema, HyperdriveCachingMaxAgeSchema, @@ -13,11 +14,10 @@ import { HyperdriveListParamPerPageSchema, HyperdriveOriginDatabaseSchema, HyperdriveOriginHostSchema, - HyperdriveOriginPasswordSchema, HyperdriveOriginPortSchema, HyperdriveOriginSchemeSchema, HyperdriveOriginUserSchema, -} from '../types/hyperdrive' +} from '../types/hyperdrive.types' export const HYPERDRIVE_TOOLS = { hyperdrive_configs_list: 'hyperdrive_configs_list', @@ -44,13 +44,20 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { order: HyperdriveListParamOrderSchema.nullable(), direction: HyperdriveListParamDirectionSchema.nullable(), }, + { + title: 'List Hyperdrive configs', + annotations: { + readOnlyHint: true, + }, + }, async ({ page, per_page, order, direction }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const response = await client.hyperdrive.configs.list({ account_id, ...(page && { page }), @@ -128,7 +135,7 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { // if (caching_stale_while_revalidate !== undefined) // caching.stale_while_revalidate = caching_stale_while_revalidate - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const hyperdriveConfig = await client.hyperdrive.configs.create({ // account_id, // name, @@ -165,13 +172,21 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { { hyperdrive_id: HyperdriveConfigIdSchema, }, + { + title: 'Delete Hyperdrive config', + annotations: { + readOnlyHint: false, + destructiveHint: true, + }, + }, async ({ hyperdrive_id }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) await client.hyperdrive.configs.delete(hyperdrive_id, { account_id }) return { content: [ @@ -203,13 +218,20 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { { hyperdrive_id: HyperdriveConfigIdSchema, }, + { + title: 'Get Hyperdrive config', + annotations: { + readOnlyHint: true, + }, + }, async ({ hyperdrive_id }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const hyperdriveConfig = await client.hyperdrive.configs.get(hyperdrive_id, { account_id, }) @@ -253,6 +275,13 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { caching_stale_while_revalidate: HyperdriveCachingStaleWhileRevalidateSchema.optional().nullable(), }, + { + title: 'Edit Hyperdrive config', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, async ({ hyperdrive_id, name, @@ -270,6 +299,7 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { return MISSING_ACCOUNT_ID_RESPONSE } try { + const props = getProps(agent) const originPatch: Record = {} if (database) originPatch.database = database if (host) originPatch.host = host @@ -299,7 +329,7 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { } } - const client = getCloudflareClient(agent.props.accessToken) + const client = getCloudflareClient(props.accessToken) const updatedConfig = await client.hyperdrive.configs.edit(hyperdrive_id, { account_id, ...editData, diff --git a/packages/mcp-common/src/tools/kv_namespace.ts b/packages/mcp-common/src/tools/kv_namespace.tools.ts similarity index 82% rename from packages/mcp-common/src/tools/kv_namespace.ts rename to packages/mcp-common/src/tools/kv_namespace.tools.ts index 39285d19..fca84287 100644 --- a/packages/mcp-common/src/tools/kv_namespace.ts +++ b/packages/mcp-common/src/tools/kv_namespace.tools.ts @@ -1,11 +1,12 @@ import { getCloudflareClient } from '../cloudflare-api' import { MISSING_ACCOUNT_ID_RESPONSE } from '../constants' -import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' +import { getProps } from '../get-props' +import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' import { KvNamespaceIdSchema, KvNamespacesListParamsSchema, KvNamespaceTitleSchema, -} from '../types/kv_namespace' +} from '../types/kv_namespace.types' export const KV_NAMESPACE_TOOLS = { kv_namespaces_list: 'kv_namespaces_list', @@ -22,20 +23,27 @@ export function registerKVTools(agent: CloudflareMcpAgent) { agent.server.tool( KV_NAMESPACE_TOOLS.kv_namespaces_list, ` - List all of the kv namespaces in your Cloudflare account. + List all of the kv namespaces in your Cloudflare account. Use this tool when you need to list all of the kv namespaces in your Cloudflare account. Returns a list of kv namespaces with the following properties: - id: The id of the kv namespace. - title: The title of the kv namespace. `, { params: KvNamespacesListParamsSchema.optional() }, + { + title: 'List KV namespaces', + annotations: { + readOnlyHint: true, + }, + }, async ({ params }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const response = await client.kv.namespaces.list({ account_id, ...params, @@ -80,13 +88,21 @@ export function registerKVTools(agent: CloudflareMcpAgent) { { title: KvNamespaceTitleSchema, }, + { + title: 'Create KV namespace', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, async ({ title }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const namespace = await client.kv.namespaces.create({ account_id, title }) return { content: [ @@ -118,13 +134,21 @@ export function registerKVTools(agent: CloudflareMcpAgent) { { namespace_id: KvNamespaceIdSchema, }, + { + title: 'Delete KV namespace', + annotations: { + readOnlyHint: false, + destructiveHint: true, + }, + }, async ({ namespace_id }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const result = await client.kv.namespaces.delete(namespace_id, { account_id }) return { content: [ @@ -163,13 +187,20 @@ export function registerKVTools(agent: CloudflareMcpAgent) { { namespace_id: KvNamespaceIdSchema, }, + { + title: 'Get KV namespace', + annotations: { + readOnlyHint: true, + }, + }, async ({ namespace_id }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const namespace = await client.kv.namespaces.get(namespace_id, { account_id }) return { content: [ @@ -202,13 +233,21 @@ export function registerKVTools(agent: CloudflareMcpAgent) { namespace_id: KvNamespaceIdSchema, title: KvNamespaceTitleSchema, }, + { + title: 'Update KV namespace', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, async ({ namespace_id, title }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const result = await client.kv.namespaces.update(namespace_id, { account_id, title, diff --git a/packages/mcp-common/src/tools/r2_bucket.ts b/packages/mcp-common/src/tools/r2_bucket.tools.ts similarity index 90% rename from packages/mcp-common/src/tools/r2_bucket.ts rename to packages/mcp-common/src/tools/r2_bucket.tools.ts index 84579f68..b440d1a2 100644 --- a/packages/mcp-common/src/tools/r2_bucket.ts +++ b/packages/mcp-common/src/tools/r2_bucket.tools.ts @@ -1,14 +1,15 @@ import { getCloudflareClient } from '../cloudflare-api' import { MISSING_ACCOUNT_ID_RESPONSE } from '../constants' -import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' +import { getProps } from '../get-props' +import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' import { BucketListCursorParam, BucketListDirectionParam, BucketListNameContainsParam, BucketListStartAfterParam, BucketNameSchema, -} from '../types/r2_bucket' -import { PaginationPerPageParam } from '../types/shared' +} from '../types/r2_bucket.types' +import { PaginationPerPageParam } from '../types/shared.types' export function registerR2BucketTools(agent: CloudflareMcpAgent) { agent.server.tool( @@ -21,13 +22,20 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { per_page: PaginationPerPageParam, start_after: BucketListStartAfterParam, }, + { + title: 'List R2 buckets', + annotations: { + readOnlyHint: true, + }, + }, async ({ cursor, direction, name_contains, per_page, start_after }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const listResponse = await client.r2.buckets.list({ account_id, cursor: cursor ?? undefined, @@ -65,13 +73,21 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { 'r2_bucket_create', 'Create a new r2 bucket in your Cloudflare account', { name: BucketNameSchema }, + { + title: 'Create R2 bucket', + annotations: { + readOnlyHint: false, + destructiveHint: false, + }, + }, async ({ name }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const bucket = await client.r2.buckets.create({ account_id, name, @@ -101,13 +117,20 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { 'r2_bucket_get', 'Get details about a specific R2 bucket', { name: BucketNameSchema }, + { + title: 'Get R2 bucket', + annotations: { + readOnlyHint: true, + }, + }, async ({ name }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const bucket = await client.r2.buckets.get(name, { account_id }) return { content: [ @@ -134,13 +157,21 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { 'r2_bucket_delete', 'Delete an R2 bucket', { name: BucketNameSchema }, + { + title: 'Delete R2 bucket', + annotations: { + readOnlyHint: false, + destructiveHint: true, + }, + }, async ({ name }) => { const account_id = await agent.getActiveAccountId() if (!account_id) { return MISSING_ACCOUNT_ID_RESPONSE } try { - const client = getCloudflareClient(agent.props.accessToken) + const props = getProps(agent) + const client = getCloudflareClient(props.accessToken) const result = await client.r2.buckets.delete(name, { account_id }) return { content: [ @@ -177,7 +208,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const cors = await client.r2.buckets.cors.get(name, { // account_id, // ...params, @@ -216,7 +247,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.cors.update(name, { // account_id, // ...cors_config, @@ -255,7 +286,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.cors.delete(name, { // account_id, // ...params, @@ -291,7 +322,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const domains = await client.r2.buckets.domains.custom.list(name, { account_id, ...params }) // return { // content: [ @@ -328,7 +359,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.domains.custom.get(name, domain, { // account_id, // ...params, @@ -364,7 +395,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.domains.custom.create(name, { // account_id, // ...params, @@ -404,7 +435,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.domains.custom.delete(name, domain, { // account_id, // ...params, @@ -444,7 +475,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.domains.custom.update(name, domain, { // account_id, // ...params, @@ -480,7 +511,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.eventNotifications.get(name, { // account_id, // ...params, @@ -520,7 +551,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.eventNotifications.update(name, queueId, { // account_id, // ...params, @@ -560,7 +591,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.eventNotifications.delete(name, queueId, { // account_id, // ...params, @@ -596,7 +627,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.locks.get(name, { account_id, ...params }) // return { // content: [ @@ -629,7 +660,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.locks.update(name, { account_id, ...params }) // return { // content: [ @@ -662,7 +693,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.temporaryCredentials.create({ // account_id, // ...params, @@ -694,7 +725,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.metrics.list({ account_id }) // return { // content: [ @@ -726,7 +757,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.sippy.get(bucketName, { account_id, ...params }) // console.log('sippy get result', result) // return { @@ -760,7 +791,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.sippy.update(bucketName, { account_id, ...params }) // return { // content: [ @@ -793,7 +824,7 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) { // return MISSING_ACCOUNT_ID_RESPONSE // } // try { - // const client = getCloudflareClient(agent.props.accessToken) + // const client = getCloudflareClient(props.accessToken) // const result = await client.r2.buckets.sippy.delete(bucketName, { account_id }) // return { // content: [ diff --git a/packages/mcp-common/src/tools/vectorize.ts b/packages/mcp-common/src/tools/vectorize.tools.ts similarity index 99% rename from packages/mcp-common/src/tools/vectorize.ts rename to packages/mcp-common/src/tools/vectorize.tools.ts index a99177ab..e8aa9fff 100644 --- a/packages/mcp-common/src/tools/vectorize.ts +++ b/packages/mcp-common/src/tools/vectorize.tools.ts @@ -8,9 +8,9 @@ import { VectorizeListOrderParam, VectorizeListPageParam, VectorizeListPerPageParam, -} from '../types/vectorize' +} from '../types/vectorize.types' -import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' +import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' export const VECTORIZE_TOOLS = { vectorize_index_create: 'vectorize_index_create', diff --git a/packages/mcp-common/src/tools/worker.tools.ts b/packages/mcp-common/src/tools/worker.tools.ts new file mode 100644 index 00000000..dbb307e5 --- /dev/null +++ b/packages/mcp-common/src/tools/worker.tools.ts @@ -0,0 +1,228 @@ +import { z } from 'zod' + +import { + handleGetWorkersService, + handleWorkerScriptDownload, + handleWorkersList, +} from '../api/workers.api' +import { getCloudflareClient } from '../cloudflare-api' +import { fmt } from '../format' +import { getProps } from '../get-props' + +import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' + +/** + * Registers the workers tools with the MCP server + * @param server The MCP server instance + * @param accountId Cloudflare account ID + * @param apiToken Cloudflare API token + */ +// Define the scriptName parameter schema +const workerNameParam = z.string().describe('The name of the worker script to retrieve') + +export function registerWorkersTools(agent: CloudflareMcpAgent) { + // Tool to list all workers + agent.server.tool( + 'workers_list', + fmt.trim(` + List all Workers in your Cloudflare account. + + If you only need details of a single Worker, use workers_get_worker. + `), + {}, + { + title: 'List Workers', + annotations: { + readOnlyHint: true, + destructiveHint: false, + }, + }, + async () => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const results = await handleWorkersList({ + client: getCloudflareClient(props.accessToken), + accountId, + }) + // Extract worker details and sort by created_on date (newest first) + const workers = results + .map((worker) => ({ + name: worker.id, + // The API client doesn't know tag exists. The tag is needed in other places such as Workers Builds + id: z.object({ tag: z.string() }).parse(worker), + modified_on: worker.modified_on || null, + created_on: worker.created_on || null, + })) + // order by created_on desc ( newest first ) + .sort((a, b) => { + if (!a.created_on) return 1 + if (!b.created_on) return -1 + return new Date(b.created_on).getTime() - new Date(a.created_on).getTime() + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ + workers, + count: workers.length, + }), + }, + ], + } + } catch (e) { + agent.server.recordError(e) + return { + content: [ + { + type: 'text', + text: `Error listing workers: ${e instanceof Error && e.message}`, + }, + ], + } + } + } + ) + + // Tool to get a specific worker's script details + agent.server.tool( + 'workers_get_worker', + 'Get the details of the Cloudflare Worker.', + { + scriptName: workerNameParam, + }, + { + title: 'Get Worker details', + annotations: { + readOnlyHint: true, + destructiveHint: false, + }, + }, + async (params) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const { scriptName } = params + const res = await handleGetWorkersService({ + apiToken: props.accessToken, + scriptName, + accountId, + }) + + if (!res.result) { + return { + content: [ + { + type: 'text', + text: 'Worker not found', + }, + ], + } + } + + return { + content: [ + { + type: 'text', + text: await fmt.asTSV([ + { + name: res.result.id, + id: res.result.default_environment.script_tag, + }, + ]), + }, + ], + } + } catch (e) { + agent.server.recordError(e) + return { + content: [ + { + type: 'text', + text: `Error retrieving worker script: ${e instanceof Error && e.message}`, + }, + ], + } + } + } + ) + + // Tool to get a specific worker's script content + agent.server.tool( + 'workers_get_worker_code', + 'Get the source code of a Cloudflare Worker. Note: This may be a bundled version of the worker.', + { scriptName: workerNameParam }, + { + title: 'Get Worker code', + annotations: { + readOnlyHint: true, + destructiveHint: false, + }, + }, + async (params) => { + const accountId = await agent.getActiveAccountId() + if (!accountId) { + return { + content: [ + { + type: 'text', + text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', + }, + ], + } + } + + try { + const props = getProps(agent) + const { scriptName } = params + const scriptContent = await handleWorkerScriptDownload({ + client: getCloudflareClient(props.accessToken), + scriptName, + accountId, + }) + return { + content: [ + { + type: 'text', + text: scriptContent, + }, + ], + } + } catch (e) { + agent.server.recordError(e) + return { + content: [ + { + type: 'text', + text: `Error retrieving worker script: ${e instanceof Error && e.message}`, + }, + ], + } + } + } + ) +} diff --git a/packages/mcp-common/src/tools/worker.ts b/packages/mcp-common/src/tools/worker.ts deleted file mode 100644 index 9da28521..00000000 --- a/packages/mcp-common/src/tools/worker.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { z } from 'zod' - -import { handleWorkerScriptDownload, handleWorkersList } from '../api/workers' -import { getCloudflareClient } from '../cloudflare-api' - -import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' - -/** - * Registers the workers tools with the MCP server - * @param server The MCP server instance - * @param accountId Cloudflare account ID - * @param apiToken Cloudflare API token - */ -// Define the scriptName parameter schema -const workerNameParam = z.string().describe('The name of the worker script to retrieve') - -export function registerWorkersTools(agent: CloudflareMcpAgent) { - // Tool to list all workers - agent.server.tool('workers_list', 'List all Workers in your Cloudflare account', async () => { - const accountId = await agent.getActiveAccountId() - if (!accountId) { - return { - content: [ - { - type: 'text', - text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', - }, - ], - } - } - try { - const results = await handleWorkersList({ - client: getCloudflareClient(agent.props.accessToken), - accountId, - }) - // Extract worker details and sort by created_on date (newest first) - const workers = results - .map((worker) => ({ - name: worker.id, - modified_on: worker.modified_on || null, - created_on: worker.created_on || null, - })) - // order by created_on desc ( newest first ) - .sort((a, b) => { - if (!a.created_on) return 1 - if (!b.created_on) return -1 - return new Date(b.created_on).getTime() - new Date(a.created_on).getTime() - }) - - return { - content: [ - { - type: 'text', - text: JSON.stringify({ - workers, - count: workers.length, - }), - }, - ], - } - } catch (e) { - agent.server.recordError(e) - return { - content: [ - { - type: 'text', - text: `Error listing workers: ${e instanceof Error && e.message}`, - }, - ], - } - } - }) - - // Tool to get a specific worker's script content - agent.server.tool( - 'worker_get_worker', - 'Get the source code of a Cloudflare Worker', - { scriptName: workerNameParam }, - async (params) => { - const accountId = await agent.getActiveAccountId() - if (!accountId) { - return { - content: [ - { - type: 'text', - text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', - }, - ], - } - } - try { - const { scriptName } = params - const scriptContent = await handleWorkerScriptDownload({ - client: getCloudflareClient(agent.props.accessToken), - scriptName, - accountId, - }) - return { - content: [ - { - type: 'text', - text: scriptContent, - }, - ], - } - } catch (e) { - agent.server.recordError(e) - return { - content: [ - { - type: 'text', - text: `Error retrieving worker script: ${e instanceof Error && e.message}`, - }, - ], - } - } - } - ) -} diff --git a/packages/mcp-common/src/tools/zone.ts b/packages/mcp-common/src/tools/zone.tools.ts similarity index 85% rename from packages/mcp-common/src/tools/zone.ts rename to packages/mcp-common/src/tools/zone.tools.ts index d4d11231..66422bc9 100644 --- a/packages/mcp-common/src/tools/zone.ts +++ b/packages/mcp-common/src/tools/zone.tools.ts @@ -1,8 +1,9 @@ import { z } from 'zod' -import { handleZonesList } from '../api/zone' +import { handleZonesList } from '../api/zone.api' import { getCloudflareClient } from '../cloudflare-api' -import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent' +import { getProps } from '../get-props' +import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' export function registerZoneTools(agent: CloudflareMcpAgent) { // Tool to list all zones under an account @@ -28,6 +29,13 @@ export function registerZoneTools(agent: CloudflareMcpAgent) { .default('desc') .describe('Direction to order results (asc, desc)'), }, + { + title: 'List zones', + annotations: { + readOnlyHint: true, + destructiveHint: false, + }, + }, async (params) => { const accountId = await agent.getActiveAccountId() if (!accountId) { @@ -42,10 +50,11 @@ export function registerZoneTools(agent: CloudflareMcpAgent) { } try { + const props = getProps(agent) const { page = 1, perPage = 50 } = params const zones = await handleZonesList({ - client: getCloudflareClient(agent.props.accessToken), + client: getCloudflareClient(props.accessToken), accountId, ...params, }) @@ -84,6 +93,13 @@ export function registerZoneTools(agent: CloudflareMcpAgent) { { zoneId: z.string().describe('The ID of the zone to get details for'), }, + { + title: 'Get zone details', + annotations: { + readOnlyHint: true, + destructiveHint: false, + }, + }, async (params) => { const accountId = await agent.getActiveAccountId() if (!accountId) { @@ -98,8 +114,9 @@ export function registerZoneTools(agent: CloudflareMcpAgent) { } try { + const props = getProps(agent) const { zoneId } = params - const client = getCloudflareClient(agent.props.accessToken) + const client = getCloudflareClient(props.accessToken) // Use the zones.get method to fetch a specific zone const response = await client.zones.get({ zone_id: zoneId }) diff --git a/packages/mcp-common/src/schemas/cf1-integrations.ts b/packages/mcp-common/src/types/cf1-integrations.types.ts similarity index 100% rename from packages/mcp-common/src/schemas/cf1-integrations.ts rename to packages/mcp-common/src/types/cf1-integrations.types.ts diff --git a/packages/mcp-common/src/types/cloudflare-mcp-agent.ts b/packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts similarity index 63% rename from packages/mcp-common/src/types/cloudflare-mcp-agent.ts rename to packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts index ca661e94..fdcc19c7 100644 --- a/packages/mcp-common/src/types/cloudflare-mcp-agent.ts +++ b/packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts @@ -1,16 +1,11 @@ import { type McpAgent } from 'agents/mcp' -import { type AccountSchema, type UserSchema } from '../cloudflare-oauth-handler' - +import type { AuthProps } from '../cloudflare-oauth-handler' import type { CloudflareMCPServer } from '../server' export type CloudflareMCPAgentState = { activeAccountId: string | null } -export type CloudflareMCPAgentProps = { - accessToken: string - user: UserSchema['result'] - accounts: AccountSchema['result'] -} +export type CloudflareMCPAgentProps = AuthProps // We omit server in this type, so that we can later use our own CloudflareMCPServer type ( which extends McpServer ) type McpAgentWithoutServer = Omit< @@ -18,8 +13,13 @@ type McpAgentWithoutServer = Omit< 'server' > -export interface CloudflareMcpAgent extends McpAgentWithoutServer { +export interface CloudflareMcpAgentNoAccount + extends McpAgentWithoutServer { server: CloudflareMCPServer +} + +export interface CloudflareMcpAgent + extends CloudflareMcpAgentNoAccount { setActiveAccountId(accountId: string): Promise getActiveAccountId(): Promise } diff --git a/packages/mcp-common/src/types/d1.ts b/packages/mcp-common/src/types/d1.types.ts similarity index 100% rename from packages/mcp-common/src/types/d1.ts rename to packages/mcp-common/src/types/d1.types.ts diff --git a/packages/mcp-common/src/types/hyperdrive.ts b/packages/mcp-common/src/types/hyperdrive.types.ts similarity index 100% rename from packages/mcp-common/src/types/hyperdrive.ts rename to packages/mcp-common/src/types/hyperdrive.types.ts diff --git a/packages/mcp-common/src/types/kv_namespace.ts b/packages/mcp-common/src/types/kv_namespace.types.ts similarity index 100% rename from packages/mcp-common/src/types/kv_namespace.ts rename to packages/mcp-common/src/types/kv_namespace.types.ts diff --git a/packages/mcp-common/src/types/r2_bucket.ts b/packages/mcp-common/src/types/r2_bucket.types.ts similarity index 100% rename from packages/mcp-common/src/types/r2_bucket.ts rename to packages/mcp-common/src/types/r2_bucket.types.ts diff --git a/packages/mcp-common/src/types/shared.ts b/packages/mcp-common/src/types/shared.types.ts similarity index 100% rename from packages/mcp-common/src/types/shared.ts rename to packages/mcp-common/src/types/shared.types.ts diff --git a/packages/mcp-common/src/types/tools.ts b/packages/mcp-common/src/types/tools.types.ts similarity index 100% rename from packages/mcp-common/src/types/tools.ts rename to packages/mcp-common/src/types/tools.types.ts diff --git a/packages/mcp-common/src/types/vectorize.ts b/packages/mcp-common/src/types/vectorize.types.ts similarity index 100% rename from packages/mcp-common/src/types/vectorize.ts rename to packages/mcp-common/src/types/vectorize.types.ts diff --git a/packages/mcp-common/src/types/workers-builds.types.ts b/packages/mcp-common/src/types/workers-builds.types.ts new file mode 100644 index 00000000..7520d4ac --- /dev/null +++ b/packages/mcp-common/src/types/workers-builds.types.ts @@ -0,0 +1,96 @@ +import { z } from 'zod' + +export type BuildDetails = z.infer +export const BuildDetails = z.object({ + // TODO: Maybe remove fields we don't need to reduce surface area of things we need to update + build_uuid: z.string(), + status: z.string(), + build_outcome: z.string().nullable(), + created_on: z.coerce.date(), + modified_on: z.coerce.date(), + initializing_on: z.coerce.date().nullable(), + running_on: z.coerce.date().nullable(), + stopped_on: z.coerce.date().nullable(), + trigger: z.object({ + trigger_uuid: z.string(), + external_script_id: z.string(), + trigger_name: z.string(), + build_command: z.string(), + deploy_command: z.string(), + root_directory: z.string(), + branch_includes: z.array(z.string()), + branch_excludes: z.array(z.string()), + path_includes: z.array(z.string()), + path_excludes: z.array(z.string()), + build_caching_enabled: z.boolean(), + created_on: z.coerce.date(), + modified_on: z.coerce.date(), + deleted_on: z.coerce.date().nullable(), + repo_connection: z.object({ + repo_connection_uuid: z.string(), + repo_id: z.string(), + repo_name: z.string(), + provider_type: z.string(), + provider_account_id: z.string(), + provider_account_name: z.string(), + created_on: z.coerce.date(), + modified_on: z.coerce.date(), + deleted_on: z.coerce.date().nullable(), + }), + }), + build_trigger_metadata: z.object({ + build_trigger_source: z.string(), + branch: z.string(), + commit_hash: z.string(), + commit_message: z.string(), + author: z.string(), + build_command: z.string(), + deploy_command: z.string(), + root_directory: z.string(), + build_token_uuid: z.string(), + environment_variables: z.record( + z.string(), + z.object({ + is_secret: z.boolean(), + created_on: z.coerce.date(), + value: z.string().nullable(), + }) + ), + repo_name: z.string(), + provider_account_name: z.string(), + provider_type: z.string(), + }), + pull_request: z.unknown(), +}) + +/** + * GET /builds/workers/:external_script_id/builds + */ +export type ListBuildsByScriptResult = z.infer +export const ListBuildsByScriptResult = z.array(BuildDetails) + +export type ListBuildsByScriptResultInfo = z.infer +export const ListBuildsByScriptResultInfo = z.object({ + next_page: z.boolean(), + page: z.number(), + per_page: z.number(), + count: z.number(), + total_count: z.number(), + total_pages: z.number(), +}) + +export type GetBuildResult = z.infer +export const GetBuildResult = BuildDetails + +export type LogLine = z.infer +export const LogLine = z.tuple([ + z.coerce.date().describe('line timestamp'), + z.string().describe('line message'), +]) + +export type GetBuildLogsResult = z.infer +export const GetBuildLogsResult = z.object({ + cursor: z.string().optional().describe('pagination cursor'), + truncated: z.boolean(), + lines: z.array(LogLine), +}) diff --git a/packages/mcp-common/src/types/workers-logs-schemas.ts b/packages/mcp-common/src/types/workers-logs.types.ts similarity index 83% rename from packages/mcp-common/src/types/workers-logs-schemas.ts rename to packages/mcp-common/src/types/workers-logs.types.ts index 89f5a7e3..1c49ae88 100644 --- a/packages/mcp-common/src/types/workers-logs-schemas.ts +++ b/packages/mcp-common/src/types/workers-logs.types.ts @@ -1,5 +1,7 @@ import { z } from 'zod' +import { nowISO, parseRelativeTime } from '../utils' + export const numericalOperations = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte'] as const export const queryOperations = [ @@ -150,27 +152,80 @@ export const zStatistics = z.object({ bytes_read: z.number(), }) -export const zTimeframe = z +export const zTimeframeAbsolute = z .object({ to: z.string(), from: z.string(), }) .describe( - `Timeframe for your query (ISO-8601 format). + `An absolute timeframe for your query (ISO-8601 format). - • Current server time: ${new Date()} + • Current server time: ${nowISO()} • Default: Last hour from current time • Maximum range: Last 7 days • Format: "YYYY-MM-DDTHH:MM:SSZ" (e.g., "2025-04-29T14:30:00Z") Examples: - - Last 30 minutes: from="2025-04-29T14:00:00Z", to="2025-04-29T14:30:00Z" - - Yesterday: from="2025-04-28T00:00:00Z", to="2025-04-29T00:00:00Z" + - Between April 1st and 5th: from="2025-04-01T00:00:00Z", to="2025-04-05T23:59:59Z" Note: Narrower timeframes provide faster responses and more specific results. Omit this parameter entirely to use the default (last hour).` ) +export const zTimeframeRelative = z + .object({ + reference: z.string(), + offset: z.string(), + }) + .describe( + `Relative timeframe for your query, composed of a reference time and an offset. + + • Current server time: ${nowISO()} + • Default: Last hour from current time + • Maximum range: Last 7 days + • Reference time format: "YYYY-MM-DDTHH:MM:SSZ" (ISO-8601) (e.g., "2025-04-29T14:30:00Z") + • Offset format: Must start with a '+' or '-' sign, which indicates whether the offset is in the past or future, followed by one or more time units (e.g., '+5d', '-2h', '+6h20m'). + Units: s (seconds), m (minutes), h (hours), d (days), w (weeks). + • You should not use a future looking offset in combination with the current server time as the reference time, as this will yield no results. (e.g. "the next 20 minutes") + + Examples: + - Last 30 minutes: reference="${nowISO()}", offset="-30m" + - Yesterday: reference="${nowISO()}", offset="-1d" + + Note: Narrower timeframes provide faster responses and more specific results. + Omit this parameter entirely to use the default (last hour).` + ) + .transform((val) => { + const referenceTime = new Date(val.reference).getTime() / 1000 + + if (isNaN(referenceTime)) { + throw new Error(`Invalid reference time: ${val.reference}`) + } + + const offsetSeconds = parseRelativeTime(val.offset) + + const from = new Date(Math.min(referenceTime + offsetSeconds, referenceTime) * 1000) + const to = new Date(Math.max(referenceTime + offsetSeconds, referenceTime) * 1000) + + return { + from: from.toISOString(), + to: to.toISOString(), + } + }) + +export const zTimeframe = z.union([zTimeframeAbsolute, zTimeframeRelative]).describe( + `Timeframe for your query, which can be either absolute or relative. + + • Absolute timeframe: Specify exact start and end times in ISO-8601 format (e.g., "2025-04-29T14:30:00Z"). + • Relative timeframe: Specify a reference time and an offset (e.g., reference="2025-04-29T14:30:00Z", offset="-30m"). + + Examples: + - Absolute: from="2025-04-01T00:00:00Z", to="2025-04-05T23:59:59Z" + - Relative: reference="2025-04-29T14:30:00Z", offset="-30m" + + Note: Narrower timeframes provide faster responses and more specific results.` +) + const zCloudflareMiniEventDetailsRequest = z.object({ url: z.string().optional(), method: z.string().optional(), diff --git a/packages/mcp-common/src/types/workers.types.ts b/packages/mcp-common/src/types/workers.types.ts new file mode 100644 index 00000000..26373041 --- /dev/null +++ b/packages/mcp-common/src/types/workers.types.ts @@ -0,0 +1,19 @@ +import { z } from 'zod' + +export type WorkersService = z.infer +export const WorkersService = z.object({ + id: z.string(), + default_environment: z.object({ + environment: z.string(), + script_tag: z.string(), + created_on: z.string(), + modified_on: z.string(), + script: z.object({ + created_on: z.string(), + modified_on: z.string(), + id: z.string(), + }), + }), + created_on: z.string(), + modified_on: z.string(), +}) diff --git a/packages/mcp-common/src/utils.spec.ts b/packages/mcp-common/src/utils.spec.ts new file mode 100644 index 00000000..46f5d972 --- /dev/null +++ b/packages/mcp-common/src/utils.spec.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest' + +import { nowISO, parseRelativeTime } from './utils' + +describe('parseRelativeTime', () => { + it('parses positive relative time correctly', () => { + expect(parseRelativeTime('+1h')).toBe(3600) + expect(parseRelativeTime('+2d')).toBe(172800) + expect(parseRelativeTime('+3w')).toBe(1814400) + }) + + it('parses negative relative time correctly', () => { + expect(parseRelativeTime('-1h')).toBe(-3600) + expect(parseRelativeTime('-2d')).toBe(-172800) + expect(parseRelativeTime('-3w')).toBe(-1814400) + }) + + it('parses mixed units correctly', () => { + expect(parseRelativeTime('+1h30m')).toBe(5400) + expect(parseRelativeTime('-2d6h')).toBe(-194400) + }) + + it('throws an error for invalid formats', () => { + expect(() => parseRelativeTime('1h')).toThrow() + expect(() => parseRelativeTime('+')).toThrow() + expect(() => parseRelativeTime('')).toThrow() + }) +}) + +describe('nowISO', () => { + it('returns the current time in ISO format without milliseconds', () => { + const isoRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/ + expect(nowISO()).toMatch(isoRegex) + }) +}) diff --git a/packages/mcp-common/src/utils.ts b/packages/mcp-common/src/utils.ts new file mode 100644 index 00000000..8032dd86 --- /dev/null +++ b/packages/mcp-common/src/utils.ts @@ -0,0 +1,40 @@ +/** + * Utility functions for common operations + */ + +/** + * Parse a relative time string into seconds + */ +export function parseRelativeTime(input: string): number { + const units = { s: 1, m: 60, h: 3600, d: 86400, w: 604800 } as const + + const cleanedInput = input.replace(/\s+/g, '').toLowerCase() + if (!/^[+-](?:\d+[smhdw]){1,}$/.test(cleanedInput)) { + throw new Error(`Invalid relative time format: ${input}`) + } + + const sign = cleanedInput.startsWith('-') ? -1 : 1 + + const timeStr = cleanedInput.slice(1) // Remove the sign + const matches = timeStr.match(/\d+[smhdw]/g) + + if (!matches) { + throw new Error(`No matches found while parsing relative time: ${timeStr}`) + } + + const seconds = matches.reduce((total, match) => { + const value = parseInt(match) + const unit = match.slice(-1) as keyof typeof units + + return total + value * units[unit] + }, 0) + + return sign * seconds +} + +/** + * Get the current time as an ISO string without milliseconds + */ +export function nowISO(): string { + return new Date().toISOString().split('.')[0] + 'Z' +} diff --git a/packages/mcp-common/src/v4-api.ts b/packages/mcp-common/src/v4-api.ts index 135c56b1..6748c68b 100644 --- a/packages/mcp-common/src/v4-api.ts +++ b/packages/mcp-common/src/v4-api.ts @@ -8,17 +8,49 @@ const V4ErrorSchema = z.array( }) ) -export const V4Schema = ( - resultType: TResultType -): z.ZodObject<{ +export type V4Schema = z.ZodObject<{ result: z.ZodNullable success: z.ZodBoolean errors: V4ErrorSchema messages: z.ZodArray -}> => - z.object({ - result: resultType.nullable(), - success: z.boolean(), - errors: V4ErrorSchema, - messages: z.array(z.any()), - }) +}> + +export type V4SchemaWithResultInfo< + TResultType extends z.ZodType, + TResultInfoType extends z.ZodType, +> = z.ZodObject<{ + result: z.ZodNullable + success: z.ZodBoolean + errors: V4ErrorSchema + messages: z.ZodArray + result_info: z.ZodOptional> +}> + +export function V4Schema( + resultType: TResultType +): V4Schema +export function V4Schema( + resultType: TResultType, + resultInfoType: TResultInfoType +): V4SchemaWithResultInfo +export function V4Schema( + resultType: TResultType, + resultInfoType?: TResultInfoType +): V4Schema | V4SchemaWithResultInfo { + if (resultInfoType) { + return z.object({ + result: resultType.nullable(), + result_info: resultInfoType.nullable().optional(), + success: z.boolean(), + errors: V4ErrorSchema, + messages: z.array(z.any()), + }) + } else { + return z.object({ + result: resultType.nullable(), + success: z.boolean(), + errors: V4ErrorSchema, + messages: z.array(z.any()), + }) + } +} diff --git a/packages/mcp-common/src/workers-oauth-utils.ts b/packages/mcp-common/src/workers-oauth-utils.ts new file mode 100644 index 00000000..c0752f69 --- /dev/null +++ b/packages/mcp-common/src/workers-oauth-utils.ts @@ -0,0 +1,779 @@ +import { z } from 'zod' + +import type { AuthRequest, ClientInfo } from '@cloudflare/workers-oauth-provider' + +const COOKIE_NAME = '__Host-MCP_APPROVED_CLIENTS' +const ONE_YEAR_IN_SECONDS = 31536000 + +/** + * OAuth error class for handling OAuth-specific errors + */ +export class OAuthError extends Error { + constructor( + public code: string, + public description: string, + public statusCode = 400 + ) { + super(description) + this.name = 'OAuthError' + } + + toResponse(): Response { + return new Response( + JSON.stringify({ + error: this.code, + error_description: this.description, + }), + { + status: this.statusCode, + headers: { 'Content-Type': 'application/json' }, + } + ) + } +} + +/** + * Imports a secret key string for HMAC-SHA256 signing. + * @param secret - The raw secret key string. + * @returns A promise resolving to the CryptoKey object. + */ +async function importKey(secret: string): Promise { + if (!secret) { + throw new Error('COOKIE_SECRET is not defined. A secret key is required for signing cookies.') + } + const enc = new TextEncoder() + return crypto.subtle.importKey( + 'raw', + enc.encode(secret), + { hash: 'SHA-256', name: 'HMAC' }, + false, // not extractable + ['sign', 'verify'] // key usages + ) +} + +/** + * Signs data using HMAC-SHA256. + * @param key - The CryptoKey for signing. + * @param data - The string data to sign. + * @returns A promise resolving to the signature as a hex string. + */ +async function signData(key: CryptoKey, data: string): Promise { + const enc = new TextEncoder() + const signatureBuffer = await crypto.subtle.sign('HMAC', key, enc.encode(data)) + // Convert ArrayBuffer to hex string + return Array.from(new Uint8Array(signatureBuffer)) + .map((b) => b.toString(16).padStart(2, '0')) + .join('') +} + +/** + * Verifies an HMAC-SHA256 signature. + * @param key - The CryptoKey for verification. + * @param signatureHex - The signature to verify (hex string). + * @param data - The original data that was signed. + * @returns A promise resolving to true if the signature is valid, false otherwise. + */ +async function verifySignature( + key: CryptoKey, + signatureHex: string, + data: string +): Promise { + const enc = new TextEncoder() + try { + const signatureBytes = new Uint8Array( + signatureHex.match(/.{1,2}/g)!.map((byte) => Number.parseInt(byte, 16)) + ) + return await crypto.subtle.verify('HMAC', key, signatureBytes.buffer, enc.encode(data)) + } catch (e) { + console.error('Error verifying signature:', e) + return false + } +} + +/** + * Parses the signed cookie and verifies its integrity. + * @param cookieHeader - The value of the Cookie header from the request. + * @param secret - The secret key used for signing. + * @returns A promise resolving to the list of approved client IDs if the cookie is valid, otherwise null. + */ +async function getApprovedClientsFromCookie( + cookieHeader: string | null, + secret: string +): Promise { + if (!cookieHeader) return null + + const cookies = cookieHeader.split(';').map((c) => c.trim()) + const targetCookie = cookies.find((c) => c.startsWith(`${COOKIE_NAME}=`)) + + if (!targetCookie) return null + + const cookieValue = targetCookie.substring(COOKIE_NAME.length + 1) + const parts = cookieValue.split('.') + + if (parts.length !== 2) { + console.warn('Invalid cookie format received.') + return null // Invalid format + } + + const [signatureHex, base64Payload] = parts + const payload = atob(base64Payload) // Assuming payload is base64 encoded JSON string + + const key = await importKey(secret) + const isValid = await verifySignature(key, signatureHex, payload) + + if (!isValid) { + console.warn('Cookie signature verification failed.') + return null // Signature invalid + } + + try { + const approvedClients = JSON.parse(payload) + if (!Array.isArray(approvedClients)) { + console.warn('Cookie payload is not an array.') + return null // Payload isn't an array + } + // Ensure all elements are strings + if (!approvedClients.every((item) => typeof item === 'string')) { + console.warn('Cookie payload contains non-string elements.') + return null + } + return approvedClients as string[] + } catch (e) { + console.error('Error parsing cookie payload:', e) + return null // JSON parsing failed + } +} + +/** + * Checks if a given client ID has already been approved by the user, + * based on a signed cookie. + * + * @param request - The incoming Request object to read cookies from. + * @param clientId - The OAuth client ID to check approval for. + * @param cookieSecret - The secret key used to sign/verify the approval cookie. + * @returns A promise resolving to true if the client ID is in the list of approved clients in a valid cookie, false otherwise. + */ +export async function clientIdAlreadyApproved( + request: Request, + clientId: string, + cookieSecret: string +): Promise { + if (!clientId) return false + const cookieHeader = request.headers.get('Cookie') + const approvedClients = await getApprovedClientsFromCookie(cookieHeader, cookieSecret) + + return approvedClients?.includes(clientId) ?? false +} + +/** + * Configuration for the approval dialog + */ +export interface ApprovalDialogOptions { + /** + * Client information to display in the approval dialog + */ + client: ClientInfo | null + /** + * Server information to display in the approval dialog + */ + server: { + name: string + logo?: string + description?: string + } + /** + * Arbitrary state data to pass through the approval flow + * Will be encoded in the form and returned when approval is complete + */ + state: Record + /** + * CSRF token to include in the approval form + */ + csrfToken: string + /** + * Set-Cookie header to include in the approval response + */ + setCookie: string +} + +/** + * Renders an approval dialog for OAuth authorization + * The dialog displays information about the client and server + * and includes a form to submit approval + * + * @param request - The HTTP request + * @param options - Configuration for the approval dialog + * @returns A Response containing the HTML approval dialog + */ +export function renderApprovalDialog(request: Request, options: ApprovalDialogOptions): Response { + const { client, server, state, csrfToken, setCookie } = options + const encodedState = btoa(JSON.stringify(state)) + + const serverName = sanitizeHtml(server.name) + const clientName = client?.clientName ? sanitizeHtml(client.clientName) : 'Unknown MCP Client' + const serverDescription = server.description ? sanitizeHtml(server.description) : '' + + const logoUrl = server.logo ? sanitizeHtml(server.logo) : '' + const clientUri = client?.clientUri ? sanitizeHtml(client.clientUri) : '' + const policyUri = client?.policyUri ? sanitizeHtml(client.policyUri) : '' + const tosUri = client?.tosUri ? sanitizeHtml(client.tosUri) : '' + + const contacts = + client?.contacts && client.contacts.length > 0 ? sanitizeHtml(client.contacts.join(', ')) : '' + + const redirectUris = + client?.redirectUris && client.redirectUris.length > 0 + ? client.redirectUris.map((uri) => sanitizeHtml(uri)).filter((uri) => uri !== '') + : [] + + const htmlContent = ` + + + + + + ${clientName} | Authorization Request + + + +

+
+
+ ${logoUrl ? `` : ''} +

${serverName}

+
+ + ${serverDescription ? `

${serverDescription}

` : ''} +
+ +
+ +

${clientName || 'A new MCP Client'} is requesting access

+ +
+
+
Name:
+
+ ${clientName} +
+
+ + ${ + clientUri + ? ` +
+ ` + : '' + } + + ${ + policyUri + ? ` +
+
Privacy Policy:
+ +
+ ` + : '' + } + + ${ + tosUri + ? ` +
+
Terms of Service:
+ +
+ ` + : '' + } + + ${ + redirectUris.length > 0 + ? ` +
+
Redirect URIs:
+
+ ${redirectUris.map((uri) => `
${uri}
`).join('')} +
+
+ ` + : '' + } + + ${ + contacts + ? ` +
+
Contact:
+
${contacts}
+
+ ` + : '' + } +
+ +

This MCP Client is requesting to be authorized on ${serverName}. If you approve, you will be redirected to complete authentication.

+ +
+ + + +
+ + +
+
+
+
+ + + ` + + return new Response(htmlContent, { + headers: { + 'Content-Security-Policy': "frame-ancestors 'none'", + 'Content-Type': 'text/html; charset=utf-8', + 'Set-Cookie': setCookie, + 'X-Frame-Options': 'DENY', + }, + }) +} + +/** + * Result of parsing the approval form submission. + */ +export interface ParsedApprovalResult { + /** The original state object containing the OAuth request information. */ + state: { oauthReqInfo?: AuthRequest } + /** Headers to set on the redirect response, including the Set-Cookie header. */ + headers: Record +} + +/** + * Parses the form submission from the approval dialog, extracts the state, + * and generates Set-Cookie headers to mark the client as approved. + * + * @param request - The incoming POST Request object containing the form data. + * @param cookieSecret - The secret key used to sign the approval cookie. + * @returns A promise resolving to an object containing the parsed state and necessary headers. + * @throws If the request method is not POST, form data is invalid, or state is missing. + */ +export async function parseRedirectApproval( + request: Request, + cookieSecret: string +): Promise { + if (request.method !== 'POST') { + throw new Error('Invalid request method. Expected POST.') + } + + const formData = await request.formData() + + const tokenFromForm = formData.get('csrf_token') + if (!tokenFromForm || typeof tokenFromForm !== 'string') { + throw new Error('Missing CSRF token in form data') + } + + const cookieHeader = request.headers.get('Cookie') || '' + const cookies = cookieHeader.split(';').map((c) => c.trim()) + const csrfCookie = cookies.find((c) => c.startsWith('__Host-CSRF_TOKEN=')) + const tokenFromCookie = csrfCookie ? csrfCookie.substring('__Host-CSRF_TOKEN='.length) : null + + if (!tokenFromCookie || tokenFromForm !== tokenFromCookie) { + throw new Error('CSRF token mismatch') + } + + const encodedState = formData.get('state') + if (!encodedState || typeof encodedState !== 'string') { + throw new Error('Missing state in form data') + } + + const state = JSON.parse(atob(encodedState)) + if (!state.oauthReqInfo || !state.oauthReqInfo.clientId) { + throw new Error('Invalid state data') + } + + const existingApprovedClients = + (await getApprovedClientsFromCookie(request.headers.get('Cookie'), cookieSecret)) || [] + const updatedApprovedClients = Array.from( + new Set([...existingApprovedClients, state.oauthReqInfo.clientId]) + ) + + const payload = JSON.stringify(updatedApprovedClients) + const key = await importKey(cookieSecret) + const signature = await signData(key, payload) + const newCookieValue = `${signature}.${btoa(payload)}` // signature.base64(payload) + + const headers: Record = { + 'Set-Cookie': `${COOKIE_NAME}=${newCookieValue}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=${ONE_YEAR_IN_SECONDS}`, + } + + return { headers, state } +} + +/** + * Result from bindStateToSession containing the cookie to set + */ +export interface BindStateResult { + /** + * Set-Cookie header value to bind the state to the user's session + */ + setCookie: string +} + +/** + * Result from validateOAuthState containing the original OAuth request info and cookie to clear + */ +export interface ValidateStateResult { + /** + * The original OAuth request information that was stored with the state token + */ + oauthReqInfo: AuthRequest + + /** + * The PKCE code verifier retrieved from server-side storage (never transmitted to client) + */ + codeVerifier: string + + /** + * Set-Cookie header value to clear the state cookie + */ + clearCookie: string +} + +export function generateCSRFProtection(): { token: string; setCookie: string } { + const token = crypto.randomUUID() + const setCookie = `__Host-CSRF_TOKEN=${token}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=600` + return { token, setCookie } +} + +export async function createOAuthState( + oauthReqInfo: AuthRequest, + kv: KVNamespace, + codeVerifier: string +): Promise { + const stateToken = crypto.randomUUID() + const stateData = { oauthReqInfo, codeVerifier } satisfies { + oauthReqInfo: AuthRequest + codeVerifier: string + } + + await kv.put(`oauth:state:${stateToken}`, JSON.stringify(stateData), { + expirationTtl: 600, + }) + return stateToken +} + +/** + * Binds an OAuth state token to the user's browser session using a secure cookie. + * + * @param stateToken - The state token to bind to the session + * @returns Object containing the Set-Cookie header to send to the client + */ +export async function bindStateToSession(stateToken: string): Promise { + const consentedStateCookieName = '__Host-CONSENTED_STATE' + + // Hash the state token to provide defense-in-depth + const encoder = new TextEncoder() + const data = encoder.encode(stateToken) + const hashBuffer = await crypto.subtle.digest('SHA-256', data) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') + + const setCookie = `${consentedStateCookieName}=${hashHex}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=600` + + return { setCookie } +} + +/** + * Validates OAuth state from the request, ensuring: + * 1. The state parameter exists in KV (proves it was created by our server) + * 2. The state hash matches the session cookie (proves this browser consented to it) + * + * This prevents attacks where an attacker's valid state token is injected into + * a victim's OAuth flow. + * + * @param request - The HTTP request containing state parameter and cookies + * @param kv - Cloudflare KV namespace for storing OAuth state data + * @returns Object containing the original OAuth request info and cookie to clear + * @throws If state is missing, mismatched, or expired + */ +export async function validateOAuthState( + request: Request, + kv: KVNamespace +): Promise { + const consentedStateCookieName = '__Host-CONSENTED_STATE' + const url = new URL(request.url) + const stateFromQuery = url.searchParams.get('state') + + if (!stateFromQuery) { + throw new Error('Missing state parameter') + } + + // Decode the state parameter to extract the embedded stateToken + let stateToken: string + try { + const decodedState = JSON.parse(atob(stateFromQuery)) + stateToken = decodedState.state + if (!stateToken) { + throw new Error('State token not found in decoded state') + } + } catch (e) { + throw new Error('Failed to decode state parameter') + } + + const storedDataJson = await kv.get(`oauth:state:${stateToken}`) + if (!storedDataJson) { + throw new Error('Invalid or expired state') + } + + const cookieHeader = request.headers.get('Cookie') || '' + const cookies = cookieHeader.split(';').map((c) => c.trim()) + const consentedStateCookie = cookies.find((c) => c.startsWith(`${consentedStateCookieName}=`)) + const consentedStateHash = consentedStateCookie + ? consentedStateCookie.substring(consentedStateCookieName.length + 1) + : null + + if (!consentedStateHash) { + throw new Error('Missing session binding cookie - authorization flow must be restarted') + } + + const encoder = new TextEncoder() + const data = encoder.encode(stateToken) + const hashBuffer = await crypto.subtle.digest('SHA-256', data) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + const stateHash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') + + if (stateHash !== consentedStateHash) { + throw new Error('State token does not match session - possible CSRF attack detected') + } + + // Parse and validate stored OAuth state data + const StoredOAuthStateSchema = z.object({ + oauthReqInfo: z + .object({ + clientId: z.string(), + scope: z.array(z.string()), + state: z.string(), + responseType: z.string(), + redirectUri: z.string(), + }) + .passthrough(), // preserve any other fields from oauth-provider + codeVerifier: z.string().min(1), // Our code verifier for Cloudflare OAuth + }) + + const parseResult = StoredOAuthStateSchema.safeParse(JSON.parse(storedDataJson)) + if (!parseResult.success) { + throw new Error('Invalid OAuth state data format - PKCE security violation') + } + + await kv.delete(`oauth:state:${stateToken}`) + const clearCookie = `${consentedStateCookieName}=; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=0` + + return { + oauthReqInfo: parseResult.data.oauthReqInfo, + codeVerifier: parseResult.data.codeVerifier, + clearCookie, + } +} + +/** + * Sanitizes HTML content to prevent XSS attacks + * @param unsafe - The unsafe string that might contain HTML + * @returns A safe string with HTML special characters escaped + */ +function sanitizeHtml(unsafe: string): string { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} diff --git a/packages/mcp-observability/CHANGELOG.md b/packages/mcp-observability/CHANGELOG.md new file mode 100644 index 00000000..46279e06 --- /dev/null +++ b/packages/mcp-observability/CHANGELOG.md @@ -0,0 +1,41 @@ +# @repo/mcp-observability + +## 0.32.5 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search + +## 0.32.4 + +### Patch Changes + +- 847fc1f: Update cloudflare-oauth-handler + +## 0.32.3 + +### Patch Changes + +- 43f493d: Update agent + modelcontextprotocol deps + +## 0.32.2 + +### Patch Changes + +- 7422e71: Update MCP sdk + +## 0.32.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol + +## 0.32.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies diff --git a/packages/mcp-observability/package.json b/packages/mcp-observability/package.json index 92f3683b..b8b7adee 100644 --- a/packages/mcp-observability/package.json +++ b/packages/mcp-observability/package.json @@ -1,6 +1,6 @@ { "name": "@repo/mcp-observability", - "version": "0.31.1", + "version": "0.32.5", "private": true, "sideEffects": false, "type": "module", @@ -12,7 +12,7 @@ "bin": "bin" }, "dependencies": { - "@modelcontextprotocol/sdk": "1.10.2", + "@modelcontextprotocol/sdk": "1.20.2", "wrangler": "4.10.0", "zod": "3.24.2" }, diff --git a/packages/tools/.eslintrc.cjs b/packages/tools/.eslintrc.cjs new file mode 100644 index 00000000..f6bf291a --- /dev/null +++ b/packages/tools/.eslintrc.cjs @@ -0,0 +1,5 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ['@repo/eslint-config/default.cjs'], +} diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md new file mode 100644 index 00000000..3e629e86 --- /dev/null +++ b/packages/tools/CHANGELOG.md @@ -0,0 +1,41 @@ +# @repo/tools + +## 0.33.2 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search + +## 0.33.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol + +## 0.33.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies + +## 0.32.1 + +### Patch Changes + +- 86c2e4f: Add API token passthrough auth + +## 0.32.0 + +### Minor Changes + +- 33e0198: BANDA-899 feat: add runx deploy-published-workers script + +### Patch Changes + +- bdb5b89: chore: remove pnpx from wrangler deploy script + + This is redundant because turbo and pnpm already add the bundled wrangler command to $PATH diff --git a/packages/tools/README.md b/packages/tools/README.md new file mode 100644 index 00000000..30167225 --- /dev/null +++ b/packages/tools/README.md @@ -0,0 +1,36 @@ +# @repo/tools + +A collection of shared scripts for automating the monorepo while ensuring consistency across packages. + +## Scripts + +### Bin Scripts + +Simple shell scripts for common development tasks: + +- `run-tsc`: Run TypeScript type checking +- `run-eslint-workers`: Run ESLint checks +- `run-vitest`: Run tests +- `run-vitest-ci`: Run tests in CI mode +- `run-turbo`: Run Turbo commands with tracking disabled +- `run-wrangler-deploy`: Deploy using Wrangler +- `run-wrangler-types`: Generate Wrangler types +- `run-fix-deps`: Fix dependencies + +### Runx CLI + +A TypeScript-based CLI for more complex automation tasks. While the bin scripts work well for simple tasks, the runx CLI provides better type safety and more sophisticated programmatic control. + +Usage: + +```bash +pnpm runx [options] +``` + +Available commands: + +- `deploy-published-workers`: Deploy Cloudflare Workers (based on which packages changesets marked as published in the release). + +Note: + +The CLI will automatically use Bun if available, but falls back to tsx if not installed. diff --git a/packages/tools/bin/run-changeset-new b/packages/tools/bin/run-changeset-new new file mode 100755 index 00000000..c75df44b --- /dev/null +++ b/packages/tools/bin/run-changeset-new @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -eu + +repo_root=$(git rev-parse --show-toplevel) +cd "$repo_root" + +# Stage changes so that changeset can see them +git add . + +changeset + +# Make sure a changeset was created +git status --porcelain | grep '.changeset/.*\.md' >/dev/null && true || + (echo "🚨 No changeset created" && exit 1) + +new_changeset=$(ls -t .changeset/*.md | head -n 1) +echo "📝 New changeset: $new_changeset" +git add "$new_changeset" \ No newline at end of file diff --git a/packages/tools/bin/run-wrangler-deploy b/packages/tools/bin/run-wrangler-deploy index 003a9bdf..a5e6ca1b 100755 --- a/packages/tools/bin/run-wrangler-deploy +++ b/packages/tools/bin/run-wrangler-deploy @@ -6,4 +6,7 @@ VERSION=$(jq -r '.version' package.json) # Deploy with wrangler using the extracted values as binding variables echo "Deploying MCP server $NAME version $VERSION" -pnpx wrangler deploy --var MCP_SERVER_NAME:"$NAME" --var MCP_SERVER_VERSION:"$VERSION" "$@" \ No newline at end of file +wrangler deploy \ + --var MCP_SERVER_NAME:"$NAME" \ + --var MCP_SERVER_VERSION:"$VERSION" \ + "$@" diff --git a/packages/tools/bin/runx b/packages/tools/bin/runx new file mode 100755 index 00000000..94e42ed1 --- /dev/null +++ b/packages/tools/bin/runx @@ -0,0 +1,13 @@ +#!/usr/bin/env sh +set -eu + +script_path="$(realpath "$(dirname "$0")/../src/bin/runx.ts")" + +bin_dir="$(realpath "$(dirname "$0")")" +tsx_path="$(realpath "$bin_dir/../node_modules/.bin/tsx")" + +if command -v bun >/dev/null 2>&1; then + bun "$script_path" "$@" +else + "$tsx_path" "$script_path" "$@" +fi diff --git a/packages/tools/package.json b/packages/tools/package.json index e992af36..23ddb208 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@repo/tools", - "version": "0.31.1", + "version": "0.33.2", "private": true, "sideEffects": false, "type": "module", @@ -8,6 +8,17 @@ "bin": "bin" }, "devDependencies": { - "@types/node": "22.14.1" + "@types/fs-extra": "11.0.4", + "@types/node": "22.14.1", + "vitest": "3.0.9" + }, + "dependencies": { + "@commander-js/extra-typings": "13.1.0", + "@jahands/cli-tools": "0.10.2", + "commander": "13.1.0", + "empathic": "1.1.0", + "tsx": "4.19.3", + "zod": "4.0.0-beta.20250505T195954", + "zx": "8.5.4" } } diff --git a/packages/tools/src/bin/runx.ts b/packages/tools/src/bin/runx.ts new file mode 100644 index 00000000..6f04a4d8 --- /dev/null +++ b/packages/tools/src/bin/runx.ts @@ -0,0 +1,20 @@ +import 'zx/globals' + +import { program } from '@commander-js/extra-typings' +import { catchProcessError } from '@jahands/cli-tools' + +import { deployPublishedWorkersCmd } from '../cmd/deploy-published-packages' + +program + .name('runx') + .description('A CLI for scripts that automate this repo') + + // While `packages/tools/bin` scripts work well for simple tasks, + // a typescript CLI is nicer for more complex things. + + .addCommand(deployPublishedWorkersCmd) + + // Don't hang for unresolved promises + .hook('postAction', () => process.exit(0)) + .parseAsync() + .catch(catchProcessError()) diff --git a/packages/tools/src/changesets.spec.ts b/packages/tools/src/changesets.spec.ts new file mode 100644 index 00000000..3da96ab1 --- /dev/null +++ b/packages/tools/src/changesets.spec.ts @@ -0,0 +1,72 @@ +import { program } from '@commander-js/extra-typings' +import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest' + +import { getPublishedPackages } from './changesets' + +describe('getPublishedPackages', () => { + const fixturesDir = path.join(__dirname, 'test/fixtures/changesets') + const fixture = (name: string) => path.join(fixturesDir, name) + + beforeAll(() => { + // throw errors instead of calling process.exit(1) + // within program.error() is called by cliError() + program.exitOverride((e) => { + throw e + }) + }) + afterEach(() => { + vi.unstubAllEnvs() + }) + + it('should read and parse valid published packages', async () => { + vi.stubEnv('RUNNER_TEMP', fixture('valid')) + + const result = await getPublishedPackages() + + expect(result).toStrictEqual([ + { name: 'package-a', version: '1.0.0' }, + { name: 'package-b', version: '2.1.3' }, + ]) + }) + + it('should throw error when RUNNER_TEMP is not set', async () => { + vi.stubEnv('RUNNER_TEMP', undefined) + + await expect(getPublishedPackages()).rejects.toThrowErrorMatchingInlineSnapshot( + `[CommanderError: error: ✖ $RUNNER_TEMP is not set]` + ) + }) + + it('should throw error when RUNNER_TEMP is empty', async () => { + vi.stubEnv('RUNNER_TEMP', '') + + await expect(getPublishedPackages()).rejects.toThrowErrorMatchingInlineSnapshot( + `[CommanderError: error: ✖ $RUNNER_TEMP is empty]` + ) + }) + + it('should throw error when published packages file is not found', async () => { + vi.stubEnv('RUNNER_TEMP', fixture('empty')) + + await expect(getPublishedPackages()).rejects.toThrowErrorMatchingInlineSnapshot( + `[CommanderError: error: No published packages file found at: ${fixture('empty/published-packages.json')}]` + ) + }) + + it('should throw error when published packages JSON is invalid', async () => { + vi.stubEnv('RUNNER_TEMP', fixture('invalid-json')) + + await expect(getPublishedPackages()).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Failed to parse published packages: SyntaxError: Unexpected token 'h', "this is not"... is not valid JSON]` + ) + }) + + it('should throw error when published packages schema is invalid', async () => { + vi.stubEnv('RUNNER_TEMP', fixture('invalid-schema')) + + await expect(getPublishedPackages()).rejects.toThrowErrorMatchingInlineSnapshot(` + [Error: Failed to parse published packages: ✖ Invalid input: expected string, received number + → at [0].version] + `) + }) +}) diff --git a/packages/tools/src/changesets.ts b/packages/tools/src/changesets.ts new file mode 100644 index 00000000..18461d8d --- /dev/null +++ b/packages/tools/src/changesets.ts @@ -0,0 +1,42 @@ +import { cliError, isNotFoundError } from '@jahands/cli-tools' +import { z } from 'zod' + +export type PublishedPackage = z.infer +export const PublishedPackage = z.object({ + name: z.string(), + version: z.string(), +}) +export const PublishedPackages = z.array(PublishedPackage) + +/** + * Reads and parses the list of published packages from the runner's temporary directory. + * This file is generated by the changesets action in the release workflow and contains + * information about packages that were just published. + * @returns Array of published packages + * @throws Error if RUNNER_TEMP is not set, file is not found, or JSON parsing fails + */ +export async function getPublishedPackages(): Promise { + const runnerTemp = await z + .string({ error: '$RUNNER_TEMP is not set' }) + .min(1, { error: '$RUNNER_TEMP is empty' }) + .parseAsync(process.env.RUNNER_TEMP) + .catch((e) => { + throw cliError(z.prettifyError(e)) + }) + + const publishedPackagesPath = path.join(runnerTemp, 'published-packages.json') + + echo(chalk.dim(`Reading published packages from ${publishedPackagesPath}`)) + + return fs + .readFile(publishedPackagesPath, 'utf8') + .then((s) => PublishedPackages.parse(JSON.parse(s))) + .catch((e) => { + if (isNotFoundError(e)) { + throw cliError(`No published packages file found at: ${publishedPackagesPath}`) + } else if (e instanceof z.ZodError) { + throw new Error(`Failed to parse published packages: ${z.prettifyError(e)}`) + } + throw new Error(`Failed to parse published packages: ${e}`) + }) +} diff --git a/packages/tools/src/cmd/deploy-published-packages.ts b/packages/tools/src/cmd/deploy-published-packages.ts new file mode 100644 index 00000000..59c52b7d --- /dev/null +++ b/packages/tools/src/cmd/deploy-published-packages.ts @@ -0,0 +1,26 @@ +import { Command } from '@commander-js/extra-typings' +import { validateArg } from '@jahands/cli-tools' +import z from 'zod' + +import { getPublishedPackages } from '../changesets' + +export const deployPublishedWorkersCmd = new Command('deploy-published-workers') + .description( + 'Deploy Cloudflare Workers (based on which packages changesets marked as published in the release)' + ) + .requiredOption( + '-e, --env ', + 'The environment to deploy to', + validateArg(z.enum(['staging', 'production'])) + ) + .action(async ({ env }) => { + const publishedPackages = await getPublishedPackages() + + // This technically includes all versioned packages (including non-Workers), + // but that's fine because only Workers include a `deploy` package.json script. + const filters = publishedPackages.flatMap((p) => ['-F', p.name]) satisfies string[] + + await $({ + verbose: true, + })`turbo deploy ${filters} -- --env ${env}` + }) diff --git a/packages/tools/src/proc.ts b/packages/tools/src/proc.ts new file mode 100644 index 00000000..84171799 --- /dev/null +++ b/packages/tools/src/proc.ts @@ -0,0 +1,7 @@ +export function getOutcome(exitCode: number | null) { + if (exitCode === 0) { + return chalk.green('Success!') + } else { + return chalk.red(`Failed with code: ${exitCode}`) + } +} diff --git a/packages/tools/src/test/fixtures/changesets/empty/.gitkeep b/packages/tools/src/test/fixtures/changesets/empty/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/tools/src/test/fixtures/changesets/invalid-json/published-packages.json b/packages/tools/src/test/fixtures/changesets/invalid-json/published-packages.json new file mode 100644 index 00000000..4ab10b4d --- /dev/null +++ b/packages/tools/src/test/fixtures/changesets/invalid-json/published-packages.json @@ -0,0 +1 @@ +this is not valid json diff --git a/packages/tools/src/test/fixtures/changesets/invalid-schema/published-packages.json b/packages/tools/src/test/fixtures/changesets/invalid-schema/published-packages.json new file mode 100644 index 00000000..a5a9d918 --- /dev/null +++ b/packages/tools/src/test/fixtures/changesets/invalid-schema/published-packages.json @@ -0,0 +1,6 @@ +[ + { + "name": "package-a", + "version": 123 + } +] diff --git a/packages/tools/src/test/fixtures/changesets/valid/published-packages.json b/packages/tools/src/test/fixtures/changesets/valid/published-packages.json new file mode 100644 index 00000000..fea15c69 --- /dev/null +++ b/packages/tools/src/test/fixtures/changesets/valid/published-packages.json @@ -0,0 +1,10 @@ +[ + { + "name": "package-a", + "version": "1.0.0" + }, + { + "name": "package-b", + "version": "2.1.3" + } +] diff --git a/packages/tools/src/test/setup.ts b/packages/tools/src/test/setup.ts new file mode 100644 index 00000000..69e8bd7e --- /dev/null +++ b/packages/tools/src/test/setup.ts @@ -0,0 +1,7 @@ +// Ensure chalk doesn't add colors to output for consistent snapshots +delete process.env.FORCE_COLOR + +// runx uses zx/globals imported in bin/runx.ts +// This import ensures that tests work without +// needing to import this manually. +await import('zx/globals') diff --git a/packages/tools/src/tsconfig.ts b/packages/tools/src/tsconfig.ts new file mode 100644 index 00000000..e63e3aba --- /dev/null +++ b/packages/tools/src/tsconfig.ts @@ -0,0 +1,71 @@ +import { inspect } from 'node:util' + +import type { + convertCompilerOptionsFromJson, + createProgram, + readConfigFile, + sys, + CompilerOptions as TSCompilerOptions, +} from 'typescript' + +export type { TSCompilerOptions } + +interface TSModule { + readConfigFile: typeof readConfigFile + convertCompilerOptionsFromJson: typeof convertCompilerOptionsFromJson + sys: typeof sys + createProgram: typeof createProgram +} + +/** + * TypeScript helpers. This is a class so that we can dynamically import the TypeScript module + * to reduce runx start time for commands that don't use the typescript package. + * + * @example + * + * ```ts + * const tsHelpers = await new TSHelpers().init() + * const { ts } = tsHelpers + * const tsConfig = tsHelpers.getTSConfig() + * ts.createProgram(entryPoints, tsConfig).emit() + * ``` + */ +export class TSHelpers { + #ts: TSModule | undefined + public get ts(): TSModule { + if (!this.#ts) { + throw new Error('TSHelpers not initialized. Call init() first.') + } + return this.#ts + } + + async init(): Promise { + this.#ts = (await import('typescript')) as TSModule + return this + } + + getTSConfig(configPath = 'tsconfig.json'): TSCompilerOptions { + const jsonCompopts = this.getCompilerOptionsJSONFollowExtends(configPath) + const tmp = this.ts.convertCompilerOptionsFromJson(jsonCompopts, '') + if (tmp.errors.length > 0) { + throw new Error(`failed parse config: ${inspect(tmp)}`) + } + const tsCompopts: TSCompilerOptions = tmp.options + return tsCompopts + } + + getCompilerOptionsJSONFollowExtends(configPath: string): { + [key: string]: unknown + } { + let compopts = {} + const config = this.ts.readConfigFile(configPath, this.ts.sys.readFile).config + if (config.extends !== undefined) { + const rqrpath = require.resolve(config.extends) + compopts = this.getCompilerOptionsJSONFollowExtends(rqrpath) + } + return { + ...compopts, + ...config.compilerOptions, + } + } +} diff --git a/packages/tools/tsconfig.json b/packages/tools/tsconfig.json new file mode 100644 index 00000000..3ddb4fd8 --- /dev/null +++ b/packages/tools/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@repo/typescript-config/tools.json", + "include": ["**/*.ts"], + "exclude": ["node_modules", "tsconfig.json"] +} diff --git a/packages/tools/vitest.config.ts b/packages/tools/vitest.config.ts new file mode 100644 index 00000000..6e43524b --- /dev/null +++ b/packages/tools/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + setupFiles: [`${__dirname}/src/test/setup.ts`], + environment: 'node', + }, +}) diff --git a/packages/typescript-config/CHANGELOG.md b/packages/typescript-config/CHANGELOG.md new file mode 100644 index 00000000..781eb7cc --- /dev/null +++ b/packages/typescript-config/CHANGELOG.md @@ -0,0 +1,31 @@ +# @repo/typescript-config + +## 0.3.2 + +### Patch Changes + +- 99e2282: Move docs MCP server to use AI Search + +## 0.3.1 + +### Patch Changes + +- cc6d41f: Update agents deps & modelcontextprotocol + +## 0.3.0 + +### Minor Changes + +- 6cf52a6: Support AOT tokens + +### Patch Changes + +- 0fc4439: Update agents and modelcontext dependencies + +## 0.2.5 + +### Patch Changes + +- bdb5b89: chore: remove publishConfig from typescript-config/package.json + + This did nothing. diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 38fa1b9d..2e8e006a 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,11 +1,8 @@ { "name": "@repo/typescript-config", - "version": "0.2.4", + "version": "0.3.2", "private": true, "sideEffects": false, - "publishConfig": { - "access": "public" - }, "devDependencies": { "@types/node": "22.14.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51962185..0566af7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 2.28.1 '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@ianvs/prettier-plugin-sort-imports': specifier: 4.4.1 version: 4.4.1(prettier@3.5.3) @@ -46,19 +46,19 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.15.17)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) apps/ai-gateway: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -66,8 +66,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -80,7 +80,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -92,22 +92,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/auditlogs: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -115,8 +115,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -129,7 +129,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -141,22 +141,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/autorag: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -164,8 +164,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -178,7 +178,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -190,22 +190,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/browser-rendering: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -213,8 +213,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -227,7 +227,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -239,28 +239,28 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/cloudflare-one-casb: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -273,7 +273,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/jsonwebtoken': specifier: 9.0.9 version: 9.0.9 @@ -288,35 +288,35 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/demo-day: dependencies: '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common '@repo/mcp-observability': specifier: workspace:* version: link:../../packages/mcp-observability - '@types/node': - specifier: 22.14.1 - version: 22.14.1 agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) zod: specifier: 3.24.2 version: 3.24.2 devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) + '@types/node': + specifier: 22.14.1 + version: 22.14.1 prettier: specifier: 3.5.3 version: 3.5.3 @@ -325,22 +325,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/dex-analysis: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -348,21 +348,24 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 hono: specifier: 4.7.6 version: 4.7.6 + jszip: + specifier: 3.10.1 + version: 3.10.1 zod: specifier: 3.24.2 version: 3.24.2 devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/jsonwebtoken': specifier: 9.0.9 version: 9.0.9 @@ -377,22 +380,71 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/dns-analytics: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 + '@hono/zod-validator': + specifier: 0.4.3 + version: 0.4.3(hono@4.7.6)(zod@3.24.2) + '@modelcontextprotocol/sdk': + specifier: 1.20.2 + version: 1.20.2 + '@repo/mcp-common': + specifier: workspace:* + version: link:../../packages/mcp-common + '@repo/mcp-observability': + specifier: workspace:* + version: link:../../packages/mcp-observability + agents: + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + cloudflare: + specifier: 4.2.0 + version: 4.2.0 + hono: + specifier: 4.7.6 + version: 4.7.6 + zod: + specifier: 3.24.2 + version: 3.24.2 + devDependencies: + '@cloudflare/vitest-pool-workers': + specifier: 0.8.14 + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) + '@types/node': + specifier: 22.14.1 + version: 22.14.1 + prettier: + specifier: 3.5.3 + version: 3.5.3 + typescript: + specifier: 5.5.4 + version: 5.5.4 + vitest: + specifier: 3.0.9 + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) + wrangler: + specifier: 4.10.0 + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + apps/docs-ai-search: + dependencies: + '@cloudflare/workers-oauth-provider': + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -400,21 +452,24 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 hono: specifier: 4.7.6 version: 4.7.6 + mime: + specifier: 4.0.6 + version: 4.0.6 zod: specifier: 3.24.2 version: 3.24.2 devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -426,22 +481,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/docs-autorag: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -449,8 +504,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -466,7 +521,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -478,22 +533,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/docs-vectorize: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -501,8 +556,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -518,7 +573,59 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) + '@types/node': + specifier: 22.14.1 + version: 22.14.1 + prettier: + specifier: 3.5.3 + version: 3.5.3 + typescript: + specifier: 5.5.4 + version: 5.5.4 + vitest: + specifier: 3.0.9 + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) + wrangler: + specifier: 4.10.0 + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + apps/graphql: + dependencies: + '@cloudflare/workers-oauth-provider': + specifier: 0.0.13 + version: 0.0.13 + '@hono/zod-validator': + specifier: 0.4.3 + version: 0.4.3(hono@4.7.6)(zod@3.24.2) + '@modelcontextprotocol/sdk': + specifier: 1.20.2 + version: 1.20.2 + '@repo/mcp-common': + specifier: workspace:* + version: link:../../packages/mcp-common + '@repo/mcp-observability': + specifier: workspace:* + version: link:../../packages/mcp-observability + agents: + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + cloudflare: + specifier: 4.2.0 + version: 4.2.0 + hono: + specifier: 4.7.6 + version: 4.7.6 + lz-string: + specifier: 1.5.0 + version: 1.5.0 + zod: + specifier: 3.24.2 + version: 3.24.2 + devDependencies: + '@cloudflare/vitest-pool-workers': + specifier: 0.8.14 + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -530,22 +637,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/logpush: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -553,8 +660,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -567,7 +674,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -579,22 +686,22 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/radar: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -602,8 +709,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -616,7 +723,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -628,16 +735,16 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/sandbox-container: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@hono/node-server': specifier: 1.13.8 version: 1.13.8(hono@4.7.6) @@ -645,8 +752,8 @@ importers: specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@n8n/json-schema-to-zod': specifier: 1.1.0 version: 1.1.0(zod@3.24.2) @@ -659,12 +766,9 @@ importers: '@repo/mcp-observability': specifier: workspace:* version: link:../../packages/mcp-observability - '@types/node': - specifier: 22.14.1 - version: 22.14.1 agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cron-schedule: specifier: 5.0.4 version: 5.0.4 @@ -692,10 +796,13 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/mock-fs': specifier: 4.13.4 version: 4.13.4 + '@types/node': + specifier: 22.14.1 + version: 22.14.1 ai: specifier: 4.3.10 version: 4.3.10(react@17.0.2)(zod@3.24.2) @@ -710,16 +817,16 @@ importers: version: 2.0.11 wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/workers-bindings: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@n8n/json-schema-to-zod': specifier: 1.1.0 version: 1.1.0(zod@3.24.2) @@ -733,8 +840,8 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) ai: specifier: 4.3.10 version: 4.3.10(react@17.0.2)(zod@3.24.2) @@ -756,7 +863,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -768,16 +875,71 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) + wrangler: + specifier: 4.10.0 + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + apps/workers-builds: + dependencies: + '@cloudflare/workers-oauth-provider': + specifier: 0.0.13 + version: 0.0.13 + '@hono/zod-validator': + specifier: 0.4.3 + version: 0.4.3(hono@4.7.6)(zod@3.24.2) + '@modelcontextprotocol/sdk': + specifier: 1.20.2 + version: 1.20.2 + '@repo/mcp-common': + specifier: workspace:* + version: link:../../packages/mcp-common + '@repo/mcp-observability': + specifier: workspace:* + version: link:../../packages/mcp-observability + agents: + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + cloudflare: + specifier: 4.2.0 + version: 4.2.0 + hono: + specifier: 4.7.6 + version: 4.7.6 + zod: + specifier: 3.24.2 + version: 3.24.2 + devDependencies: + '@cloudflare/vite-plugin': + specifier: 1.1.0 + version: 1.1.0(bufferutil@4.0.9)(rollup@4.35.0)(utf-8-validate@5.0.10)(vite@6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3))(workerd@1.20250507.0)(wrangler@4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@cloudflare/vitest-pool-workers': + specifier: 0.8.14 + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) + '@types/node': + specifier: 22.14.1 + version: 22.14.1 + prettier: + specifier: 3.5.3 + version: 3.5.3 + typescript: + specifier: 5.5.4 + version: 5.5.4 + vite: + specifier: 6.3.4 + version: 6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3) + vitest: + specifier: 3.0.9 + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) apps/workers-observability: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 '@fast-csv/format': specifier: 5.0.2 version: 5.0.2 @@ -785,8 +947,8 @@ importers: specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-common': specifier: workspace:* version: link:../../packages/mcp-common @@ -794,21 +956,24 @@ importers: specifier: workspace:* version: link:../../packages/mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 hono: specifier: 4.7.6 version: 4.7.6 + workers-tagged-logger: + specifier: 0.13.5 + version: 0.13.5 zod: specifier: 3.24.2 version: 3.24.2 devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@types/node': specifier: 22.14.1 version: 22.14.1 @@ -820,10 +985,10 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) packages/eslint-config: devDependencies: @@ -865,28 +1030,37 @@ importers: version: 5.5.4 vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) packages/eval-tools: dependencies: + '@ai-sdk/anthropic': + specifier: 1.2.11 + version: 1.2.11(zod@3.24.2) + '@ai-sdk/google': + specifier: 1.2.17 + version: 1.2.17(zod@3.24.2) '@ai-sdk/openai': specifier: 1.3.20 version: 1.3.20(zod@3.24.2) '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) ai: specifier: 4.3.10 version: 4.3.10(react@17.0.2)(zod@3.24.2) + ai-gateway-provider: + specifier: 0.0.6 + version: 0.0.6(react@17.0.2)(zod@3.24.2) workers-ai-provider: specifier: 0.3.0 version: 0.3.0 wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: specifier: 3.24.2 version: 3.24.2 @@ -904,20 +1078,23 @@ importers: packages/mcp-common: dependencies: '@cloudflare/workers-oauth-provider': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.0.13 + version: 0.0.13 + '@fast-csv/format': + specifier: 5.0.2 + version: 5.0.2 '@hono/zod-validator': specifier: 0.4.3 version: 0.4.3(hono@4.7.6)(zod@3.24.2) '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 '@repo/mcp-observability': specifier: workspace:* version: link:../mcp-observability agents: - specifier: 0.0.67 - version: 0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2) + specifier: 0.2.19 + version: 0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) cloudflare: specifier: 4.2.0 version: 4.2.0 @@ -933,7 +1110,7 @@ importers: devDependencies: '@cloudflare/vitest-pool-workers': specifier: 0.8.14 - version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9) + version: 0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9) '@repo/eslint-config': specifier: workspace:* version: link:../eslint-config @@ -954,19 +1131,19 @@ importers: version: 3.0.9(vitest@3.0.9) vitest: specifier: 3.0.9 - version: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) packages/mcp-observability: dependencies: '@modelcontextprotocol/sdk': - specifier: 1.10.2 - version: 1.10.2 + specifier: 1.20.2 + version: 1.20.2 wrangler: specifier: 4.10.0 - version: 4.10.0(@cloudflare/workers-types@4.20250416.0) + version: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: specifier: 3.24.2 version: 3.24.2 @@ -979,10 +1156,38 @@ importers: version: 22.14.1 packages/tools: + dependencies: + '@commander-js/extra-typings': + specifier: 13.1.0 + version: 13.1.0(commander@13.1.0) + '@jahands/cli-tools': + specifier: 0.10.2 + version: 0.10.2(@commander-js/extra-typings@13.1.0(commander@13.1.0))(commander@13.1.0)(typescript@5.5.4)(zod@4.0.0-beta.20250505T195954)(zx@8.5.4) + commander: + specifier: 13.1.0 + version: 13.1.0 + empathic: + specifier: 1.1.0 + version: 1.1.0 + tsx: + specifier: 4.19.3 + version: 4.19.3 + zod: + specifier: 4.0.0-beta.20250505T195954 + version: 4.0.0-beta.20250505T195954 + zx: + specifier: 8.5.4 + version: 8.5.4 devDependencies: + '@types/fs-extra': + specifier: 11.0.4 + version: 11.0.4 '@types/node': specifier: 22.14.1 version: 22.14.1 + vitest: + specifier: 3.0.9 + version: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) packages/typescript-config: devDependencies: @@ -996,22 +1201,75 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@ai-sdk/anthropic@1.2.11': + resolution: {integrity: sha512-lZLcEMh8MXY4NVSrN/7DyI2rnid8k7cn/30nMmd3bwJrnIsOuIuuFvY8f0nj+pFcTi6AYK7ujLdqW5dQVz1YQw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/gateway@2.0.1': + resolution: {integrity: sha512-vPVIbnP35ZnayS937XLo85vynR85fpBQWHCdUweq7apzqFOTU2YkUd4V3msebEHbQ2Zro60ZShDDy9SMiyWTqA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/google@1.2.17': + resolution: {integrity: sha512-mLFLDMCJaDK+j1nvoqeNszazSZIyeSMPi5X+fs5Wh3xWZljGGE0WmFg32RNkFujRB+UnM63EnhPG70WdqOx/MA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@ai-sdk/openai@1.3.20': resolution: {integrity: sha512-/DflUy7ROG9k6n6YTXMBFPbujBKnbGY58f3CwvicLvDar9nDAloVnUWd3LUoOxpSVnX8vtQ7ngxF52SLWO6RwQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 + '@ai-sdk/openai@2.0.53': + resolution: {integrity: sha512-GIkR3+Fyif516ftXv+YPSPstnAHhcZxNoR2s8uSHhQ1yBT7I7aQYTVwpjAuYoT3GR+TeP50q7onj2/nDRbT2FQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@2.2.7': resolution: {integrity: sha512-kM0xS3GWg3aMChh9zfeM+80vEZfXzR3JEUBdycZLtbRZ2TRT8xOj3WodGHPb06sUK5yD7pAXC/P7ctsi2fvUGQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider-utils@3.0.12': + resolution: {integrity: sha512-ZtbdvYxdMoria+2SlNarEk6Hlgyf+zzcznlD55EAl+7VZvJaSg2sqPvwArY7L6TfDEDJsnCq0fdhBSkYo0Xqdg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider@1.1.3': resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} engines: {node: '>=18'} + '@ai-sdk/provider@2.0.0': + resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + '@ai-sdk/react@1.2.9': resolution: {integrity: sha512-/VYm8xifyngaqFDLXACk/1czDRCefNCdALUyp+kIX6DUIYUWTM93ISoZ+qJ8+3E+FiJAKBQz61o8lIIl+vYtzg==} engines: {node: '>=18'} @@ -1022,12 +1280,22 @@ packages: zod: optional: true + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + '@ai-sdk/ui-utils@1.2.8': resolution: {integrity: sha512-nls/IJCY+ks3Uj6G/agNhXqQeLVqhNfoJbuNgCny+nX2veY5ADB91EcZUqVeQ/ionul2SeUswPY6Q/DxteY29Q==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 + '@apidevtools/json-schema-ref-parser@11.9.3': + resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} + engines: {node: '>= 16'} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -1049,6 +1317,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime-corejs3@7.28.2': + resolution: {integrity: sha512-FVFaVs2/dZgD3Y9ZD+AKNKjyGKzwu0C54laAXWUXgLcVXcCX6YZ6GhK2cp7FogSN2OA0Fu+QT8dP3FUdo9ShSQ==} + engines: {node: '>=6.9.0'} + '@babel/runtime@7.26.10': resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} engines: {node: '>=6.9.0'} @@ -1065,6 +1337,9 @@ packages: resolution: {integrity: sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==} engines: {node: '>=6.9.0'} + '@base-org/account@1.1.1': + resolution: {integrity: sha512-IfVJPrDPhHfqXRDb89472hXkpvJuQQR7FDI9isLPHEqSYt/45whIoBxSPgZ0ssTt379VhQo4+87PWI1DoLSfAQ==} + '@changesets/apply-release-plan@7.0.10': resolution: {integrity: sha512-wNyeIJ3yDsVspYvHnEz1xQDq18D9ifed3lI+wxRQRK4pArUcuHgCTrHv0QRnnwjhVCQACxZ+CBih3wgOct6UXw==} @@ -1133,6 +1408,12 @@ packages: workerd: optional: true + '@cloudflare/vite-plugin@1.1.0': + resolution: {integrity: sha512-b265RnBqZE57KBPPwhDWFu8W51RNnl4LkxNgY/GzbXoztc6qDcnMs7IVyPCcCvyXa4ogSQz5MvQ3yB5Ehn5E8A==} + peerDependencies: + vite: ^6.1.0 + wrangler: ^3.101.0 || ^4.0.0 + '@cloudflare/vitest-pool-workers@0.8.14': resolution: {integrity: sha512-uqUvelQQkU/8JD/mgd9OV3byB6CaHxkw/DzHZ4z2haM9epR9D5mDshw5+AfPu7x/IWZ9zciaDawjM8QW8QrjIA==} peerDependencies: @@ -1152,6 +1433,18 @@ packages: cpu: [x64] os: [darwin] + '@cloudflare/workerd-darwin-64@1.20250428.0': + resolution: {integrity: sha512-6nVe9oV4Hdec6ctzMtW80TiDvNTd2oFPi3VsKqSDVaJSJbL+4b6seyJ7G/UEPI+si6JhHBSLV2/9lNXNGLjClA==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-64@1.20250507.0': + resolution: {integrity: sha512-xC+8hmQuOUUNCVT9DWpLMfxhR4Xs4kI8v7Bkybh4pzGC85moH6fMfCBNaP0YQCNAA/BR56aL/AwfvMVGskTK/A==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + '@cloudflare/workerd-darwin-arm64@1.20250408.0': resolution: {integrity: sha512-5XZ2Oykr8bSo7zBmERtHh18h5BZYC/6H1YFWVxEj3PtalF3+6SHsO4KZsbGvDml9Pu7sHV277jiZE5eny8Hlyw==} engines: {node: '>=16'} @@ -1164,6 +1457,18 @@ packages: cpu: [arm64] os: [darwin] + '@cloudflare/workerd-darwin-arm64@1.20250428.0': + resolution: {integrity: sha512-/TB7bh7SIJ5f+6r4PHsAz7+9Qal/TK1cJuKFkUno1kqGlZbdrMwH0ATYwlWC/nBFeu2FB3NUolsTntEuy23hnQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20250507.0': + resolution: {integrity: sha512-Oynff5H8yM4trfUFaKdkOvPV3jac8mg7QC19ILZluCVgLx/JGEVLEJ7do1Na9rLqV8CK4gmUXPrUMX7uerhQgg==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + '@cloudflare/workerd-linux-64@1.20250408.0': resolution: {integrity: sha512-WbgItXWln6G5d7GvYLWcuOzAVwafysZaWunH3UEfsm95wPuRofpYnlDD861gdWJX10IHSVgMStGESUcs7FLerQ==} engines: {node: '>=16'} @@ -1176,6 +1481,18 @@ packages: cpu: [x64] os: [linux] + '@cloudflare/workerd-linux-64@1.20250428.0': + resolution: {integrity: sha512-9eCbj+R3CKqpiXP6DfAA20DxKge+OTj7Hyw3ZewiEhWH9INIHiJwJQYybu4iq9kJEGjnGvxgguLFjSCWm26hgg==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-64@1.20250507.0': + resolution: {integrity: sha512-/HAA+Zg/R7Q/Smyl835FUFKjotZN1UzN9j/BHBd0xKmKov97QkXAX8gsyGnyKqRReIOinp8x/8+UebTICR7VJw==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + '@cloudflare/workerd-linux-arm64@1.20250408.0': resolution: {integrity: sha512-pAhEywPPvr92SLylnQfZEPgXz+9pOG9G9haAPLpEatncZwYiYd9yiR6HYWhKp2erzCoNrOqKg9IlQwU3z1IDiw==} engines: {node: '>=16'} @@ -1188,6 +1505,18 @@ packages: cpu: [arm64] os: [linux] + '@cloudflare/workerd-linux-arm64@1.20250428.0': + resolution: {integrity: sha512-D9NRBnW46nl1EQsP13qfkYb5lbt4C6nxl38SBKY/NOcZAUoHzNB5K0GaK8LxvpkM7X/97ySojlMfR5jh5DNXYQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20250507.0': + resolution: {integrity: sha512-NMPibSdOYeycU0IrKkgOESFJQy7dEpHvuatZxQxlT+mIQK0INzI3irp2kKxhF99s25kPC4p+xg9bU3ugTrs3VQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + '@cloudflare/workerd-windows-64@1.20250408.0': resolution: {integrity: sha512-nJ3RjMKGae2aF2rZ/CNeBvQPM+W5V1SUK0FYWG/uomyr7uQ2l4IayHna1ODg/OHHTEgIjwom0Mbn58iXb0WOcQ==} engines: {node: '>=16'} @@ -1200,19 +1529,48 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-oauth-provider@0.0.5': - resolution: {integrity: sha512-t1x5KAzsubCvb4APnJ93z407X1x7SGj/ga5ziRnwIb/iLy4PMkT/hgd1y5z7Bbsdy5Fy6mywhCP4lym24bX66w==} + '@cloudflare/workerd-windows-64@1.20250428.0': + resolution: {integrity: sha512-RQCRj28eitjKD0tmei6iFOuWqMuHMHdNGEigRmbkmuTlpbWHNAoHikgCzZQ/dkKDdatA76TmcpbyECNf31oaTA==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workerd-windows-64@1.20250507.0': + resolution: {integrity: sha512-c91fhNP8ufycdIDqjVyKTqeb4ewkbAYXFQbLreMVgh4LLQQPDDEte8wCdmaFy5bIL0M9d85PpdCq51RCzq/FaQ==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workers-oauth-provider@0.0.13': + resolution: {integrity: sha512-W9n1LHUkbEvJNsoBSVUkKUdMhGXeAfqjRoqhaKP3ky35SJ5IVRSYNV9PYff+7VxbKyX98KJjEpX0PFzLJ8ekRw==} - '@cloudflare/workers-types@4.20250414.0': - resolution: {integrity: sha512-ZHl8LiyUMWiIxYqpasen8Lc75Ef+0afqL26TEd95eRIi5kgkEbjDJ7uIUnpxMoZTRI0J8Hy5YEPtt4nFXt+TpA==} + '@cloudflare/workers-types@4.20250410.0': + resolution: {integrity: sha512-Yx9VUi6QpmXtUIhOL+em+V02gue12kmVBVL6RGH5mhFh50M0x9JyOmm6wKwKZUny2uQd+22nuouE2q3z1OrsIQ==} '@cloudflare/workers-types@4.20250416.0': resolution: {integrity: sha512-i37TX0Clp+MrPdXMBdvKZM7JghCrWD9GtG7E+8ANOAPmtZjUkZfEy9qq46IG3XlNpagPaWDkY3SgJ3s01gPxCw==} + '@coinbase/wallet-sdk@3.9.3': + resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} + + '@coinbase/wallet-sdk@4.3.6': + resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==} + + '@commander-js/extra-typings@13.1.0': + resolution: {integrity: sha512-q5P52BYb1hwVWE6dtID7VvuJWrlfbCv4klj7BjUUOqMz4jbSZD4C9fJ9lRjL2jnBGTg+gDDlaXN51rkWcLk4fg==} + peerDependencies: + commander: ~13.1.0 + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@ecies/ciphers@0.2.4': + resolution: {integrity: sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 + '@effect/schema@0.75.5': resolution: {integrity: sha512-TQInulTVCuF+9EIbJpyLP6dvxbQJMphrnRqgexm/Ze39rSjfhJuufF7XvU3SxTgg3HnL7B/kpORTJbHhlE6thw==} deprecated: this package has been merged into the main effect package @@ -1390,6 +1748,22 @@ packages: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@ethereumjs/common@3.2.0': + resolution: {integrity: sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==} + + '@ethereumjs/rlp@4.0.1': + resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} + engines: {node: '>=14'} + hasBin: true + + '@ethereumjs/tx@4.2.0': + resolution: {integrity: sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==} + engines: {node: '>=14'} + + '@ethereumjs/util@8.1.0': + resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} + engines: {node: '>=14'} + '@fast-csv/format@5.0.2': resolution: {integrity: sha512-fRYcWvI8vs0Zxa/8fXd/QlmQYWWkJqKZPAXM+vksnplb3owQFKTPPh9JqOtD0L3flQw/AZjjXdPkD7Kp/uHm8g==} @@ -1397,12 +1771,33 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} + '@gemini-wallet/core@0.2.0': + resolution: {integrity: sha512-vv9aozWnKrrPWQ3vIFcWk7yta4hQW1Ie0fsNNPeXnjAxkbXr2hqMagEptLuMxpEP2W3mnRu05VDNKzcvAuuZDw==} + peerDependencies: + viem: '>=2.0.0' + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@hattip/adapter-node@0.0.49': + resolution: {integrity: sha512-BE+Y8Q4U0YcH34FZUYU4DssGKOaZLbNL0zK57Z41UZp0m9kS79ZIolBmjjpPhTVpIlRY3Rs+uhXbVXKk7mUcJA==} + + '@hattip/core@0.0.49': + resolution: {integrity: sha512-3/ZJtC17cv8m6Sph8+nw4exUp9yhEf2Shi7HK6AHSUSBtaaQXZ9rJBVxTfZj3PGNOR/P49UBXOym/52WYKFTJQ==} + + '@hattip/headers@0.0.49': + resolution: {integrity: sha512-rrB2lEhTf0+MNVt5WdW184Ky706F1Ze9Aazn/R8c+/FMUYF9yjem2CgXp49csPt3dALsecrnAUOHFiV0LrrHXA==} + + '@hattip/polyfills@0.0.49': + resolution: {integrity: sha512-5g7W5s6Gq+HDxwULGFQ861yAnEx3yd9V8GDwS96HBZ1nM1u93vN+KTuwXvNsV7Z3FJmCrD/pgU8WakvchclYuA==} + + '@hattip/walk@0.0.49': + resolution: {integrity: sha512-AgJgKLooZyQnzMfoFg5Mo/aHM+HGBC9ExpXIjNqGimYTRgNbL/K7X5EM1kR2JY90BNKk9lo6Usq1T/nWFdT7TQ==} + hasBin: true + '@hono/node-server@1.13.8': resolution: {integrity: sha512-fsn8ucecsAXUoVxrUil0m13kOEq4mkX4/4QozCqmY+HpGfKl74OYSn8JcMA8GnG0ClfdRI4/ZSeG7zhFaVg+wg==} engines: {node: '>=18.14.1'} @@ -1542,6 +1937,15 @@ packages: cpu: [x64] os: [win32] + '@jahands/cli-tools@0.10.2': + resolution: {integrity: sha512-iJSIwh1A2AWIeOSLtcJywy/kKUyB63mq4OGSJWwyG2gkCyZhijyxEgrsdf0MCttNwTDBmOiGzTcja5wlF8ph/A==} + peerDependencies: + '@commander-js/extra-typings': ^13.0.0 + commander: ^13.0.0 + typescript: ^5.5.4 + zod: ^3.24.1 || ^4 + zx: ^8.3.0 + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -1563,14 +1967,106 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - '@manypkg/get-packages@1.1.3': + '@kamilkisiela/fast-url-parser@1.1.4': + resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==} + + '@lit-labs/ssr-dom-shim@1.4.0': + resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} + + '@lit/reactive-element@2.1.1': + resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@modelcontextprotocol/sdk@1.10.2': - resolution: {integrity: sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA==} + '@metamask/eth-json-rpc-provider@1.0.1': + resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} + engines: {node: '>=14.0.0'} + + '@metamask/json-rpc-engine@7.3.3': + resolution: {integrity: sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==} + engines: {node: '>=16.0.0'} + + '@metamask/json-rpc-engine@8.0.2': + resolution: {integrity: sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==} + engines: {node: '>=16.0.0'} + + '@metamask/json-rpc-middleware-stream@7.0.2': + resolution: {integrity: sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==} + engines: {node: '>=16.0.0'} + + '@metamask/object-multiplex@2.1.0': + resolution: {integrity: sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==} + engines: {node: ^16.20 || ^18.16 || >=20} + + '@metamask/onboarding@1.0.1': + resolution: {integrity: sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==} + + '@metamask/providers@16.1.0': + resolution: {integrity: sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==} + engines: {node: ^18.18 || >=20} + + '@metamask/rpc-errors@6.4.0': + resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==} + engines: {node: '>=16.0.0'} + + '@metamask/rpc-errors@7.0.2': + resolution: {integrity: sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==} + engines: {node: ^18.20 || ^20.17 || >=22} + + '@metamask/safe-event-emitter@2.0.0': + resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} + + '@metamask/safe-event-emitter@3.1.2': + resolution: {integrity: sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==} + engines: {node: '>=12.0.0'} + + '@metamask/sdk-analytics@0.0.5': + resolution: {integrity: sha512-fDah+keS1RjSUlC8GmYXvx6Y26s3Ax1U9hGpWb6GSY5SAdmTSIqp2CvYy6yW0WgLhnYhW+6xERuD0eVqV63QIQ==} + + '@metamask/sdk-communication-layer@0.33.1': + resolution: {integrity: sha512-0bI9hkysxcfbZ/lk0T2+aKVo1j0ynQVTuB3sJ5ssPWlz+Z3VwveCkP1O7EVu1tsVVCb0YV5WxK9zmURu2FIiaA==} + peerDependencies: + cross-fetch: ^4.0.0 + eciesjs: '*' + eventemitter2: ^6.4.9 + readable-stream: ^3.6.2 + socket.io-client: ^4.5.1 + + '@metamask/sdk-install-modal-web@0.32.1': + resolution: {integrity: sha512-MGmAo6qSjf1tuYXhCu2EZLftq+DSt5Z7fsIKr2P+lDgdTPWgLfZB1tJKzNcwKKOdf6q9Qmmxn7lJuI/gq5LrKw==} + + '@metamask/sdk@0.33.1': + resolution: {integrity: sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==} + + '@metamask/superstruct@3.2.1': + resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} + engines: {node: '>=16.0.0'} + + '@metamask/utils@11.8.1': + resolution: {integrity: sha512-DIbsNUyqWLFgqJlZxi1OOCMYvI23GqFCvNJAtzv8/WXWzJfnJnvp1M24j7VvUe3URBi3S86UgQ7+7aWU9p/cnQ==} + engines: {node: ^18.18 || ^20.14 || >=22} + + '@metamask/utils@5.0.2': + resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==} + engines: {node: '>=14.0.0'} + + '@metamask/utils@8.5.0': + resolution: {integrity: sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==} + engines: {node: '>=16.0.0'} + + '@metamask/utils@9.3.0': + resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} + engines: {node: '>=16.0.0'} + + '@modelcontextprotocol/sdk@1.20.2': + resolution: {integrity: sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg==} engines: {node: '>=18'} '@n8n/json-schema-to-zod@1.1.0': @@ -1578,6 +2074,49 @@ packages: peerDependencies: zod: ^3.0.0 + '@noble/ciphers@1.2.1': + resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.4.2': + resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} + + '@noble/curves@1.8.0': + resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.7.0': + resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1598,9 +2137,60 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@paulmillr/qr@0.2.1': + resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==} + deprecated: 'The package is now available as "qr": npm install qr' + '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@reown/appkit-common@1.7.8': + resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==} + + '@reown/appkit-controllers@1.7.8': + resolution: {integrity: sha512-IdXlJlivrlj6m63VsGLsjtPHHsTWvKGVzWIP1fXZHVqmK+rZCBDjCi9j267Rb9/nYRGHWBtlFQhO8dK35WfeDA==} + + '@reown/appkit-pay@1.7.8': + resolution: {integrity: sha512-OSGQ+QJkXx0FEEjlpQqIhT8zGJKOoHzVnyy/0QFrl3WrQTjCzg0L6+i91Ad5Iy1zb6V5JjqtfIFpRVRWN4M3pw==} + + '@reown/appkit-polyfills@1.7.8': + resolution: {integrity: sha512-W/kq786dcHHAuJ3IV2prRLEgD/2iOey4ueMHf1sIFjhhCGMynMkhsOhQMUH0tzodPqUgAC494z4bpIDYjwWXaA==} + + '@reown/appkit-scaffold-ui@1.7.8': + resolution: {integrity: sha512-RCeHhAwOrIgcvHwYlNWMcIDibdI91waaoEYBGw71inE0kDB8uZbE7tE6DAXJmDkvl0qPh+DqlC4QbJLF1FVYdQ==} + + '@reown/appkit-ui@1.7.8': + resolution: {integrity: sha512-1hjCKjf6FLMFzrulhl0Y9Vb9Fu4royE+SXCPSWh4VhZhWqlzUFc7kutnZKx8XZFVQH4pbBvY62SpRC93gqoHow==} + + '@reown/appkit-utils@1.7.8': + resolution: {integrity: sha512-8X7UvmE8GiaoitCwNoB86pttHgQtzy4ryHZM9kQpvjQ0ULpiER44t1qpVLXNM4X35O0v18W0Dk60DnYRMH2WRw==} + peerDependencies: + valtio: 1.13.2 + + '@reown/appkit-wallet@1.7.8': + resolution: {integrity: sha512-kspz32EwHIOT/eg/ZQbFPxgXq0B/olDOj3YMu7gvLEFz4xyOFd/wgzxxAXkp5LbG4Cp++s/elh79rVNmVFdB9A==} + + '@reown/appkit@1.7.8': + resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} + + '@rollup/plugin-replace@6.0.2': + resolution: {integrity: sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.35.0': resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==} cpu: [arm] @@ -1699,6 +2289,40 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@safe-global/safe-apps-provider@0.18.6': + resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} + + '@safe-global/safe-apps-sdk@9.1.0': + resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==} + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': + resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==} + engines: {node: '>=16'} + + '@scure/base@1.1.9': + resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.4.0': + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + + '@scure/bip32@1.6.2': + resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.3.0': + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + + '@scure/bip39@1.5.4': + resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@sentry/core@8.9.2': resolution: {integrity: sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==} engines: {node: '>=14.18'} @@ -1724,9 +2348,258 @@ packages: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@solana-program/compute-budget@0.8.0': + resolution: {integrity: sha512-qPKxdxaEsFxebZ4K5RPuy7VQIm/tfJLa1+Nlt3KNA8EYQkz9Xm8htdoEaXVrer9kpgzzp9R3I3Bh6omwCM06tQ==} + peerDependencies: + '@solana/kit': ^2.1.0 + + '@solana-program/token-2022@0.4.2': + resolution: {integrity: sha512-zIpR5t4s9qEU3hZKupzIBxJ6nUV5/UVyIT400tu9vT1HMs5JHxaTTsb5GUhYjiiTvNwU0MQavbwc4Dl29L0Xvw==} + peerDependencies: + '@solana/kit': ^2.1.0 + '@solana/sysvars': ^2.1.0 + + '@solana-program/token@0.5.1': + resolution: {integrity: sha512-bJvynW5q9SFuVOZ5vqGVkmaPGA0MCC+m9jgJj1nk5m20I389/ms69ASnhWGoOPNcie7S9OwBX0gTj2fiyWpfag==} + peerDependencies: + '@solana/kit': ^2.1.0 + + '@solana/accounts@2.3.0': + resolution: {integrity: sha512-QgQTj404Z6PXNOyzaOpSzjgMOuGwG8vC66jSDB+3zHaRcEPRVRd2sVSrd1U6sHtnV3aiaS6YyDuPQMheg4K2jw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/addresses@2.3.0': + resolution: {integrity: sha512-ypTNkY2ZaRFpHLnHAgaW8a83N0/WoqdFvCqf4CQmnMdFsZSdC7qOwcbd7YzdaQn9dy+P2hybewzB+KP7LutxGA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/assertions@2.3.0': + resolution: {integrity: sha512-Ekoet3khNg3XFLN7MIz8W31wPQISpKUGDGTylLptI+JjCDWx3PIa88xjEMqFo02WJ8sBj2NLV64Xg1sBcsHjZQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-core@2.3.0': + resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-data-structures@2.3.0': + resolution: {integrity: sha512-qvU5LE5DqEdYMYgELRHv+HMOx73sSoV1ZZkwIrclwUmwTbTaH8QAJURBj0RhQ/zCne7VuLLOZFFGv6jGigWhSw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-numbers@2.3.0': + resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-strings@2.3.0': + resolution: {integrity: sha512-y5pSBYwzVziXu521hh+VxqUtp0hYGTl1eWGoc1W+8mdvBdC1kTqm/X7aYQw33J42hw03JjryvYOvmGgk3Qz/Ug==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5.3.3' + + '@solana/codecs@2.3.0': + resolution: {integrity: sha512-JVqGPkzoeyU262hJGdH64kNLH0M+Oew2CIPOa/9tR3++q2pEd4jU2Rxdfye9sd0Ce3XJrR5AIa8ZfbyQXzjh+g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/errors@2.3.0': + resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.3.3' + + '@solana/fast-stable-stringify@2.3.0': + resolution: {integrity: sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/functional@2.3.0': + resolution: {integrity: sha512-AgsPh3W3tE+nK3eEw/W9qiSfTGwLYEvl0rWaxHht/lRcuDVwfKRzeSa5G79eioWFFqr+pTtoCr3D3OLkwKz02Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/instructions@2.3.0': + resolution: {integrity: sha512-PLMsmaIKu7hEAzyElrk2T7JJx4D+9eRwebhFZpy2PXziNSmFF929eRHKUsKqBFM3cYR1Yy3m6roBZfA+bGE/oQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/keys@2.3.0': + resolution: {integrity: sha512-ZVVdga79pNH+2pVcm6fr2sWz9HTwfopDVhYb0Lh3dh+WBmJjwkabXEIHey2rUES7NjFa/G7sV8lrUn/v8LDCCQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/kit@2.3.0': + resolution: {integrity: sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/nominal-types@2.3.0': + resolution: {integrity: sha512-uKlMnlP4PWW5UTXlhKM8lcgIaNj8dvd8xO4Y9l+FVvh9RvW2TO0GwUO6JCo7JBzCB0PSqRJdWWaQ8pu1Ti/OkA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/options@2.3.0': + resolution: {integrity: sha512-PPnnZBRCWWoZQ11exPxf//DRzN2C6AoFsDI/u2AsQfYih434/7Kp4XLpfOMT/XESi+gdBMFNNfbES5zg3wAIkw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/programs@2.3.0': + resolution: {integrity: sha512-UXKujV71VCI5uPs+cFdwxybtHZAIZyQkqDiDnmK+DawtOO9mBn4Nimdb/6RjR2CXT78mzO9ZCZ3qfyX+ydcB7w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/promises@2.3.0': + resolution: {integrity: sha512-GjVgutZKXVuojd9rWy1PuLnfcRfqsaCm7InCiZc8bqmJpoghlyluweNc7ml9Y5yQn1P2IOyzh9+p/77vIyNybQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-api@2.3.0': + resolution: {integrity: sha512-UUdiRfWoyYhJL9PPvFeJr4aJ554ob2jXcpn4vKmRVn9ire0sCbpQKYx6K8eEKHZWXKrDW8IDspgTl0gT/aJWVg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-parsed-types@2.3.0': + resolution: {integrity: sha512-B5pHzyEIbBJf9KHej+zdr5ZNAdSvu7WLU2lOUPh81KHdHQs6dEb310LGxcpCc7HVE8IEdO20AbckewDiAN6OCg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-spec-types@2.3.0': + resolution: {integrity: sha512-xQsb65lahjr8Wc9dMtP7xa0ZmDS8dOE2ncYjlvfyw/h4mpdXTUdrSMi6RtFwX33/rGuztQ7Hwaid5xLNSLvsFQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-spec@2.3.0': + resolution: {integrity: sha512-fA2LMX4BMixCrNB2n6T83AvjZ3oUQTu7qyPLyt8gHQaoEAXs8k6GZmu6iYcr+FboQCjUmRPgMaABbcr9j2J9Sw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-subscriptions-api@2.3.0': + resolution: {integrity: sha512-9mCjVbum2Hg9KGX3LKsrI5Xs0KX390lS+Z8qB80bxhar6MJPugqIPH8uRgLhCW9GN3JprAfjRNl7our8CPvsPQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-subscriptions-channel-websocket@2.3.0': + resolution: {integrity: sha512-2oL6ceFwejIgeWzbNiUHI2tZZnaOxNTSerszcin7wYQwijxtpVgUHiuItM/Y70DQmH9sKhmikQp+dqeGalaJxw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + ws: ^8.18.0 + + '@solana/rpc-subscriptions-spec@2.3.0': + resolution: {integrity: sha512-rdmVcl4PvNKQeA2l8DorIeALCgJEMSu7U8AXJS1PICeb2lQuMeaR+6cs/iowjvIB0lMVjYN2sFf6Q3dJPu6wWg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-subscriptions@2.3.0': + resolution: {integrity: sha512-Uyr10nZKGVzvCOqwCZgwYrzuoDyUdwtgQRefh13pXIrdo4wYjVmoLykH49Omt6abwStB0a4UL5gX9V4mFdDJZg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-transformers@2.3.0': + resolution: {integrity: sha512-UuHYK3XEpo9nMXdjyGKkPCOr7WsZsxs7zLYDO1A5ELH3P3JoehvrDegYRAGzBS2VKsfApZ86ZpJToP0K3PhmMA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-transport-http@2.3.0': + resolution: {integrity: sha512-HFKydmxGw8nAF5N+S0NLnPBDCe5oMDtI2RAmW8DMqP4U3Zxt2XWhvV1SNkAldT5tF0U1vP+is6fHxyhk4xqEvg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-types@2.3.0': + resolution: {integrity: sha512-O09YX2hED2QUyGxrMOxQ9GzH1LlEwwZWu69QbL4oYmIf6P5dzEEHcqRY6L1LsDVqc/dzAdEs/E1FaPrcIaIIPw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc@2.3.0': + resolution: {integrity: sha512-ZWN76iNQAOCpYC7yKfb3UNLIMZf603JckLKOOLTHuy9MZnTN8XV6uwvDFhf42XvhglgUjGCEnbUqWtxQ9pa/pQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/signers@2.3.0': + resolution: {integrity: sha512-OSv6fGr/MFRx6J+ZChQMRqKNPGGmdjkqarKkRzkwmv7v8quWsIRnJT5EV8tBy3LI4DLO/A8vKiNSPzvm1TdaiQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/subscribable@2.3.0': + resolution: {integrity: sha512-DkgohEDbMkdTWiKAoatY02Njr56WXx9e/dKKfmne8/Ad6/2llUIrax78nCdlvZW9quXMaXPTxZvdQqo9N669Og==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/sysvars@2.3.0': + resolution: {integrity: sha512-LvjADZrpZ+CnhlHqfI5cmsRzX9Rpyb1Ox2dMHnbsRNzeKAMhu9w4ZBIaeTdO322zsTr509G1B+k2ABD3whvUBA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/transaction-confirmation@2.3.0': + resolution: {integrity: sha512-UiEuiHCfAAZEKdfne/XljFNJbsKAe701UQHKXEInYzIgBjRbvaeYZlBmkkqtxwcasgBTOmEaEKT44J14N9VZDw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/transaction-messages@2.3.0': + resolution: {integrity: sha512-bgqvWuy3MqKS5JdNLH649q+ngiyOu5rGS3DizSnWwYUd76RxZl1kN6CoqHSrrMzFMvis6sck/yPGG3wqrMlAww==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/transactions@2.3.0': + resolution: {integrity: sha512-LnTvdi8QnrQtuEZor5Msje61sDpPstTVwKg4y81tNxDhiyomjuvnSNLAq6QsB9gIxUqbNzPZgOG9IU4I4/Uaug==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@tanstack/query-core@5.90.2': + resolution: {integrity: sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ==} + + '@tanstack/react-query@5.90.2': + resolution: {integrity: sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw==} + peerDependencies: + react: ^18 || ^19 + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/diff-match-patch@1.0.36': resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} @@ -1736,15 +2609,24 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/fs-extra@11.0.4': + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/jsonfile@6.1.4': + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/jsonwebtoken@9.0.9': resolution: {integrity: sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/mock-fs@4.13.4': resolution: {integrity: sha512-mXmM0o6lULPI8z3XNnQCpL0BGxPwx1Ul1wXYEPBGl4efShyxW2Rln0JOPEWGyZaYZMM6OVXM/15zUuFMY52ljg==} @@ -1763,6 +2645,12 @@ packages: '@types/node@22.14.1': resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} + '@types/node@22.15.17': + resolution: {integrity: sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@typescript-eslint/eslint-plugin@7.18.0': resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1824,6 +2712,10 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@vercel/oidc@3.0.3': + resolution: {integrity: sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg==} + engines: {node: '>= 20'} + '@vitest/expect@3.0.9': resolution: {integrity: sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==} @@ -1861,6 +2753,165 @@ packages: '@vitest/utils@3.0.9': resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} + '@wagmi/connectors@5.11.2': + resolution: {integrity: sha512-OkiElOI8xXGPDZE5UdG6NgDT3laSkEh9llX1DDapUnfnKecK3Tr/HUf5YzgwDhEoox8mdxp+8ZCjtnTKz56SdA==} + peerDependencies: + '@wagmi/core': 2.21.2 + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + + '@wagmi/core@2.21.2': + resolution: {integrity: sha512-Rp4waam2z0FQUDINkJ91jq38PI5wFUHCv1YBL2LXzAQswaEk1ZY8d6+WG3vYGhFHQ22DXy2AlQ8IWmj+2EG3zQ==} + peerDependencies: + '@tanstack/query-core': '>=5.0.0' + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + '@tanstack/query-core': + optional: true + typescript: + optional: true + + '@walletconnect/core@2.21.0': + resolution: {integrity: sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==} + engines: {node: '>=18'} + + '@walletconnect/core@2.21.1': + resolution: {integrity: sha512-Tp4MHJYcdWD846PH//2r+Mu4wz1/ZU/fr9av1UWFiaYQ2t2TPLDiZxjLw54AAEpMqlEHemwCgiRiAmjR1NDdTQ==} + engines: {node: '>=18'} + + '@walletconnect/environment@1.0.1': + resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + + '@walletconnect/ethereum-provider@2.21.1': + resolution: {integrity: sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + + '@walletconnect/events@1.0.1': + resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + + '@walletconnect/heartbeat@1.2.2': + resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} + + '@walletconnect/jsonrpc-http-connection@1.0.8': + resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} + + '@walletconnect/jsonrpc-provider@1.0.14': + resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + + '@walletconnect/jsonrpc-types@1.0.4': + resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} + + '@walletconnect/jsonrpc-utils@1.0.8': + resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} + + '@walletconnect/keyvaluestorage@1.1.1': + resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + peerDependencies: + '@react-native-async-storage/async-storage': 1.x + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@walletconnect/logger@2.1.2': + resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==} + + '@walletconnect/relay-api@1.0.11': + resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} + + '@walletconnect/relay-auth@1.1.0': + resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} + + '@walletconnect/safe-json@1.0.2': + resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + + '@walletconnect/sign-client@2.21.0': + resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + + '@walletconnect/sign-client@2.21.1': + resolution: {integrity: sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + + '@walletconnect/time@1.0.2': + resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + + '@walletconnect/types@2.21.0': + resolution: {integrity: sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==} + + '@walletconnect/types@2.21.1': + resolution: {integrity: sha512-UeefNadqP6IyfwWC1Yi7ux+ljbP2R66PLfDrDm8izmvlPmYlqRerJWJvYO4t0Vvr9wrG4Ko7E0c4M7FaPKT/sQ==} + + '@walletconnect/universal-provider@2.21.0': + resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + + '@walletconnect/universal-provider@2.21.1': + resolution: {integrity: sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' + + '@walletconnect/utils@2.21.0': + resolution: {integrity: sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==} + + '@walletconnect/utils@2.21.1': + resolution: {integrity: sha512-VPZvTcrNQCkbGOjFRbC24mm/pzbRMUq2DSQoiHlhh0X1U7ZhuIrzVtAoKsrzu6rqjz0EEtGxCr3K1TGRqDG4NA==} + + '@walletconnect/window-getters@1.0.1': + resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} + + '@walletconnect/window-metadata@1.0.1': + resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + + '@whatwg-node/fetch@0.9.23': + resolution: {integrity: sha512-7xlqWel9JsmxahJnYVUj/LLxWcnA93DR4c9xlw3U814jWTiYalryiH1qToik1hOxweKKRLi4haXHM5ycRksPBA==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/node-fetch@0.6.0': + resolution: {integrity: sha512-tcZAhrpx6oVlkEsRngeTEEE7I5/QdLjeEz4IlekabGaESP7+Dkm/6a9KcF1KdCBB7mO9PXtBkwCuTCt8+UPg8Q==} + engines: {node: '>=18.0.0'} + + '@zod/core@0.11.6': + resolution: {integrity: sha512-03Bv82fFSfjDAvMfdHHdGSS6SOJs0iCcJlWJv1kJHRtoTT02hZpyip/2Lk6oo4l4FtjuwTrsEQTwg/LD8I7dJA==} + + abitype@1.0.8: + resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.1.1: + resolution: {integrity: sha512-Loe5/6tAgsBukY95eGaPSDmQHIjRZYQq8PB1MpsNccDIK8WiV+Uw6WzaIXipvaxTEL2yEB0OpEaQv3gs8pkS9Q==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -1887,10 +2938,20 @@ packages: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} - agents@0.0.67: - resolution: {integrity: sha512-Bjn8w9230xcgeP/UKvWbZVvQvPuCpA1S0f8X6859xSvkviAcKWgHidnyRBUMUCniljNTzbU1ZgR8YWveJ4xbqg==} + agents@0.2.19: + resolution: {integrity: sha512-8icBmJp5A+PUdqQniq4JXZ9ef0ln6ImAVCJVp3oXxDTX/OjVQ25+gckxbIAUKtHbp2y6spK+QgbD2y+jn7hzyw==} peerDependencies: react: '*' + viem: '>=2.0.0' + x402: ^0.6.5 + peerDependenciesMeta: + viem: + optional: true + x402: + optional: true + + ai-gateway-provider@0.0.6: + resolution: {integrity: sha512-ArCgDH3C8SZJkk2VXgmFX39h3rnm5wui0ZwsMNwdKfhB0z+OTT4VSP6dpOfHKgtArwaBvcp5wc3WTCMl91A3dQ==} ai@4.3.10: resolution: {integrity: sha512-jw+ahNu+T4SHj9gtraIKtYhanJI6gj2IZ5BFcfEHgoyQVMln5a5beGjzl/nQSX6FxyLqJ/UBpClRa279EEKK/Q==} @@ -1902,6 +2963,22 @@ packages: react: optional: true + ai@4.3.15: + resolution: {integrity: sha512-TYKRzbWg6mx/pmTadlAEIhuQtzfHUV0BbLY72+zkovXwq/9xhcH24IlQmkyBpElK6/4ArS0dHdOOtR1jOPVwtg==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + + ai@5.0.78: + resolution: {integrity: sha512-ec77fmQwJGLduswMrW4AAUGSOiu8dZaIwMmWHHGKsrMUFFS6ugfkTyx0srtuKYHNRRLRC2dT7cPirnUl98VnxA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1921,6 +2998,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -1965,9 +3046,16 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + async-mutex@0.2.6: + resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -1978,10 +3066,19 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - better-path-resolve@1.0.0: + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + birpc@0.2.14: resolution: {integrity: sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==} @@ -1991,10 +3088,16 @@ packages: bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + bn.js@5.2.2: + resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + body-parser@2.2.0: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} engines: {node: '>=18'} + bowser@2.12.1: + resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -2005,6 +3108,20 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2017,18 +3134,30 @@ packages: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} - call-bound@1.0.3: - resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -2056,6 +3185,10 @@ packages: resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} engines: {node: '>= 0.8.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -2071,6 +3204,9 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2078,6 +3214,10 @@ packages: cloudflare@4.2.0: resolution: {integrity: sha512-L9IainZq+7PN3NG/Mg7T9oRm7SB7AskNe6QLD8j7FrJY+2Dn1qpeLqXF8Im04ANnssezf0KAOtSuxNAAyNEgkg==} + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -2100,6 +3240,10 @@ packages: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} + commander@14.0.1: + resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} + engines: {node: '>=20'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2116,6 +3260,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -2124,6 +3271,12 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + core-js-pure@3.45.0: + resolution: {integrity: sha512-OtwjqcDpY2X/eIIg1ol/n0y/X8A9foliaNt1dSK0gV3J2/zw+89FcNG3mPK+N8YWts4ZFUPxnrAzsxs/lf8yDA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -2137,10 +3290,21 @@ packages: typescript: optional: true + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + cron-schedule@5.0.4: resolution: {integrity: sha512-nH0a49E/kSVk6BeFgKZy4uUsy6D2A16p120h5bYD9ILBhQu7o2sJFH+WI4R731TSBQ0dB1Ik7inB/dRAB4C8QQ==} engines: {node: '>=18'} + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2149,6 +3313,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + data-uri-to-buffer@2.0.2: resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} @@ -2164,6 +3331,13 @@ packages: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2172,6 +3346,15 @@ packages: supports-color: optional: true + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.3.5: resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} @@ -2190,6 +3373,14 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -2220,6 +3411,17 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + derive-valtio@0.1.0: + resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} + peerDependencies: + valtio: '*' + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -2238,6 +3440,9 @@ packages: diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2261,6 +3466,13 @@ packages: duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + + eciesjs@0.4.15: + resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -2273,10 +3485,27 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + empathic@1.1.0: + resolution: {integrity: sha512-rsPft6CK3eHtrlp9Y5ALBb+hfK+DWnA4WFebbazxjWyx8vSm3rZeoM3z9irsjcqO3PYRzlfv27XIB4tz2DV7RA==} + engines: {node: '>=14'} + + encode-utf8@1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -2315,6 +3544,10 @@ packages: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} engines: {node: '>= 0.4'} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.3: resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} @@ -2330,6 +3563,9 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} + es-toolkit@1.33.0: + resolution: {integrity: sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==} + esbuild@0.25.1: resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} engines: {node: '>=18'} @@ -2464,6 +3700,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -2475,6 +3714,23 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + eth-block-tracker@7.1.0: + resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==} + engines: {node: '>=14.0.0'} + + eth-json-rpc-filters@6.0.1: + resolution: {integrity: sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==} + engines: {node: '>=14.0.0'} + + eth-query@2.1.2: + resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==} + + eth-rpc-errors@4.0.3: + resolution: {integrity: sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==} + + ethereum-cryptography@2.2.1: + resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + event-stream@3.3.4: resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} @@ -2485,8 +3741,18 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventsource-parser@3.0.1: - resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + eventemitter2@6.4.9: + resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} eventsource@3.0.6: @@ -2521,6 +3787,10 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + extension-port-stream@3.0.0: + resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==} + engines: {node: '>=12.0.0'} + external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} @@ -2529,6 +3799,9 @@ packages: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2542,6 +3815,19 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fastestsmallesttextencoderdecoder@1.0.22: + resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -2553,6 +3839,14 @@ packages: picomatch: optional: true + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -2564,6 +3858,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + finalhandler@2.1.0: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} @@ -2595,6 +3893,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} @@ -2643,6 +3945,10 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2659,6 +3965,14 @@ packages: resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} engines: {node: '>= 0.4'} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -2725,6 +4039,9 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + h3@1.15.4: + resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -2759,6 +4076,10 @@ packages: resolution: {integrity: sha512-564rVzELU+9BRqqx5k8sT2NFwGD3I3Vifdb6P7CmM6FiarOSY+fDC+6B+k9wcCb86ReoayteZP2ki0cRLN1jbw==} engines: {node: '>=16.9.0'} + hono@4.9.9: + resolution: {integrity: sha512-Hxw4wT6zjJGZJdkJzAx9PyBdf7ZpxaTSA0NfxqjLghwMrLBX8p33hJBzoETRakF3UJu6OdNQBZAlNSkGqKFukw==} + engines: {node: '>=16.9.0'} + hosted-git-info@8.0.2: resolution: {integrity: sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==} engines: {node: ^18.17.0 || >=20.5.0} @@ -2786,6 +4107,15 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + idb-keyval@6.2.1: + resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} + + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -2798,6 +4128,9 @@ packages: resolution: {integrity: sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -2825,6 +4158,13 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -2869,6 +4209,10 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -2900,6 +4244,10 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} @@ -2924,6 +4272,10 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -2939,15 +4291,31 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + joi@17.13.3: resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + js-base64@3.7.8: + resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2970,6 +4338,18 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-rpc-engine@6.1.0: + resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} + engines: {node: '>=10.0.0'} + + json-rpc-random-id@1.0.1: + resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==} + + json-schema-to-typescript@15.0.4: + resolution: {integrity: sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==} + engines: {node: '>=16.0.0'} + hasBin: true + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -2994,9 +4374,19 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + keccak@3.0.4: + resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} + engines: {node: '>=10.0.0'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyvaluestorage-interface@1.0.0: + resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -3009,6 +4399,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-darwin-arm64@1.29.2: resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} engines: {node: '>= 12.0.0'} @@ -3076,6 +4469,15 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lit-element@4.2.1: + resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} + + lit-html@3.3.1: + resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} + + lit@3.3.0: + resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -3123,6 +4525,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -3153,6 +4559,9 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micro-ftch@0.3.1: + resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3183,6 +4592,9 @@ packages: engines: {node: '>=16'} hasBin: true + mimetext@3.0.27: + resolution: {integrity: sha512-mUhWAsZD1N/K6dbN4+a5Yq78OPnYQw1ubOSMasBntsLQ2S7KVNlvDEA8dwpr4a7PszWMzeslKahAprtwYMgaBA==} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3201,6 +4613,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + miniflare@4.20250428.1: + resolution: {integrity: sha512-M3qcJXjeAEimHrEeWXEhrJiC3YHB5M3QSqqK67pOTI+lHn0QyVG/2iFUjVJ/nv+i10uxeAEva8GRGeu+tKRCmQ==} + engines: {node: '>=18.0.0'} + hasBin: true + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3211,6 +4628,14 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mipd@0.0.7: + resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + mock-fs@5.5.0: resolution: {integrity: sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==} engines: {node: '>=12.0.0'} @@ -3229,6 +4654,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true @@ -3238,8 +4666,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.5: - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} engines: {node: ^18 || >=20} hasBin: true @@ -3250,9 +4678,19 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + node-addon-api@2.0.2: + resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch-native@1.6.6: + resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} @@ -3263,6 +4701,17 @@ packages: encoding: optional: true + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-mock-http@1.0.3: + resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + npm-package-arg@12.0.2: resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} engines: {node: ^18.17.0 || >=20.5.0} @@ -3271,6 +4720,9 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + obj-multiplex@1.0.0: + resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3303,9 +4755,15 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-exit-leak-free@0.2.0: + resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3321,6 +4779,12 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + openapi-fetch@0.13.8: + resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} + + openapi-typescript-helpers@0.0.15: + resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} + optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -3336,6 +4800,38 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + ox@0.6.7: + resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.6: + resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.8: + resolution: {integrity: sha512-bedy2pidGW8/XKVlXiAo/sIJxO4RAY9DsLyzZ7ppzGdPrECjS/7RN26CDoeABkbCtZWtGH5k/+Sx/KD/8J3xUQ==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -3367,6 +4863,9 @@ packages: package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3379,13 +4878,13 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - partyserver@0.0.67: - resolution: {integrity: sha512-GQ0fjJ7n5r5LrsFHFUkGR3Bd50YdBZaDNjvRTi8PowZgI5fvCFliT/XdDpRVuwfDDk0jb9es2cXcaAh1z5GsLA==} + partyserver@0.0.75: + resolution: {integrity: sha512-i/18vvdxuGjx+rpQ+fDdExlvQoRb7EfTF+6b+kA2ILEpHemtpLWV8NdgDrOPEklRNdCc/4WlzDtYn05d17aZAQ==} peerDependencies: '@cloudflare/workers-types': ^4.20240729.0 - partysocket@1.1.3: - resolution: {integrity: sha512-87Jd/nqPoWnVfzHE6Z12WLWTJ+TAgxs0b7i2S163HfQSrVDUK5tW/FC64T5N8L5ss+gqF+EV0BwjZMWggMY3UA==} + partysocket@1.1.6: + resolution: {integrity: sha512-LkEk8N9hMDDsDT0iDK0zuwUDFVrVMUXFXCeN3850Ng8wtjPqPBeJlwdeY6ROlJSEh3tPoTTasXoSBYH76y118w==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -3438,14 +4937,60 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pify@5.0.0: + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} + + pino-abstract-transport@0.5.0: + resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} + + pino-std-serializers@4.0.0: + resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} + + pino@7.11.0: + resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} + hasBin: true + pkce-challenge@5.0.0: resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} engines: {node: '>=16.20.0'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + pony-cause@2.1.11: + resolution: {integrity: sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==} + engines: {node: '>=12.0.0'} + + porto@0.2.19: + resolution: {integrity: sha512-q1vEJgdtlEOf6byWgD31GHiMwpfLuxFSfx9f7Sw4RGdvpQs2ANBGfnzzardADZegr87ZXsebSp+3vaaznEUzPQ==} + hasBin: true + peerDependencies: + '@tanstack/react-query': '>=5.59.0' + '@wagmi/core': '>=2.16.3' + react: '>=18' + typescript: '>=5.4.0' + viem: '>=2.37.0' + wagmi: '>=2.0.0' + peerDependenciesMeta: + '@tanstack/react-query': + optional: true + react: + optional: true + typescript: + optional: true + wagmi: + optional: true + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -3454,6 +4999,12 @@ packages: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + + preact@10.27.2: + resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3475,6 +5026,12 @@ packages: resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} engines: {node: ^18.17.0 || >=20.5.0} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@1.0.0: + resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -3483,6 +5040,9 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + proxy-compare@2.6.0: + resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -3491,6 +5051,9 @@ packages: engines: {node: '>= 0.10'} hasBin: true + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3498,6 +5061,11 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + qrcode@1.5.3: + resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} + engines: {node: '>=10.13.0'} + hasBin: true + qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -3505,9 +5073,19 @@ packages: quansync@0.2.8: resolution: {integrity: sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==} + query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -3528,6 +5106,21 @@ packages: resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} engines: {node: '>=10.13'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + real-require@0.1.0: + resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} + engines: {node: '>= 12.13.0'} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -3539,6 +5132,9 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3586,6 +5182,9 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -3593,6 +5192,14 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -3616,6 +5223,9 @@ packages: resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} engines: {node: '>= 18'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -3624,9 +5234,17 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3695,6 +5313,17 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + sonic-boom@2.8.0: + resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3706,6 +5335,14 @@ packages: spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + split@0.3.3: resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} @@ -3744,6 +5381,17 @@ packages: stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3763,6 +5411,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3787,6 +5438,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + superstruct@1.0.4: + resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} + engines: {node: '>=14.0.0'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3820,6 +5475,9 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + thread-stream@0.15.2: + resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + throttleit@2.1.0: resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} engines: {node: '>=18'} @@ -3841,6 +5499,10 @@ packages: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3857,6 +5519,10 @@ packages: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3891,6 +5557,9 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3949,6 +5618,10 @@ packages: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} @@ -3969,15 +5642,27 @@ packages: ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + uint8arrays@3.1.0: + resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==} + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici@5.28.5: resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==} engines: {node: '>=14.0'} @@ -3997,43 +5682,169 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - uri-js@4.4.1: + unstorage@1.17.1: + resolution: {integrity: sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + + use-sync-external-store@1.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + use-sync-external-store@1.5.0: resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + validate-npm-package-name@6.0.0: resolution: {integrity: sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==} engines: {node: ^18.17.0 || >=20.5.0} + valtio@1.13.2: + resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=16.8' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + viem@2.23.2: + resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.37.9: + resolution: {integrity: sha512-XXUOE5yJcjr9/M9kRoQcPMUfetwHprO9aTho6vNELjBKJIBx7rYq1fjvBw+xEnhsRjhh5lsORi6B0h8fYFB7NA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vite-node@3.0.9: resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.3.4: + resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -4048,6 +5859,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vitest-evals@0.1.4: resolution: {integrity: sha512-hsid14fRwsTme/50TmdWNw7klXfRGuBXyFOxFb8MvEjKx16CCrjL0T26fD7GThEzdn3WH/kDVOYb9Gtd6AvFTA==} @@ -4082,6 +5897,17 @@ packages: jsdom: optional: true + wagmi@2.17.5: + resolution: {integrity: sha512-Sk2e40gfo68gbJ6lHkpIwCMkH76rO0+toCPjf3PzdQX37rZo9042DdNTYcSg3zhnx8abFJtrk/5vAWfR8APTDw==} + peerDependencies: + '@tanstack/react-query': '>=5.0.0' + react: '>=18' + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + wait-on@8.0.3: resolution: {integrity: sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==} engines: {node: '>=12.0.0'} @@ -4091,6 +5917,9 @@ packages: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} + webextension-polyfill@0.10.0: + resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -4100,10 +5929,17 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which-typed-array@1.1.15: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4124,9 +5960,22 @@ packages: engines: {node: '>=16'} hasBin: true + workerd@1.20250428.0: + resolution: {integrity: sha512-JJNWkHkwPQKQdvtM9UORijgYdcdJsihA4SfYjwh02IUQsdMyZ9jizV1sX9yWi9B9ptlohTW8UNHJEATuphGgdg==} + engines: {node: '>=16'} + hasBin: true + + workerd@1.20250507.0: + resolution: {integrity: sha512-OXaGjEh5THT9iblwWIyPrYBoaPe/d4zN03Go7/w8CmS8sma7//O9hjbk43sboWkc89taGPmU0/LNyZUUiUlHeQ==} + engines: {node: '>=16'} + hasBin: true + workers-ai-provider@0.3.0: resolution: {integrity: sha512-NCnwRJ0OVgyVIZwbHRmn+8A80vi4iXxnjvLaxn0CCYMsooVKgf52rEIcbap5FJNd7fx3nLOi2OgNKjqS1ipz5A==} + workers-tagged-logger@0.13.5: + resolution: {integrity: sha512-B+cLaM38vOpnEikAB19PK8RkUS4QaeK7iq0lle6Xvp5V5a55mpIO9Gn2nSAT18Ph4QvF/H2vnCReYlAMcBe6Jw==} + wrangler@4.10.0: resolution: {integrity: sha512-fTE4hZ79msEUt8+HEjl/8Q72haCyzPLu4PgrU3L81ysmjrMEdiYfUPqnvCkBUVtJvrDNdctTEimkufT1Y0ipNg==} engines: {node: '>=18.0.0'} @@ -4147,6 +5996,10 @@ packages: '@cloudflare/workers-types': optional: true + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4154,6 +6007,30 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -4166,14 +6043,48 @@ packages: utf-8-validate: optional: true + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + x402@0.6.5: + resolution: {integrity: sha512-Pk+JZiCwuXSzwuhY6Dsz5mu/O9T6QnZD7VPVcgI3gixe4F/rwv4h09bUygB9kckRpq+XYYx3Hqv4nTk8uBCzvQ==} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -4190,25 +6101,127 @@ packages: peerDependencies: zod: ^3.24.1 + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + zod@3.22.3: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} + zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} - zod@3.24.3: - resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.0.0-beta.20250505T195954: + resolution: {integrity: sha512-iB8WvxkobVIXMARvQu20fKvbS7mUTiYRpcD8OQV1xjRhxO0EEpYIRJBk6yfBzHAHEdOSDh3SxDITr5Eajr2vtg==} + + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + + zustand@5.0.0: + resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.3: + resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.8: + resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zx@8.5.4: + resolution: {integrity: sha512-44oKea9Sa8ZnOkTnS6fRJpg3quzgnbB43nLrVfYnqE86J4sxgZMUDLezzKET/FdOAVkF4X+Alm9Bume+W+RW9Q==} + engines: {node: '>= 12.17.0'} + hasBin: true snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} + '@adraffy/ens-normalize@1.11.1': + optional: true + + '@ai-sdk/anthropic@1.2.11(zod@3.24.2)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + zod: 3.24.2 + + '@ai-sdk/gateway@2.0.1(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.12(zod@3.25.76) + '@vercel/oidc': 3.0.3 + zod: 3.25.76 + + '@ai-sdk/google@1.2.17(zod@3.24.2)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + zod: 3.24.2 + '@ai-sdk/openai@1.3.20(zod@3.24.2)': dependencies: '@ai-sdk/provider': 1.1.3 '@ai-sdk/provider-utils': 2.2.7(zod@3.24.2) zod: 3.24.2 + '@ai-sdk/openai@2.0.53(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.12(zod@3.25.76) + zod: 3.25.76 + '@ai-sdk/provider-utils@2.2.7(zod@3.24.2)': dependencies: '@ai-sdk/provider': 1.1.3 @@ -4216,36 +6229,54 @@ snapshots: secure-json-parse: 2.7.0 zod: 3.24.2 - '@ai-sdk/provider-utils@2.2.7(zod@3.24.3)': + '@ai-sdk/provider-utils@2.2.8(zod@3.24.2)': dependencies: '@ai-sdk/provider': 1.1.3 nanoid: 3.3.8 secure-json-parse: 2.7.0 - zod: 3.24.3 + zod: 3.24.2 + + '@ai-sdk/provider-utils@3.0.12(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.6 + zod: 3.25.76 '@ai-sdk/provider@1.1.3': dependencies: json-schema: 0.4.0 - '@ai-sdk/react@1.2.9(react@17.0.2)(zod@3.24.2)': + '@ai-sdk/provider@2.0.0': dependencies: - '@ai-sdk/provider-utils': 2.2.7(zod@3.24.2) - '@ai-sdk/ui-utils': 1.2.8(zod@3.24.2) + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@17.0.2)(zod@3.24.2)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + '@ai-sdk/ui-utils': 1.2.11(zod@3.24.2) react: 17.0.2 swr: 2.3.3(react@17.0.2) throttleit: 2.1.0 optionalDependencies: zod: 3.24.2 - '@ai-sdk/react@1.2.9(react@17.0.2)(zod@3.24.3)': + '@ai-sdk/react@1.2.9(react@17.0.2)(zod@3.24.2)': dependencies: - '@ai-sdk/provider-utils': 2.2.7(zod@3.24.3) - '@ai-sdk/ui-utils': 1.2.8(zod@3.24.3) + '@ai-sdk/provider-utils': 2.2.7(zod@3.24.2) + '@ai-sdk/ui-utils': 1.2.8(zod@3.24.2) react: 17.0.2 swr: 2.3.3(react@17.0.2) throttleit: 2.1.0 optionalDependencies: - zod: 3.24.3 + zod: 3.24.2 + + '@ai-sdk/ui-utils@1.2.11(zod@3.24.2)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + zod: 3.24.2 + zod-to-json-schema: 3.24.5(zod@3.24.2) '@ai-sdk/ui-utils@1.2.8(zod@3.24.2)': dependencies: @@ -4254,12 +6285,11 @@ snapshots: zod: 3.24.2 zod-to-json-schema: 3.24.5(zod@3.24.2) - '@ai-sdk/ui-utils@1.2.8(zod@3.24.3)': + '@apidevtools/json-schema-ref-parser@11.9.3': dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.7(zod@3.24.3) - zod: 3.24.3 - zod-to-json-schema: 3.24.5(zod@3.24.3) + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + js-yaml: 4.1.0 '@babel/code-frame@7.26.2': dependencies: @@ -4283,6 +6313,10 @@ snapshots: dependencies: '@babel/types': 7.26.10 + '@babel/runtime-corejs3@7.28.2': + dependencies: + core-js-pure: 3.45.0 + '@babel/runtime@7.26.10': dependencies: regenerator-runtime: 0.14.1 @@ -4310,6 +6344,27 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@base-org/account@1.1.1(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.5.4)(zod@3.24.2) + preact: 10.24.2 + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + zustand: 5.0.3(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + optional: true + '@changesets/apply-release-plan@7.0.10': dependencies: '@changesets/config': 3.1.1 @@ -4468,7 +6523,32 @@ snapshots: optionalDependencies: workerd: 1.20250409.0 - '@cloudflare/vitest-pool-workers@0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(vitest@3.0.9)': + '@cloudflare/unenv-preset@2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250507.0)': + dependencies: + unenv: 2.0.0-rc.15 + optionalDependencies: + workerd: 1.20250507.0 + + '@cloudflare/vite-plugin@1.1.0(bufferutil@4.0.9)(rollup@4.35.0)(utf-8-validate@5.0.10)(vite@6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3))(workerd@1.20250507.0)(wrangler@4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@cloudflare/unenv-preset': 2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250507.0) + '@hattip/adapter-node': 0.0.49 + '@rollup/plugin-replace': 6.0.2(rollup@4.35.0) + get-port: 7.1.0 + miniflare: 4.20250428.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + picocolors: 1.1.1 + tinyglobby: 0.2.13 + unenv: 2.0.0-rc.15 + vite: 6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3) + wrangler: 4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - rollup + - utf-8-validate + - workerd + + '@cloudflare/vitest-pool-workers@0.8.14(@cloudflare/workers-types@4.20250416.0)(@vitest/runner@3.0.9)(@vitest/snapshot@3.0.9)(bufferutil@4.0.9)(utf-8-validate@5.0.10)(vitest@3.0.9)': dependencies: '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 @@ -4476,10 +6556,10 @@ snapshots: cjs-module-lexer: 1.3.1 devalue: 4.3.3 esbuild: 0.25.1 - miniflare: 4.20250408.0 + miniflare: 4.20250408.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) semver: 7.7.1 - vitest: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) - wrangler: 4.9.1(@cloudflare/workers-types@4.20250416.0) + vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) + wrangler: 4.9.1(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 3.24.2 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -4492,42 +6572,115 @@ snapshots: '@cloudflare/workerd-darwin-64@1.20250409.0': optional: true + '@cloudflare/workerd-darwin-64@1.20250428.0': + optional: true + + '@cloudflare/workerd-darwin-64@1.20250507.0': + optional: true + '@cloudflare/workerd-darwin-arm64@1.20250408.0': optional: true '@cloudflare/workerd-darwin-arm64@1.20250409.0': optional: true + '@cloudflare/workerd-darwin-arm64@1.20250428.0': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20250507.0': + optional: true + '@cloudflare/workerd-linux-64@1.20250408.0': optional: true '@cloudflare/workerd-linux-64@1.20250409.0': optional: true + '@cloudflare/workerd-linux-64@1.20250428.0': + optional: true + + '@cloudflare/workerd-linux-64@1.20250507.0': + optional: true + '@cloudflare/workerd-linux-arm64@1.20250408.0': optional: true '@cloudflare/workerd-linux-arm64@1.20250409.0': optional: true + '@cloudflare/workerd-linux-arm64@1.20250428.0': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20250507.0': + optional: true + '@cloudflare/workerd-windows-64@1.20250408.0': optional: true '@cloudflare/workerd-windows-64@1.20250409.0': optional: true - '@cloudflare/workers-oauth-provider@0.0.5': - dependencies: - '@cloudflare/workers-types': 4.20250416.0 + '@cloudflare/workerd-windows-64@1.20250428.0': + optional: true + + '@cloudflare/workerd-windows-64@1.20250507.0': + optional: true + + '@cloudflare/workers-oauth-provider@0.0.13': {} - '@cloudflare/workers-types@4.20250414.0': {} + '@cloudflare/workers-types@4.20250410.0': {} '@cloudflare/workers-types@4.20250416.0': {} + '@coinbase/wallet-sdk@3.9.3': + dependencies: + bn.js: 5.2.2 + buffer: 6.0.3 + clsx: 1.2.1 + eth-block-tracker: 7.1.0 + eth-json-rpc-filters: 6.0.1 + eventemitter3: 5.0.1 + keccak: 3.0.4 + preact: 10.27.2 + sha.js: 2.4.12 + transitivePeerDependencies: + - supports-color + optional: true + + '@coinbase/wallet-sdk@4.3.6(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.5.4)(zod@3.24.2) + preact: 10.24.2 + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + zustand: 5.0.3(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + optional: true + + '@commander-js/extra-typings@13.1.0(commander@13.1.0)': + dependencies: + commander: 13.1.0 + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@ecies/ciphers@0.2.4(@noble/ciphers@1.3.0)': + dependencies: + '@noble/ciphers': 1.3.0 + optional: true + '@effect/schema@0.75.5(effect@3.13.10)': dependencies: effect: 3.13.10 @@ -4636,6 +6789,30 @@ snapshots: '@eslint/js@8.57.0': {} + '@ethereumjs/common@3.2.0': + dependencies: + '@ethereumjs/util': 8.1.0 + crc-32: 1.2.2 + optional: true + + '@ethereumjs/rlp@4.0.1': + optional: true + + '@ethereumjs/tx@4.2.0': + dependencies: + '@ethereumjs/common': 3.2.0 + '@ethereumjs/rlp': 4.0.1 + '@ethereumjs/util': 8.1.0 + ethereum-cryptography: 2.2.1 + optional: true + + '@ethereumjs/util@8.1.0': + dependencies: + '@ethereumjs/rlp': 4.0.1 + ethereum-cryptography: 2.2.1 + micro-ftch: 0.3.1 + optional: true + '@fast-csv/format@5.0.2': dependencies: lodash.escaperegexp: 4.1.2 @@ -4646,11 +6823,44 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@hapi/hoek@9.3.0': {} - - '@hapi/topo@5.1.0': + '@gemini-wallet/core@0.2.0(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))': dependencies: - '@hapi/hoek': 9.3.0 + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - supports-color + optional: true + + '@hapi/hoek@9.3.0': {} + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + + '@hattip/adapter-node@0.0.49': + dependencies: + '@hattip/core': 0.0.49 + '@hattip/polyfills': 0.0.49 + '@hattip/walk': 0.0.49 + + '@hattip/core@0.0.49': {} + + '@hattip/headers@0.0.49': + dependencies: + '@hattip/core': 0.0.49 + + '@hattip/polyfills@0.0.49': + dependencies: + '@hattip/core': 0.0.49 + '@whatwg-node/fetch': 0.9.23 + node-fetch-native: 1.6.6 + + '@hattip/walk@0.0.49': + dependencies: + '@hattip/headers': 0.0.49 + cac: 6.7.14 + mime-types: 2.1.35 '@hono/node-server@1.13.8(hono@4.7.6)': dependencies: @@ -4759,6 +6969,14 @@ snapshots: '@img/sharp-win32-x64@0.33.5': optional: true + '@jahands/cli-tools@0.10.2(@commander-js/extra-typings@13.1.0(commander@13.1.0))(commander@13.1.0)(typescript@5.5.4)(zod@4.0.0-beta.20250505T195954)(zx@8.5.4)': + dependencies: + '@commander-js/extra-typings': 13.1.0(commander@13.1.0) + commander: 13.1.0 + typescript: 5.5.4 + zod: 4.0.0-beta.20250505T195954 + zx: 8.5.4 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -4781,6 +6999,18 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jsdevtools/ono@7.1.3': {} + + '@kamilkisiela/fast-url-parser@1.1.4': {} + + '@lit-labs/ssr-dom-shim@1.4.0': + optional: true + + '@lit/reactive-element@2.1.1': + dependencies: + '@lit-labs/ssr-dom-shim': 1.4.0 + optional: true + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.26.10 @@ -4797,12 +7027,219 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@modelcontextprotocol/sdk@1.10.2': + '@metamask/eth-json-rpc-provider@1.0.1': + dependencies: + '@metamask/json-rpc-engine': 7.3.3 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 5.0.2 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/json-rpc-engine@7.3.3': + dependencies: + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/json-rpc-engine@8.0.2': + dependencies: + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/json-rpc-middleware-stream@7.0.2': + dependencies: + '@metamask/json-rpc-engine': 8.0.2 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + readable-stream: 3.6.2 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/object-multiplex@2.1.0': + dependencies: + once: 1.4.0 + readable-stream: 3.6.2 + optional: true + + '@metamask/onboarding@1.0.1': + dependencies: + bowser: 2.12.1 + optional: true + + '@metamask/providers@16.1.0': + dependencies: + '@metamask/json-rpc-engine': 8.0.2 + '@metamask/json-rpc-middleware-stream': 7.0.2 + '@metamask/object-multiplex': 2.1.0 + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + detect-browser: 5.3.0 + extension-port-stream: 3.0.0 + fast-deep-equal: 3.1.3 + is-stream: 2.0.1 + readable-stream: 3.6.2 + webextension-polyfill: 0.10.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/rpc-errors@6.4.0': + dependencies: + '@metamask/utils': 9.3.0 + fast-safe-stringify: 2.1.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/rpc-errors@7.0.2': + dependencies: + '@metamask/utils': 11.8.1 + fast-safe-stringify: 2.1.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/safe-event-emitter@2.0.0': + optional: true + + '@metamask/safe-event-emitter@3.1.2': + optional: true + + '@metamask/sdk-analytics@0.0.5': + dependencies: + openapi-fetch: 0.13.8 + optional: true + + '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@metamask/sdk-analytics': 0.0.5 + bufferutil: 4.0.9 + cross-fetch: 4.1.0 + date-fns: 2.30.0 + debug: 4.3.4 + eciesjs: 0.4.15 + eventemitter2: 6.4.9 + readable-stream: 3.6.2 + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + utf-8-validate: 5.0.10 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/sdk-install-modal-web@0.32.1': + dependencies: + '@paulmillr/qr': 0.2.1 + optional: true + + '@metamask/sdk@0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@babel/runtime': 7.26.10 + '@metamask/onboarding': 1.0.1 + '@metamask/providers': 16.1.0 + '@metamask/sdk-analytics': 0.0.5 + '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@metamask/sdk-install-modal-web': 0.32.1 + '@paulmillr/qr': 0.2.1 + bowser: 2.12.1 + cross-fetch: 4.1.0 + debug: 4.3.4 + eciesjs: 0.4.15 + eth-rpc-errors: 4.0.3 + eventemitter2: 6.4.9 + obj-multiplex: 1.0.0 + pump: 3.0.3 + readable-stream: 3.6.2 + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + tslib: 2.8.1 + util: 0.12.5 + uuid: 8.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + optional: true + + '@metamask/superstruct@3.2.1': + optional: true + + '@metamask/utils@11.8.1': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.12 + '@types/lodash': 4.17.20 + debug: 4.4.0 + lodash: 4.17.21 + pony-cause: 2.1.11 + semver: 7.7.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/utils@5.0.2': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@types/debug': 4.1.12 + debug: 4.4.0 + semver: 7.7.1 + superstruct: 1.0.4 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/utils@8.5.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.12 + debug: 4.4.0 + pony-cause: 2.1.11 + semver: 7.7.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@metamask/utils@9.3.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.12 + debug: 4.4.0 + pony-cause: 2.1.11 + semver: 7.7.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@modelcontextprotocol/sdk@1.20.2': dependencies: + ajv: 6.12.6 content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.6 + eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.0(express@5.1.0) pkce-challenge: 5.0.0 @@ -4816,6 +7253,49 @@ snapshots: dependencies: zod: 3.24.2 + '@noble/ciphers@1.2.1': + optional: true + + '@noble/ciphers@1.3.0': + optional: true + + '@noble/curves@1.4.2': + dependencies: + '@noble/hashes': 1.4.0 + optional: true + + '@noble/curves@1.8.0': + dependencies: + '@noble/hashes': 1.7.0 + optional: true + + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + optional: true + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + optional: true + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + optional: true + + '@noble/hashes@1.4.0': + optional: true + + '@noble/hashes@1.7.0': + optional: true + + '@noble/hashes@1.7.1': + optional: true + + '@noble/hashes@1.8.0': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4832,8 +7312,297 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@paulmillr/qr@0.2.1': + optional: true + '@polka/url@1.0.0-next.28': {} + '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + + '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + + '@reown/appkit-controllers@1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + valtio: 1.13.2(react@17.0.2) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@reown/appkit-pay@1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-controllers': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-ui': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-utils': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(react@17.0.2))(zod@3.24.2) + lit: 3.3.0 + valtio: 1.13.2(react@17.0.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@reown/appkit-polyfills@1.7.8': + dependencies: + buffer: 6.0.3 + optional: true + + '@reown/appkit-scaffold-ui@1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(react@17.0.2))(zod@3.24.2)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-controllers': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-ui': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-utils': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(react@17.0.2))(zod@3.24.2) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - valtio + - zod + optional: true + + '@reown/appkit-ui@1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-controllers': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@reown/appkit-utils@1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(react@17.0.2))(zod@3.24.2)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-controllers': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + valtio: 1.13.2(react@17.0.2) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@reown/appkit-wallet@1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + '@reown/appkit-polyfills': 1.7.8 + '@walletconnect/logger': 2.1.2 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + optional: true + + '@reown/appkit@1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-controllers': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-pay': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-scaffold-ui': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(react@17.0.2))(zod@3.24.2) + '@reown/appkit-ui': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@reown/appkit-utils': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(react@17.0.2))(zod@3.24.2) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.21.0 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + bs58: 6.0.0 + valtio: 1.13.2(react@17.0.2) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@rollup/plugin-replace@6.0.2(rollup@4.35.0)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.35.0) + magic-string: 0.30.17 + optionalDependencies: + rollup: 4.35.0 + + '@rollup/pluginutils@5.1.4(rollup@4.35.0)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.35.0 + '@rollup/rollup-android-arm-eabi@4.35.0': optional: true @@ -4893,18 +7662,88 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@sentry/core@8.9.2': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': dependencies: - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 - - '@sentry/types@8.9.2': {} + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true - '@sentry/utils@8.9.2': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': dependencies: - '@sentry/types': 8.9.2 - - '@sideway/address@4.1.5': + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + optional: true + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': + optional: true + + '@scure/base@1.1.9': + optional: true + + '@scure/base@1.2.6': + optional: true + + '@scure/bip32@1.4.0': + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + optional: true + + '@scure/bip32@1.6.2': + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + optional: true + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + optional: true + + '@scure/bip39@1.3.0': + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + optional: true + + '@scure/bip39@1.5.4': + dependencies: + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + optional: true + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + optional: true + + '@sentry/core@8.9.2': + dependencies: + '@sentry/types': 8.9.2 + '@sentry/utils': 8.9.2 + + '@sentry/types@8.9.2': {} + + '@sentry/utils@8.9.2': + dependencies: + '@sentry/types': 8.9.2 + + '@sideway/address@4.1.5': dependencies: '@hapi/hoek': 9.3.0 @@ -4914,8 +7753,429 @@ snapshots: '@sindresorhus/merge-streams@2.3.0': {} + '@socket.io/component-emitter@3.1.2': + optional: true + + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + dependencies: + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + optional: true + + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))': + dependencies: + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + optional: true + + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + dependencies: + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + optional: true + + '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec': 2.3.0(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/addresses@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/assertions': 2.3.0(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/assertions@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/codecs-core@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/codecs-data-structures@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/codecs-numbers@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/codecs-strings@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.5.4 + optional: true + + '@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-data-structures': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/options': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/errors@2.3.0(typescript@5.5.4)': + dependencies: + chalk: 5.4.1 + commander: 14.0.1 + typescript: 5.5.4 + optional: true + + '@solana/fast-stable-stringify@2.3.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + optional: true + + '@solana/functional@2.3.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + optional: true + + '@solana/instructions@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/keys@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/assertions': 2.3.0(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/instructions': 2.3.0(typescript@5.5.4) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + optional: true + + '@solana/nominal-types@2.3.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + optional: true + + '@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-data-structures': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/promises@2.3.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + optional: true + + '@solana/rpc-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec': 2.3.0(typescript@5.5.4) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-parsed-types@2.3.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + optional: true + + '@solana/rpc-spec-types@2.3.0(typescript@5.5.4)': + dependencies: + typescript: 5.5.4 + optional: true + + '@solana/rpc-spec@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/rpc-subscriptions-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.5.4) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.5.4) + '@solana/subscribable': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optional: true + + '@solana/rpc-subscriptions-spec@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/promises': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + '@solana/subscribable': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/promises': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.5.4) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/subscribable': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + optional: true + + '@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc-transport-http@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + undici-types: 7.16.0 + optional: true + + '@solana/rpc-types@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/rpc@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/rpc-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-spec': 2.3.0(typescript@5.5.4) + '@solana/rpc-spec-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-transport-http': 2.3.0(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/instructions': 2.3.0(typescript@5.5.4) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/subscribable@2.3.0(typescript@5.5.4)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.5.4) + typescript: 5.5.4 + optional: true + + '@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/promises': 2.3.0(typescript@5.5.4) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + optional: true + + '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-data-structures': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/instructions': 2.3.0(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + + '@solana/transactions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.3.0(typescript@5.5.4) + '@solana/codecs-data-structures': 2.3.0(typescript@5.5.4) + '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.3.0(typescript@5.5.4) + '@solana/functional': 2.3.0(typescript@5.5.4) + '@solana/instructions': 2.3.0(typescript@5.5.4) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/nominal-types': 2.3.0(typescript@5.5.4) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + optional: true + '@standard-schema/spec@1.0.0': {} + '@tanstack/query-core@5.90.2': + optional: true + + '@tanstack/react-query@5.90.2(react@17.0.2)': + dependencies: + '@tanstack/query-core': 5.90.2 + react: 17.0.2 + optional: true + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + optional: true + '@types/diff-match-patch@1.0.36': {} '@types/eslint@8.56.10': @@ -4925,15 +8185,26 @@ snapshots: '@types/estree@1.0.6': {} + '@types/fs-extra@11.0.4': + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 22.14.1 + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} + '@types/jsonfile@6.1.4': + dependencies: + '@types/node': 22.14.1 + '@types/jsonwebtoken@9.0.9': dependencies: '@types/ms': 2.1.0 '@types/node': 22.14.1 + '@types/lodash@4.17.20': {} + '@types/mock-fs@4.13.4': dependencies: '@types/node': 22.14.1 @@ -4955,6 +8226,14 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.15.17': + dependencies: + undici-types: 6.21.0 + optional: true + + '@types/trusted-types@2.0.7': + optional: true + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 @@ -5038,6 +8317,8 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@vercel/oidc@3.0.3': {} + '@vitest/expect@3.0.9': dependencies: '@vitest/spy': 3.0.9 @@ -5045,53 +8326,739 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.9(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.2))': + '@vitest/mocker@3.0.9(vite@6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3))': dependencies: '@vitest/spy': 3.0.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.2) + vite: 6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3) + + '@vitest/mocker@3.0.9(vite@6.3.4(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3))': + dependencies: + '@vitest/spy': 3.0.9 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.4(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3) + + '@vitest/pretty-format@3.0.9': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/pretty-format@3.1.1': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.0.9': + dependencies: + '@vitest/utils': 3.0.9 + pathe: 2.0.3 + + '@vitest/snapshot@3.0.9': + dependencies: + '@vitest/pretty-format': 3.0.9 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.0.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/ui@3.0.9(vitest@3.0.9)': + dependencies: + '@vitest/utils': 3.0.9 + fflate: 0.8.2 + flatted: 3.3.3 + pathe: 2.0.3 + sirv: 3.0.1 + tinyglobby: 0.2.12 + tinyrainbow: 2.0.0 + vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) + + '@vitest/utils@3.0.9': + dependencies: + '@vitest/pretty-format': 3.0.9 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + + '@wagmi/connectors@5.11.2(@tanstack/react-query@5.90.2(react@17.0.2))(@wagmi/core@2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(wagmi@2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2))(zod@3.24.2)': + dependencies: + '@base-org/account': 1.1.1(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(utf-8-validate@5.0.10)(zod@3.24.2) + '@coinbase/wallet-sdk': 4.3.6(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(utf-8-validate@5.0.10)(zod@3.24.2) + '@gemini-wallet/core': 0.2.0(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)) + '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@wagmi/core': 2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)) + '@walletconnect/ethereum-provider': 2.21.1(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + porto: 0.2.19(@tanstack/react-query@5.90.2(react@17.0.2))(@wagmi/core@2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)))(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(wagmi@2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2)) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - immer + - ioredis + - react + - supports-color + - uploadthing + - use-sync-external-store + - utf-8-validate + - wagmi + - zod + optional: true + + '@wagmi/core@2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.5.4) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + zustand: 5.0.0(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)) + optionalDependencies: + '@tanstack/query-core': 5.90.2 + typescript: 5.5.4 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + optional: true + + '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/environment@1.0.1': + dependencies: + tslib: 1.14.1 + optional: true + + '@walletconnect/ethereum-provider@2.21.1(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@reown/appkit': 1.7.8(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/types': 2.21.1 + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/events@1.0.1': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + optional: true + + '@walletconnect/heartbeat@1.2.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + events: 3.3.0 + optional: true + + '@walletconnect/jsonrpc-http-connection@1.0.8': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + cross-fetch: 3.2.0 + events: 3.3.0 + transitivePeerDependencies: + - encoding + optional: true + + '@walletconnect/jsonrpc-provider@1.0.14': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + optional: true + + '@walletconnect/jsonrpc-types@1.0.4': + dependencies: + events: 3.3.0 + keyvaluestorage-interface: 1.0.0 + optional: true + + '@walletconnect/jsonrpc-utils@1.0.8': + dependencies: + '@walletconnect/environment': 1.0.1 + '@walletconnect/jsonrpc-types': 1.0.4 + tslib: 1.14.1 + optional: true + + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + + '@walletconnect/keyvaluestorage@1.1.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + idb-keyval: 6.2.2 + unstorage: 1.17.1(idb-keyval@6.2.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + optional: true + + '@walletconnect/logger@2.1.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 7.11.0 + optional: true + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + optional: true + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.0 + optional: true + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + optional: true + + '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/time@1.0.2': + dependencies: + tslib: 1.14.1 + optional: true + + '@walletconnect/types@2.21.0': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + optional: true + + '@walletconnect/types@2.21.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + optional: true + + '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true + + '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + optional: true - '@vitest/pretty-format@3.0.9': + '@walletconnect/window-getters@1.0.1': dependencies: - tinyrainbow: 2.0.0 + tslib: 1.14.1 + optional: true - '@vitest/pretty-format@3.1.1': + '@walletconnect/window-metadata@1.0.1': dependencies: - tinyrainbow: 2.0.0 + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + optional: true - '@vitest/runner@3.0.9': + '@whatwg-node/fetch@0.9.23': dependencies: - '@vitest/utils': 3.0.9 - pathe: 2.0.3 + '@whatwg-node/node-fetch': 0.6.0 + urlpattern-polyfill: 10.0.0 - '@vitest/snapshot@3.0.9': + '@whatwg-node/node-fetch@0.6.0': dependencies: - '@vitest/pretty-format': 3.0.9 - magic-string: 0.30.17 - pathe: 2.0.3 + '@kamilkisiela/fast-url-parser': 1.1.4 + busboy: 1.6.0 + fast-querystring: 1.1.2 + tslib: 2.8.1 - '@vitest/spy@3.0.9': - dependencies: - tinyspy: 3.0.2 + '@zod/core@0.11.6': {} - '@vitest/ui@3.0.9(vitest@3.0.9)': - dependencies: - '@vitest/utils': 3.0.9 - fflate: 0.8.2 - flatted: 3.3.3 - pathe: 2.0.3 - sirv: 3.0.1 - tinyglobby: 0.2.12 - tinyrainbow: 2.0.0 - vitest: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + abitype@1.0.8(typescript@5.5.4)(zod@3.24.2): + optionalDependencies: + typescript: 5.5.4 + zod: 3.24.2 + optional: true - '@vitest/utils@3.0.9': - dependencies: - '@vitest/pretty-format': 3.0.9 - loupe: 3.1.3 - tinyrainbow: 2.0.0 + abitype@1.1.0(typescript@5.5.4)(zod@3.22.4): + optionalDependencies: + typescript: 5.5.4 + zod: 3.22.4 + optional: true + + abitype@1.1.0(typescript@5.5.4)(zod@3.24.2): + optionalDependencies: + typescript: 5.5.4 + zod: 3.24.2 + optional: true + + abitype@1.1.1(typescript@5.5.4)(zod@3.22.4): + optionalDependencies: + typescript: 5.5.4 + zod: 3.22.4 + optional: true + + abitype@1.1.1(typescript@5.5.4)(zod@3.24.2): + optionalDependencies: + typescript: 5.5.4 + zod: 3.24.2 + optional: true + + abitype@1.1.1(typescript@5.5.4)(zod@4.1.12): + optionalDependencies: + typescript: 5.5.4 + zod: 4.1.12 + optional: true abort-controller@3.0.0: dependencies: @@ -5114,19 +9081,37 @@ snapshots: dependencies: humanize-ms: 1.2.1 - agents@0.0.67(@cloudflare/workers-types@4.20250416.0)(react@17.0.2): + agents@0.2.19(@cloudflare/workers-types@4.20250416.0)(react@17.0.2)(typescript@5.5.4)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))): dependencies: - '@modelcontextprotocol/sdk': 1.10.2 - ai: 4.3.10(react@17.0.2)(zod@3.24.3) + '@ai-sdk/openai': 2.0.53(zod@3.25.76) + '@modelcontextprotocol/sdk': 1.20.2 + ai: 5.0.78(zod@3.25.76) cron-schedule: 5.0.4 - nanoid: 5.1.5 - partyserver: 0.0.67(@cloudflare/workers-types@4.20250416.0) - partysocket: 1.1.3 + json-schema: 0.4.0 + json-schema-to-typescript: 15.0.4 + mimetext: 3.0.27 + nanoid: 5.1.6 + partyserver: 0.0.75(@cloudflare/workers-types@4.20250416.0) + partysocket: 1.1.6 react: 17.0.2 - zod: 3.24.3 + zod: 3.25.76 + zod-to-ts: 1.2.0(typescript@5.5.4)(zod@3.25.76) + optionalDependencies: + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + x402: 0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) transitivePeerDependencies: - '@cloudflare/workers-types' - supports-color + - typescript + + ai-gateway-provider@0.0.6(react@17.0.2)(zod@3.24.2): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + ai: 4.3.15(react@17.0.2)(zod@3.24.2) + transitivePeerDependencies: + - react + - zod ai@4.3.10(react@17.0.2)(zod@3.24.2): dependencies: @@ -5140,18 +9125,26 @@ snapshots: optionalDependencies: react: 17.0.2 - ai@4.3.10(react@17.0.2)(zod@3.24.3): + ai@4.3.15(react@17.0.2)(zod@3.24.2): dependencies: '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.7(zod@3.24.3) - '@ai-sdk/react': 1.2.9(react@17.0.2)(zod@3.24.3) - '@ai-sdk/ui-utils': 1.2.8(zod@3.24.3) + '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + '@ai-sdk/react': 1.2.12(react@17.0.2)(zod@3.24.2) + '@ai-sdk/ui-utils': 1.2.11(zod@3.24.2) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 - zod: 3.24.3 + zod: 3.24.2 optionalDependencies: react: 17.0.2 + ai@5.0.78(zod@3.25.76): + dependencies: + '@ai-sdk/gateway': 2.0.1(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.12(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + zod: 3.25.76 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -5169,6 +9162,12 @@ snapshots: dependencies: color-convert: 2.0.1 + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + optional: true + arg@5.0.2: {} argparse@1.0.10: @@ -5233,8 +9232,16 @@ snapshots: assertion-error@2.0.1: {} + async-mutex@0.2.6: + dependencies: + tslib: 2.8.1 + optional: true + asynckit@0.4.0: {} + atomic-sleep@1.0.0: + optional: true + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 @@ -5249,16 +9256,28 @@ snapshots: balanced-match@1.0.2: {} + base-x@5.0.1: + optional: true + + base64-js@1.5.1: + optional: true + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 + big.js@6.2.2: + optional: true + birpc@0.2.14: {} blake3-wasm@2.1.5: {} bluebird@3.7.2: {} + bn.js@5.2.2: + optional: true + body-parser@2.2.0: dependencies: bytes: 3.1.2 @@ -5273,6 +9292,9 @@ snapshots: transitivePeerDependencies: - supports-color + bowser@2.12.1: + optional: true + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -5286,6 +9308,26 @@ snapshots: dependencies: fill-range: 7.1.1 + bs58@6.0.0: + dependencies: + base-x: 5.0.1 + optional: true + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + optional: true + + bufferutil@4.0.9: + dependencies: + node-gyp-build: 4.8.4 + optional: true + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} cac@6.7.14: {} @@ -5295,6 +9337,11 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 @@ -5303,13 +9350,24 @@ snapshots: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - call-bound@1.0.3: + call-bind@1.0.8: dependencies: - call-bind-apply-helpers: 1.0.1 - get-intrinsic: 1.2.7 + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + optional: true + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 callsites@3.1.0: {} + camelcase@5.3.1: + optional: true + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -5335,6 +9393,11 @@ snapshots: check-more-types@2.24.0: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + optional: true + ci-info@3.9.0: {} cjs-module-lexer@1.3.1: {} @@ -5345,6 +9408,13 @@ snapshots: cli-spinners@2.9.2: {} + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + optional: true + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -5363,6 +9433,9 @@ snapshots: transitivePeerDependencies: - encoding + clsx@1.2.1: + optional: true + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -5387,6 +9460,9 @@ snapshots: commander@13.1.0: {} + commander@14.0.1: + optional: true + concat-map@0.0.1: {} concurrently@9.1.2: @@ -5405,10 +9481,17 @@ snapshots: content-type@1.0.5: {} + cookie-es@1.2.2: + optional: true + cookie-signature@1.2.2: {} cookie@0.7.1: {} + core-js-pure@3.45.0: {} + + core-util-is@1.0.3: {} + cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -5423,8 +9506,25 @@ snapshots: optionalDependencies: typescript: 5.5.4 + crc-32@1.2.2: + optional: true + cron-schedule@5.0.4: {} + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + optional: true + + cross-fetch@4.1.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + optional: true + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -5437,6 +9537,11 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + optional: true + data-uri-to-buffer@2.0.2: {} data-view-buffer@1.0.1: @@ -5457,10 +9562,23 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.26.10 + optional: true + + dayjs@1.11.13: + optional: true + debug@3.2.7: dependencies: ms: 2.1.3 + debug@4.3.4: + dependencies: + ms: 2.1.2 + optional: true + debug@4.3.5: dependencies: ms: 2.1.2 @@ -5469,6 +9587,12 @@ snapshots: dependencies: ms: 2.1.3 + decamelize@1.2.0: + optional: true + + decode-uri-component@0.2.2: + optional: true + deep-eql@5.0.2: {} deep-is@0.1.4: {} @@ -5493,6 +9617,17 @@ snapshots: dequal@2.0.3: {} + derive-valtio@0.1.0(valtio@1.13.2(react@17.0.2)): + dependencies: + valtio: 1.13.2(react@17.0.2) + optional: true + + destr@2.0.5: + optional: true + + detect-browser@5.3.0: + optional: true + detect-indent@6.1.0: {} detect-libc@2.0.3: @@ -5505,6 +9640,9 @@ snapshots: diff-match-patch@1.0.5: {} + dijkstrajs@1.0.3: + optional: true + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -5527,6 +9665,22 @@ snapshots: duplexer@0.1.2: {} + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + optional: true + + eciesjs@0.4.15: + dependencies: + '@ecies/ciphers': 0.2.4(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + optional: true + ee-first@1.1.1: {} effect@3.13.10: @@ -5538,8 +9692,34 @@ snapshots: emoji-regex@8.0.0: {} + empathic@1.1.0: {} + + encode-utf8@1.0.3: + optional: true + encodeurl@2.0.0: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + optional: true + + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5 + engine.io-parser: 5.2.3 + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + optional: true + + engine.io-parser@5.2.3: + optional: true + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -5619,6 +9799,10 @@ snapshots: dependencies: es-errors: 1.3.0 + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 @@ -5642,6 +9826,9 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 + es-toolkit@1.33.0: + optional: true + esbuild@0.25.1: optionalDependencies: '@esbuild/aix-ppc64': 0.25.1 @@ -5834,6 +10021,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.6 @@ -5842,6 +10031,45 @@ snapshots: etag@1.8.1: {} + eth-block-tracker@7.1.0: + dependencies: + '@metamask/eth-json-rpc-provider': 1.0.1 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 5.0.2 + json-rpc-random-id: 1.0.1 + pify: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + + eth-json-rpc-filters@6.0.1: + dependencies: + '@metamask/safe-event-emitter': 3.1.2 + async-mutex: 0.2.6 + eth-query: 2.1.2 + json-rpc-engine: 6.1.0 + pify: 5.0.0 + optional: true + + eth-query@2.1.2: + dependencies: + json-rpc-random-id: 1.0.1 + xtend: 4.0.2 + optional: true + + eth-rpc-errors@4.0.3: + dependencies: + fast-safe-stringify: 2.1.1 + optional: true + + ethereum-cryptography@2.2.1: + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + optional: true + event-stream@3.3.4: dependencies: duplexer: 0.1.2 @@ -5856,11 +10084,20 @@ snapshots: event-target-shim@5.0.1: {} - eventsource-parser@3.0.1: {} + eventemitter2@6.4.9: + optional: true + + eventemitter3@5.0.1: + optional: true + + events@3.3.0: + optional: true + + eventsource-parser@3.0.6: {} eventsource@3.0.6: dependencies: - eventsource-parser: 3.0.1 + eventsource-parser: 3.0.6 execa@5.1.1: dependencies: @@ -5918,6 +10155,12 @@ snapshots: extendable-error@0.1.7: {} + extension-port-stream@3.0.0: + dependencies: + readable-stream: 3.6.2 + webextension-polyfill: 0.10.0 + optional: true + external-editor@3.1.0: dependencies: chardet: 0.7.0 @@ -5928,6 +10171,8 @@ snapshots: dependencies: pure-rand: 6.1.0 + fast-decode-uri-component@1.0.1: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -5942,6 +10187,19 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-querystring@1.1.2: + dependencies: + fast-decode-uri-component: 1.0.1 + + fast-redact@3.5.0: + optional: true + + fast-safe-stringify@2.1.1: + optional: true + + fastestsmallesttextencoderdecoder@1.0.22: + optional: true + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -5950,6 +10208,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + fflate@0.8.2: {} file-entry-cache@6.0.1: @@ -5960,6 +10222,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 + filter-obj@1.1.0: + optional: true + finalhandler@2.1.0: dependencies: debug: 4.4.0 @@ -5997,6 +10262,11 @@ snapshots: dependencies: is-callable: 1.2.7 + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + optional: true + form-data-encoder@1.7.2: {} form-data@4.0.2: @@ -6045,6 +10315,9 @@ snapshots: functions-have-names@1.2.3: {} + generator-function@2.0.1: + optional: true + get-caller-file@2.0.5: {} get-east-asian-width@1.3.0: {} @@ -6070,6 +10343,21 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-port@7.1.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -6150,6 +10438,19 @@ snapshots: graphemer@1.4.0: {} + h3@1.15.4: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.3 + radix3: 1.1.2 + ufo: 1.6.1 + uncrypto: 0.1.3 + optional: true + has-bigints@1.0.2: {} has-flag@4.0.0: {} @@ -6174,6 +10475,9 @@ snapshots: hono@4.7.6: {} + hono@4.9.9: + optional: true + hosted-git-info@8.0.2: dependencies: lru-cache: 10.4.3 @@ -6202,12 +10506,23 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb-keyval@6.2.1: + optional: true + + idb-keyval@6.2.2: + optional: true + + ieee754@1.2.1: + optional: true + ignore@5.3.1: {} ignore@5.3.2: {} ignore@7.0.3: {} + immediate@3.0.6: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -6235,6 +10550,15 @@ snapshots: ipaddr.js@1.9.1: {} + iron-webcrypto@1.2.1: + optional: true + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + optional: true + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -6276,6 +10600,15 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + optional: true + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -6299,6 +10632,14 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + optional: true + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 @@ -6321,6 +10662,11 @@ snapshots: dependencies: which-typed-array: 1.1.15 + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + optional: true + is-unicode-supported@1.3.0: {} is-unicode-supported@2.1.0: {} @@ -6331,10 +10677,22 @@ snapshots: is-windows@1.0.2: {} + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} + isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optional: true + + isows@1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optional: true + joi@17.13.3: dependencies: '@hapi/hoek': 9.3.0 @@ -6343,6 +10701,8 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 + js-base64@3.7.8: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -6360,6 +10720,27 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-rpc-engine@6.1.0: + dependencies: + '@metamask/safe-event-emitter': 2.0.0 + eth-rpc-errors: 4.0.3 + optional: true + + json-rpc-random-id@1.0.1: + optional: true + + json-schema-to-typescript@15.0.4: + dependencies: + '@apidevtools/json-schema-ref-parser': 11.9.3 + '@types/json-schema': 7.0.15 + '@types/lodash': 4.17.20 + is-glob: 4.0.3 + js-yaml: 4.1.0 + lodash: 4.17.21 + minimist: 1.2.8 + prettier: 3.5.3 + tinyglobby: 0.2.13 + json-schema-traverse@0.4.1: {} json-schema@0.4.0: {} @@ -6382,10 +10763,27 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + keccak@3.0.4: + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.8.4 + readable-stream: 3.6.2 + optional: true + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + keyvaluestorage-interface@1.0.0: + optional: true + kleur@3.0.3: {} lazy-ass@1.6.0: {} @@ -6395,6 +10793,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lightningcss-darwin-arm64@1.29.2: optional: true @@ -6443,6 +10845,25 @@ snapshots: lines-and-columns@1.2.4: {} + lit-element@4.2.1: + dependencies: + '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit/reactive-element': 2.1.1 + lit-html: 3.3.1 + optional: true + + lit-html@3.3.1: + dependencies: + '@types/trusted-types': 2.0.7 + optional: true + + lit@3.3.0: + dependencies: + '@lit/reactive-element': 2.1.1 + lit-element: 4.2.1 + lit-html: 3.3.1 + optional: true + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -6480,6 +10901,8 @@ snapshots: lru-cache@10.4.3: {} + lz-string@1.5.0: {} + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -6498,6 +10921,9 @@ snapshots: merge2@1.4.1: {} + micro-ftch@0.3.1: + optional: true + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -6519,11 +10945,18 @@ snapshots: mime@4.0.6: {} + mimetext@3.0.27: + dependencies: + '@babel/runtime': 7.26.10 + '@babel/runtime-corejs3': 7.28.2 + js-base64: 3.7.8 + mime-types: 2.1.35 + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} - miniflare@4.20250408.0: + miniflare@4.20250408.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 acorn: 8.14.0 @@ -6533,14 +10966,14 @@ snapshots: stoppable: 1.1.0 undici: 5.28.5 workerd: 1.20250408.0 - ws: 8.18.0 + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) youch: 3.3.4 zod: 3.22.3 transitivePeerDependencies: - bufferutil - utf-8-validate - miniflare@4.20250409.0: + miniflare@4.20250409.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 acorn: 8.14.0 @@ -6550,7 +10983,24 @@ snapshots: stoppable: 1.1.0 undici: 5.28.5 workerd: 1.20250409.0 - ws: 8.18.0 + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + youch: 3.3.4 + zod: 3.22.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + miniflare@4.20250428.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.14.0 + acorn-walk: 8.3.2 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + stoppable: 1.1.0 + undici: 5.28.5 + workerd: 1.20250428.0 + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) youch: 3.3.4 zod: 3.22.3 transitivePeerDependencies: @@ -6567,6 +11017,11 @@ snapshots: minimist@1.2.8: {} + mipd@0.0.7(typescript@5.5.4): + optionalDependencies: + typescript: 5.5.4 + optional: true + mock-fs@5.5.0: {} mri@1.2.0: {} @@ -6577,22 +11032,42 @@ snapshots: ms@2.1.3: {} + multiformats@9.9.0: + optional: true + mustache@4.2.0: {} nanoid@3.3.8: {} - nanoid@5.1.5: {} + nanoid@5.1.6: {} natural-compare@1.4.0: {} negotiator@1.0.0: {} + node-addon-api@2.0.2: + optional: true + node-domexception@1.0.0: {} + node-fetch-native@1.6.6: {} + + node-fetch-native@1.6.7: + optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + node-gyp-build@4.8.4: + optional: true + + node-mock-http@1.0.3: + optional: true + + normalize-path@3.0.0: + optional: true + npm-package-arg@12.0.2: dependencies: hosted-git-info: 8.0.2 @@ -6604,6 +11079,13 @@ snapshots: dependencies: path-key: 3.1.1 + obj-multiplex@1.0.0: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + readable-stream: 2.3.8 + optional: true + object-assign@4.1.1: {} object-inspect@1.13.2: {} @@ -6638,8 +11120,18 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + ofetch@1.4.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.1 + optional: true + ohash@2.0.11: {} + on-exit-leak-free@0.2.0: + optional: true + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -6656,6 +11148,14 @@ snapshots: dependencies: mimic-function: 5.0.1 + openapi-fetch@0.13.8: + dependencies: + openapi-typescript-helpers: 0.0.15 + optional: true + + openapi-typescript-helpers@0.0.15: + optional: true + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -6681,6 +11181,84 @@ snapshots: outdent@0.5.0: {} + ox@0.6.7(typescript@5.5.4)(zod@3.24.2): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.5.4)(zod@3.24.2) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + optional: true + + ox@0.6.9(typescript@5.5.4)(zod@3.24.2): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.5.4)(zod@3.24.2) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + optional: true + + ox@0.9.6(typescript@5.5.4)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.5.4)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + optional: true + + ox@0.9.6(typescript@5.5.4)(zod@3.24.2): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.5.4)(zod@3.24.2) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + optional: true + + ox@0.9.8(typescript@5.5.4)(zod@4.1.12): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.1(typescript@5.5.4)(zod@4.1.12) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + optional: true + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -6709,6 +11287,8 @@ snapshots: dependencies: quansync: 0.2.8 + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -6722,12 +11302,12 @@ snapshots: parseurl@1.3.3: {} - partyserver@0.0.67(@cloudflare/workers-types@4.20250416.0): + partyserver@0.0.75(@cloudflare/workers-types@4.20250416.0): dependencies: '@cloudflare/workers-types': 4.20250416.0 - nanoid: 5.1.5 + nanoid: 5.1.6 - partysocket@1.1.3: + partysocket@1.1.6: dependencies: event-target-polyfill: 0.0.4 @@ -6761,10 +11341,67 @@ snapshots: picomatch@4.0.2: {} + pify@3.0.0: + optional: true + pify@4.0.1: {} + pify@5.0.0: + optional: true + + pino-abstract-transport@0.5.0: + dependencies: + duplexify: 4.1.3 + split2: 4.2.0 + optional: true + + pino-std-serializers@4.0.0: + optional: true + + pino@7.11.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 0.2.0 + pino-abstract-transport: 0.5.0 + pino-std-serializers: 4.0.0 + process-warning: 1.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.1.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 2.8.0 + thread-stream: 0.15.2 + optional: true + pkce-challenge@5.0.0: {} + pngjs@5.0.0: + optional: true + + pony-cause@2.1.11: + optional: true + + porto@0.2.19(@tanstack/react-query@5.90.2(react@17.0.2))(@wagmi/core@2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)))(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(wagmi@2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2)): + dependencies: + '@wagmi/core': 2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)) + hono: 4.9.9 + idb-keyval: 6.2.2 + mipd: 0.0.7(typescript@5.5.4) + ox: 0.9.8(typescript@5.5.4)(zod@4.1.12) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + zod: 4.1.12 + zustand: 5.0.8(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)) + optionalDependencies: + '@tanstack/react-query': 5.90.2(react@17.0.2) + react: 17.0.2 + typescript: 5.5.4 + wagmi: 2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + optional: true + possible-typed-array-names@1.0.0: {} postcss@8.5.3: @@ -6773,6 +11410,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact@10.24.2: + optional: true + + preact@10.27.2: + optional: true + prelude-ls@1.2.1: {} prettier@2.8.8: {} @@ -6783,6 +11426,11 @@ snapshots: proc-log@5.0.0: {} + process-nextick-args@2.0.1: {} + + process-warning@1.0.0: + optional: true + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -6793,24 +11441,55 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxy-compare@2.6.0: + optional: true + proxy-from-env@1.1.0: {} ps-tree@1.2.0: dependencies: event-stream: 3.3.4 + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + optional: true + punycode@2.3.1: {} pure-rand@6.1.0: {} + qrcode@1.5.3: + dependencies: + dijkstrajs: 1.0.3 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + optional: true + qs@6.14.0: dependencies: side-channel: 1.1.0 quansync@0.2.8: {} + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + optional: true + queue-microtask@1.2.3: {} + quick-format-unescaped@4.0.4: + optional: true + + radix3@1.1.2: + optional: true + range-parser@1.2.1: {} raw-body@3.0.0: @@ -6837,6 +11516,29 @@ snapshots: js-yaml: 4.1.0 strip-bom: 4.0.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + optional: true + + readdirp@4.1.2: + optional: true + + real-require@0.1.0: + optional: true + regenerator-runtime@0.14.1: {} regexp.prototype.flags@1.5.2: @@ -6848,6 +11550,9 @@ snapshots: require-directory@2.1.1: {} + require-main-filename@2.0.0: + optional: true + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -6921,6 +11626,8 @@ snapshots: has-symbols: 1.0.3 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-regex-test@1.0.3: @@ -6929,6 +11636,16 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + optional: true + + safe-stable-stringify@2.5.0: + optional: true + safer-buffer@2.1.2: {} secure-json-parse@2.7.0: {} @@ -6962,6 +11679,9 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: + optional: true + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -6978,8 +11698,17 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + optional: true + sharp@0.33.5: dependencies: color: 4.2.3 @@ -7022,16 +11751,16 @@ snapshots: side-channel-map@1.0.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-inspect: 1.13.3 side-channel-weakmap@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-inspect: 1.13.3 side-channel-map: 1.0.1 @@ -7063,17 +11792,42 @@ snapshots: is-arrayish: 0.3.2 optional: true - sirv@3.0.1: + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.1 + totalist: 3.0.1 + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slash@5.1.0: {} + + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5 + engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + optional: true + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + optional: true + + sonic-boom@2.8.0: dependencies: - '@polka/url': 1.0.0-next.28 - mrmime: 2.0.1 - totalist: 3.0.1 - - sisteransi@1.0.5: {} - - slash@3.0.0: {} - - slash@5.1.0: {} + atomic-sleep: 1.0.0 + optional: true source-map-js@1.2.1: {} @@ -7084,6 +11838,12 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + split-on-first@1.1.0: + optional: true + + split2@4.2.0: + optional: true + split@0.3.3: dependencies: through: 2.3.8 @@ -7124,6 +11884,14 @@ snapshots: dependencies: duplexer: 0.1.2 + stream-shift@1.0.3: + optional: true + + streamsearch@1.1.0: {} + + strict-uri-encode@2.0.0: + optional: true + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -7155,6 +11923,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -7171,6 +11943,9 @@ snapshots: strip-json-comments@3.1.1: {} + superstruct@1.0.4: + optional: true + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -7216,6 +11991,11 @@ snapshots: text-table@0.2.0: {} + thread-stream@0.15.2: + dependencies: + real-require: 0.1.0 + optional: true + throttleit@2.1.0: {} through@2.3.8: {} @@ -7231,6 +12011,11 @@ snapshots: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.2: {} tinyrainbow@2.0.0: {} @@ -7241,6 +12026,13 @@ snapshots: dependencies: os-tmpdir: 1.0.2 + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + optional: true + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -7272,6 +12064,9 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@1.14.1: + optional: true + tslib@2.8.1: {} tsx@4.19.3: @@ -7326,6 +12121,13 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.13 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + optional: true + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 @@ -7356,6 +12158,14 @@ snapshots: ufo@1.5.4: {} + ufo@1.6.1: + optional: true + + uint8arrays@3.1.0: + dependencies: + multiformats: 9.9.0 + optional: true + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -7363,10 +12173,16 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + uncrypto@0.1.3: + optional: true + undici-types@5.26.5: {} undici-types@6.21.0: {} + undici-types@7.16.0: + optional: true + undici@5.28.5: dependencies: '@fastify/busboy': 2.1.1 @@ -7385,27 +12201,160 @@ snapshots: unpipe@1.0.0: {} + unstorage@1.17.1(idb-keyval@6.2.2): + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.4 + lru-cache: 10.4.3 + node-fetch-native: 1.6.7 + ofetch: 1.4.1 + ufo: 1.6.1 + optionalDependencies: + idb-keyval: 6.2.2 + optional: true + uri-js@4.4.1: dependencies: punycode: 2.3.1 + urlpattern-polyfill@10.0.0: {} + + use-sync-external-store@1.2.0(react@17.0.2): + dependencies: + react: 17.0.2 + optional: true + + use-sync-external-store@1.4.0(react@17.0.2): + dependencies: + react: 17.0.2 + optional: true + use-sync-external-store@1.5.0(react@17.0.2): dependencies: react: 17.0.2 + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.4 + optional: true + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.19 + optional: true + + uuid@8.3.2: + optional: true + + uuid@9.0.1: + optional: true + validate-npm-package-name@6.0.0: {} + valtio@1.13.2(react@17.0.2): + dependencies: + derive-valtio: 0.1.0(valtio@1.13.2(react@17.0.2)) + proxy-compare: 2.6.0 + use-sync-external-store: 1.2.0(react@17.0.2) + optionalDependencies: + react: 17.0.2 + optional: true + vary@1.1.2: {} - vite-node@3.0.9(@types/node@22.14.1)(lightningcss@1.29.2): + viem@2.23.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2): + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.5.4)(zod@3.24.2) + isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.6.7(typescript@5.5.4)(zod@3.24.2) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + optional: true + + viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.5.4)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.5.4)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + optional: true + + viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.5.4)(zod@3.24.2) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.5.4)(zod@3.24.2) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + optional: true + + vite-node@3.0.9(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-node@3.0.9(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.2) + vite: 6.3.4(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass @@ -7414,25 +12363,45 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml - vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.2): + vite@6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3): dependencies: esbuild: 0.25.1 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.35.0 + tinyglobby: 0.2.13 optionalDependencies: '@types/node': 22.14.1 fsevents: 2.3.3 lightningcss: 1.29.2 + tsx: 4.19.3 + + vite@6.3.4(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3): + dependencies: + esbuild: 0.25.1 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.35.0 + tinyglobby: 0.2.13 + optionalDependencies: + '@types/node': 22.15.17 + fsevents: 2.3.3 + lightningcss: 1.29.2 + tsx: 4.19.3 vitest-evals@0.1.4(vitest@3.0.9): dependencies: - vitest: 3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2) + vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3) - vitest@3.0.9(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2): + vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.14.1)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3): dependencies: '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(vite@5.4.14(@types/node@22.14.1)(lightningcss@1.29.2)) + '@vitest/mocker': 3.0.9(vite@6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3)) '@vitest/pretty-format': 3.1.1 '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 @@ -7448,13 +12417,55 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 5.4.14(@types/node@22.14.1)(lightningcss@1.29.2) - vite-node: 3.0.9(@types/node@22.14.1)(lightningcss@1.29.2) + vite: 6.3.4(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3) + vite-node: 3.0.9(@types/node@22.14.1)(lightningcss@1.29.2)(tsx@4.19.3) why-is-node-running: 2.3.0 optionalDependencies: + '@types/debug': 4.1.12 '@types/node': 22.14.1 '@vitest/ui': 3.0.9(vitest@3.0.9) transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.15.17)(@vitest/ui@3.0.9)(lightningcss@1.29.2)(tsx@4.19.3): + dependencies: + '@vitest/expect': 3.0.9 + '@vitest/mocker': 3.0.9(vite@6.3.4(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3)) + '@vitest/pretty-format': 3.1.1 + '@vitest/runner': 3.0.9 + '@vitest/snapshot': 3.0.9 + '@vitest/spy': 3.0.9 + '@vitest/utils': 3.0.9 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.2.0 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.8.1 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.3.4(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3) + vite-node: 3.0.9(@types/node@22.15.17)(lightningcss@1.29.2)(tsx@4.19.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.15.17 + '@vitest/ui': 3.0.9(vitest@3.0.9) + transitivePeerDependencies: + - jiti - less - lightningcss - msw @@ -7464,6 +12475,48 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml + + wagmi@2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2): + dependencies: + '@tanstack/react-query': 5.90.2(react@17.0.2) + '@wagmi/connectors': 5.11.2(@tanstack/react-query@5.90.2(react@17.0.2))(@wagmi/core@2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(wagmi@2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2))(zod@3.24.2) + '@wagmi/core': 2.21.2(@tanstack/query-core@5.90.2)(react@17.0.2)(typescript@5.5.4)(use-sync-external-store@1.4.0(react@17.0.2))(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2)) + react: 17.0.2 + use-sync-external-store: 1.4.0(react@17.0.2) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - immer + - ioredis + - supports-color + - uploadthing + - utf-8-validate + - zod + optional: true wait-on@8.0.3(debug@4.4.0): dependencies: @@ -7477,6 +12530,9 @@ snapshots: web-streams-polyfill@4.0.0-beta.3: {} + webextension-polyfill@0.10.0: + optional: true + webidl-conversions@3.0.1: {} whatwg-url@5.0.0: @@ -7492,6 +12548,9 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 + which-module@2.0.1: + optional: true + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 @@ -7500,6 +12559,17 @@ snapshots: gopd: 1.0.1 has-tostringtag: 1.0.2 + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + optional: true + which@2.0.2: dependencies: isexe: 2.0.0 @@ -7525,17 +12595,40 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20250409.0 '@cloudflare/workerd-windows-64': 1.20250409.0 + workerd@1.20250428.0: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20250428.0 + '@cloudflare/workerd-darwin-arm64': 1.20250428.0 + '@cloudflare/workerd-linux-64': 1.20250428.0 + '@cloudflare/workerd-linux-arm64': 1.20250428.0 + '@cloudflare/workerd-windows-64': 1.20250428.0 + + workerd@1.20250507.0: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20250507.0 + '@cloudflare/workerd-darwin-arm64': 1.20250507.0 + '@cloudflare/workerd-linux-64': 1.20250507.0 + '@cloudflare/workerd-linux-arm64': 1.20250507.0 + '@cloudflare/workerd-windows-64': 1.20250507.0 + optional: true + workers-ai-provider@0.3.0: dependencies: - '@cloudflare/workers-types': 4.20250414.0 + '@cloudflare/workers-types': 4.20250410.0 + + workers-tagged-logger@0.13.5: + dependencies: + zod: 4.1.12 + optionalDependencies: + hono: 4.7.6 - wrangler@4.10.0(@cloudflare/workers-types@4.20250416.0): + wrangler@4.10.0(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.4.0 '@cloudflare/unenv-preset': 2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250409.0) blake3-wasm: 2.1.5 esbuild: 0.25.1 - miniflare: 4.20250409.0 + miniflare: 4.20250409.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.15 workerd: 1.20250409.0 @@ -7547,13 +12640,13 @@ snapshots: - bufferutil - utf-8-validate - wrangler@4.9.1(@cloudflare/workers-types@4.20250416.0): + wrangler@4.9.1(@cloudflare/workers-types@4.20250416.0)(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.4.0 '@cloudflare/unenv-preset': 2.3.1(unenv@2.0.0-rc.15)(workerd@1.20250408.0) blake3-wasm: 2.1.5 esbuild: 0.25.1 - miniflare: 4.20250408.0 + miniflare: 4.20250408.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.15 workerd: 1.20250408.0 @@ -7565,6 +12658,13 @@ snapshots: - bufferutil - utf-8-validate + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + optional: true + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -7573,12 +12673,109 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.0: {} + ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + optional: true + + ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + optional: true + + ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + + ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + optional: true + + x402@0.6.5(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4))(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + '@scure/base': 1.2.6 + '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + viem: 2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2) + wagmi: 2.17.5(@tanstack/query-core@5.90.2)(@tanstack/react-query@5.90.2(react@17.0.2))(bufferutil@4.0.9)(react@17.0.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.37.9(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.24.2))(zod@3.24.2) + zod: 3.24.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@solana/sysvars' + - '@tanstack/query-core' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - supports-color + - typescript + - uploadthing + - utf-8-validate + - ws + optional: true + + xmlhttprequest-ssl@2.1.2: + optional: true + + xtend@4.0.2: + optional: true + + y18n@4.0.3: + optional: true y18n@5.0.8: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + optional: true + yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + optional: true + yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -7601,12 +12798,42 @@ snapshots: dependencies: zod: 3.24.2 - zod-to-json-schema@3.24.5(zod@3.24.3): + zod-to-ts@1.2.0(typescript@5.5.4)(zod@3.25.76): dependencies: - zod: 3.24.3 + typescript: 5.5.4 + zod: 3.25.76 zod@3.22.3: {} + zod@3.22.4: + optional: true + zod@3.24.2: {} - zod@3.24.3: {} + zod@3.25.76: {} + + zod@4.0.0-beta.20250505T195954: + dependencies: + '@zod/core': 0.11.6 + + zod@4.1.12: {} + + zustand@5.0.0(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)): + optionalDependencies: + react: 17.0.2 + use-sync-external-store: 1.4.0(react@17.0.2) + optional: true + + zustand@5.0.3(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)): + optionalDependencies: + react: 17.0.2 + use-sync-external-store: 1.4.0(react@17.0.2) + optional: true + + zustand@5.0.8(react@17.0.2)(use-sync-external-store@1.4.0(react@17.0.2)): + optionalDependencies: + react: 17.0.2 + use-sync-external-store: 1.4.0(react@17.0.2) + optional: true + + zx@8.5.4: {} diff --git a/server.json b/server.json new file mode 100644 index 00000000..90559f96 --- /dev/null +++ b/server.json @@ -0,0 +1,358 @@ +{ + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json", + "name": "com.cloudflare.mcp/mcp", + "description": "Cloudflare MCP servers", + "status": "active", + "repository": { + "url": "https://github.com/cloudflare/mcp-server-cloudflare", + "source": "github" + }, + "version": "1.0.0", + "remotes": [ + { + "url": "https://docs.mcp.cloudflare.com/mcp", + "type": "streamable-http" + }, + { + "url": "https://observability.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://bindings.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://builds.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://radar.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://containers.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://browser.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://logs.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://ai-gateway.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://autorag.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://auditlogs.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://dns-analytics.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://dex.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://casb.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://graphql.mcp.cloudflare.com/mcp", + "type": "streamable-http", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + + { + "url": "https://docs.mcp.cloudflare.com/sse", + "type": "sse" + }, + { + "url": "https://observability.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://bindings.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://builds.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://radar.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://containers.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://browser.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://logs.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://ai-gateway.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://autorag.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://auditlogs.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://dns-analytics.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://dex.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://casb.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + }, + { + "url": "https://graphql.mcp.cloudflare.com/sse", + "type": "sse", + "headers": [ + { + "name": "Authentication", + "description": "Optional Cloudflare API key for authentication if not using OAuth. Can use User or Account owned tokens as a Bearer token.", + "is_required": false, + "is_secret": true + } + ] + } + ] +} diff --git a/turbo.json b/turbo.json index 46f3cb9d..c08f6be4 100644 --- a/turbo.json +++ b/turbo.json @@ -1,31 +1,39 @@ { "$schema": "https://turbo.build/schema.json", + "globalPassThroughEnv": ["FORCE_COLOR", "RUNNER_TEMP"], "tasks": { "deploy": { "cache": false, - "passThroughEnv": [ - "CLOUDFLARE_ACCOUNT_ID", - "CLOUDFLARE_API_TOKEN", - "CLOUDFLARE_STAGING_API_TOKEN" - ], - "outputs": ["dist"] + "env": ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN", "CLOUDFLARE_STAGING_API_TOKEN"], + "outputs": ["dist"], + "outputLogs": "new-only" }, "check": { - "dependsOn": ["^check:types", "^check:lint", "check:types", "check:lint"] + "dependsOn": ["^check:types", "^check:lint", "check:types", "check:lint"], + "outputLogs": "new-only" }, "check:types": { - "dependsOn": ["^check:types"] + "dependsOn": ["^check:types"], + "outputLogs": "new-only" }, "check:lint": { - "dependsOn": ["^check:lint"] + "env": ["GITHUB_ACTIONS"], + "dependsOn": ["^check:lint"], + "outputLogs": "new-only" }, "eval:ci": { - "dependsOn": ["^eval:ci"] + "dependsOn": ["^eval:ci"], + "outputLogs": "new-only" }, "types": { - "dependsOn": ["^types"] + "dependsOn": ["^types"], + "outputLogs": "new-only" }, - "//#check:format": {}, - "//#check:deps": {} + "//#check:format": { + "outputLogs": "new-only" + }, + "//#check:deps": { + "outputLogs": "new-only" + } } }