diff --git a/apps/api/package.json b/apps/api/package.json index 6581ad60..1c9afae4 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -34,6 +34,8 @@ "@agentic/platform-hono": "workspace:*", "@agentic/platform-types": "workspace:*", "@agentic/platform-validators": "workspace:*", + "@dicebear/collection": "^9.2.3", + "@dicebear/core": "^9.2.3", "@fisch0920/drizzle-orm": "catalog:", "@fisch0920/drizzle-zod": "catalog:", "@hono/node-server": "catalog:", diff --git a/apps/api/src/api-v1/deployments/create-deployment.ts b/apps/api/src/api-v1/deployments/create-deployment.ts index 9d9c292e..91fd2da1 100644 --- a/apps/api/src/api-v1/deployments/create-deployment.ts +++ b/apps/api/src/api-v1/deployments/create-deployment.ts @@ -1,5 +1,5 @@ import { resolveAgenticProjectConfig } from '@agentic/platform' -import { assert, parseZodSchema, sha256 } from '@agentic/platform-core' +import { assert, parseZodSchema, sha256, slugify } from '@agentic/platform-core' import { isValidDeploymentIdentifier, parseProjectIdentifier @@ -66,8 +66,9 @@ export function registerV1CreateDeployment( const logger = c.get('logger') const inputNamespace = teamMember ? teamMember.teamSlug : user.username - const inputProjectIdentifier = `@${inputNamespace}/${body.name}` - const { projectIdentifier, projectNamespace, projectName } = + const slug = body.slug ?? slugify(body.name) // TODO: don't duplicate this logic here + const inputProjectIdentifier = `@${inputNamespace}/${slug}` + const { projectIdentifier, projectNamespace, projectSlug } = parseProjectIdentifier(inputProjectIdentifier) let project = await db.query.projects.findFirst({ @@ -99,9 +100,10 @@ export function registerV1CreateDeployment( await db .insert(schema.projects) .values({ + name: body.name, identifier: projectIdentifier, namespace: projectNamespace, - name: projectName, + slug: projectSlug, userId: user.id, teamId: teamMember?.teamId, private: isPrivate, diff --git a/apps/api/src/api-v1/deployments/get-deployment-by-identifier.ts b/apps/api/src/api-v1/deployments/get-deployment-by-identifier.ts index b7bc3b09..bd06cc78 100644 --- a/apps/api/src/api-v1/deployments/get-deployment-by-identifier.ts +++ b/apps/api/src/api-v1/deployments/get-deployment-by-identifier.ts @@ -15,7 +15,7 @@ import { deploymentIdentifierAndPopulateSchema } from './schemas' const route = createRoute({ description: - 'Gets a deployment by its identifier (eg, "@username/project-name@latest").', + 'Gets a deployment by its identifier (eg, "@username/project-slug@latest").', tags: ['deployments'], operationId: 'getDeploymentByIdentifier', method: 'get', diff --git a/apps/api/src/api-v1/deployments/get-public-deployment-by-identifier.ts b/apps/api/src/api-v1/deployments/get-public-deployment-by-identifier.ts index 4770eda0..9b77020e 100644 --- a/apps/api/src/api-v1/deployments/get-public-deployment-by-identifier.ts +++ b/apps/api/src/api-v1/deployments/get-public-deployment-by-identifier.ts @@ -15,7 +15,7 @@ import { deploymentIdentifierAndPopulateSchema } from './schemas' const route = createRoute({ description: - 'Gets a public deployment by its identifier (eg, "@username/project-name@latest").', + 'Gets a public deployment by its identifier (eg, "@username/project-slug@latest").', tags: ['deployments'], operationId: 'getPublicDeploymentByIdentifier', method: 'get', diff --git a/apps/api/src/api-v1/projects/create-project.ts b/apps/api/src/api-v1/projects/create-project.ts index f9456c1b..3903d9b7 100644 --- a/apps/api/src/api-v1/projects/create-project.ts +++ b/apps/api/src/api-v1/projects/create-project.ts @@ -54,8 +54,8 @@ export function registerV1CreateProject( const teamMember = c.get('teamMember') const namespace = teamMember ? teamMember.teamSlug : user.username - const identifier = `@${namespace}/${body.name}` - const { projectIdentifier, projectNamespace, projectName } = + const identifier = `@${namespace}/${body.slug}` + const { projectIdentifier, projectNamespace, projectSlug } = parseProjectIdentifier(identifier) // Used for testing e2e fixtures in the development marketplace @@ -77,7 +77,7 @@ export function registerV1CreateProject( ...body, identifier: projectIdentifier, namespace: projectNamespace, - name: projectName, + slug: projectSlug, teamId: teamMember?.teamId, userId: user.id, private: isPrivate, diff --git a/apps/api/src/api-v1/projects/get-project-by-identifier.ts b/apps/api/src/api-v1/projects/get-project-by-identifier.ts index 3ad3c320..69761c74 100644 --- a/apps/api/src/api-v1/projects/get-project-by-identifier.ts +++ b/apps/api/src/api-v1/projects/get-project-by-identifier.ts @@ -14,7 +14,7 @@ import { projectIdentifierAndPopulateSchema } from './schemas' const route = createRoute({ description: - 'Gets a project by its public identifier (eg, "@username/project-name").', + 'Gets a project by its public identifier (eg, "@username/project-slug").', tags: ['projects'], operationId: 'getProjectByIdentifier', method: 'get', diff --git a/apps/api/src/api-v1/projects/get-public-project-by-identifier.ts b/apps/api/src/api-v1/projects/get-public-project-by-identifier.ts index ec378277..bc5965d5 100644 --- a/apps/api/src/api-v1/projects/get-public-project-by-identifier.ts +++ b/apps/api/src/api-v1/projects/get-public-project-by-identifier.ts @@ -14,7 +14,7 @@ import { projectIdentifierAndPopulateSchema } from './schemas' const route = createRoute({ description: - 'Gets a public project by its public identifier (eg, "@username/project-name").', + 'Gets a public project by its public identifier (eg, "@username/project-slug").', tags: ['projects'], operationId: 'getPublicProjectByIdentifier', method: 'get', diff --git a/apps/api/src/db/schema/common.ts b/apps/api/src/db/schema/common.ts index 5c865a4f..8e41ad11 100644 --- a/apps/api/src/db/schema/common.ts +++ b/apps/api/src/db/schema/common.ts @@ -15,6 +15,8 @@ import { z } from '@hono/zod-openapi' import { createId as createCuid2 } from '@paralleldrive/cuid2' export const namespaceMaxLength = 256 as const +export const projectSlugMaxLength = 256 as const +export const projectNameMaxLength = 1024 as const // prefix is max 5 characters // separator is 1 character @@ -87,7 +89,7 @@ export function stripeId>( } /** - * `namespace/project-name` + * `namespace/project-slug` */ export function projectIdentifier< U extends string, @@ -99,7 +101,7 @@ export function projectIdentifier< } /** - * `namespace/project-name@hash` + * `namespace/project-slug@hash` */ export function deploymentIdentifier< U extends string, @@ -122,6 +124,27 @@ export function teamSlug>( return varchar({ length: namespaceMaxLength, ...config }) } +export function projectNamespace< + U extends string, + T extends Readonly<[U, ...U[]]> +>( + config?: PgVarcharConfig, never> +): PgVarcharBuilderInitial<'', Writable, typeof namespaceMaxLength> { + return varchar({ length: namespaceMaxLength, ...config }) +} + +export function projectSlug>( + config?: PgVarcharConfig, never> +): PgVarcharBuilderInitial<'', Writable, typeof projectSlugMaxLength> { + return varchar({ length: projectSlugMaxLength, ...config }) +} + +export function projectName>( + config?: PgVarcharConfig, never> +): PgVarcharBuilderInitial<'', Writable, typeof projectNameMaxLength> { + return varchar({ length: projectNameMaxLength, ...config }) +} + /** * Timestamp with mode `string` */ diff --git a/apps/api/src/db/schema/project.ts b/apps/api/src/db/schema/project.ts index a1ff7a26..a303eedc 100644 --- a/apps/api/src/db/schema/project.ts +++ b/apps/api/src/db/schema/project.ts @@ -1,4 +1,5 @@ import { + agenticProjectConfigSchema, pricingIntervalSchema, type StripeMeterIdMap, stripeMeterIdMapSchema, @@ -7,7 +8,6 @@ import { type StripeProductIdMap, stripeProductIdMapSchema } from '@agentic/platform-types' -import { isValidProjectName } from '@agentic/platform-validators' import { relations } from '@fisch0920/drizzle-orm' import { boolean, @@ -35,7 +35,10 @@ import { pricingCurrencyEnum, pricingIntervalEnum, projectIdentifier, + projectName, + projectNamespace, projectPrimaryId, + projectSlug, stripeId, teamId, timestamps, @@ -63,9 +66,17 @@ export const projects = pgTable( ...projectPrimaryId, ...timestamps, + // display name + name: projectName().notNull(), + + // identifier is `@namespace/slug` identifier: projectIdentifier().unique().notNull(), - namespace: text().notNull(), - name: text().notNull(), + + // namespace is either a username or team slug + namespace: projectNamespace().notNull(), + + // slug is a unique identifier for the project within its namespace + slug: projectSlug().notNull(), // Defaulting to `true` for now to hide all projects from the marketplace // by default. Will need to manually set to `true` to allow projects to be @@ -200,6 +211,8 @@ export const projectSelectSchema = createSelectSchema(projects, { userId: userIdSchema, teamId: teamIdSchema.optional(), identifier: projectIdentifierSchema, + name: agenticProjectConfigSchema.shape.name, + slug: agenticProjectConfigSchema.shape.slug, lastPublishedDeploymentId: deploymentIdSchema.optional(), lastDeploymentId: deploymentIdSchema.optional(), @@ -299,13 +312,12 @@ Internally, Projects manage all of the Stripe billing resources across Deploymen export const projectInsertSchema = createInsertSchema(projects, { identifier: projectIdentifierSchema, - name: (schema) => - schema.refine((name) => isValidProjectName(name), { - message: 'Invalid project name' - }) + name: agenticProjectConfigSchema.shape.name, + slug: agenticProjectConfigSchema.shape.slug }) .pick({ - name: true + name: true, + slug: true }) .strict() diff --git a/apps/api/src/db/schemas.ts b/apps/api/src/db/schemas.ts index d20308e3..eab2f36d 100644 --- a/apps/api/src/db/schemas.ts +++ b/apps/api/src/db/schemas.ts @@ -60,7 +60,7 @@ export const projectIdentifierSchema = z message: 'Invalid project identifier' } ) - .describe('Public project identifier (e.g. "@namespace/project-name")') + .describe('Public project identifier (e.g. "@namespace/project-slug")') .openapi('ProjectIdentifier') export const deploymentIdentifierSchema = z @@ -74,7 +74,7 @@ export const deploymentIdentifierSchema = z } ) .describe( - 'Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}")' + 'Public deployment identifier (e.g. "@namespace/project-slug@{hash|version|latest}")' ) .openapi('DeploymentIdentifier') diff --git a/apps/api/src/lib/auth/upsert-or-link-user-account.ts b/apps/api/src/lib/auth/upsert-or-link-user-account.ts index 9c679aa4..e0fc789e 100644 --- a/apps/api/src/lib/auth/upsert-or-link-user-account.ts +++ b/apps/api/src/lib/auth/upsert-or-link-user-account.ts @@ -3,6 +3,7 @@ import { assert } from '@agentic/platform-core' import { and, db, eq, type RawAccount, type RawUser, schema } from '@/db' +import { createAvatar } from '../create-avatar' import { getUniqueNamespace } from '../ensure-unique-namespace' /** @@ -50,6 +51,9 @@ export async function upsertOrLinkUserAccount({ }): Promise { const { provider, accountId } = partialAccount + // Set a default profile image if one isn't provided + partialUser.image ??= createAvatar(partialUser.email) + const [existingAccount, existingUser] = await Promise.all([ db.query.accounts.findFirst({ where: and( diff --git a/apps/api/src/lib/create-avatar.ts b/apps/api/src/lib/create-avatar.ts new file mode 100644 index 00000000..cdbed55e --- /dev/null +++ b/apps/api/src/lib/create-avatar.ts @@ -0,0 +1,6 @@ +import { identicon } from '@dicebear/collection' +import { createAvatar as createAvatarImpl } from '@dicebear/core' + +export function createAvatar(seed: string): string { + return createAvatarImpl(identicon, { seed }).toDataUri() +} diff --git a/apps/e2e/src/http-fixtures.ts b/apps/e2e/src/http-fixtures.ts index 89d103fc..112cb8ad 100644 --- a/apps/e2e/src/http-fixtures.ts +++ b/apps/e2e/src/http-fixtures.ts @@ -716,24 +716,25 @@ export const fixtureSuites: E2ETestFixtureSuite[] = [ } } ] - }, - { - title: 'HTTP => Production MCP origin "search" tool', - // NOTE: this one actually hits a production service and costs a small - // amount of $ per request. - fixtures: [ - { - path: '@agentic/search/search', - request: { - method: 'POST', - json: { - query: 'latest ai news' - } - }, - response: { - snapshot: false - } - } - ] } + // TODO + // { + // title: 'HTTP => Production MCP origin "search" tool', + // // NOTE: this one actually hits a production service and costs a small + // // amount of $ per request. + // fixtures: [ + // { + // path: '@agentic/search/search', + // request: { + // method: 'POST', + // json: { + // query: 'latest ai news' + // } + // }, + // response: { + // snapshot: false + // } + // } + // ] + // } ] diff --git a/apps/web/src/app/app/projects/[namespace]/[project-name]/app-project-index.tsx b/apps/web/src/app/app/projects/[namespace]/[project-slug]/app-project-index.tsx similarity index 97% rename from apps/web/src/app/app/projects/[namespace]/[project-name]/app-project-index.tsx rename to apps/web/src/app/app/projects/[namespace]/[project-slug]/app-project-index.tsx index a388cc06..d54d8a8d 100644 --- a/apps/web/src/app/app/projects/[namespace]/[project-name]/app-project-index.tsx +++ b/apps/web/src/app/app/projects/[namespace]/[project-slug]/app-project-index.tsx @@ -40,7 +40,7 @@ export function AppProjectIndex({ className='text-center text-balance leading-snug md:leading-none text-4xl font-extrabold' > - Project {project.name} + {project.name}
diff --git a/apps/web/src/app/app/projects/[namespace]/[project-name]/page.tsx b/apps/web/src/app/app/projects/[namespace]/[project-slug]/page.tsx similarity index 78% rename from apps/web/src/app/app/projects/[namespace]/[project-name]/page.tsx rename to apps/web/src/app/app/projects/[namespace]/[project-slug]/page.tsx index 8a387459..f1e83389 100644 --- a/apps/web/src/app/app/projects/[namespace]/[project-name]/page.tsx +++ b/apps/web/src/app/app/projects/[namespace]/[project-slug]/page.tsx @@ -10,18 +10,18 @@ export default async function AppProjectIndexPage({ }: { params: Promise<{ namespace: string - 'project-name': string + 'project-slug': string }> }) { - const { namespace: rawNamespace, 'project-name': rawProjectName } = + const { namespace: rawNamespace, 'project-slug': rawProjectSlug } = await params try { const namespace = decodeURIComponent(rawNamespace) - const projectName = decodeURIComponent(rawProjectName) + const projectSlug = decodeURIComponent(rawProjectSlug) const { projectIdentifier } = parseProjectIdentifier( - `${namespace}/${projectName}`, + `${namespace}/${projectSlug}`, { strict: true } ) diff --git a/apps/web/src/app/marketplace/projects/[namespace]/[project-name]/marketplace-project-index.tsx b/apps/web/src/app/marketplace/projects/[namespace]/[project-slug]/marketplace-project-index.tsx similarity index 100% rename from apps/web/src/app/marketplace/projects/[namespace]/[project-name]/marketplace-project-index.tsx rename to apps/web/src/app/marketplace/projects/[namespace]/[project-slug]/marketplace-project-index.tsx diff --git a/apps/web/src/app/marketplace/projects/[namespace]/[project-name]/page.tsx b/apps/web/src/app/marketplace/projects/[namespace]/[project-slug]/page.tsx similarity index 78% rename from apps/web/src/app/marketplace/projects/[namespace]/[project-name]/page.tsx rename to apps/web/src/app/marketplace/projects/[namespace]/[project-slug]/page.tsx index 1b9fa9b9..e945fd6e 100644 --- a/apps/web/src/app/marketplace/projects/[namespace]/[project-name]/page.tsx +++ b/apps/web/src/app/marketplace/projects/[namespace]/[project-slug]/page.tsx @@ -10,18 +10,18 @@ export default async function MarketplaceProjectIndexPage({ }: { params: Promise<{ namespace: string - 'project-name': string + 'project-slug': string }> }) { - const { namespace: rawNamespace, 'project-name': rawProjectName } = + const { namespace: rawNamespace, 'project-slug': rawProjectSlug } = await params try { const namespace = decodeURIComponent(rawNamespace) - const projectName = decodeURIComponent(rawProjectName) + const projectSlug = decodeURIComponent(rawProjectSlug) const { projectIdentifier } = parseProjectIdentifier( - `${namespace}/${projectName}`, + `${namespace}/${projectSlug}`, { strict: true } ) diff --git a/examples/search/agentic.config.ts b/examples/search/agentic.config.ts index c4f37eee..50ab9837 100644 --- a/examples/search/agentic.config.ts +++ b/examples/search/agentic.config.ts @@ -3,7 +3,8 @@ import 'dotenv/config' import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'search', + name: 'Search', + slug: 'search', description: 'Google Search tool. Useful for finding up-to-date news and information about any topic.', origin: { @@ -13,8 +14,63 @@ export default defineConfig({ toolConfigs: [ { name: 'search', - // Allow results to be cached publicly for ~1 minute cacheControl: 'public, max-age=60, s-maxage=60 stale-while-revalidate=10' } + ], + pricingPlans: [ + { + name: 'Free', + slug: 'free', + lineItems: [ + { + slug: 'requests', + usageType: 'metered', + billingScheme: 'per_unit', + unitAmount: 0 + } + ], + rateLimit: { + interval: '1d', + limit: 10 + } + }, + { + name: 'Standard', + slug: 'standard', + lineItems: [ + { + slug: 'requests', + usageType: 'metered', + billingScheme: 'tiered', + tiersMode: 'volume', + tiers: [ + { + upTo: 1000, + unitAmount: 0 + }, + { + upTo: 50_000, + unitAmount: 0.01 + }, + { + upTo: 500_000, + unitAmount: 0.008 + }, + { + upTo: 2_500_000, + unitAmount: 0.006 + }, + { + upTo: 'inf', + unitAmount: 0.005 + } + ] + } + ], + rateLimit: { + interval: '1s', + limit: 100 + } + } ] }) diff --git a/packages/cli/src/commands/deploy.ts b/packages/cli/src/commands/deploy.ts index c4057a8e..128ba1ec 100644 --- a/packages/cli/src/commands/deploy.ts +++ b/packages/cli/src/commands/deploy.ts @@ -54,7 +54,7 @@ export function registerDeployCommand({ publish: opts.publish ? 'true' : 'false' }), { - text: `Creating deployment for project "${config.name}"`, + text: `Creating deployment for project "${config.slug}"`, successText: `Deployment created successfully`, failText: 'Failed to create deployment' } diff --git a/packages/cli/src/lib/resolve-deployment.ts b/packages/cli/src/lib/resolve-deployment.ts index aca49778..d62f6092 100644 --- a/packages/cli/src/lib/resolve-deployment.ts +++ b/packages/cli/src/lib/resolve-deployment.ts @@ -24,10 +24,10 @@ export async function resolveDeployment({ const auth = AuthStore.getAuth() const namespace = auth.user.username - // TODO: resolve deploymentIdentifier; config name may include namespace? + // TODO: resolve deploymentIdentifier; config identifier may include namespace? // TODO: this needs work... - deploymentIdentifier = `@${namespace}/${config.name}${fuzzyDeploymentIdentifierVersion ? `@${fuzzyDeploymentIdentifierVersion}` : ''}` + deploymentIdentifier = `@${namespace}/${config.slug}${fuzzyDeploymentIdentifierVersion ? `@${fuzzyDeploymentIdentifierVersion}` : ''}` } const deployment = await client.getDeploymentByIdentifier({ diff --git a/packages/core/package.json b/packages/core/package.json index 61ccdc9d..1d090d3f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,6 +27,7 @@ "test:unit": "vitest run" }, "dependencies": { + "@sindresorhus/slugify": "^2.2.1", "decircular": "catalog:", "is-obj": "catalog:", "parse-json": "catalog:", diff --git a/packages/core/src/utils.test.ts b/packages/core/src/utils.test.ts index 2d7494cb..c5b11620 100644 --- a/packages/core/src/utils.test.ts +++ b/packages/core/src/utils.test.ts @@ -1,6 +1,13 @@ import { expect, test } from 'vitest' -import { omit, pick, pruneEmpty, pruneEmptyDeep, sha256 } from './utils' +import { + omit, + pick, + pruneEmpty, + pruneEmptyDeep, + sha256, + slugify +} from './utils' test('pick', () => { expect(pick({ a: 1, b: 2, c: 3 }, 'a', 'c')).toEqual({ a: 1, c: 3 }) @@ -101,3 +108,22 @@ test('pruneEmptyDeep', () => { }) ).toEqual(undefined) }) + +test('slugify', () => { + expect(slugify('Foo Bar')).toBe('foo-bar') + expect(slugify('FooBar')).toBe('foo-bar') + expect(slugify('FooBarBaz')).toBe('foo-bar-baz') + expect(slugify('FooBarBazQux')).toBe('foo-bar-baz-qux') + expect(slugify('FooBarBazQuxQuux')).toBe('foo-bar-baz-qux-quux') + expect(slugify('foo-bar')).toBe('foo-bar') + expect(slugify('--foo BAR --')).toBe('foo-bar') + expect(slugify('я люблю единорогов')).toBe('ya-lyublyu-edinorogov') + expect(slugify('fooBar 123 $#%')).toBe('foo-bar-123') + expect(slugify(' Déjà Vu! ')).toBe('deja-vu') + expect(slugify('I ♥ Dogs')).toBe('i-love-dogs') + expect(slugify('')).toBe('') + expect(slugify(' ')).toBe('') + expect(slugify('-')).toBe('') + expect(slugify('--')).toBe('') + expect(slugify('- -')).toBe('') +}) diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 1ed9e13b..c0f10691 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -1,4 +1,5 @@ import type { z, ZodType } from 'zod' +import slugifyImpl from '@sindresorhus/slugify' import { HttpError, ZodValidationError } from './errors' @@ -280,3 +281,21 @@ export function pruneEmptyDeep( return value as any } + +/** + * Slugifies a string. + * + * - converts to lowercase + * - decamelizes (fooBar -> foo-bar) + * - replaces non-latin characters with latin equivalents (transliteration) + * - replaces spaces with hyphens + * - removes trailing hyphens + * - removes leading hyphens + * - removes multiple consecutive hyphens + * - removes multiple consecutive spaces + * + * @see https://github.com/sindresorhus/slugify + */ +export function slugify(input: string): string { + return slugifyImpl(input) +} diff --git a/packages/fixtures/invalid/invalid-name-0/agentic.config.ts b/packages/fixtures/invalid/invalid-name-0/agentic.config.ts index b4ed7f46..297b1749 100644 --- a/packages/fixtures/invalid/invalid-name-0/agentic.config.ts +++ b/packages/fixtures/invalid/invalid-name-0/agentic.config.ts @@ -1,9 +1,9 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'Test Invalid Name 0', + slug: 'test-invalid-name-0', origin: { type: 'raw', url: 'https://jsonplaceholder.typicode.com' } -}) +} as any) // invalid; missing name diff --git a/packages/fixtures/invalid/invalid-name-1/agentic.config.ts b/packages/fixtures/invalid/invalid-name-1/agentic.config.ts index b118fbcd..3bdb78c6 100644 --- a/packages/fixtures/invalid/invalid-name-1/agentic.config.ts +++ b/packages/fixtures/invalid/invalid-name-1/agentic.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'Test-Invalid-Name-1', + name: 'a'.repeat(1025), // invalid; too long + slug: 'test-invalid-name-2', origin: { type: 'raw', url: 'https://jsonplaceholder.typicode.com' diff --git a/packages/fixtures/invalid/invalid-name-2/agentic.config.ts b/packages/fixtures/invalid/invalid-name-2/agentic.config.ts index 2d783157..22ba2359 100644 --- a/packages/fixtures/invalid/invalid-name-2/agentic.config.ts +++ b/packages/fixtures/invalid/invalid-name-2/agentic.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'test_invalid_name_2', + name: '', // invalid; must not be empty + slug: 'test-invalid-name-1', origin: { type: 'raw', url: 'https://jsonplaceholder.typicode.com' diff --git a/packages/fixtures/invalid/invalid-name-4/agentic.config.ts b/packages/fixtures/invalid/invalid-slug-0/agentic.config.ts similarity index 71% rename from packages/fixtures/invalid/invalid-name-4/agentic.config.ts rename to packages/fixtures/invalid/invalid-slug-0/agentic.config.ts index c7fd0eac..dedbbc7f 100644 --- a/packages/fixtures/invalid/invalid-name-4/agentic.config.ts +++ b/packages/fixtures/invalid/invalid-slug-0/agentic.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: '@foo/bar', // invalid; name contains invalid characters + name: 'Test Invalid Slug 0', + slug: 'Test Invalid Slug 0', origin: { type: 'raw', url: 'https://jsonplaceholder.typicode.com' diff --git a/packages/fixtures/invalid/invalid-slug-1/agentic.config.ts b/packages/fixtures/invalid/invalid-slug-1/agentic.config.ts new file mode 100644 index 00000000..bae98d6b --- /dev/null +++ b/packages/fixtures/invalid/invalid-slug-1/agentic.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from '@agentic/platform' + +export default defineConfig({ + name: 'Test Invalid Slug 1', + slug: 'Test-Invalid-Slug-1', + origin: { + type: 'raw', + url: 'https://jsonplaceholder.typicode.com' + } +}) diff --git a/packages/fixtures/invalid/invalid-slug-2/agentic.config.ts b/packages/fixtures/invalid/invalid-slug-2/agentic.config.ts new file mode 100644 index 00000000..65a88441 --- /dev/null +++ b/packages/fixtures/invalid/invalid-slug-2/agentic.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from '@agentic/platform' + +export default defineConfig({ + name: 'Test Invalid Slug 2', + slug: 'test_invalid_slug_2', + origin: { + type: 'raw', + url: 'https://jsonplaceholder.typicode.com' + } +}) diff --git a/packages/fixtures/invalid/invalid-name-3/agentic.config.ts b/packages/fixtures/invalid/invalid-slug-3/agentic.config.ts similarity index 77% rename from packages/fixtures/invalid/invalid-name-3/agentic.config.ts rename to packages/fixtures/invalid/invalid-slug-3/agentic.config.ts index e7288481..e45f132e 100644 --- a/packages/fixtures/invalid/invalid-name-3/agentic.config.ts +++ b/packages/fixtures/invalid/invalid-slug-3/agentic.config.ts @@ -1,8 +1,9 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ + name: ' ', origin: { type: 'raw', url: 'https://jsonplaceholder.typicode.com' } -} as any) // invalid; missing name +} as any) // invalid; missing slug diff --git a/packages/fixtures/invalid/invalid-slug-4/agentic.config.ts b/packages/fixtures/invalid/invalid-slug-4/agentic.config.ts new file mode 100644 index 00000000..635a54d0 --- /dev/null +++ b/packages/fixtures/invalid/invalid-slug-4/agentic.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from '@agentic/platform' + +export default defineConfig({ + name: 'Test Invalid Name 4', + slug: '@foo/bar', // invalid; slug contains invalid characters + origin: { + type: 'raw', + url: 'https://jsonplaceholder.typicode.com' + } +}) diff --git a/packages/fixtures/valid/basic-mcp/agentic.config.ts b/packages/fixtures/valid/basic-mcp/agentic.config.ts index 8580da38..0521715a 100644 --- a/packages/fixtures/valid/basic-mcp/agentic.config.ts +++ b/packages/fixtures/valid/basic-mcp/agentic.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'test-basic-mcp', + name: 'Test Basic MCP', + slug: 'test-basic-mcp', origin: { type: 'mcp', url: 'https://agentic-basic-mcp-test.onrender.com/mcp' diff --git a/packages/fixtures/valid/basic-openapi/agentic.config.ts b/packages/fixtures/valid/basic-openapi/agentic.config.ts index 4ec8a69c..2ef9f2ba 100644 --- a/packages/fixtures/valid/basic-openapi/agentic.config.ts +++ b/packages/fixtures/valid/basic-openapi/agentic.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'test-basic-openapi', + name: 'Test Basic OpenAPI', + slug: 'test-basic-openapi', origin: { type: 'openapi', url: 'https://jsonplaceholder.typicode.com', diff --git a/packages/fixtures/valid/everything-openapi/agentic.config.ts b/packages/fixtures/valid/everything-openapi/agentic.config.ts index 3bac5c28..76abc280 100644 --- a/packages/fixtures/valid/everything-openapi/agentic.config.ts +++ b/packages/fixtures/valid/everything-openapi/agentic.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from '@agentic/platform' export default defineConfig({ - name: 'test-everything-openapi', + name: 'Test Everything OpenAPI', + slug: 'test-everything-openapi', origin: { type: 'openapi', url: 'https://agentic-platform-fixtures-everything.onrender.com', diff --git a/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap b/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap index 3d443876..d7dd297b 100644 --- a/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap +++ b/packages/platform/src/__snapshots__/load-agentic-config.test.ts.snap @@ -8,7 +8,7 @@ exports[`loadAgenticConfig > basic-mcp 1`] = ` "limit": 1000, "mode": "approximate", }, - "name": "test-basic-mcp", + "name": "Test Basic MCP", "origin": { "location": "external", "type": "mcp", @@ -36,6 +36,7 @@ exports[`loadAgenticConfig > basic-mcp 1`] = ` "slug": "free", }, ], + "slug": "test-basic-mcp", "toolConfigs": [], "version": undefined, } @@ -49,7 +50,7 @@ exports[`loadAgenticConfig > basic-openapi 1`] = ` "limit": 1000, "mode": "approximate", }, - "name": "test-basic-openapi", + "name": "Test Basic OpenAPI", "origin": { "location": "external", "spec": "{"openapi":"3.1.0","info":{"title":"JSONPlaceholder","version":"1.0.0"},"paths":{"/posts":{"get":{"summary":"Get posts","operationId":"getPosts","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Post"}}}}}}},"post":{"summary":"Create post","operationId":"createPost","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userId","title","body"],"properties":{"userId":{"type":"integer"},"title":{"type":"string"},"body":{"type":"string"}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}}}}},"/posts/{postId}":{"get":{"summary":"Get post","operationId":"getPost","parameters":[{"required":true,"schema":{"type":"integer"},"name":"postId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}}}}}},"components":{"schemas":{"Post":{"type":"object","required":["id","userId","title","body"],"properties":{"id":{"type":"integer"},"userId":{"type":"integer"},"title":{"type":"string"},"body":{"type":"string"}}}}}}", @@ -78,6 +79,7 @@ exports[`loadAgenticConfig > basic-openapi 1`] = ` "slug": "free", }, ], + "slug": "test-basic-openapi", "toolConfigs": [ { "name": "get_posts", @@ -128,6 +130,7 @@ exports[`loadAgenticConfig > basic-raw-free-json 1`] = ` "slug": "free", }, ], + "slug": "test-basic-raw-free-json", "toolConfigs": [], "version": undefined, } @@ -169,6 +172,7 @@ exports[`loadAgenticConfig > basic-raw-free-ts 1`] = ` "slug": "free", }, ], + "slug": "test-basic-raw-free-ts", "toolConfigs": [], "version": undefined, } @@ -182,7 +186,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "limit": 1000, "mode": "approximate", }, - "name": "test-everything-openapi", + "name": "Test Everything OpenAPI", "origin": { "location": "external", "spec": "{"openapi":"3.1.0","info":{"title":"OpenAPI server everything","description":"OpenAPI kitchen sink server meant for testing Agentic's origin OpenAPI adapter and ToolConfig features.","version":"0.1.0"},"components":{"schemas":{"User":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}},"required":["id","name","email"]}},"parameters":{}},"paths":{"/health":{"get":{"description":"Check if the server is healthy","operationId":"healthCheck","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/users/{userId}":{"get":{"description":"Gets a user","tags":["users"],"operationId":"getUser","parameters":[{"schema":{"type":"string"},"required":true,"description":"User ID","name":"userId","in":"path"}],"responses":{"200":{"description":"A user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}}}}},"/disabled-tool":{"get":{"description":"Disabled tool","operationId":"disabledTool","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/disabled-for-free-plan-tool":{"get":{"description":"Disabled for free plan tool","operationId":"disabledForFreePlanTool","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}}}}},"/echo":{"post":{"description":"Echoes the request body","operationId":"echo","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/echo-headers":{"get":{"description":"Echoes the request headers","operationId":"echoHeaders","responses":{"200":{"description":"Echoed request headers","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/pure":{"post":{"description":"Pure tool","operationId":"pure","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/unpure-marked-pure":{"post":{"description":"Unpure tool marked pure","operationId":"unpure_marked_pure","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body with current timestamp to not be pure","content":{"application/json":{"schema":{"type":"object","properties":{"now":{"type":"number"}},"required":["now"]}}}}}}},"/custom-cache-control-tool":{"post":{"description":"Custom cache control tool","operationId":"customCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/custom-rate-limit-approximate-tool":{"post":{"description":"Custom rate limit tool (approximate mode)","operationId":"customRateLimitApproximateTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/no-store-cache-control-tool":{"post":{"description":"No store cache control tool","operationId":"noStoreCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/no-cache-cache-control-tool":{"post":{"description":"No cache cache control tool","operationId":"noCacheCacheControlTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/custom-rate-limit-tool":{"post":{"description":"Custom rate limit tool (strict mode)","operationId":"customRateLimitTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/disabled-rate-limit-tool":{"post":{"description":"Disabled rate limit tool","operationId":"disabledRateLimitTool","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{}}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{}}}}}}}},"/strict-additional-properties":{"post":{"description":"Echoes the request body only allowing a single \\"foo\\" field.","operationId":"strictAdditionalProperties","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"foo":{"type":"string"}},"required":["foo"]}}}},"responses":{"200":{"description":"Echoed request body","content":{"application/json":{"schema":{"type":"object","properties":{"foo":{"type":"string"}},"required":["foo"]}}}}}}}},"webhooks":{}}", @@ -267,6 +271,7 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` "slug": "pro", }, ], + "slug": "test-everything-openapi", "toolConfigs": [ { "enabled": true, @@ -349,15 +354,11 @@ exports[`loadAgenticConfig > everything-openapi 1`] = ` } `; -exports[`loadAgenticConfig > invalid: invalid-name-0 1`] = `[Error: Invalid project name "Test Invalid Name 0". Must be ascii-only lower kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"]`; +exports[`loadAgenticConfig > invalid: invalid-name-0 1`] = `[ZodValidationError: Validation error: Required at "name"]`; -exports[`loadAgenticConfig > invalid: invalid-name-1 1`] = `[Error: Invalid project name "Test-Invalid-Name-1". Must be ascii-only lower kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"]`; +exports[`loadAgenticConfig > invalid: invalid-name-1 1`] = `[ZodValidationError: Validation error: String must contain at most 1024 character(s) at "name"]`; -exports[`loadAgenticConfig > invalid: invalid-name-2 1`] = `[Error: Invalid project name "test_invalid_name_2". Must be ascii-only lower kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"]`; - -exports[`loadAgenticConfig > invalid: invalid-name-3 1`] = `[ZodValidationError: Validation error: Required at "name"]`; - -exports[`loadAgenticConfig > invalid: invalid-name-4 1`] = `[Error: Invalid project name "@foo/bar". Must be ascii-only lower kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"]`; +exports[`loadAgenticConfig > invalid: invalid-name-2 1`] = `[ZodValidationError: Validation error: String must contain at least 1 character(s) at "name"]`; exports[`loadAgenticConfig > invalid: invalid-origin-url-0 1`] = `[Error: Invalid originUrl: must be a valid https URL]`; @@ -367,6 +368,16 @@ exports[`loadAgenticConfig > invalid: invalid-origin-url-2 1`] = `[ZodValidation exports[`loadAgenticConfig > invalid: invalid-origin-url-3 1`] = `[ZodValidationError: Validation error: Invalid url at "origin.url"]`; +exports[`loadAgenticConfig > invalid: invalid-slug-0 1`] = `[ZodValidationError: Validation error: Invalid project slug at "slug"]`; + +exports[`loadAgenticConfig > invalid: invalid-slug-1 1`] = `[ZodValidationError: Validation error: Invalid project slug at "slug"]`; + +exports[`loadAgenticConfig > invalid: invalid-slug-2 1`] = `[ZodValidationError: Validation error: Invalid project slug at "slug"]`; + +exports[`loadAgenticConfig > invalid: invalid-slug-3 1`] = `[Error: Invalid project slug "" for project name " ". Must be ascii-only, lower-case, and kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"]`; + +exports[`loadAgenticConfig > invalid: invalid-slug-4 1`] = `[ZodValidationError: Validation error: Invalid project slug at "slug"]`; + exports[`loadAgenticConfig > invalid: pricing-base-inconsistent 1`] = `[ZodValidationError: Validation error: Invalid PricingPlanLineItem "base": reserved "base" LineItems must have "licensed" usage type. at "pricingPlans[1].lineItems[0]"]`; exports[`loadAgenticConfig > invalid: pricing-custom-inconsistent 1`] = `[Error: Invalid pricingPlans: all PricingPlans which contain the same LineItems (by slug "custom-test") must have the same usage type ("licensed" or "metered").]`; @@ -450,6 +461,7 @@ exports[`loadAgenticConfig > pricing-3-plans 1`] = ` "slug": "pro", }, ], + "slug": "test-pricing-3-plans", "toolConfigs": [], "version": undefined, } @@ -534,6 +546,7 @@ exports[`loadAgenticConfig > pricing-custom-0 1`] = ` "slug": "basic-annual", }, ], + "slug": "test-pricing-custom-0", "toolConfigs": [], "version": undefined, } @@ -588,6 +601,7 @@ exports[`loadAgenticConfig > pricing-freemium 1`] = ` "trialPeriodDays": 7, }, ], + "slug": "test-pricing-freemium", "toolConfigs": [], "version": undefined, } @@ -648,6 +662,7 @@ exports[`loadAgenticConfig > pricing-monthly-annual 1`] = ` "slug": "basic-annual", }, ], + "slug": "test-pricing-monthly-annual", "toolConfigs": [], "version": undefined, } @@ -724,6 +739,7 @@ exports[`loadAgenticConfig > pricing-pay-as-you-go 1`] = ` "slug": "pay-as-you-go", }, ], + "slug": "test-pricing-pay-as-you-go", "toolConfigs": [], "version": undefined, } diff --git a/packages/platform/src/load-agentic-config.test.ts b/packages/platform/src/load-agentic-config.test.ts index 36ad75e5..b9702ea3 100644 --- a/packages/platform/src/load-agentic-config.test.ts +++ b/packages/platform/src/load-agentic-config.test.ts @@ -33,8 +33,11 @@ const invalidFixtures = [ 'invalid-name-0', 'invalid-name-1', 'invalid-name-2', - 'invalid-name-3', - 'invalid-name-4' + 'invalid-slug-0', + 'invalid-slug-1', + 'invalid-slug-2', + 'invalid-slug-3', + 'invalid-slug-4' ] const fixturesDir = path.join( diff --git a/packages/platform/src/resolve-agentic-project-config.ts b/packages/platform/src/resolve-agentic-project-config.ts index 9dad7d05..cdace3d2 100644 --- a/packages/platform/src/resolve-agentic-project-config.ts +++ b/packages/platform/src/resolve-agentic-project-config.ts @@ -20,27 +20,27 @@ export async function resolveAgenticProjectConfig( ): Promise { const config = parseAgenticProjectConfig(inputConfig) - const { name, version } = resolveMetadata(config) + const { slug, version } = resolveMetadata(config) validatePricing(config) const { origin, tools } = await resolveOriginAdapter({ - name, + slug, version, - label: `project "${name}"`, + label: `project "${slug}"`, ...opts, origin: config.origin }) const resolvedConfig = parseResolvedAgenticProjectConfig({ ...config, - name, + slug, version, origin, tools }) validateTools({ - label: `project "${name}"`, + label: `project "${slug}"`, ...opts, origin: resolvedConfig.origin, tools: resolvedConfig.tools, diff --git a/packages/platform/src/resolve-metadata.ts b/packages/platform/src/resolve-metadata.ts index 86eac6ab..a3d94f29 100644 --- a/packages/platform/src/resolve-metadata.ts +++ b/packages/platform/src/resolve-metadata.ts @@ -1,27 +1,33 @@ -import type { AgenticProjectConfig } from '@agentic/platform-types' -import { assert } from '@agentic/platform-core' -import { isValidProjectName } from '@agentic/platform-validators' +import type { + AgenticProjectConfig, + ResolvedAgenticProjectConfig +} from '@agentic/platform-types' +import { assert, slugify } from '@agentic/platform-core' +import { isValidProjectSlug } from '@agentic/platform-validators' import { clean as cleanSemver, valid as isValidSemver } from 'semver' export function resolveMetadata({ name, + slug, version -}: Pick): Pick< - AgenticProjectConfig, - 'name' | 'version' +}: Pick): Pick< + ResolvedAgenticProjectConfig, + 'slug' | 'version' > { + slug ??= slugify(name) + assert( - isValidProjectName(name), - `Invalid project name "${name}". Must be ascii-only lower kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"` + isValidProjectSlug(slug), + `Invalid project slug "${slug}" for project name "${name}". Must be ascii-only, lower-case, and kebab-case with no spaces between 1 and 256 characters. For example: "my-project" or "linkedin-resolver-23"` ) if (version) { const normalizedVersion = cleanSemver(version)! - assert(version, `Invalid semver version "${version}" for project "${name}"`) + assert(version, `Invalid semver version "${version}" for project "${slug}"`) assert( isValidSemver(version), - `Invalid semver version "${version}" for project "${name}"` + `Invalid semver version "${version}" for project "${slug}"` ) // Update the config with the normalized semver version @@ -29,7 +35,7 @@ export function resolveMetadata({ } return { - name, + slug, version } } diff --git a/packages/platform/src/resolve-origin-adapter.ts b/packages/platform/src/resolve-origin-adapter.ts index 06f16caa..8e55104b 100644 --- a/packages/platform/src/resolve-origin-adapter.ts +++ b/packages/platform/src/resolve-origin-adapter.ts @@ -13,14 +13,14 @@ import { validateOriginUrl } from './validate-origin-url' * Validates, normalizes, and resolves the origin adapter config for a project. */ export async function resolveOriginAdapter({ - name, + slug, version = '0.0.0', origin, label, cwd, logger }: { - name: string + slug: string origin: OriginAdapterConfig label: string version?: string @@ -41,7 +41,7 @@ export async function resolveOriginAdapter({ }) } else if (origin.type === 'mcp') { return resolveMCPOriginAdapter({ - name, + name: slug, version, origin, label diff --git a/packages/platform/src/validate-agentic-project-config.ts b/packages/platform/src/validate-agentic-project-config.ts index eedeeaf5..9b7d74fe 100644 --- a/packages/platform/src/validate-agentic-project-config.ts +++ b/packages/platform/src/validate-agentic-project-config.ts @@ -18,13 +18,13 @@ export async function validateAgenticProjectConfig( strict: !strip }) - const { name, version } = resolveMetadata(config) + const { slug, version } = resolveMetadata(config) validatePricing(config) const origin = await validateOriginAdapter({ - name, + slug, version, - label: `project "${name}"`, + label: `project "${slug}"`, ...opts, origin: config.origin }) @@ -32,7 +32,7 @@ export async function validateAgenticProjectConfig( return parseAgenticProjectConfig( { ...config, - name, + slug, version, origin }, diff --git a/packages/platform/src/validate-origin-adapter.ts b/packages/platform/src/validate-origin-adapter.ts index 8c2bc8b0..4da48dbd 100644 --- a/packages/platform/src/validate-origin-adapter.ts +++ b/packages/platform/src/validate-origin-adapter.ts @@ -9,14 +9,14 @@ import { validateOriginUrl } from './validate-origin-url' * Validates and normalizes the origin adapter for a project. */ export async function validateOriginAdapter({ - name, + slug, version = '0.0.0', origin, label, cwd, logger }: { - name: string + slug: string origin: Readonly label: string version?: string @@ -47,7 +47,7 @@ export async function validateOriginAdapter({ // We intentionally ignore the resolved version and tools here because the // server will need to re-validate the MCP server info and tools anyway. await resolveMCPOriginAdapter({ - name, + name: slug, version, origin, label diff --git a/packages/tool-client/readme.md b/packages/tool-client/readme.md index a4ae6d76..825448a6 100644 --- a/packages/tool-client/readme.md +++ b/packages/tool-client/readme.md @@ -1,6 +1,6 @@ ## Project Identifier -`@namespace/project-name` +`@namespace/project-slug` ## Deployment Identifier diff --git a/packages/types/package.json b/packages/types/package.json index 6bc7bcfe..dc824ff0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -28,6 +28,7 @@ "test:unit": "vitest run" }, "dependencies": { + "@agentic/platform-core": "workspace:*", "@agentic/platform-validators": "workspace:*", "@hono/zod-openapi": "catalog:", "ms": "catalog:", diff --git a/packages/types/src/agentic-project-config.ts b/packages/types/src/agentic-project-config.ts index c171ca63..b8d55218 100644 --- a/packages/types/src/agentic-project-config.ts +++ b/packages/types/src/agentic-project-config.ts @@ -1,4 +1,5 @@ import type { Simplify } from 'type-fest' +import { isValidProjectSlug } from '@agentic/platform-validators' import { z } from '@hono/zod-openapi' import { @@ -35,17 +36,49 @@ import { export const agenticProjectConfigSchema = z .object({ /** - * Required name of the project. + * Display name for the project. * - * Must be lower kebab-case with no spaces and between 2 and 64 characters. + * Max length 1024 characters. + * + * @required + * @example "My Project" + * @example "LinkedIn Resolver" + */ + name: z + .string() + .max(1024) + .nonempty() + .describe('Display name for the project. Max length 1024 characters.'), + + /** + * Slug for the project. + * + * Must be ascii-only, lower-case, and kebab-case with no spaces between 1 + * and 256 characters. + * + * The project's fully qualified identifier will be `@namespace/slug`, where + * the `namespace` is determined by the author's `username` or team slug. + * + * If not provided, the project `slug` will be derived by slugifying `name`. * * @example "my-project" * @example "linkedin-resolver-23" */ - name: z.string().nonempty().describe('Name of the project.'), + slug: z + .string() + .nonempty() + .describe( + 'Unique project slug. Must be ascii-only, lower-case, and kebab-case with no spaces between 1 and 256 characters. If not provided, it will be derived by slugifying `name`.' + ) + .optional() + .refine((slug) => (slug ? isValidProjectSlug(slug) : true), { + message: 'Invalid project slug' + }), /** * Optional semantic version of the project as a semver string. + * + * @example "1.0.0" */ version: z .string() @@ -55,13 +88,19 @@ export const agenticProjectConfigSchema = z ) .optional(), - /** Optional short description of the project. */ + /** + * Optional short description of the project. + * + * Should be no longer than a few lines. + */ description: z .string() .describe('A short description of the project.') .optional(), - /** Optional readme documenting the project (supports GitHub-flavored markdown). */ + /** + * Optional embedded markdown readme documenting the project (supports GitHub-flavored markdown). + */ readme: z .string() .describe( @@ -70,7 +109,10 @@ export const agenticProjectConfigSchema = z .optional(), /** - * Optional logo image URL to use for the project. Logos should have a square aspect ratio. + * Optional logo image URL to use for the project. Logos should have a + * square aspect ratio. + * + * @example "https://example.com/logo.png" */ iconUrl: z .string() @@ -80,7 +122,11 @@ export const agenticProjectConfigSchema = z 'Optional logo image URL to use for the project. Logos should have a square aspect ratio.' ), - /** Optional URL to the source code of the project. */ + /** + * Optional URL to the source code of the project (eg, GitHub repo). + * + * @example "https://github.com/my-org/my-project" + */ sourceUrl: z .string() .url() @@ -102,6 +148,8 @@ export const agenticProjectConfigSchema = z * @note Currently, only external origin servers are supported. If you'd like * to host your API or MCP server on Agentic's infrastructure, please reach * out to support@agentic.so. + * + * @required */ origin: originAdapterConfigSchema, @@ -195,14 +243,18 @@ export type AgenticProjectConfigRaw = z.output< > export type AgenticProjectConfig = Simplify< Omit & { + slug: string pricingPlans: PricingPlanList toolConfigs: ToolConfig[] defaultRateLimit: RateLimit } > -export const resolvedAgenticProjectConfigSchema = - agenticProjectConfigSchema.extend({ +export const resolvedAgenticProjectConfigSchema = agenticProjectConfigSchema + .required({ + slug: true + }) + .extend({ origin: originAdapterSchema, tools: z.array(toolSchema).default([]) }) @@ -211,6 +263,7 @@ export type ResolvedAgenticProjectConfig = Simplify< z.output, 'pricingPlans' | 'toolConfigs' > & { + slug: string pricingPlans: PricingPlanList toolConfigs: ToolConfig[] defaultRateLimit: RateLimit diff --git a/packages/types/src/openapi.d.ts b/packages/types/src/openapi.d.ts index 6109e282..9876bef6 100644 --- a/packages/types/src/openapi.d.ts +++ b/packages/types/src/openapi.d.ts @@ -113,7 +113,7 @@ export interface paths { path?: never; cookie?: never; }; - /** @description Gets a public project by its public identifier (eg, "@username/project-name"). */ + /** @description Gets a public project by its public identifier (eg, "@username/project-slug"). */ get: operations["getPublicProjectByIdentifier"]; put?: never; post?: never; @@ -147,7 +147,7 @@ export interface paths { path?: never; cookie?: never; }; - /** @description Gets a public deployment by its identifier (eg, "@username/project-name@latest"). */ + /** @description Gets a public deployment by its identifier (eg, "@username/project-slug@latest"). */ get: operations["getPublicDeploymentByIdentifier"]; put?: never; post?: never; @@ -272,7 +272,7 @@ export interface paths { path?: never; cookie?: never; }; - /** @description Gets a project by its public identifier (eg, "@username/project-name"). */ + /** @description Gets a project by its public identifier (eg, "@username/project-slug"). */ get: operations["getProjectByIdentifier"]; put?: never; post?: never; @@ -445,7 +445,7 @@ export interface paths { path?: never; cookie?: never; }; - /** @description Gets a deployment by its identifier (eg, "@username/project-name@latest"). */ + /** @description Gets a deployment by its identifier (eg, "@username/project-slug@latest"). */ get: operations["getDeploymentByIdentifier"]; put?: never; post?: never; @@ -582,7 +582,7 @@ export interface components { token: string; user: components["schemas"]["User"]; }; - /** @description Public project identifier (e.g. "@namespace/project-name") */ + /** @description Public project identifier (e.g. "@namespace/project-slug") */ ProjectIdentifier: string; /** @description The frequency at which a subscription is billed. */ PricingInterval: "day" | "week" | "month" | "year"; @@ -598,9 +598,12 @@ export interface components { createdAt: string; updatedAt: string; deletedAt?: string; + /** @description Display name for the project. Max length 1024 characters. */ + name: string; identifier: components["schemas"]["ProjectIdentifier"]; namespace: string; - name: string; + /** @description Unique project slug. Must be ascii-only, lower-case, and kebab-case with no spaces between 1 and 256 characters. If not provided, it will be derived by slugifying `name`. */ + slug?: string; private: boolean; /** @description User id (e.g. "user_tz4a98xxat96iws9zmbrgj3a") */ userId: string; @@ -620,7 +623,7 @@ export interface components { lastDeployment?: unknown; deployment?: unknown; }; - /** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */ + /** @description Public deployment identifier (e.g. "@namespace/project-slug@{hash|version|latest}") */ DeploymentIdentifier: string; JsonSchemaObject: { /** @enum {string} */ @@ -1264,7 +1267,7 @@ export interface operations { parameters: { query: { populate?: ("user" | "team" | "lastPublishedDeployment" | "lastDeployment") | ("user" | "team" | "lastPublishedDeployment" | "lastDeployment")[]; - /** @description Public project identifier (e.g. "@namespace/project-name") */ + /** @description Public project identifier (e.g. "@namespace/project-slug") */ projectIdentifier: components["schemas"]["ProjectIdentifier"]; }; header?: never; @@ -1321,7 +1324,7 @@ export interface operations { parameters: { query: { populate?: ("user" | "team" | "project") | ("user" | "team" | "project")[]; - /** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */ + /** @description Public deployment identifier (e.g. "@namespace/project-slug@{hash|version|latest}") */ deploymentIdentifier: components["schemas"]["DeploymentIdentifier"]; }; header?: never; @@ -1736,7 +1739,10 @@ export interface operations { requestBody: { content: { "application/json": { + /** @description Display name for the project. Max length 1024 characters. */ name: string; + /** @description Unique project slug. Must be ascii-only, lower-case, and kebab-case with no spaces between 1 and 256 characters. If not provided, it will be derived by slugifying `name`. */ + slug?: string; }; }; }; @@ -1759,7 +1765,7 @@ export interface operations { parameters: { query: { populate?: ("user" | "team" | "lastPublishedDeployment" | "lastDeployment") | ("user" | "team" | "lastPublishedDeployment" | "lastDeployment")[]; - /** @description Public project identifier (e.g. "@namespace/project-name") */ + /** @description Public project identifier (e.g. "@namespace/project-slug") */ projectIdentifier: components["schemas"]["ProjectIdentifier"]; }; header?: never; @@ -1848,7 +1854,7 @@ export interface operations { getConsumerByProjectIdentifier: { parameters: { query: { - /** @description Public project identifier (e.g. "@namespace/project-name") */ + /** @description Public project identifier (e.g. "@namespace/project-slug") */ projectIdentifier: components["schemas"]["ProjectIdentifier"]; populate?: ("user" | "project" | "deployment") | ("user" | "project" | "deployment")[]; }; @@ -2171,7 +2177,7 @@ export interface operations { parameters: { query: { populate?: ("user" | "team" | "project") | ("user" | "team" | "project")[]; - /** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */ + /** @description Public deployment identifier (e.g. "@namespace/project-slug@{hash|version|latest}") */ deploymentIdentifier: components["schemas"]["DeploymentIdentifier"]; }; header?: never; @@ -2266,9 +2272,9 @@ export interface operations { sort?: "asc" | "desc"; sortBy?: "createdAt" | "updatedAt"; populate?: ("user" | "team" | "project") | ("user" | "team" | "project")[]; - /** @description Public project identifier (e.g. "@namespace/project-name") */ + /** @description Public project identifier (e.g. "@namespace/project-slug") */ projectIdentifier?: components["schemas"]["ProjectIdentifier"]; - /** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */ + /** @description Public deployment identifier (e.g. "@namespace/project-slug@{hash|version|latest}") */ deploymentIdentifier?: components["schemas"]["DeploymentIdentifier"]; }; header?: never; @@ -2303,8 +2309,10 @@ export interface operations { requestBody: { content: { "application/json": { - /** @description Name of the project. */ + /** @description Display name for the project. Max length 1024 characters. */ name: string; + /** @description Unique project slug. Must be ascii-only, lower-case, and kebab-case with no spaces between 1 and 256 characters. If not provided, it will be derived by slugifying `name`. */ + slug?: string; /** @description Optional semantic version of the project as a semver string. Ex: 1.0.0, 0.0.1, 5.0.1, etc. */ version?: string; /** @description A short description of the project. */ @@ -2474,7 +2482,7 @@ export interface operations { parameters: { query: { populate?: ("user" | "team" | "project") | ("user" | "team" | "project")[]; - /** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */ + /** @description Public deployment identifier (e.g. "@namespace/project-slug@{hash|version|latest}") */ deploymentIdentifier: components["schemas"]["DeploymentIdentifier"]; }; header?: never; diff --git a/packages/types/src/pricing.ts b/packages/types/src/pricing.ts index 57804c7b..4a99f34a 100644 --- a/packages/types/src/pricing.ts +++ b/packages/types/src/pricing.ts @@ -202,9 +202,12 @@ export const pricingPlanMeteredLineItemSchema = /** * 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. + * determines the per unit price. + * + * In `graduated`-based tiering, the per-unit price changes successively + * as the quantity grows. * * This field requires `billingScheme` to be set to `tiered`. */ @@ -466,10 +469,11 @@ export type PricingPlanLineItem = export const pricingPlanSchema = z .object({ /** - * Human-readable name for the pricing plan. + * Display name for the pricing plan. * * Used in UI and billing invoices. * + * @required * @example "Free" * @example "Starter Monthly" * @example "Pro Annual" @@ -493,6 +497,7 @@ export const pricingPlanSchema = z * as a suffix so pricing plans can be uniquely differentiated from each * other across billing intervals. * + * @required * @example "free" * @example "starter-monthly" * @example "pro-annual" @@ -503,6 +508,7 @@ export const pricingPlanSchema = z .describe( 'PricingPlan slug (eg, "free", "starter-monthly", "pro-annual", etc). Should be lower-cased and kebab-cased. Should be stable across deployments.' ) + // TODO: Make `slug` optional and derive it from `name` if not provided. .openapi('slug', { example: 'starter-monthly' }), /** diff --git a/packages/validators/readme.md b/packages/validators/readme.md index a4ae6d76..825448a6 100644 --- a/packages/validators/readme.md +++ b/packages/validators/readme.md @@ -1,6 +1,6 @@ ## Project Identifier -`@namespace/project-name` +`@namespace/project-slug` ## Deployment Identifier diff --git a/packages/validators/src/__snapshots__/parse-deployment-identifier.test.ts.snap b/packages/validators/src/__snapshots__/parse-deployment-identifier.test.ts.snap index 8b8ffeb7..de387a31 100644 --- a/packages/validators/src/__snapshots__/parse-deployment-identifier.test.ts.snap +++ b/packages/validators/src/__snapshots__/parse-deployment-identifier.test.ts.snap @@ -5,8 +5,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 1`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -15,8 +15,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 2`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -25,8 +25,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 3`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -35,8 +35,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 4`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -45,8 +45,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 5`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -55,8 +55,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 6`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -65,8 +65,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 7`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -75,8 +75,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 8`] = ` "deploymentIdentifier": "@username/foo-bar@dev", "deploymentVersion": "dev", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -85,8 +85,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 9`] = ` "deploymentIdentifier": "@username/foo-bar@1.0.0", "deploymentVersion": "1.0.0", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -95,8 +95,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 10`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -105,8 +105,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 11`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -116,8 +116,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 12`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -126,8 +126,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 13`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -136,8 +136,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 14`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -147,8 +147,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 15`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -158,8 +158,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 16`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -169,8 +169,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 17`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -180,8 +180,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 18`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -191,8 +191,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 19`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -202,8 +202,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 20`] = ` "deploymentIdentifier": "@username/foo-bar@dev", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -213,8 +213,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 21`] = ` "deploymentIdentifier": "@username/foo-bar@1.0.0", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -224,8 +224,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 22`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -235,8 +235,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 23`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -246,8 +246,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 24`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -257,8 +257,8 @@ exports[`parseDeploymentIdentifier > non-strict mode valid 25`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -268,8 +268,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 1`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -278,8 +278,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 2`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -288,8 +288,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 3`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -298,8 +298,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 4`] = ` "deploymentIdentifier": "@username/foo-bar@dev", "deploymentVersion": "dev", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -308,8 +308,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 5`] = ` "deploymentIdentifier": "@username/foo-bar@1.0.0", "deploymentVersion": "1.0.0", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -318,8 +318,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 6`] = ` "deploymentHash": "abc123lz", "deploymentIdentifier": "@username/foo-bar@abc123lz", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -328,8 +328,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 7`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foobar123-yo@01234567", "projectIdentifier": "@username/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "username", + "projectSlug": "foobar123-yo", } `; @@ -338,8 +338,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 8`] = ` "deploymentIdentifier": "@u/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@u/foo-bar", - "projectName": "foo-bar", "projectNamespace": "u", + "projectSlug": "foo-bar", } `; @@ -348,8 +348,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 9`] = ` "deploymentIdentifier": "@a/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@a/foo-bar", - "projectName": "foo-bar", "projectNamespace": "a", + "projectSlug": "foo-bar", } `; @@ -358,8 +358,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 10`] = ` "deploymentIdentifier": "@foo/foobar123-yo@latest", "deploymentVersion": "latest", "projectIdentifier": "@foo/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "foo", + "projectSlug": "foobar123-yo", } `; @@ -368,8 +368,8 @@ exports[`parseDeploymentIdentifier > strict mode valid 11`] = ` "deploymentIdentifier": "@foo/foobar123-yo@latest", "deploymentVersion": "latest", "projectIdentifier": "@foo/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "foo", + "projectSlug": "foobar123-yo", } `; @@ -378,7 +378,7 @@ exports[`parseDeploymentIdentifier > strict mode valid 12`] = ` "deploymentIdentifier": "@foo/bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@foo/bar", - "projectName": "bar", "projectNamespace": "foo", + "projectSlug": "bar", } `; diff --git a/packages/validators/src/__snapshots__/parse-project-identifier.test.ts.snap b/packages/validators/src/__snapshots__/parse-project-identifier.test.ts.snap index c266dacb..a9c5ba55 100644 --- a/packages/validators/src/__snapshots__/parse-project-identifier.test.ts.snap +++ b/packages/validators/src/__snapshots__/parse-project-identifier.test.ts.snap @@ -5,8 +5,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 1`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -15,8 +15,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 2`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -25,8 +25,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 3`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -35,8 +35,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 4`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -45,8 +45,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 5`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -55,8 +55,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 6`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -65,8 +65,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 7`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -75,8 +75,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 8`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -85,8 +85,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 9`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -95,8 +95,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 10`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; @@ -105,8 +105,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 11`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -116,8 +116,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 12`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -127,8 +127,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 13`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -138,8 +138,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 14`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -149,8 +149,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 15`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -160,8 +160,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 16`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -171,8 +171,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 17`] = ` "deploymentIdentifier": "@username/foo-bar@dev", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -182,8 +182,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 18`] = ` "deploymentIdentifier": "@username/foo-bar@1.0.0", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -193,8 +193,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 19`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -204,8 +204,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 20`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -215,8 +215,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 21`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -226,8 +226,8 @@ exports[`parseProjectIdentifier > non-strict mode valid 22`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -235,55 +235,55 @@ exports[`parseProjectIdentifier > non-strict mode valid 22`] = ` exports[`parseProjectIdentifier > strict mode valid 1`] = ` { "projectIdentifier": "@username/foo", - "projectName": "foo", "projectNamespace": "username", + "projectSlug": "foo", } `; exports[`parseProjectIdentifier > strict mode valid 2`] = ` { "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", } `; exports[`parseProjectIdentifier > strict mode valid 3`] = ` { "projectIdentifier": "@username/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "username", + "projectSlug": "foobar123-yo", } `; exports[`parseProjectIdentifier > strict mode valid 4`] = ` { "projectIdentifier": "@u/foo-bar", - "projectName": "foo-bar", "projectNamespace": "u", + "projectSlug": "foo-bar", } `; exports[`parseProjectIdentifier > strict mode valid 5`] = ` { "projectIdentifier": "@a/foo-bar", - "projectName": "foo-bar", "projectNamespace": "a", + "projectSlug": "foo-bar", } `; exports[`parseProjectIdentifier > strict mode valid 6`] = ` { "projectIdentifier": "@foo/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "foo", + "projectSlug": "foobar123-yo", } `; exports[`parseProjectIdentifier > strict mode valid 7`] = ` { "projectIdentifier": "@foo/bar", - "projectName": "bar", "projectNamespace": "foo", + "projectSlug": "bar", } `; diff --git a/packages/validators/src/__snapshots__/parse-tool-identifier.test.ts.snap b/packages/validators/src/__snapshots__/parse-tool-identifier.test.ts.snap index 033a4195..1efaeb2f 100644 --- a/packages/validators/src/__snapshots__/parse-tool-identifier.test.ts.snap +++ b/packages/validators/src/__snapshots__/parse-tool-identifier.test.ts.snap @@ -5,8 +5,8 @@ exports[`parseToolIdentifier > non-strict mode valid 1`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -16,8 +16,8 @@ exports[`parseToolIdentifier > non-strict mode valid 2`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -27,8 +27,8 @@ exports[`parseToolIdentifier > non-strict mode valid 3`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -38,8 +38,8 @@ exports[`parseToolIdentifier > non-strict mode valid 4`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -49,8 +49,8 @@ exports[`parseToolIdentifier > non-strict mode valid 5`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -60,8 +60,8 @@ exports[`parseToolIdentifier > non-strict mode valid 6`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -71,8 +71,8 @@ exports[`parseToolIdentifier > non-strict mode valid 7`] = ` "deploymentIdentifier": "@username/foo-bar@dev", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -82,8 +82,8 @@ exports[`parseToolIdentifier > non-strict mode valid 8`] = ` "deploymentIdentifier": "@username/foo-bar@1.0.0", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -93,8 +93,8 @@ exports[`parseToolIdentifier > non-strict mode valid 9`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -104,8 +104,8 @@ exports[`parseToolIdentifier > non-strict mode valid 10`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -115,8 +115,8 @@ exports[`parseToolIdentifier > non-strict mode valid 11`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -126,8 +126,8 @@ exports[`parseToolIdentifier > non-strict mode valid 12`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -137,8 +137,8 @@ exports[`parseToolIdentifier > strict mode valid 1`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -148,8 +148,8 @@ exports[`parseToolIdentifier > strict mode valid 2`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foo-bar@01234567", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -159,8 +159,8 @@ exports[`parseToolIdentifier > strict mode valid 3`] = ` "deploymentHash": "abc123lz", "deploymentIdentifier": "@username/foo-bar@abc123lz", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -170,8 +170,8 @@ exports[`parseToolIdentifier > strict mode valid 4`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -181,8 +181,8 @@ exports[`parseToolIdentifier > strict mode valid 5`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foobar123-yo@01234567", "projectIdentifier": "@username/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "username", + "projectSlug": "foobar123-yo", "toolName": "foo_bar_BAR_901", } `; @@ -192,8 +192,8 @@ exports[`parseToolIdentifier > strict mode valid 6`] = ` "deploymentHash": "01234567", "deploymentIdentifier": "@username/foobar@01234567", "projectIdentifier": "@username/foobar", - "projectName": "foobar", "projectNamespace": "username", + "projectSlug": "foobar", "toolName": "get_weather01", } `; @@ -203,8 +203,8 @@ exports[`parseToolIdentifier > strict mode valid 7`] = ` "deploymentIdentifier": "@username/foobar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foobar", - "projectName": "foobar", "projectNamespace": "username", + "projectSlug": "foobar", "toolName": "foo", } `; @@ -214,8 +214,8 @@ exports[`parseToolIdentifier > strict mode valid 8`] = ` "deploymentIdentifier": "@username/foobar@dev", "deploymentVersion": "latest", "projectIdentifier": "@username/foobar", - "projectName": "foobar", "projectNamespace": "username", + "projectSlug": "foobar", "toolName": "foo", } `; @@ -225,8 +225,8 @@ exports[`parseToolIdentifier > strict mode valid 9`] = ` "deploymentIdentifier": "@username/foobar@1.0.0", "deploymentVersion": "latest", "projectIdentifier": "@username/foobar", - "projectName": "foobar", "projectNamespace": "username", + "projectSlug": "foobar", "toolName": "foo", } `; @@ -236,8 +236,8 @@ exports[`parseToolIdentifier > strict mode valid 10`] = ` "deploymentIdentifier": "@username/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -247,8 +247,8 @@ exports[`parseToolIdentifier > strict mode valid 11`] = ` "deploymentIdentifier": "@username/foo-bar@dev", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -258,8 +258,8 @@ exports[`parseToolIdentifier > strict mode valid 12`] = ` "deploymentIdentifier": "@username/foo-bar@1.0.0", "deploymentVersion": "latest", "projectIdentifier": "@username/foo-bar", - "projectName": "foo-bar", "projectNamespace": "username", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -269,8 +269,8 @@ exports[`parseToolIdentifier > strict mode valid 13`] = ` "deploymentIdentifier": "@username/foobar123-yo@0.0.1", "deploymentVersion": "latest", "projectIdentifier": "@username/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "username", + "projectSlug": "foobar123-yo", "toolName": "foo_bar_BAR_901", } `; @@ -280,8 +280,8 @@ exports[`parseToolIdentifier > strict mode valid 14`] = ` "deploymentIdentifier": "@username/foobar123-yo@0.0.1", "deploymentVersion": "latest", "projectIdentifier": "@username/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "username", + "projectSlug": "foobar123-yo", "toolName": "foo", } `; @@ -291,8 +291,8 @@ exports[`parseToolIdentifier > strict mode valid 15`] = ` "deploymentIdentifier": "@u/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@u/foo-bar", - "projectName": "foo-bar", "projectNamespace": "u", + "projectSlug": "foo-bar", "toolName": "foo", } `; @@ -302,8 +302,8 @@ exports[`parseToolIdentifier > strict mode valid 16`] = ` "deploymentIdentifier": "@a/foo-bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@a/foo-bar", - "projectName": "foo-bar", "projectNamespace": "a", + "projectSlug": "foo-bar", "toolName": "foo_123", } `; @@ -313,8 +313,8 @@ exports[`parseToolIdentifier > strict mode valid 17`] = ` "deploymentIdentifier": "@foo/foobar123-yo@latest", "deploymentVersion": "latest", "projectIdentifier": "@foo/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "foo", + "projectSlug": "foobar123-yo", "toolName": "foo_bar_BAR_901", } `; @@ -324,8 +324,8 @@ exports[`parseToolIdentifier > strict mode valid 18`] = ` "deploymentIdentifier": "@foo/foobar123-yo@latest", "deploymentVersion": "latest", "projectIdentifier": "@foo/foobar123-yo", - "projectName": "foobar123-yo", "projectNamespace": "foo", + "projectSlug": "foobar123-yo", "toolName": "foo", } `; @@ -335,8 +335,8 @@ exports[`parseToolIdentifier > strict mode valid 19`] = ` "deploymentIdentifier": "@foo/bar@latest", "deploymentVersion": "latest", "projectIdentifier": "@foo/bar", - "projectName": "bar", "projectNamespace": "foo", + "projectSlug": "bar", "toolName": "baz", } `; diff --git a/packages/validators/src/namespace-blacklist.ts b/packages/validators/src/namespace-blacklist.ts index 49aa97b7..e391ac29 100644 --- a/packages/validators/src/namespace-blacklist.ts +++ b/packages/validators/src/namespace-blacklist.ts @@ -35,7 +35,7 @@ export const namespaceBlacklist = new Set([ 'public', 'private', '404', - '500', + '429', '403', '401', '400', @@ -43,6 +43,7 @@ export const namespaceBlacklist = new Set([ '408', '407', '406', + '500', // bad words 'fuck', diff --git a/packages/validators/src/parse-deployment-identifier.test.ts b/packages/validators/src/parse-deployment-identifier.test.ts index abbc08fb..95aff608 100644 --- a/packages/validators/src/parse-deployment-identifier.test.ts +++ b/packages/validators/src/parse-deployment-identifier.test.ts @@ -6,7 +6,7 @@ import { isValidDeploymentIdentifier, isValidNamespace, isValidProjectIdentifier, - isValidProjectName + isValidProjectSlug } from './validators' function success(...args: Parameters) { @@ -14,11 +14,11 @@ function success(...args: Parameters) { expect(result).toBeTruthy() expect(result!.projectIdentifier).toBeTruthy() expect(result!.projectNamespace).toBeTruthy() - expect(result!.projectName).toBeTruthy() + expect(result!.projectSlug).toBeTruthy() expect(result!.deploymentIdentifier).toBeTruthy() expect(result!.deploymentVersion || result!.deploymentHash).toBeTruthy() expect(isValidProjectIdentifier(result!.projectIdentifier)).toBe(true) - expect(isValidProjectName(result!.projectName)).toBe(true) + expect(isValidProjectSlug(result!.projectSlug)).toBe(true) expect(isValidNamespace(result!.projectNamespace)).toBe(true) expect(isValidDeploymentIdentifier(result!.deploymentIdentifier)).toBe(true) diff --git a/packages/validators/src/parse-deployment-identifier.ts b/packages/validators/src/parse-deployment-identifier.ts index ae79cd5e..d9e7f867 100644 --- a/packages/validators/src/parse-deployment-identifier.ts +++ b/packages/validators/src/parse-deployment-identifier.ts @@ -50,7 +50,7 @@ export function parseDeploymentIdentifier( return { projectIdentifier: `@${iMatch[1]!}/${iMatch[2]!}`, projectNamespace: iMatch[1]!, - projectName: iMatch[2]!, + projectSlug: iMatch[2]!, deploymentIdentifier: `@${iMatch[1]!}/${iMatch[2]!}@latest`, deploymentVersion: 'latest' } @@ -62,7 +62,7 @@ export function parseDeploymentIdentifier( return { projectIdentifier: `@${hMatch[1]!}/${hMatch[2]!}`, projectNamespace: hMatch[1]!, - projectName: hMatch[2]!, + projectSlug: hMatch[2]!, deploymentIdentifier: `@${hMatch[1]!}/${hMatch[2]!}@${hMatch[3]!}`, deploymentHash: hMatch[3]! } @@ -74,7 +74,7 @@ export function parseDeploymentIdentifier( return { projectIdentifier: `@${vMatch[1]!}/${vMatch[2]!}`, projectNamespace: vMatch[1]!, - projectName: vMatch[2]!, + projectSlug: vMatch[2]!, deploymentIdentifier: `@${vMatch[1]!}/${vMatch[2]!}@${vMatch[3]!}`, deploymentVersion: vMatch[3]! } diff --git a/packages/validators/src/parse-project-identifier.test.ts b/packages/validators/src/parse-project-identifier.test.ts index c43ea006..d7dacda9 100644 --- a/packages/validators/src/parse-project-identifier.test.ts +++ b/packages/validators/src/parse-project-identifier.test.ts @@ -4,7 +4,7 @@ import { parseProjectIdentifier } from './parse-project-identifier' import { isValidNamespace, isValidProjectIdentifier, - isValidProjectName + isValidProjectSlug } from './validators' function success(...args: Parameters) { @@ -12,9 +12,9 @@ function success(...args: Parameters) { expect(result).toBeTruthy() expect(result!.projectIdentifier).toBeTruthy() expect(result!.projectNamespace).toBeTruthy() - expect(result!.projectName).toBeTruthy() + expect(result!.projectSlug).toBeTruthy() expect(isValidProjectIdentifier(result!.projectIdentifier)).toBe(true) - expect(isValidProjectName(result!.projectName)).toBe(true) + expect(isValidProjectSlug(result!.projectSlug)).toBe(true) expect(isValidNamespace(result!.projectNamespace)).toBe(true) expect(result).toMatchSnapshot() } diff --git a/packages/validators/src/parse-project-identifier.ts b/packages/validators/src/parse-project-identifier.ts index bf60fb4c..a15efaa7 100644 --- a/packages/validators/src/parse-project-identifier.ts +++ b/packages/validators/src/parse-project-identifier.ts @@ -40,7 +40,7 @@ export function parseProjectIdentifier( return { projectIdentifier: `@${match[1]!}/${match[2]!}`, projectNamespace: match[1]!, - projectName: match[2]! + projectSlug: match[2]! } } diff --git a/packages/validators/src/parse-tool-identifier.test.ts b/packages/validators/src/parse-tool-identifier.test.ts index afbd000b..02eb3daf 100644 --- a/packages/validators/src/parse-tool-identifier.test.ts +++ b/packages/validators/src/parse-tool-identifier.test.ts @@ -6,7 +6,7 @@ import { isValidDeploymentIdentifier, isValidNamespace, isValidProjectIdentifier, - isValidProjectName, + isValidProjectSlug, isValidToolName } from './validators' @@ -15,11 +15,11 @@ function success(...args: Parameters) { expect(result).toBeTruthy() expect(result!.projectIdentifier).toBeTruthy() expect(result!.projectNamespace).toBeTruthy() - expect(result!.projectName).toBeTruthy() + expect(result!.projectSlug).toBeTruthy() expect(result!.deploymentIdentifier).toBeTruthy() expect(result!.deploymentVersion || result!.deploymentHash).toBeTruthy() expect(isValidProjectIdentifier(result!.projectIdentifier)).toBe(true) - expect(isValidProjectName(result!.projectName)).toBe(true) + expect(isValidProjectSlug(result!.projectSlug)).toBe(true) expect(isValidNamespace(result!.projectNamespace)).toBe(true) expect(isValidDeploymentIdentifier(result!.deploymentIdentifier)).toBe(true) expect(isValidToolName(result!.toolName)).toBe(true) diff --git a/packages/validators/src/parse-tool-identifier.ts b/packages/validators/src/parse-tool-identifier.ts index 5589d69a..512f64f9 100644 --- a/packages/validators/src/parse-tool-identifier.ts +++ b/packages/validators/src/parse-tool-identifier.ts @@ -35,7 +35,7 @@ export function parseToolIdentifier( return { projectIdentifier: `@${iMatch[1]!}/${iMatch[2]!}`, projectNamespace: iMatch[1]!, - projectName: iMatch[2]!, + projectSlug: iMatch[2]!, deploymentIdentifier: `@${iMatch[1]!}/${iMatch[2]!}@latest`, deploymentVersion: 'latest', toolName: iMatch[3]! @@ -48,7 +48,7 @@ export function parseToolIdentifier( return { projectIdentifier: `@${hMatch[1]!}/${hMatch[2]!}`, projectNamespace: hMatch[1]!, - projectName: hMatch[2]!, + projectSlug: hMatch[2]!, deploymentIdentifier: `@${hMatch[1]!}/${hMatch[2]!}@${hMatch[3]!}`, deploymentHash: hMatch[3]!, toolName: hMatch[4]! @@ -61,7 +61,7 @@ export function parseToolIdentifier( return { projectIdentifier: `@${vMatch[1]!}/${vMatch[2]!}`, projectNamespace: vMatch[1]!, - projectName: vMatch[2]!, + projectSlug: vMatch[2]!, deploymentIdentifier: `@${vMatch[1]!}/${vMatch[2]!}@${vMatch[3]!}`, deploymentVersion: 'latest', toolName: vMatch[4]! diff --git a/packages/validators/src/types.ts b/packages/validators/src/types.ts index 142851d4..280c46ce 100644 --- a/packages/validators/src/types.ts +++ b/packages/validators/src/types.ts @@ -8,7 +8,7 @@ export type ParseIdentifierOptions = { export type ParsedProjectIdentifier = { projectIdentifier: string projectNamespace: string - projectName: string + projectSlug: string } export type ParsedDeploymentIdentifier = Simplify< diff --git a/packages/validators/src/validators.test.ts b/packages/validators/src/validators.test.ts index d73946f8..774c2761 100644 --- a/packages/validators/src/validators.test.ts +++ b/packages/validators/src/validators.test.ts @@ -8,7 +8,7 @@ import { isValidEmail, isValidPassword, isValidProjectIdentifier, - isValidProjectName, + isValidProjectSlug, isValidToolName, isValidUsername } from './validators' @@ -63,23 +63,23 @@ test('isValidPassword failure', () => { expect(isValidPassword('a'.repeat(1025))).toBe(false) }) -test('isValidProjectName success', () => { - expect(isValidProjectName('a')).toBe(true) - expect(isValidProjectName('ai')).toBe(true) - expect(isValidProjectName('aaa')).toBe(true) - expect(isValidProjectName('hello-world')).toBe(true) - expect(isValidProjectName('123-abc')).toBe(true) - expect(isValidProjectName('f'.repeat(256))).toBe(true) +test('isValidProjectSlug success', () => { + expect(isValidProjectSlug('a')).toBe(true) + expect(isValidProjectSlug('ai')).toBe(true) + expect(isValidProjectSlug('aaa')).toBe(true) + expect(isValidProjectSlug('hello-world')).toBe(true) + expect(isValidProjectSlug('123-abc')).toBe(true) + expect(isValidProjectSlug('f'.repeat(256))).toBe(true) }) -test('isValidProjectName failure', () => { - expect(isValidProjectName('hello_world')).toBe(false) - expect(isValidProjectName('a_bc')).toBe(false) - expect(isValidProjectName('abc.')).toBe(false) - expect(isValidProjectName('abc_123')).toBe(false) - expect(isValidProjectName('ah^23')).toBe(false) - expect(isValidProjectName('Hello-World')).toBe(false) - expect(isValidProjectName('f'.repeat(257))).toBe(false) +test('isValidProjectSlug failure', () => { + expect(isValidProjectSlug('hello_world')).toBe(false) + expect(isValidProjectSlug('a_bc')).toBe(false) + expect(isValidProjectSlug('abc.')).toBe(false) + expect(isValidProjectSlug('abc_123')).toBe(false) + expect(isValidProjectSlug('ah^23')).toBe(false) + expect(isValidProjectSlug('Hello-World')).toBe(false) + expect(isValidProjectSlug('f'.repeat(257))).toBe(false) }) test('isValidDeploymentHash success', () => { @@ -96,7 +96,7 @@ test('isValidDeploymentHash failure', () => { }) test('isValidProjectIdentifier success', () => { - expect(isValidProjectIdentifier('@username/project-name')).toBe(true) + expect(isValidProjectIdentifier('@username/project-slug')).toBe(true) expect(isValidProjectIdentifier('@a/123')).toBe(true) }) @@ -114,20 +114,20 @@ test('isValidProjectIdentifier failure', () => { expect(isValidProjectIdentifier('@abc/1.23')).toBe(false) expect(isValidProjectIdentifier('@012345678/123@latest')).toBe(false) expect(isValidProjectIdentifier('@foo@dev')).toBe(false) - expect(isValidProjectIdentifier('@username/Project-Name')).toBe(false) + expect(isValidProjectIdentifier('@username/Project-Slug')).toBe(false) expect(isValidProjectIdentifier('@_/___')).toBe(false) }) test('isValidDeploymentIdentifier success', () => { - expect(isValidDeploymentIdentifier('@username/project-name@01234567')).toBe( + expect(isValidDeploymentIdentifier('@username/project-slug@01234567')).toBe( true ) - expect(isValidDeploymentIdentifier('@username/project-name@latest')).toBe( + expect(isValidDeploymentIdentifier('@username/project-slug@latest')).toBe( true ) - expect(isValidDeploymentIdentifier('@username/project-name@dev')).toBe(true) - expect(isValidDeploymentIdentifier('@username/project-name@0.0.1')).toBe(true) - expect(isValidDeploymentIdentifier('@username/project-name')).toBe(true) + expect(isValidDeploymentIdentifier('@username/project-slug@dev')).toBe(true) + expect(isValidDeploymentIdentifier('@username/project-slug@0.0.1')).toBe(true) + expect(isValidDeploymentIdentifier('@username/project-slug')).toBe(true) expect(isValidDeploymentIdentifier('@a/123@01234567')).toBe(true) expect(isValidDeploymentIdentifier('@a/123@0.1.0')).toBe(true) expect(isValidDeploymentIdentifier('@a/123@latest')).toBe(true) diff --git a/packages/validators/src/validators.ts b/packages/validators/src/validators.ts index 40278c62..55b24782 100644 --- a/packages/validators/src/validators.ts +++ b/packages/validators/src/validators.ts @@ -10,7 +10,7 @@ import { toolNameBlacklist } from './tool-name-blacklist' export const namespaceRe = /^[a-z0-9-]{1,256}$/ export const passwordRe = /^.{3,1024}$/ -export const projectNameRe = /^[a-z0-9-]{1,256}$/ +export const projectSlugRe = /^[a-z0-9-]{1,256}$/ export const deploymentHashRe = /^[a-z0-9]{8}$/ export const toolNameRe = /^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/ @@ -39,8 +39,8 @@ export function isValidPassword(value?: string): boolean { return !!value && passwordRe.test(value) } -export function isValidProjectName(value?: string): boolean { - return !!value && projectNameRe.test(value) +export function isValidProjectSlug(value?: string): boolean { + return !!value && projectSlugRe.test(value) } export function isValidDeploymentHash(value?: string): boolean { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccd65ede..c368111a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,9 +6,63 @@ settings: catalogs: default: + '@agentic/core': + specifier: ^7.6.7 + version: 7.6.7 + '@agentic/serper': + specifier: ^7.6.7 + version: 7.6.7 + '@apideck/better-ajv-errors': + specifier: ^0.3.6 + version: 0.3.6 + '@clack/prompts': + specifier: 0.11.0 + version: 0.11.0 + '@cloudflare/workers-types': + specifier: ^4.20250620.0 + version: 4.20250620.0 + '@commander-js/extra-typings': + specifier: ^14.0.0 + version: 14.0.0 + '@dotenvx/dotenvx': + specifier: ^1.45.1 + version: 1.45.1 + '@edge-runtime/vm': + specifier: ^5.0.0 + version: 5.0.0 + '@fisch0920/config': + specifier: ^1.1.3 + version: 1.1.4 + '@fisch0920/drizzle-orm': + specifier: ^0.43.7 + version: 0.43.7 + '@fisch0920/drizzle-zod': + specifier: ^0.7.9 + version: 0.7.9 + '@hono/mcp': + specifier: ^0.1.0 + version: 0.1.0 + '@hono/node-server': + specifier: 1.14.4 + version: 1.14.4 + '@hono/sentry': + specifier: ^1.2.2 + version: 1.2.2 + '@hono/zod-openapi': + specifier: ^0.19.8 + version: 0.19.8 + '@hono/zod-validator': + specifier: ^0.7.0 + version: 0.7.0 + '@modelcontextprotocol/sdk': + specifier: ^1.13.0 + version: 1.13.0 '@number-flow/react': specifier: ^0.5.10 version: 0.5.10 + '@paralleldrive/cuid2': + specifier: ^2.2.2 + version: 2.2.2 '@pmndrs/assets': specifier: ^1.7.0 version: 1.7.0 @@ -27,6 +81,9 @@ catalogs: '@radix-ui/react-tooltip': specifier: ^1.2.7 version: 1.2.7 + '@react-email/components': + specifier: ^0.1.0 + version: 0.1.0 '@react-three/cannon': specifier: ^6.6.0 version: 6.6.0 @@ -42,6 +99,24 @@ catalogs: '@react-three/rapier': specifier: ^2.1.0 version: 2.1.0 + '@redocly/openapi-core': + specifier: ^1.34.3 + version: 1.34.3 + '@sentry/cli': + specifier: ^2.46.0 + version: 2.46.0 + '@sentry/cloudflare': + specifier: ^9.30.0 + version: 9.30.0 + '@sentry/core': + specifier: ^9.30.0 + version: 9.30.0 + '@sentry/node': + specifier: ^9.30.0 + version: 9.30.0 + '@standard-schema/spec': + specifier: ^1.0.0 + version: 1.0.0 '@tailwindcss/postcss': specifier: ^4.1.10 version: 4.1.10 @@ -60,18 +135,45 @@ catalogs: '@types/canvas-confetti': specifier: ^1.9.0 version: 1.9.0 + '@types/ms': + specifier: ^2.1.0 + version: 2.1.0 + '@types/node': + specifier: ^24.0.4 + version: 24.0.4 '@types/react': specifier: ^19.1.8 version: 19.1.8 '@types/react-dom': specifier: ^19.1.6 version: 19.1.6 + '@types/semver': + specifier: ^7.7.0 + version: 7.7.0 '@types/three': specifier: ^0.177.0 version: 0.177.0 + agents: + specifier: ^0.0.95 + version: 0.0.95 + ajv: + specifier: ^8.17.1 + version: 8.17.1 + ajv-formats: + specifier: ^3.0.1 + version: 3.0.1 autoprefixer: specifier: ^10.4.21 version: 10.4.21 + bcryptjs: + specifier: ^3.0.2 + version: 3.0.2 + bumpp: + specifier: ^10.2.0 + version: 10.2.0 + camelcase: + specifier: ^8.0.0 + version: 8.0.0 canvas-confetti: specifier: ^1.9.3 version: 1.9.3 @@ -81,36 +183,144 @@ catalogs: clsx: specifier: ^2.1.1 version: 2.1.1 + commander: + specifier: ^14.0.0 + version: 14.0.0 + conf: + specifier: ^14.0.0 + version: 14.0.0 + decamelize: + specifier: ^6.0.0 + version: 6.0.0 + decircular: + specifier: ^1.0.0 + version: 1.0.0 + del-cli: + specifier: ^6.0.0 + version: 6.0.0 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.1 + drizzle-orm: + specifier: ^0.44.2 + version: 0.44.2 + email-validator: + specifier: ^2.0.4 + version: 2.0.4 + eslint: + specifier: ^9.29.0 + version: 9.29.0 + eslint-plugin-drizzle: + specifier: ^0.2.3 + version: 0.2.3 + eventid: + specifier: ^2.0.1 + version: 2.0.1 + exit-hook: + specifier: 4.0.0 + version: 4.0.0 + fast-content-type-parse: + specifier: ^3.0.0 + version: 3.0.0 + fast-uri: + specifier: ^3.0.6 + version: 3.0.6 + fastmcp: + specifier: ^3.4.0 + version: 3.4.0 + get-port: + specifier: ^7.1.0 + version: 7.1.0 hast-util-to-jsx-runtime: specifier: ^2.3.6 version: 2.3.6 + hono: + specifier: 4.8.1 + version: 4.8.1 + is-obj: + specifier: ^3.0.0 + version: 3.0.0 + knip: + specifier: ^5.61.2 + version: 5.61.2 ky: specifier: 1.8.1 version: 1.8.1 + lint-staged: + specifier: ^16.1.2 + version: 16.1.2 lucide-react: specifier: ^0.518.0 version: 0.518.0 motion: specifier: ^12.18.1 version: 12.18.1 + ms: + specifier: ^2.1.3 + version: 2.1.3 next: specifier: ^15.3.4 version: 15.3.4 next-themes: specifier: ^0.4.6 version: 0.4.6 + npm-run-all2: + specifier: ^8.0.4 + version: 8.0.4 + octokit: + specifier: ^5.0.3 + version: 5.0.3 + only-allow: + specifier: ^1.2.1 + version: 1.2.1 + open: + specifier: ^10.1.2 + version: 10.1.2 + openapi-typescript: + specifier: ^7.8.0 + version: 7.8.0 + ora: + specifier: ^8.2.0 + version: 8.2.0 + p-all: + specifier: ^5.0.0 + version: 5.0.0 + p-map: + specifier: 7.0.3 + version: 7.0.3 + p-times: + specifier: ^4.0.0 + version: 4.0.0 + parse-json: + specifier: ^8.3.0 + version: 8.3.0 + plur: + specifier: ^5.1.0 + version: 5.1.0 postcss: specifier: ^8.5.6 version: 8.5.6 + postgres: + specifier: ^3.4.7 + version: 3.4.7 posthog-js: specifier: ^1.255.0 version: 1.255.0 + prettier: + specifier: ^3.6.0 + version: 3.6.0 react: specifier: ^19.1.0 version: 19.1.0 react-dom: specifier: ^19.1.0 version: 19.1.0 + react-email: + specifier: ^4.0.16 + version: 4.0.16 react-infinite-scroll-hook: specifier: ^6.0.1 version: 6.0.1 @@ -120,15 +330,30 @@ catalogs: react-use: specifier: ^17.6.0 version: 17.6.0 + resend: + specifier: ^4.6.0 + version: 4.6.0 + restore-cursor: + specifier: ^5.1.0 + version: 5.1.0 + semver: + specifier: ^7.7.2 + version: 7.7.2 server-only: specifier: ^0.0.1 version: 0.0.1 shiki: specifier: ^3.7.0 version: 3.7.0 + simple-git-hooks: + specifier: ^2.13.0 + version: 2.13.0 sonner: specifier: ^2.0.5 version: 2.0.5 + sort-keys: + specifier: ^5.1.0 + version: 5.1.0 stripe: specifier: ^18.2.1 version: 18.2.1 @@ -144,12 +369,45 @@ catalogs: three: specifier: ^0.177.0 version: 0.177.0 + tsup: + specifier: ^8.5.0 + version: 8.5.0 + tsx: + specifier: ^4.20.3 + version: 4.20.3 + turbo: + specifier: ^2.5.4 + version: 2.5.4 tw-animate-css: specifier: ^1.3.4 version: 1.3.4 type-fest: specifier: ^4.41.0 version: 4.41.0 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + unconfig: + specifier: ^7.3.2 + version: 7.3.2 + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4 + vitest: + specifier: ^3.2.4 + version: 3.2.4 + wrangler: + specifier: ^4.21.0 + version: 4.21.0 + zod: + specifier: ^3.25.67 + version: 3.25.67 + zod-to-json-schema: + specifier: ^3.24.5 + version: 3.24.5 + zod-validation-error: + specifier: ^3.5.2 + version: 3.5.2 importers: @@ -236,6 +494,12 @@ importers: '@agentic/platform-validators': specifier: workspace:* version: link:../../packages/validators + '@dicebear/collection': + specifier: ^9.2.3 + version: 9.2.3(@dicebear/core@9.2.3) + '@dicebear/core': + specifier: ^9.2.3 + version: 9.2.3 '@fisch0920/drizzle-orm': specifier: 'catalog:' version: 0.43.7(@cloudflare/workers-types@4.20250620.0)(@neondatabase/serverless@1.0.1)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(kysely@0.28.2)(postgres@3.4.7) @@ -692,6 +956,9 @@ importers: packages/core: dependencies: + '@sindresorhus/slugify': + specifier: ^2.2.1 + version: 2.2.1 decircular: specifier: 'catalog:' version: 1.0.0 @@ -925,6 +1192,9 @@ importers: packages/types: dependencies: + '@agentic/platform-core': + specifier: workspace:* + version: link:../core '@agentic/platform-validators': specifier: workspace:* version: link:../validators @@ -1127,6 +1397,196 @@ packages: '@date-fns/utc@2.1.0': resolution: {integrity: sha512-176grgAgU2U303rD2/vcOmNg0kGPbhzckuH1TEP2al7n0AQipZIy9P15usd2TKQCG1g+E1jX/ZVQSzs4sUDwgA==} + '@dicebear/adventurer-neutral@9.2.3': + resolution: {integrity: sha512-yjsflrlGIZolEJDcIhdh5l8R6QoyZ0Fc5ZaRS95Na15GeCA0bcq92fDpyb/87F8QRYR4hcn8wYim+MZKyDnm3A==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/adventurer@9.2.3': + resolution: {integrity: sha512-kF97966JCLbLFkavLKLplhubC4k8G19e1V3vtq+XWKKSBNNLMc0yhiEXnFjS6eVI4rnQ6dUpTff4gUmpREmEnQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/avataaars-neutral@9.2.3': + resolution: {integrity: sha512-v9Ga01GVKGchqNBc4Z2Gk5Y4P0c/jzNg6l/r4kX0xCMDXNd/vchi6QI464pHnJpDC/I8BzL1wYbpZrUEmTWJmA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/avataaars@9.2.3': + resolution: {integrity: sha512-Jh6mGKMA99KCF+S5rnS+QIlrApCy7NJI6SwmCzixJQmlX0GbWrgBuRxPEy2XJo4FWKGI9eFKOQ4QtNFEwGKjWQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/big-ears-neutral@9.2.3': + resolution: {integrity: sha512-y4T8ukm+A/ZXBnwyx3CFZxxJlzoTGJ1xmJL2j4/6zXxjlzl3wkc2mZX36t16i6NKiBs9LJwOUNx6IezbmYjDjg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/big-ears@9.2.3': + resolution: {integrity: sha512-YTZ3QIWlG6HlkDyASAaQoetxYtQgOwq4GTJxDpzzawFbhhtMJabqAU7dzWkPY6IgJfI/MAKVxdeqXK0Aiw4E6w==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/big-smile@9.2.3': + resolution: {integrity: sha512-/QvZlUmNIjnA6GN5VPUiD5g1MYr+qTOun3CfBSrPAav+tFo8RQGszM6ggvkmn4Q1aHwJ0XQNRiIvTJtQisuqKg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/bottts-neutral@9.2.3': + resolution: {integrity: sha512-6wFfWwXHE3kJKP0DqgN/SmSBDPs/s8o46APst8RfHnmcDLEsQExg0WquGwXfP+3XqGjPhsm/LGaA/yXKlE/84w==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/bottts@9.2.3': + resolution: {integrity: sha512-udHSuK7uDYMFiCPZsHkaqCyo0hoJFMehsTSBtvOUOQqlQGYA2E7+gLgLmfvBQlYuk61lBM4yLyQdbGj0W7Nl1Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/collection@9.2.3': + resolution: {integrity: sha512-PWW1x6EnDKw66dtpy3+1/W/UTrxq9jOGaWgRj+PV6Y0Pvc2a7ZMi6tBzfGWpy0r+XdhREUQ3vyFqim+R/iE2Rg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/core@9.2.3': + resolution: {integrity: sha512-rsWmQO/TxBtnYbQSOrOESmf/XD5TC30cwKw9LBT2+7Q9HwIf5qJKBJT25bmwvn3RPHVe4A6DkTVMtmsoSaErFg==} + engines: {node: '>=18.0.0'} + + '@dicebear/croodles-neutral@9.2.3': + resolution: {integrity: sha512-peurufAge4ZbzqGVAyFxhZ64qKv+MLBuQK2XvXsXX0uHgtEeAgSjFl6+O0wgJ7KVKtHL56qY68Acdhy3U91BYw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/croodles@9.2.3': + resolution: {integrity: sha512-dzFhchlmhst+PBMkNbLNVcOyNFWAhaFnxQOMwWluHPzg8ryiFt/LEKWB7bPKMue1jBwta6LfoUv7vDG4LpdXIw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/dylan@9.2.3': + resolution: {integrity: sha512-gtRhE1jxKwgUE4d2N7KHJ/rSdAg8zxgViALygs8/kcs2y4uinNx5NW3OvJwNPMBAwHRqeM6GbLKycbfiYMKdwA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/fun-emoji@9.2.3': + resolution: {integrity: sha512-zR+SmA7hWT8FDkJsdvg5uI7fOabilZfheM8RnMkEXXA0NmKgNKuWX9yFjLxeWESbJwBZFJ/SKasKcZSJP88eyw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/glass@9.2.3': + resolution: {integrity: sha512-ucaM6TrTt/JsDa26ZQbtxevN0JLu2S06Q2+M/kAT2f0OdR6hSNdPnj/hDIVWg/u4XgW9HXrJ4Lt2KG2nZGq1MA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/icons@9.2.3': + resolution: {integrity: sha512-2IaSmvgxzEvbtXHxhuxaCrBCtTG7HouxbInzPOetWz/YroTe5qj2k81uhl6ctbviO1PgGgkhIJ12nCIey6O/Tw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/identicon@9.2.3': + resolution: {integrity: sha512-WCMYjOV19ocaJQDgD3E1z2FRgY4Zbnjieb2FSXPwW6zi04l52rhGaywk8YlCbaHilE43Iybyu1v6iDf4I71qKg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/initials@9.2.3': + resolution: {integrity: sha512-Qz668xB/XHRNsxW3vpZ0q9xq/M6tscbv1FaFvAGbnFO2hzfqiCWSK1cfNrrqlPJbz7Pz5LIlL5xObLZ0UYhFRA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/lorelei-neutral@9.2.3': + resolution: {integrity: sha512-C0XcfvB1Td9y1jeaBSkuMrQTAv/RiupQ62cE25f18Mw9UuTC3TwnHR6Ms/4SAEhCZxE2xYHZccs3Lvgkn/b3dA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/lorelei@9.2.3': + resolution: {integrity: sha512-ribIq7I669JyUSZ0s8jDx5N2NqQUhGSoFGK5d82JqI2HYIAury/0WDsMh0fKeuYUJb9KuDEv+e9nO3OJVa8Hog==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/micah@9.2.3': + resolution: {integrity: sha512-CcA/vVqrFEu5oDNAwJRRmLAE1gGJseP6BJTHMOdROkIOMpt1sy7OBqOFmajGnpOxOhvqBmvTZy0bw0G2TnnKSQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/miniavs@9.2.3': + resolution: {integrity: sha512-l111IVYtbUXypFQz1U4hTFM5DxGThDmluG7mNi1Uu92I5/3mH7Eltm2mX3T6g1HFW8MDaTeSBKee1xGVaHmmaA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/notionists-neutral@9.2.3': + resolution: {integrity: sha512-4WXCE9e9xIe1QdxiGXONxWhAzDVT4nMjoZA0rtgX8Sl0lqSIeBrO4RTnyaqFbGG1V1MiPj9CbE5OB8YbwexFBg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/notionists@9.2.3': + resolution: {integrity: sha512-0bqS1pwAR8aAm6BDShgD5eI+u0f2CtgeFIDsOER6Je3WwFGyIBUTwwVSL7gmiA5qcvQAB9Slohj7vbN0pifTyA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/open-peeps@9.2.3': + resolution: {integrity: sha512-QcqKF3geA2Aqw1aJo8PO7+mKXz2I882D61I19vm94V1ojWnyw+8ihFmCLPReVcxQatxNjraTXCoKyT37TbgKBg==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/personas@9.2.3': + resolution: {integrity: sha512-YUXyoOwgt9CGhEuWl+dChDLmcVyuoU5UB7GY8PLWyHoy+1K3MzHJI7XFeDXyOYFpfuBiVY2++YjUZg26loqCNw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/pixel-art-neutral@9.2.3': + resolution: {integrity: sha512-Eblj/m8x0VSsJfN++GRMPdqOXCcTh0TIIf2waLbNlaOEU1xdhkYC9wSdYfjNv3gUIdbButiYWHwPgRZETcx1qQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/pixel-art@9.2.3': + resolution: {integrity: sha512-4JZS/zbZZXg99P10sza0yxC2v+ugEWcoNy+c4I98ylWOjs2BrKcupSsU/2R11egT4u7hEknlVqR1zxKBGjuIDA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/rings@9.2.3': + resolution: {integrity: sha512-L4gZAzMsf9TNS99Um1ElG7Ncs9VSpQugLL0fCfDkMEkGxvwM+Aqu1UfX7iV3qNsEiu/zaR+3qCMBOEfNGUE1vA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/shapes@9.2.3': + resolution: {integrity: sha512-4MlhZED2ZDfHymNiPtj67UwW79ECoK1gDjRsUS5BPQUBFIQ22cYrKrRhDcs1CxMZCStnVsH26tt3atIkFeIo9Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + + '@dicebear/thumbs@9.2.3': + resolution: {integrity: sha512-C9vFXjYpLWa98tc4n+FcWkpJAWaGSJOqPUeaom+KVU/ibXLkr21RlcDCRc63YJfiULlE8bQhDWw7FaUAFV3gew==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@dicebear/core': ^9.0.0 + '@dimforge/rapier3d-compat@0.12.0': resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} @@ -3310,6 +3770,14 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@sindresorhus/slugify@2.2.1': + resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==} + engines: {node: '>=12'} + + '@sindresorhus/transliterate@1.6.0': + resolution: {integrity: sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==} + engines: {node: '>=12'} + '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -7944,6 +8412,164 @@ snapshots: '@date-fns/utc@2.1.0': {} + '@dicebear/adventurer-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/adventurer@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/avataaars-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/avataaars@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/big-ears-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/big-ears@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/big-smile@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/bottts-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/bottts@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/collection@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/adventurer': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/adventurer-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/avataaars': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/avataaars-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/big-ears': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/big-ears-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/big-smile': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/bottts': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/bottts-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/core': 9.2.3 + '@dicebear/croodles': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/croodles-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/dylan': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/fun-emoji': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/glass': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/icons': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/identicon': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/initials': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/lorelei': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/lorelei-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/micah': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/miniavs': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/notionists': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/notionists-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/open-peeps': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/personas': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/pixel-art': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/pixel-art-neutral': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/rings': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/shapes': 9.2.3(@dicebear/core@9.2.3) + '@dicebear/thumbs': 9.2.3(@dicebear/core@9.2.3) + + '@dicebear/core@9.2.3': + dependencies: + '@types/json-schema': 7.0.15 + + '@dicebear/croodles-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/croodles@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/dylan@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/fun-emoji@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/glass@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/icons@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/identicon@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/initials@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/lorelei-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/lorelei@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/micah@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/miniavs@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/notionists-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/notionists@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/open-peeps@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/personas@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/pixel-art-neutral@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/pixel-art@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/rings@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/shapes@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + + '@dicebear/thumbs@9.2.3(@dicebear/core@9.2.3)': + dependencies: + '@dicebear/core': 9.2.3 + '@dimforge/rapier3d-compat@0.12.0': {} '@dimforge/rapier3d-compat@0.15.0': {} @@ -9850,6 +10476,15 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@sindresorhus/slugify@2.2.1': + dependencies: + '@sindresorhus/transliterate': 1.6.0 + escape-string-regexp: 5.0.0 + + '@sindresorhus/transliterate@1.6.0': + dependencies: + escape-string-regexp: 5.0.0 + '@socket.io/component-emitter@3.1.2': {} '@standard-schema/spec@1.0.0': {} @@ -10846,10 +11481,6 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.1: - dependencies: - ms: 2.1.3 - debug@4.4.1(supports-color@10.0.0): dependencies: ms: 2.1.3 @@ -12672,7 +13303,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 diff --git a/readme.md b/readme.md index 84c1d3b9..ab9d0350 100644 --- a/readme.md +++ b/readme.md @@ -7,8 +7,8 @@ ## API Gateway -- **MCP**: `https://gateway.agentic.so/@{username}/{project-name}/mcp` -- **HTTP**: `GET/POST` `https://gateway.agentic.so/@{username}/{project-name}/{tool-name}` +- **MCP**: `https://gateway.agentic.so/@{username}/{project-slug}/mcp` +- **HTTP**: `GET/POST` `https://gateway.agentic.so/@{username}/{project-slug}/{tool-name}` ## TODO: MVP @@ -41,6 +41,7 @@ - social images - simplify `AgenticToolClient` and only require one package per TS LLM SDK - `createAISDKToolsFromIdentifier(projectIdentifier)` +- add really strict free rate-limits to `@agentic/search` ## TODO: Post-MVP @@ -96,9 +97,7 @@ - prompts - other MCP features? - additional transactional emails -- consider `projectName` and `projectSlug` or `projectIdentifier`? - handle or validate against dynamic MCP origin tools -- allow config name to be `project-name` or `@namespace/project-name`? - upgrade to zod v4 - decide whether deployment fields like `defaultRateLimit` and others should be generated and stored in the db, or should be inferred based on `undefined` values - support multiple rate-limits by slug