diff --git a/apps/api/src/db/schema/deployment.ts b/apps/api/src/db/schema/deployment.ts index 1c6d6087..13d82c37 100644 --- a/apps/api/src/db/schema/deployment.ts +++ b/apps/api/src/db/schema/deployment.ts @@ -1,6 +1,6 @@ import { agenticProjectConfigSchema, - type DeploymentOriginAdapter, + type OriginAdapter, type PricingPlanList } from '@agentic/platform-schemas' import { validators } from '@agentic/platform-validators' @@ -88,7 +88,7 @@ export const deployments = pgTable( originUrl: text().notNull(), // Origin API adapter config (openapi, mcp, hosted externally or internally, etc) - originAdapter: jsonb().$type().notNull(), + originAdapter: jsonb().$type().notNull(), // Array pricingPlans: jsonb().$type().notNull(), diff --git a/packages/api-client/src/openapi.d.ts b/packages/api-client/src/openapi.d.ts index 4cf1f086..ac71368d 100644 --- a/packages/api-client/src/openapi.d.ts +++ b/packages/api-client/src/openapi.d.ts @@ -448,7 +448,7 @@ export interface components { * "type": "raw" * } */ - DeploymentOriginAdapter: { + OriginAdapter: { /** @enum {string} */ type: "openapi"; /** @description JSON stringified OpenAPI spec describing the origin API server. */ @@ -552,7 +552,7 @@ export interface components { teamId?: string; /** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */ projectId: string; - originAdapter?: components["schemas"]["DeploymentOriginAdapter"]; + originAdapter?: components["schemas"]["OriginAdapter"]; /** * @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project. * @default [ @@ -1441,7 +1441,7 @@ export interface operations { * NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so. */ originUrl: string; - originAdapter?: components["schemas"]["DeploymentOriginAdapter"]; + originAdapter?: components["schemas"]["OriginAdapter"]; /** * @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project. * @default [ diff --git a/packages/api-client/src/types.ts b/packages/api-client/src/types.ts index 59531300..786df2d2 100644 --- a/packages/api-client/src/types.ts +++ b/packages/api-client/src/types.ts @@ -13,8 +13,7 @@ export type TeamMember = components['schemas']['TeamMember'] export type ProjectIdentifier = components['schemas']['ProjectIdentifier'] export type DeploymentIdentifier = components['schemas']['DeploymentIdentifier'] -export type DeploymentOriginAdapter = - components['schemas']['DeploymentOriginAdapter'] +export type OriginAdapter = components['schemas']['OriginAdapter'] export type RateLimit = components['schemas']['RateLimit'] export type PricingInterval = components['schemas']['PricingInterval'] diff --git a/packages/schemas/src/agentic-project-config.ts b/packages/schemas/src/agentic-project-config.ts index f90eddf1..f252c8b6 100644 --- a/packages/schemas/src/agentic-project-config.ts +++ b/packages/schemas/src/agentic-project-config.ts @@ -1,11 +1,12 @@ import { z } from '@hono/zod-openapi' +import { originAdapterSchema } from './origin-adapter' import { - deploymentOriginAdapterSchema, pricingIntervalListSchema, type PricingPlan, pricingPlanListSchema -} from './schemas' +} from './pricing' +import { toolConfigSchema } from './tools' // TODO: // - **service / tool definitions** @@ -93,19 +94,19 @@ export const agenticProjectConfigSchema = z NOTE: Agentic currently only supports \`external\` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.`), /** - * Optional deployment origin API adapter used to configure the origin API - * server downstream from Agentic's API gateway. It specifies whether the - * origin API server denoted by \`originUrl\` is hosted externally or deployed + * Optional origin API adapter used to configure the origin API server + * downstream from Agentic's API gateway. It specifies whether the origin + * API server denoted by \`originUrl\` is hosted externally or deployed * internally to Agentic's infrastructure. It also specifies the format * for how origin tools / services are defined: either as an OpenAPI spec, * an MCP server, or as a raw HTTP REST API. */ - originAdapter: deploymentOriginAdapterSchema.optional().default({ + originAdapter: originAdapterSchema.optional().default({ location: 'external', type: 'raw' }), - /** Optional subscription pricing config */ + /** Optional subscription pricing config for this project. */ pricingPlans: pricingPlanListSchema .describe( 'List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project.' @@ -137,7 +138,32 @@ Defaults to a single monthly interval \`['month']\`. To add support for annual pricing plans, for example, you can use: \`['month', 'year']\`.` ) .optional() - .default(['month']) + .default(['month']), + + /** + * Optional list of tool configs to customize the behavior of tools. + * + * Make sure the tool `name` matches the origin server's tool names, either + * via its MCP server or OpenAPI operationIds. + * + * Tool names are expected to be unique and stable across deployments. + * + * With `toolConfigs`, tools can be disabled, set custom rate-limits, + * customize reporting usage for metered billing, and they can also + * override behavior for different pricing plans. + * + * For example, you may want to disable certain tools on a `free` pricing + * plan or remove the rate-limit for a specific tool on a `pro` pricing + * plan while keeping the defualt rate-limit in place for other tools. + * + * Note that tool-specific configs override the defaults defined in + * pricing plans. + * + * If a tool is defined on the origin server but not specified in + * `toolConfigs`, it will use the default behavior of the Agentic API + * gateway. + */ + toolConfigs: z.array(toolConfigSchema).optional() }) .strip() diff --git a/packages/schemas/src/define-config.ts b/packages/schemas/src/define-config.ts index 01032cee..147330fd 100644 --- a/packages/schemas/src/define-config.ts +++ b/packages/schemas/src/define-config.ts @@ -4,7 +4,7 @@ import { type AgenticProjectConfig, type AgenticProjectConfigInput, agenticProjectConfigSchema -} from './agentic-project-config-schema' +} from './agentic-project-config' /** * This method allows Agentic projects to define their configs in a type-safe diff --git a/packages/schemas/src/origin-adapter.ts b/packages/schemas/src/origin-adapter.ts index c136833b..295319d2 100644 --- a/packages/schemas/src/origin-adapter.ts +++ b/packages/schemas/src/origin-adapter.ts @@ -1,29 +1,27 @@ import { z } from '@hono/zod-openapi' -export const deploymentOriginAdapterLocationSchema = z.literal('external') +export const originAdapterLocationSchema = z.literal('external') // z.union([ // z.literal('external'), // z.literal('internal') // ]) -export type DeploymentOriginAdapterLocation = z.infer< - typeof deploymentOriginAdapterLocationSchema -> +export type OriginAdapterLocation = z.infer -// export const deploymentOriginAdapterInternalTypeSchema = z.union([ +// export const originAdapterInternalTypeSchema = z.union([ // z.literal('docker'), // z.literal('mcp'), // z.literal('python-fastapi'), // // etc // ]) -// export type DeploymentOriginAdapterInternalType = z.infer< -// typeof deploymentOriginAdapterInternalTypeSchema +// export type OriginAdapterInternalType = z.infer< +// typeof originAdapterInternalTypeSchema // > -export const commonDeploymentOriginAdapterSchema = z.object({ - location: deploymentOriginAdapterLocationSchema +export const commonOriginAdapterSchema = z.object({ + location: originAdapterLocationSchema // TODO: Add support for `internal` hosted API servers - // internalType: deploymentOriginAdapterInternalTypeSchema.optional() + // internalType: originAdapterInternalTypeSchema.optional() }) // TODO: add future support for: @@ -34,11 +32,18 @@ export const commonDeploymentOriginAdapterSchema = z.object({ // - etc /** - * Deployment origin API adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools / services are defined: either as an OpenAPI spec, an MCP server, or as a raw HTTP REST API. - - NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so. + * Origin API adapter is used to configure the origin API server downstream + * from Agentic's API gateway. It specifies whether the origin API server + * denoted by `originUrl` is hosted externally or deployed internally to + * Agentic's infrastructure. It also specifies the format for how origin tools + * are defined: either as an OpenAPI spec, an MCP server, or as a raw HTTP + * REST API. + * + * NOTE: Agentic currently only supports `external` API servers. If you'd like + * to host your API or MCP server on Agentic's infrastructure, please reach out + * to support@agentic.so. */ -export const deploymentOriginAdapterSchema = z +export const originAdapterSchema = z .discriminatedUnion('type', [ z .object({ @@ -60,7 +65,7 @@ export const deploymentOriginAdapterSchema = z 'JSON stringified OpenAPI spec describing the origin API server.' ) }) - .merge(commonDeploymentOriginAdapterSchema), + .merge(commonOriginAdapterSchema), z .object({ @@ -73,14 +78,12 @@ export const deploymentOriginAdapterSchema = z */ type: z.literal('raw') }) - .merge(commonDeploymentOriginAdapterSchema) + .merge(commonOriginAdapterSchema) ]) .describe( `Deployment origin API adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by \`originUrl\` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools / services are defined: either as an OpenAPI spec, an MCP server, or as a raw HTTP REST API. NOTE: Agentic currently only supports \`external\` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.` ) - .openapi('DeploymentOriginAdapter') -export type DeploymentOriginAdapter = z.infer< - typeof deploymentOriginAdapterSchema -> + .openapi('OriginAdapter') +export type OriginAdapter = z.infer diff --git a/packages/schemas/src/pricing.ts b/packages/schemas/src/pricing.ts index 70f5570f..6fc92707 100644 --- a/packages/schemas/src/pricing.ts +++ b/packages/schemas/src/pricing.ts @@ -117,6 +117,139 @@ const commonPricingPlanLineItemSchema = z.object({ label: z.string().optional().openapi('label', { example: 'API calls' }) }) +export const pricingPlanLicensedLineItemSchema = + commonPricingPlanLineItemSchema.merge( + z.object({ + /** + * Licensed LineItems are used to charge for fixed-price services. + */ + usageType: z.literal('licensed'), + + /** + * The fixed amount to charge per billing interval. + * + * Specified in the smallest currency unit (e.g. cents for USD). + * + * So 100 = $1.00 USD, 1000 = $10.00 USD, etc. + */ + amount: z.number().nonnegative() + }) + ) + +export const pricingPlanMeteredLineItemSchema = + commonPricingPlanLineItemSchema.merge( + z.object({ + /** + * Metered LineItems are used to charge for usage-based services. + */ + usageType: z.literal('metered'), + + /** + * Optional label for the line-item which will be displayed on customer + * bills. + * + * If unset, the line-item's `slug` will be used as the unit label. + */ + unitLabel: z.string().optional(), + + /** + * Optional rate limit to enforce for this metered line-item. + * + * You can use this, for example, to limit the number of API calls that + * can be made during a given interval. + */ + rateLimit: rateLimitSchema.optional(), + + /** + * Describes how to compute the price per period. Either `per_unit` or + * `tiered`. + * + * `per_unit` indicates that the fixed amount (specified in + * `unitAmount`) will be charged per unit of total usage. + * + * `tiered` indicates that the unit pricing will be computed using a + * tiering strategy as defined using `tiers` and `tiersMode`. + */ + billingScheme: z.union([z.literal('per_unit'), z.literal('tiered')]), + + /** + * The fixed amount to charge per unit of usage. + * + * Only applicable for `per_unit` billing schemes. + * + * Specified in the smallest currency unit (e.g. cents for USD). + * + * So 100 = $1.00 USD, 1000 = $10.00 USD, etc. + */ + unitAmount: z.number().nonnegative().optional(), + + // Only applicable for `tiered` billing schemes + + /** + * Defines if the tiering price should be `graduated` or `volume` based. + * In `volume`-based tiering, the maximum quantity within a period + * determines the per unit price, in `graduated` tiering pricing can + * successively change as the quantity grows. + * + * This field requires `billingScheme` to be set to `tiered`. + */ + tiersMode: z + .union([z.literal('graduated'), z.literal('volume')]) + .optional(), + + /** + * Pricing tiers for `tiered` billing schemes. + * + * This field requires `billingScheme` to be set to `tiered`. + */ + tiers: z.array(pricingPlanTierSchema).optional(), + + // TODO: add support for tiered rate limits? + + /** + * The default settings to aggregate the Stripe Meter's events with. + * + * Deafults to `{ formula: 'sum' }`. + */ + defaultAggregation: z + .object({ + /** + * Specifies how events are aggregated for a Stripe Meter. + * Allowed values are `count` to count the number of events, `sum` + * to sum each event's value and `last` to take the last event's + * value in the window. + * + * Defaults to `sum`. + */ + formula: z + .union([z.literal('sum'), z.literal('count'), z.literal('last')]) + .default('sum') + }) + .optional(), + + /** + * Optionally apply a transformation to the reported usage or set + * quantity before computing the amount billed. Cannot be combined + * with `tiers`. + */ + transformQuantity: z + .object({ + /** + * Divide usage by this number. + * + * Must be a positive number. + */ + divideBy: z.number().positive(), + + /** + * After division, either round the result `up` or `down`. + */ + round: z.union([z.literal('down'), z.literal('up')]) + }) + .optional() + }) + ) + /** * PricingPlanLineItems represent a single line-item in a Stripe Subscription. * @@ -125,136 +258,8 @@ const commonPricingPlanLineItemSchema = z.object({ */ export const pricingPlanLineItemSchema = z .discriminatedUnion('usageType', [ - commonPricingPlanLineItemSchema.merge( - z.object({ - /** - * Licensed LineItems are used to charge for fixed-price services. - */ - usageType: z.literal('licensed'), - - /** - * The fixed amount to charge per billing interval. - * - * Specified in the smallest currency unit (e.g. cents for USD). - * - * So 100 = $1.00 USD, 1000 = $10.00 USD, etc. - */ - amount: z.number().nonnegative() - }) - ), - - commonPricingPlanLineItemSchema.merge( - z.object({ - /** - * Metered LineItems are used to charge for usage-based services. - */ - usageType: z.literal('metered'), - - /** - * Optional label for the line-item which will be displayed on customer - * bills. - * - * If unset, the line-item's `slug` will be used as the unit label. - */ - unitLabel: z.string().optional(), - - /** - * Optional rate limit to enforce for this metered line-item. - * - * You can use this, for example, to limit the number of API calls that - * can be made during a given interval. - */ - rateLimit: rateLimitSchema.optional(), - - /** - * Describes how to compute the price per period. Either `per_unit` or - * `tiered`. - * - * `per_unit` indicates that the fixed amount (specified in - * `unitAmount`) will be charged per unit of total usage. - * - * `tiered` indicates that the unit pricing will be computed using a - * tiering strategy as defined using `tiers` and `tiersMode`. - */ - billingScheme: z.union([z.literal('per_unit'), z.literal('tiered')]), - - /** - * The fixed amount to charge per unit of usage. - * - * Only applicable for `per_unit` billing schemes. - * - * Specified in the smallest currency unit (e.g. cents for USD). - * - * So 100 = $1.00 USD, 1000 = $10.00 USD, etc. - */ - unitAmount: z.number().nonnegative().optional(), - - // Only applicable for `tiered` billing schemes - - /** - * Defines if the tiering price should be `graduated` or `volume` based. - * In `volume`-based tiering, the maximum quantity within a period - * determines the per unit price, in `graduated` tiering pricing can - * successively change as the quantity grows. - * - * This field requires `billingScheme` to be set to `tiered`. - */ - tiersMode: z - .union([z.literal('graduated'), z.literal('volume')]) - .optional(), - - /** - * Pricing tiers for `tiered` billing schemes. - * - * This field requires `billingScheme` to be set to `tiered`. - */ - tiers: z.array(pricingPlanTierSchema).optional(), - - // TODO: add support for tiered rate limits? - - /** - * The default settings to aggregate the Stripe Meter's events with. - * - * Deafults to `{ formula: 'sum' }`. - */ - defaultAggregation: z - .object({ - /** - * Specifies how events are aggregated for a Stripe Meter. - * Allowed values are `count` to count the number of events, `sum` - * to sum each event's value and `last` to take the last event's - * value in the window. - * - * Defaults to `sum`. - */ - formula: z - .union([z.literal('sum'), z.literal('count'), z.literal('last')]) - .default('sum') - }) - .optional(), - - /** - * Optionally apply a transformation to the reported usage or set - * quantity before computing the amount billed. Cannot be combined - * with `tiers`. - */ - transformQuantity: z - .object({ - /** - * Divide usage by this number. - * - * Must be a positive number. - */ - divideBy: z.number().positive(), - - /** - * After division, either round the result `up` or `down`. - */ - round: z.union([z.literal('down'), z.literal('up')]) - }) - .optional() - }) - ) + pricingPlanLicensedLineItemSchema, + pricingPlanMeteredLineItemSchema ]) .refine( (data) => { diff --git a/packages/schemas/src/rate-limit.ts b/packages/schemas/src/rate-limit.ts index ded84811..41a28f11 100644 --- a/packages/schemas/src/rate-limit.ts +++ b/packages/schemas/src/rate-limit.ts @@ -9,7 +9,7 @@ export const rateLimitSchema = z /** * The interval at which the rate limit is applied. * - * Either a positive number expressed in seconds or a valid positive + * Either a positive integer expressed in seconds or a valid positive * [ms](https://github.com/vercel/ms) string (eg, "10s", "1m", "8h", "2d", * "1w", "1y", etc). */ @@ -54,7 +54,7 @@ export const rateLimitSchema = z }) ]) .describe( - `The interval at which the rate limit is applied. Either a positive number in seconds or a valid positive [ms](https://github.com/vercel/ms) string (eg, "10s", "1m", "8h", "2d", "1w", "1y", etc).` + `The interval at which the rate limit is applied. Either a positive integer expressed in seconds or a valid positive [ms](https://github.com/vercel/ms) string (eg, "10s", "1m", "8h", "2d", "1w", "1y", etc).` ), /** diff --git a/packages/schemas/src/tools.ts b/packages/schemas/src/tools.ts index bdcbc04c..a3a30aff 100644 --- a/packages/schemas/src/tools.ts +++ b/packages/schemas/src/tools.ts @@ -1,5 +1,8 @@ import { z } from '@hono/zod-openapi' +import { pricingPlanSlugSchema } from './pricing' +import { rateLimitSchema } from './rate-limit' + export const toolNameSchema = z .string() // TODO: validate this regex constraint @@ -69,7 +72,8 @@ export const toolAnnotationsSchema = z export const toolSchema = z .object({ /** - * The name of the tool. + * The name of the tool, which acts as a unique, stable identifier for the + * tool across deployments. * * @example `"get_weather"` * @example `"google_search"` @@ -115,5 +119,131 @@ export const toolSchema = z .openapi('Tool') export type Tool = z.infer +/** + * Customizes a tool's behavior for a given pricing plan. + */ +export const pricingPlanToolConfigSchema = z + .object({ + /** + * Whether this tool should be enabled for customers on a given pricing plan. + * + * @default true + */ + enabled: z.boolean().default(true).optional(), + + /** + * Overrides whether to report default `requests` usage for metered billing + * for customers a given pricing plan. + * + * Note: This is only relevant if the pricing plan includes a `requests` + * line-item. + * + * @default undefined + */ + reportUsage: z.boolean().optional(), + + /** + * Customize or disable rate limits for this tool for customers on a given + * pricing plan. + * + * Set to `null` to disable the default request-based rate-limiting for + * this tool on a given pricing plan. + * + * @default undefined + */ + rateLimit: z.union([rateLimitSchema, z.null()]).optional() + }) + .openapi('PricingPlanToolConfig') +export type PricingPlanToolConfig = z.infer + +/** + * Customizes a tool's default behavior across all pricing plans. + */ +export const toolConfigSchema = z + .object({ + /** + * The name of the tool, which acts as a unique, stable identifier for the + * tool across deployments. + */ + name: toolNameSchema, + + /** + * Whether this tool should be enabled for all customers (default). + * + * If you want to hide a tool from customers but still have it present on + * your origin server, set this to `false` for the given tool. + * + * @default true + */ + enabled: z.boolean().default(true).optional(), + + /** + * Whether this tool's output is deterministic and idempotent given the + * same input. + * + * If `true`, tool outputs will be cached aggressively for identical + * requests, though origin server response headers can still override this + * behavior on a per-request basis. + * + * If `false`, tool outputs will be cached according to the origin server's + * response headers on a per-request basis. + * + * @default false + */ + immutable: z.boolean().default(false).optional(), + + /** + * Whether calls to this tool should be reported as usage for the default + * `requests` line-item's metered billing. + * + * Note: This is only relevant if the customer's active pricing plan + * includes a `requests` line-item. + * + * @default true + */ + reportUsage: z.boolean().default(true).optional(), + + /** + * Customize the default `requests`-based rate-limiting for this tool. + * + * Set to `null` to disable the built-in rate-limiting. + * + * If not set, the default rate-limiting for the active pricing plan will be + * used. + * + * @default undefined + */ + rateLimit: z.union([rateLimitSchema, z.null()]).optional(), + + /** + * Allows you to customize this tool's behavior or disable it entirely for + * different pricing plans. + * + * This is a map from PricingPlan slug to PricingPlanToolConfig. + * + * @example + * { + * "free": { + * "disabled": true + * } + * } + */ + pricingPlanConfig: z + .record(pricingPlanSlugSchema, pricingPlanToolConfigSchema) + .optional() + .describe( + 'Map of PricingPlan slug to tool config overrides for a given plan. This is useful to customize tool behavior or disable tools completely on different pricing plans.' + ) + + // TODO: mapping from OpenAPI operationId to tools + // TODO? + // name + // path, httpMethod + // examples + // headers + }) + .openapi('ToolConfig') +export type ToolConfig = z.infer + export const toolMapSchema = z.record(toolNameSchema, toolSchema) export type ToolMap = z.infer diff --git a/packages/schemas/src/utils.ts b/packages/schemas/src/utils.ts index abcf9fac..1d2b4680 100644 --- a/packages/schemas/src/utils.ts +++ b/packages/schemas/src/utils.ts @@ -1,4 +1,4 @@ -import type { PricingInterval, PricingPlan, PricingPlanList } from './schemas' +import type { PricingInterval, PricingPlan, PricingPlanList } from './pricing' export function getPricingPlansByInterval({ pricingInterval, diff --git a/packages/schemas/src/validate-agentic-project-config.ts b/packages/schemas/src/validate-agentic-project-config.ts index a7acf7f1..e91f0eb6 100644 --- a/packages/schemas/src/validate-agentic-project-config.ts +++ b/packages/schemas/src/validate-agentic-project-config.ts @@ -3,12 +3,12 @@ import { assert, type Logger, parseZodSchema } from '@agentic/platform-core' import { validators } from '@agentic/platform-validators' import { clean as cleanSemver, valid as isValidSemver } from 'semver' -import type { PricingPlanLineItem } from './schemas' +import type { PricingPlanLineItem } from './pricing' import { type AgenticProjectConfig, type AgenticProjectConfigInput, agenticProjectConfigSchema -} from './agentic-project-config-schema' +} from './agentic-project-config' import { getPricingPlansByInterval } from './utils' import { validateOriginAdapter } from './validate-origin-adapter' diff --git a/packages/schemas/src/validate-origin-adapter.ts b/packages/schemas/src/validate-origin-adapter.ts index e498e34d..c7ac88b4 100644 --- a/packages/schemas/src/validate-origin-adapter.ts +++ b/packages/schemas/src/validate-origin-adapter.ts @@ -1,4 +1,4 @@ -import type { DeploymentOriginAdapter } from '@agentic/platform-schemas' +import type { OriginAdapter } from '@agentic/platform-schemas' import { assert, type Logger } from '@agentic/platform-core' import { validateOpenAPISpec } from '@agentic/platform-openapi' @@ -15,7 +15,7 @@ export async function validateOriginAdapter({ logger }: { originUrl: string - originAdapter: DeploymentOriginAdapter + originAdapter: OriginAdapter label: string cwd?: URL logger?: Logger