kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: add tool-client and search example; refactor e2e
rodzic
e388bcee46
commit
cf4a404fd0
|
@ -14,7 +14,8 @@ import {
|
||||||
import { deploymentIdentifierAndPopulateSchema } from './schemas'
|
import { deploymentIdentifierAndPopulateSchema } from './schemas'
|
||||||
|
|
||||||
const route = createRoute({
|
const route = createRoute({
|
||||||
description: 'Gets a deployment by its public identifier',
|
description:
|
||||||
|
'Gets a deployment by its identifier (eg, "@username/project-name@latest").',
|
||||||
tags: ['deployments'],
|
tags: ['deployments'],
|
||||||
operationId: 'getDeploymentByIdentifier',
|
operationId: 'getDeploymentByIdentifier',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import type { DefaultHonoEnv } from '@agentic/platform-hono'
|
||||||
|
import { assert, parseZodSchema } from '@agentic/platform-core'
|
||||||
|
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||||
|
|
||||||
|
import { schema } from '@/db'
|
||||||
|
import { aclPublicProject } from '@/lib/acl-public-project'
|
||||||
|
import { tryGetDeploymentByIdentifier } from '@/lib/deployments/try-get-deployment-by-identifier'
|
||||||
|
import {
|
||||||
|
openapiAuthenticatedSecuritySchemas,
|
||||||
|
openapiErrorResponse404,
|
||||||
|
openapiErrorResponses
|
||||||
|
} from '@/lib/openapi-utils'
|
||||||
|
|
||||||
|
import { deploymentIdentifierAndPopulateSchema } from './schemas'
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
description:
|
||||||
|
'Gets a public deployment by its identifier (eg, "@username/project-name@latest").',
|
||||||
|
tags: ['deployments'],
|
||||||
|
operationId: 'getPublicDeploymentByIdentifier',
|
||||||
|
method: 'get',
|
||||||
|
path: 'deployments/public/by-identifier',
|
||||||
|
security: openapiAuthenticatedSecuritySchemas,
|
||||||
|
request: {
|
||||||
|
query: deploymentIdentifierAndPopulateSchema
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'A deployment object',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: schema.deploymentSelectSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...openapiErrorResponses,
|
||||||
|
...openapiErrorResponse404
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export function registerV1GetPublicDeploymentByIdentifier(
|
||||||
|
app: OpenAPIHono<DefaultHonoEnv>
|
||||||
|
) {
|
||||||
|
return app.openapi(route, async (c) => {
|
||||||
|
const { deploymentIdentifier, populate = [] } = c.req.valid('query')
|
||||||
|
|
||||||
|
const deployment = await tryGetDeploymentByIdentifier(c, {
|
||||||
|
deploymentIdentifier,
|
||||||
|
with: {
|
||||||
|
project: true,
|
||||||
|
...Object.fromEntries(populate.map((field) => [field, true]))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
assert(deployment, 404, `Deployment not found "${deploymentIdentifier}"`)
|
||||||
|
assert(
|
||||||
|
deployment.project,
|
||||||
|
404,
|
||||||
|
`Project not found for deployment "${deploymentIdentifier}"`
|
||||||
|
)
|
||||||
|
await aclPublicProject(deployment.project!)
|
||||||
|
|
||||||
|
return c.json(parseZodSchema(schema.deploymentSelectSchema, deployment))
|
||||||
|
})
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import { registerV1AdminGetDeploymentByIdentifier } from './deployments/admin-ge
|
||||||
import { registerV1CreateDeployment } from './deployments/create-deployment'
|
import { registerV1CreateDeployment } from './deployments/create-deployment'
|
||||||
import { registerV1GetDeployment } from './deployments/get-deployment'
|
import { registerV1GetDeployment } from './deployments/get-deployment'
|
||||||
import { registerV1GetDeploymentByIdentifier } from './deployments/get-deployment-by-identifier'
|
import { registerV1GetDeploymentByIdentifier } from './deployments/get-deployment-by-identifier'
|
||||||
|
import { registerV1GetPublicDeploymentByIdentifier } from './deployments/get-public-deployment-by-identifier'
|
||||||
import { registerV1ListDeployments } from './deployments/list-deployments'
|
import { registerV1ListDeployments } from './deployments/list-deployments'
|
||||||
import { registerV1PublishDeployment } from './deployments/publish-deployment'
|
import { registerV1PublishDeployment } from './deployments/publish-deployment'
|
||||||
import { registerV1UpdateDeployment } from './deployments/update-deployment'
|
import { registerV1UpdateDeployment } from './deployments/update-deployment'
|
||||||
|
@ -133,6 +134,7 @@ registerV1ListConsumers(privateRouter)
|
||||||
registerV1ListConsumersForProject(privateRouter)
|
registerV1ListConsumersForProject(privateRouter)
|
||||||
|
|
||||||
// Deployments
|
// Deployments
|
||||||
|
registerV1GetPublicDeploymentByIdentifier(publicRouter)
|
||||||
registerV1GetDeploymentByIdentifier(privateRouter) // must be before `registerV1GetDeployment`
|
registerV1GetDeploymentByIdentifier(privateRouter) // must be before `registerV1GetDeployment`
|
||||||
registerV1GetDeployment(privateRouter)
|
registerV1GetDeployment(privateRouter)
|
||||||
registerV1CreateDeployment(privateRouter)
|
registerV1CreateDeployment(privateRouter)
|
||||||
|
|
|
@ -248,9 +248,23 @@ export const projectSelectSchema = createSelectSchema(projects, {
|
||||||
return deploymentSelectSchema.parse(deployment)
|
return deploymentSelectSchema.parse(deployment)
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
// .openapi('Deployment', { type: 'object' }),
|
|
||||||
|
|
||||||
lastDeployment: z
|
lastDeployment: z
|
||||||
|
.any()
|
||||||
|
.refine(
|
||||||
|
(deployment): boolean =>
|
||||||
|
!deployment || deploymentSelectSchema.safeParse(deployment).success,
|
||||||
|
{
|
||||||
|
message: 'Invalid lastDeployment'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.transform((deployment): any => {
|
||||||
|
if (!deployment) return undefined
|
||||||
|
return deploymentSelectSchema.parse(deployment)
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
deployment: z
|
||||||
.any()
|
.any()
|
||||||
.refine(
|
.refine(
|
||||||
(deployment): boolean =>
|
(deployment): boolean =>
|
||||||
|
@ -264,7 +278,6 @@ export const projectSelectSchema = createSelectSchema(projects, {
|
||||||
return deploymentSelectSchema.parse(deployment)
|
return deploymentSelectSchema.parse(deployment)
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
// .openapi('Deployment', { type: 'object' })
|
|
||||||
})
|
})
|
||||||
.strip()
|
.strip()
|
||||||
// TODO
|
// TODO
|
||||||
|
|
|
@ -28,9 +28,11 @@ export const users = pgTable(
|
||||||
username: username().unique().notNull(),
|
username: username().unique().notNull(),
|
||||||
role: userRoleEnum().default('user').notNull(),
|
role: userRoleEnum().default('user').notNull(),
|
||||||
|
|
||||||
name: text(),
|
|
||||||
email: text().notNull().unique(),
|
email: text().notNull().unique(),
|
||||||
isEmailVerified: boolean().default(false).notNull(),
|
isEmailVerified: boolean().default(false).notNull(),
|
||||||
|
|
||||||
|
name: text(),
|
||||||
|
bio: text(),
|
||||||
image: text(),
|
image: text(),
|
||||||
|
|
||||||
//isStripeConnectEnabledByDefault: boolean().default(true).notNull(),
|
//isStripeConnectEnabledByDefault: boolean().default(true).notNull(),
|
||||||
|
@ -57,6 +59,7 @@ export const userSelectSchema = createSelectSchema(users)
|
||||||
export const userUpdateSchema = createUpdateSchema(users)
|
export const userUpdateSchema = createUpdateSchema(users)
|
||||||
.pick({
|
.pick({
|
||||||
name: true,
|
name: true,
|
||||||
|
bio: true,
|
||||||
image: true
|
image: true
|
||||||
//isStripeConnectEnabledByDefault: true
|
//isStripeConnectEnabledByDefault: true
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import type { DefaultHonoContext } from '@agentic/platform-hono'
|
||||||
import { assert } from '@agentic/platform-core'
|
import { assert } from '@agentic/platform-core'
|
||||||
import { parseDeploymentIdentifier } from '@agentic/platform-validators'
|
import { parseDeploymentIdentifier } from '@agentic/platform-validators'
|
||||||
|
|
||||||
import type { AuthenticatedHonoContext } from '@/lib/types'
|
|
||||||
import {
|
import {
|
||||||
and,
|
and,
|
||||||
db,
|
db,
|
||||||
|
@ -12,6 +12,8 @@ import {
|
||||||
} from '@/db'
|
} from '@/db'
|
||||||
import { setPublicCacheControl } from '@/lib/cache-control'
|
import { setPublicCacheControl } from '@/lib/cache-control'
|
||||||
|
|
||||||
|
import type { AuthenticatedHonoContext } from '../types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to find the Deployment matching the given deployment ID or
|
* Attempts to find the Deployment matching the given deployment ID or
|
||||||
* identifier.
|
* identifier.
|
||||||
|
@ -21,7 +23,7 @@ import { setPublicCacheControl } from '@/lib/cache-control'
|
||||||
* Does not take care of ACLs.
|
* Does not take care of ACLs.
|
||||||
*/
|
*/
|
||||||
export async function tryGetDeploymentByIdentifier(
|
export async function tryGetDeploymentByIdentifier(
|
||||||
ctx: AuthenticatedHonoContext,
|
ctx: AuthenticatedHonoContext | DefaultHonoContext,
|
||||||
{
|
{
|
||||||
deploymentIdentifier,
|
deploymentIdentifier,
|
||||||
strict = false,
|
strict = false,
|
||||||
|
|
|
@ -13,3 +13,6 @@ AGENTIC_GATEWAY_BASE_URL='http://localhost:8787'
|
||||||
AGENTIC_DEV_EMAIL=
|
AGENTIC_DEV_EMAIL=
|
||||||
AGENTIC_DEV_PASSWORD=
|
AGENTIC_DEV_PASSWORD=
|
||||||
AGENTIC_DEV_ACCESS_TOKEN=
|
AGENTIC_DEV_ACCESS_TOKEN=
|
||||||
|
|
||||||
|
AGENTIC_AGENTIC_EMAIL=
|
||||||
|
AGENTIC_AGENTIC_PASSWORD=
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { deployFixtures } from '../src/project-fixtures'
|
import { deployProjects } from '../src/deploy-projects'
|
||||||
|
import { devClient } from '../src/dev-client'
|
||||||
|
import { fixtures } from '../src/dev-fixtures'
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('\n\nDeploying fixtures...\n\n')
|
console.log('\n\nDeploying dev fixtures...\n\n')
|
||||||
|
|
||||||
const deployments = await deployFixtures()
|
const deployments = await deployProjects(fixtures, { client: devClient })
|
||||||
console.log(JSON.stringify(deployments, null, 2))
|
console.log(JSON.stringify(deployments, null, 2))
|
||||||
|
|
||||||
// eslint-disable-next-line unicorn/no-process-exit
|
// eslint-disable-next-line unicorn/no-process-exit
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import { deployFixtures, publishDeployments } from '../src/project-fixtures'
|
import { deployProjects } from '../src/deploy-projects'
|
||||||
|
import { devClient } from '../src/dev-client'
|
||||||
|
import { fixtures } from '../src/dev-fixtures'
|
||||||
|
import { publishDeployments } from '../src/publish-deployments'
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('\n\nDeploying fixtures...\n\n')
|
console.log('\n\nDeploying dev fixtures...\n\n')
|
||||||
|
|
||||||
const deployments = await deployFixtures()
|
const deployments = await deployProjects(fixtures, { client: devClient })
|
||||||
console.log(JSON.stringify(deployments, null, 2))
|
console.log(JSON.stringify(deployments, null, 2))
|
||||||
|
|
||||||
console.log('\n\nPublishing deployments...\n\n')
|
console.log('\n\nPublishing dev fixture deployments...\n\n')
|
||||||
|
|
||||||
const publishedDeployments = await publishDeployments(deployments)
|
const publishedDeployments = await publishDeployments(deployments, {
|
||||||
|
client: devClient
|
||||||
|
})
|
||||||
console.log(JSON.stringify(publishedDeployments, null, 2))
|
console.log(JSON.stringify(publishedDeployments, null, 2))
|
||||||
|
|
||||||
// eslint-disable-next-line unicorn/no-process-exit
|
// eslint-disable-next-line unicorn/no-process-exit
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { AgenticApiClient } from '@agentic/platform-api-client'
|
import { AgenticApiClient } from '@agentic/platform-api-client'
|
||||||
|
|
||||||
|
import { examples } from '../src/agentic-examples'
|
||||||
|
import { deployProjects } from '../src/deploy-projects'
|
||||||
|
import { fixtures } from '../src/dev-fixtures'
|
||||||
import { env, isProd } from '../src/env'
|
import { env, isProd } from '../src/env'
|
||||||
import { deployFixtures, publishDeployments } from '../src/project-fixtures'
|
import { publishDeployments } from '../src/publish-deployments'
|
||||||
|
|
||||||
export const client = new AgenticApiClient({
|
export const client = new AgenticApiClient({
|
||||||
apiBaseUrl: env.AGENTIC_API_BASE_URL
|
apiBaseUrl: env.AGENTIC_API_BASE_URL
|
||||||
|
@ -10,7 +13,8 @@ export const client = new AgenticApiClient({
|
||||||
async function main() {
|
async function main() {
|
||||||
// TODO: clear existing tables? and include prompt to double check if so...
|
// TODO: clear existing tables? and include prompt to double check if so...
|
||||||
|
|
||||||
console.log('\n\nCreating dev user...\n\n')
|
{
|
||||||
|
console.log('\n\nCreating "dev" user...\n\n')
|
||||||
|
|
||||||
const devAuthSession = await client.signUpWithPassword({
|
const devAuthSession = await client.signUpWithPassword({
|
||||||
username: 'dev',
|
username: 'dev',
|
||||||
|
@ -23,15 +27,41 @@ async function main() {
|
||||||
`\n\nREMEMBER TO UPDATE "AGENTIC_DEV_ACCESS_TOKEN" in e2e/.env${isProd ? '.production' : ''}\n\n`
|
`\n\nREMEMBER TO UPDATE "AGENTIC_DEV_ACCESS_TOKEN" in e2e/.env${isProd ? '.production' : ''}\n\n`
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('\n\nDeploying fixtures...\n\n')
|
console.log('\n\nDeploying dev fixtures...\n\n')
|
||||||
|
|
||||||
const deployments = await deployFixtures()
|
const deployments = await deployProjects(fixtures, { client })
|
||||||
console.log(JSON.stringify(deployments, null, 2))
|
console.log(JSON.stringify(deployments, null, 2))
|
||||||
|
|
||||||
console.log('\n\nPublishing deployments...\n\n')
|
console.log('\n\nPublishing dev fixture deployments...\n\n')
|
||||||
|
|
||||||
const publishedDeployments = await publishDeployments(deployments)
|
const publishedDeployments = await publishDeployments(deployments, {
|
||||||
|
client
|
||||||
|
})
|
||||||
console.log(JSON.stringify(publishedDeployments, null, 2))
|
console.log(JSON.stringify(publishedDeployments, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
console.log('\n\nCreating "agentic" user...\n\n')
|
||||||
|
|
||||||
|
const agenticAuthSession = await client.signUpWithPassword({
|
||||||
|
username: 'agentic',
|
||||||
|
email: env.AGENTIC_AGENTIC_EMAIL,
|
||||||
|
password: env.AGENTIC_AGENTIC_PASSWORD
|
||||||
|
})
|
||||||
|
console.log(JSON.stringify(agenticAuthSession, null, 2))
|
||||||
|
|
||||||
|
console.log('\n\nDeploying example projects...\n\n')
|
||||||
|
|
||||||
|
const deployments = await deployProjects(examples, { client })
|
||||||
|
console.log(JSON.stringify(deployments, null, 2))
|
||||||
|
|
||||||
|
console.log('\n\nPublishing example project deployments...\n\n')
|
||||||
|
|
||||||
|
const publishedDeployments = await publishDeployments(deployments, {
|
||||||
|
client
|
||||||
|
})
|
||||||
|
console.log(JSON.stringify(publishedDeployments, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line unicorn/no-process-exit
|
// eslint-disable-next-line unicorn/no-process-exit
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const exampleProjectNames = ['search']
|
||||||
|
|
||||||
|
const examplesDir = path.join(
|
||||||
|
fileURLToPath(import.meta.url),
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'examples'
|
||||||
|
)
|
||||||
|
|
||||||
|
export const examples = exampleProjectNames.map((name) =>
|
||||||
|
path.join(examplesDir, name)
|
||||||
|
)
|
|
@ -0,0 +1,30 @@
|
||||||
|
import type { AgenticApiClient } from '@agentic/platform-api-client'
|
||||||
|
import { loadAgenticConfig } from '@agentic/platform'
|
||||||
|
import pMap from 'p-map'
|
||||||
|
|
||||||
|
export async function deployProjects(
|
||||||
|
projects: string[],
|
||||||
|
{
|
||||||
|
client,
|
||||||
|
concurrency = 1
|
||||||
|
}: {
|
||||||
|
client: AgenticApiClient
|
||||||
|
concurrency?: number
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const deployments = await pMap(
|
||||||
|
projects,
|
||||||
|
async (project) => {
|
||||||
|
const config = await loadAgenticConfig({ cwd: project })
|
||||||
|
const deployment = await client.createDeployment(config)
|
||||||
|
console.log(`Deployed ${project} => ${deployment.identifier}`)
|
||||||
|
|
||||||
|
return deployment
|
||||||
|
},
|
||||||
|
{
|
||||||
|
concurrency
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return deployments
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { AgenticApiClient } from '@agentic/platform-api-client'
|
||||||
|
|
||||||
import { env } from './env'
|
import { env } from './env'
|
||||||
|
|
||||||
export const client = new AgenticApiClient({
|
export const devClient = new AgenticApiClient({
|
||||||
apiBaseUrl: env.AGENTIC_API_BASE_URL,
|
apiBaseUrl: env.AGENTIC_API_BASE_URL,
|
||||||
apiKey: env.AGENTIC_DEV_ACCESS_TOKEN
|
apiKey: env.AGENTIC_DEV_ACCESS_TOKEN
|
||||||
})
|
})
|
|
@ -0,0 +1,31 @@
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const fixtureNames = [
|
||||||
|
// TODO: re-add these
|
||||||
|
// 'basic-raw-free-ts',
|
||||||
|
// 'basic-raw-free-json',
|
||||||
|
// 'pricing-freemium',
|
||||||
|
// 'pricing-pay-as-you-go',
|
||||||
|
// 'pricing-3-plans',
|
||||||
|
// 'pricing-monthly-annual',
|
||||||
|
// 'pricing-custom-0',
|
||||||
|
'basic-openapi',
|
||||||
|
'basic-mcp',
|
||||||
|
'everything-openapi'
|
||||||
|
]
|
||||||
|
|
||||||
|
const fixturesDir = path.join(
|
||||||
|
fileURLToPath(import.meta.url),
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'packages',
|
||||||
|
'fixtures'
|
||||||
|
)
|
||||||
|
const validFixturesDir = path.join(fixturesDir, 'valid')
|
||||||
|
|
||||||
|
export const fixtures = fixtureNames.map((name) =>
|
||||||
|
path.join(validFixturesDir, name)
|
||||||
|
)
|
|
@ -14,9 +14,12 @@ export const envSchema = z.object({
|
||||||
|
|
||||||
AGENTIC_API_BASE_URL: z.string().url().optional(),
|
AGENTIC_API_BASE_URL: z.string().url().optional(),
|
||||||
|
|
||||||
AGENTIC_DEV_ACCESS_TOKEN: z.string().nonempty(),
|
|
||||||
AGENTIC_DEV_EMAIL: z.string().email(),
|
AGENTIC_DEV_EMAIL: z.string().email(),
|
||||||
AGENTIC_DEV_PASSWORD: z.string().nonempty(),
|
AGENTIC_DEV_PASSWORD: z.string().nonempty(),
|
||||||
|
AGENTIC_DEV_ACCESS_TOKEN: z.string().nonempty(),
|
||||||
|
|
||||||
|
AGENTIC_AGENTIC_EMAIL: z.string().email(),
|
||||||
|
AGENTIC_AGENTIC_PASSWORD: z.string().nonempty(),
|
||||||
|
|
||||||
AGENTIC_GATEWAY_BASE_URL: z
|
AGENTIC_GATEWAY_BASE_URL: z
|
||||||
.string()
|
.string()
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
import path from 'node:path'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
import type { Deployment } from '@agentic/platform-types'
|
|
||||||
import { loadAgenticConfig } from '@agentic/platform'
|
|
||||||
import { assert } from '@agentic/platform-core'
|
|
||||||
import pMap from 'p-map'
|
|
||||||
import semver from 'semver'
|
|
||||||
|
|
||||||
import { client } from './client'
|
|
||||||
|
|
||||||
const fixtures = [
|
|
||||||
// TODO: re-add these
|
|
||||||
// 'basic-raw-free-ts',
|
|
||||||
// 'basic-raw-free-json',
|
|
||||||
// 'pricing-freemium',
|
|
||||||
// 'pricing-pay-as-you-go',
|
|
||||||
// 'pricing-3-plans',
|
|
||||||
// 'pricing-monthly-annual',
|
|
||||||
// 'pricing-custom-0',
|
|
||||||
'basic-openapi',
|
|
||||||
'basic-mcp',
|
|
||||||
'everything-openapi'
|
|
||||||
]
|
|
||||||
|
|
||||||
const fixturesDir = path.join(
|
|
||||||
fileURLToPath(import.meta.url),
|
|
||||||
'..',
|
|
||||||
'..',
|
|
||||||
'..',
|
|
||||||
'..',
|
|
||||||
'packages',
|
|
||||||
'fixtures'
|
|
||||||
)
|
|
||||||
const validFixturesDir = path.join(fixturesDir, 'valid')
|
|
||||||
|
|
||||||
export async function deployFixtures({
|
|
||||||
concurrency = 1
|
|
||||||
}: {
|
|
||||||
concurrency?: number
|
|
||||||
} = {}) {
|
|
||||||
const deployments = await pMap(
|
|
||||||
fixtures,
|
|
||||||
async (fixture) => {
|
|
||||||
const fixtureDir = path.join(validFixturesDir, fixture)
|
|
||||||
|
|
||||||
const config = await loadAgenticConfig({ cwd: fixtureDir })
|
|
||||||
const deployment = await client.createDeployment(config)
|
|
||||||
console.log(`Deployed ${fixture} => ${deployment.identifier}`)
|
|
||||||
|
|
||||||
return deployment
|
|
||||||
},
|
|
||||||
{
|
|
||||||
concurrency
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return deployments
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function publishDeployments(
|
|
||||||
deployments: Deployment[],
|
|
||||||
{
|
|
||||||
concurrency = 1
|
|
||||||
}: {
|
|
||||||
concurrency?: number
|
|
||||||
} = {}
|
|
||||||
) {
|
|
||||||
const publishedDeployments = await pMap(
|
|
||||||
deployments,
|
|
||||||
async (deployment) => {
|
|
||||||
const project = await client.getProject({
|
|
||||||
projectId: deployment.projectId,
|
|
||||||
populate: ['lastDeployment']
|
|
||||||
})
|
|
||||||
|
|
||||||
const baseVersion = project.lastPublishedDeploymentVersion || '0.0.0'
|
|
||||||
const version = semver.inc(baseVersion, 'patch')
|
|
||||||
assert(version, `Failed to increment deployment version "${baseVersion}"`)
|
|
||||||
|
|
||||||
const publishedDeployment = await client.publishDeployment(
|
|
||||||
{ version },
|
|
||||||
{
|
|
||||||
deploymentId: deployment.id
|
|
||||||
}
|
|
||||||
)
|
|
||||||
console.log(`Published ${deployment.identifier} => ${version}`)
|
|
||||||
|
|
||||||
return publishedDeployment
|
|
||||||
},
|
|
||||||
{
|
|
||||||
concurrency
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return publishedDeployments
|
|
||||||
}
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import type { AgenticApiClient } from '@agentic/platform-api-client'
|
||||||
|
import type { Deployment } from '@agentic/platform-types'
|
||||||
|
import { assert } from '@agentic/platform-core'
|
||||||
|
import pMap from 'p-map'
|
||||||
|
import semver from 'semver'
|
||||||
|
|
||||||
|
export async function publishDeployments(
|
||||||
|
deployments: Deployment[],
|
||||||
|
{
|
||||||
|
client,
|
||||||
|
concurrency = 1
|
||||||
|
}: {
|
||||||
|
client: AgenticApiClient
|
||||||
|
concurrency?: number
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const publishedDeployments = await pMap(
|
||||||
|
deployments,
|
||||||
|
async (deployment) => {
|
||||||
|
const project = await client.getProject({
|
||||||
|
projectId: deployment.projectId,
|
||||||
|
populate: ['lastDeployment']
|
||||||
|
})
|
||||||
|
|
||||||
|
const baseVersion = project.lastPublishedDeploymentVersion || '0.0.0'
|
||||||
|
const version = semver.inc(baseVersion, 'patch')
|
||||||
|
assert(version, `Failed to increment deployment version "${baseVersion}"`)
|
||||||
|
|
||||||
|
const publishedDeployment = await client.publishDeployment(
|
||||||
|
{ version },
|
||||||
|
{
|
||||||
|
deploymentId: deployment.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
console.log(`Published ${deployment.identifier} => ${version}`)
|
||||||
|
|
||||||
|
return publishedDeployment
|
||||||
|
},
|
||||||
|
{
|
||||||
|
concurrency
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return publishedDeployments
|
||||||
|
}
|
|
@ -12,11 +12,6 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"source": "./src/worker.ts",
|
"source": "./src/worker.ts",
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": "./src/worker.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "wrangler deploy --outdir dist --upload-source-maps --var SENTRY_RELEASE:$(sentry-cli releases propose-version)",
|
"deploy": "wrangler deploy --outdir dist --upload-source-maps --var SENTRY_RELEASE:$(sentry-cli releases propose-version)",
|
||||||
"dev": "wrangler dev",
|
"dev": "wrangler dev",
|
||||||
|
@ -35,7 +30,6 @@
|
||||||
"@agentic/platform-hono": "workspace:*",
|
"@agentic/platform-hono": "workspace:*",
|
||||||
"@agentic/platform-types": "workspace:*",
|
"@agentic/platform-types": "workspace:*",
|
||||||
"@agentic/platform-validators": "workspace:*",
|
"@agentic/platform-validators": "workspace:*",
|
||||||
"@cloudflare/workers-oauth-provider": "^0.0.5",
|
|
||||||
"@hono/zod-validator": "catalog:",
|
"@hono/zod-validator": "catalog:",
|
||||||
"@modelcontextprotocol/sdk": "catalog:",
|
"@modelcontextprotocol/sdk": "catalog:",
|
||||||
"@sentry/cloudflare": "catalog:",
|
"@sentry/cloudflare": "catalog:",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { AgenticMcpRequestMetadata } from '@agentic/platform-types'
|
||||||
import { assert } from '@agentic/platform-core'
|
import { assert } from '@agentic/platform-core'
|
||||||
import { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js'
|
import { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js'
|
||||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||||
|
@ -5,7 +6,6 @@ import * as Sentry from '@sentry/cloudflare'
|
||||||
import { DurableObject } from 'cloudflare:workers'
|
import { DurableObject } from 'cloudflare:workers'
|
||||||
|
|
||||||
import type { RawEnv } from './env'
|
import type { RawEnv } from './env'
|
||||||
import type { AgenticMcpRequestMetadata } from './types'
|
|
||||||
|
|
||||||
export type DurableMcpClientInfo = {
|
export type DurableMcpClientInfo = {
|
||||||
url: string
|
url: string
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type {
|
import type {
|
||||||
AdminDeployment,
|
AdminDeployment,
|
||||||
|
AgenticMcpRequestMetadata,
|
||||||
PricingPlan,
|
PricingPlan,
|
||||||
Tool
|
Tool
|
||||||
} from '@agentic/platform-types'
|
} from '@agentic/platform-types'
|
||||||
|
@ -11,7 +12,6 @@ import type { DurableMcpClientBase } from './durable-mcp-client'
|
||||||
import type { RawEnv } from './env'
|
import type { RawEnv } from './env'
|
||||||
import type {
|
import type {
|
||||||
AdminConsumer,
|
AdminConsumer,
|
||||||
AgenticMcpRequestMetadata,
|
|
||||||
CacheStatus,
|
CacheStatus,
|
||||||
McpToolCallResponse,
|
McpToolCallResponse,
|
||||||
RateLimitResult,
|
RateLimitResult,
|
||||||
|
|
|
@ -112,47 +112,3 @@ export type ResolvedOriginToolCallResult = {
|
||||||
toolCallResponse: McpToolCallResponse
|
toolCallResponse: McpToolCallResponse
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export type AgenticMcpRequestMetadata = {
|
|
||||||
agenticProxySecret: string
|
|
||||||
sessionId: string
|
|
||||||
isCustomerSubscriptionActive: boolean
|
|
||||||
|
|
||||||
customerId?: string
|
|
||||||
customerSubscriptionStatus?: string
|
|
||||||
customerSubscriptionPlan?: string
|
|
||||||
|
|
||||||
userId?: string
|
|
||||||
userEmail?: string
|
|
||||||
userUsername?: string
|
|
||||||
userName?: string
|
|
||||||
userCreatedAt?: string
|
|
||||||
userUpdatedAt?: string
|
|
||||||
|
|
||||||
deploymentId: string
|
|
||||||
deploymentIdentifier: string
|
|
||||||
projectId: string
|
|
||||||
projectIdentifier: string
|
|
||||||
|
|
||||||
ip?: string
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
// If the customer has an active subscription, these fields are guaranteed
|
|
||||||
// to be present in the metadata.
|
|
||||||
isCustomerSubscriptionActive: true
|
|
||||||
|
|
||||||
customerId: string
|
|
||||||
customerSubscriptionStatus: string
|
|
||||||
|
|
||||||
userId: string
|
|
||||||
userEmail: string
|
|
||||||
userUsername: string
|
|
||||||
userCreatedAt: string
|
|
||||||
userUpdatedAt: string
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
// If the customer does not have an active subscription, then the customer
|
|
||||||
// fields may or may not be present.
|
|
||||||
isCustomerSubscriptionActive: false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
NEXT_PUBLIC_API_BASE_URL=
|
NEXT_PUBLIC_AGENTIC_API_BASE_URL=
|
||||||
|
NEXT_PUBLIC_AGENTIC_GATEWAY_BASE_URL=
|
||||||
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
|
||||||
STRIPE_SECRET_KEY=
|
STRIPE_SECRET_KEY=
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
|
"@radix-ui/react-tabs": "^1.1.12",
|
||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"@radix-ui/react-tooltip": "^1.2.7",
|
||||||
"@react-three/cannon": "^6.6.0",
|
"@react-three/cannon": "^6.6.0",
|
||||||
"@react-three/drei": "^10.2.0",
|
"@react-three/drei": "^10.2.0",
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
"canvas-confetti": "^1.9.3",
|
"canvas-confetti": "^1.9.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
||||||
"ky": "catalog:",
|
"ky": "catalog:",
|
||||||
"lucide-react": "^0.518.0",
|
"lucide-react": "^0.518.0",
|
||||||
"motion": "^12.18.1",
|
"motion": "^12.18.1",
|
||||||
|
@ -54,6 +56,7 @@
|
||||||
"react-infinite-scroll-hook": "^6.0.1",
|
"react-infinite-scroll-hook": "^6.0.1",
|
||||||
"react-lottie-player": "^2.1.0",
|
"react-lottie-player": "^2.1.0",
|
||||||
"react-use": "^17.6.0",
|
"react-use": "^17.6.0",
|
||||||
|
"shiki": "^3.7.0",
|
||||||
"sonner": "^2.0.5",
|
"sonner": "^2.0.5",
|
||||||
"stripe": "catalog:",
|
"stripe": "catalog:",
|
||||||
"suspend-react": "^0.1.3",
|
"suspend-react": "^0.1.3",
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useLocalStorage } from 'react-use'
|
||||||
|
|
||||||
|
import { CodeBlock } from '@/components/code-block'
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
|
import {
|
||||||
|
defaultConfig,
|
||||||
|
type DeveloperConfig,
|
||||||
|
type HTTPTarget,
|
||||||
|
httpTargetLabels,
|
||||||
|
httpTargets,
|
||||||
|
type MCPClientTarget,
|
||||||
|
mcpClientTargetLabels,
|
||||||
|
mcpClientTargets,
|
||||||
|
type PyFrameworkTarget,
|
||||||
|
pyFrameworkTargetLabels,
|
||||||
|
pyFrameworkTargets,
|
||||||
|
type Target,
|
||||||
|
targetLabels,
|
||||||
|
targets,
|
||||||
|
type TsFrameworkTarget,
|
||||||
|
tsFrameworkTargetLabels,
|
||||||
|
tsFrameworkTargets
|
||||||
|
} from '@/lib/developer-config'
|
||||||
|
|
||||||
|
export function ExampleUsage() {
|
||||||
|
const [config, setConfig] = useLocalStorage<DeveloperConfig>(
|
||||||
|
'config',
|
||||||
|
defaultConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
defaultValue={config!.target}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setConfig({
|
||||||
|
...defaultConfig,
|
||||||
|
...config,
|
||||||
|
target: value as Target
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className='w-full max-w-2xl'
|
||||||
|
>
|
||||||
|
<TabsList>
|
||||||
|
{targets.map((target) => (
|
||||||
|
<TabsTrigger key={target} value={target} className='cursor-pointer'>
|
||||||
|
{targetLabels[target]}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsContent value='mcp' className='w-full'>
|
||||||
|
<Tabs
|
||||||
|
defaultValue={config!.mcpClientTarget}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setConfig({
|
||||||
|
...defaultConfig,
|
||||||
|
...config,
|
||||||
|
mcpClientTarget: value as MCPClientTarget
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
<TabsList className='h-auto flex-wrap'>
|
||||||
|
{mcpClientTargets.map((mcpClientTarget) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={mcpClientTarget}
|
||||||
|
value={mcpClientTarget}
|
||||||
|
className='cursor-pointer'
|
||||||
|
>
|
||||||
|
{mcpClientTargetLabels[mcpClientTarget]}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
{mcpClientTargets.map((mcpClientTarget) => (
|
||||||
|
<TabsContent
|
||||||
|
key={mcpClientTarget}
|
||||||
|
value={mcpClientTarget}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
<CodeBlock
|
||||||
|
code={JSON.stringify(config, null, 2)}
|
||||||
|
lang='json'
|
||||||
|
className='p-4 rounded-sm w-full'
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value='typescript' className='w-full'>
|
||||||
|
<Tabs
|
||||||
|
defaultValue={config!.tsFrameworkTarget ?? 'ai'}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setConfig({
|
||||||
|
...defaultConfig,
|
||||||
|
...config,
|
||||||
|
tsFrameworkTarget: value as TsFrameworkTarget
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
<TabsList className='h-auto flex-wrap'>
|
||||||
|
{tsFrameworkTargets.map((framework) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={framework}
|
||||||
|
value={framework}
|
||||||
|
className='cursor-pointer'
|
||||||
|
>
|
||||||
|
{tsFrameworkTargetLabels[framework]}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
{tsFrameworkTargets.map((framework) => (
|
||||||
|
<TabsContent key={framework} value={framework} className='w-full'>
|
||||||
|
<CodeBlock
|
||||||
|
code={JSON.stringify(config, null, 2)}
|
||||||
|
lang='ts'
|
||||||
|
className='p-4 rounded-sm w-full'
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value='python' className='w-full'>
|
||||||
|
<Tabs
|
||||||
|
defaultValue={config!.pyFrameworkTarget}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setConfig({
|
||||||
|
...defaultConfig,
|
||||||
|
...config,
|
||||||
|
pyFrameworkTarget: value as PyFrameworkTarget
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
<TabsList className='h-auto flex-wrap'>
|
||||||
|
{pyFrameworkTargets.map((framework) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={framework}
|
||||||
|
value={framework}
|
||||||
|
className='cursor-pointer'
|
||||||
|
>
|
||||||
|
{pyFrameworkTargetLabels[framework]}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
{pyFrameworkTargets.map((framework) => (
|
||||||
|
<TabsContent key={framework} value={framework} className='w-full'>
|
||||||
|
<CodeBlock
|
||||||
|
code={JSON.stringify(config, null, 2)}
|
||||||
|
lang='py'
|
||||||
|
className='p-4 rounded-sm w-full'
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value='http' className='w-full'>
|
||||||
|
<Tabs
|
||||||
|
defaultValue={config!.httpTarget}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setConfig({
|
||||||
|
...defaultConfig,
|
||||||
|
...config,
|
||||||
|
httpTarget: value as HTTPTarget
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
<TabsList className='h-auto flex-wrap'>
|
||||||
|
{httpTargets.map((httpTarget) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={httpTarget}
|
||||||
|
value={httpTarget}
|
||||||
|
className='cursor-pointer'
|
||||||
|
>
|
||||||
|
{httpTargetLabels[httpTarget]}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
{httpTargets.map((httpTarget) => (
|
||||||
|
<TabsContent key={httpTarget} value={httpTarget} className='w-full'>
|
||||||
|
<CodeBlock
|
||||||
|
code={JSON.stringify(config, null, 2)}
|
||||||
|
lang='bash'
|
||||||
|
className='p-4 rounded-sm w-full'
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ import { DemandSideCTA } from '@/components/demand-side-cta'
|
||||||
import { GitHubStarCounter } from '@/components/github-star-counter'
|
import { GitHubStarCounter } from '@/components/github-star-counter'
|
||||||
import { githubUrl, twitterUrl } from '@/lib/config'
|
import { githubUrl, twitterUrl } from '@/lib/config'
|
||||||
|
|
||||||
|
import { ExampleUsage } from './example-usage'
|
||||||
|
|
||||||
export default function TheBestDamnLandingPageEver() {
|
export default function TheBestDamnLandingPageEver() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -35,7 +37,7 @@ export default function TheBestDamnLandingPageEver() {
|
||||||
How It Works
|
How It Works
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div>TODO</div>
|
<ExampleUsage />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Marketplace section */}
|
{/* Marketplace section */}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
|
||||||
|
import { Fragment, type JSX, useEffect, useState } from 'react'
|
||||||
|
import { jsx, jsxs } from 'react/jsx-runtime'
|
||||||
|
import { type BundledLanguage, codeToHast } from 'shiki/bundle/web'
|
||||||
|
|
||||||
|
import { LoadingIndicator } from './loading-indicator'
|
||||||
|
|
||||||
|
export async function highlight({
|
||||||
|
code,
|
||||||
|
lang = 'ts',
|
||||||
|
theme = 'github-dark',
|
||||||
|
className
|
||||||
|
}: {
|
||||||
|
code: string
|
||||||
|
lang?: BundledLanguage
|
||||||
|
theme?: string
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const out = await codeToHast(code, {
|
||||||
|
lang,
|
||||||
|
theme,
|
||||||
|
transformers: [
|
||||||
|
{
|
||||||
|
pre(node) {
|
||||||
|
if (className) {
|
||||||
|
this.addClassToHast(node, className)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return toJsxRuntime(out, {
|
||||||
|
Fragment,
|
||||||
|
jsx,
|
||||||
|
jsxs
|
||||||
|
}) as JSX.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CodeBlock({
|
||||||
|
initial,
|
||||||
|
code,
|
||||||
|
lang,
|
||||||
|
theme,
|
||||||
|
className
|
||||||
|
}: {
|
||||||
|
initial?: JSX.Element
|
||||||
|
code: string
|
||||||
|
lang?: BundledLanguage
|
||||||
|
theme?: string
|
||||||
|
className?: string
|
||||||
|
}) {
|
||||||
|
const [nodes, setNodes] = useState(initial)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void highlight({ code, lang, theme, className }).then(setNodes)
|
||||||
|
}, [code, lang, theme, className])
|
||||||
|
|
||||||
|
return nodes ?? <LoadingIndicator />
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ export function DemandSideCTA() {
|
||||||
|
|
||||||
<Button variant='outline' asChild className='h-full'>
|
<Button variant='outline' asChild className='h-full'>
|
||||||
<Link href={docsMarketplaceUrl} className='font-mono'>
|
<Link href={docsMarketplaceUrl} className='font-mono'>
|
||||||
readDocs();
|
readTheDocs();
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
function Tabs({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Root
|
||||||
|
data-slot='tabs'
|
||||||
|
className={cn('flex flex-col gap-2', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsList({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
data-slot='tabs-list'
|
||||||
|
className={cn(
|
||||||
|
'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsTrigger({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
data-slot='tabs-trigger'
|
||||||
|
className={cn(
|
||||||
|
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabsContent({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
data-slot='tabs-content'
|
||||||
|
className={cn('flex-1 outline-none', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Tabs, TabsContent,TabsList, TabsTrigger }
|
|
@ -59,7 +59,8 @@ export const url = isDev ? `http://localhost:${port}` : prodUrl
|
||||||
export const vercelUrl =
|
export const vercelUrl =
|
||||||
process.env.VERCEL_URL ?? process.env.NEXT_PUBLIC_VERCEL_URL
|
process.env.VERCEL_URL ?? process.env.NEXT_PUBLIC_VERCEL_URL
|
||||||
// export const webBaseUrl = isDev || !vercelUrl ? url : `https://${vercelUrl}`
|
// export const webBaseUrl = isDev || !vercelUrl ? url : `https://${vercelUrl}`
|
||||||
export const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL!
|
export const apiBaseUrl = process.env.NEXT_PUBLIC_AGENTIC_API_BASE_URL!
|
||||||
|
export const gatewayBaseUrl = process.env.NEXT_PUBLIC_AGENTIC_GATEWAY_BASE_URL!
|
||||||
|
|
||||||
export const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY!
|
export const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY!
|
||||||
export const posthogHost =
|
export const posthogHost =
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
import type { Deployment, Project } from '@agentic/platform-types'
|
||||||
|
import { assert } from '@agentic/platform-core'
|
||||||
|
|
||||||
|
import { gatewayBaseUrl } from './config'
|
||||||
|
|
||||||
|
export const targetLabels = {
|
||||||
|
mcp: 'MCP',
|
||||||
|
typescript: 'TypeScript',
|
||||||
|
python: 'Python',
|
||||||
|
http: 'HTTP'
|
||||||
|
} as const
|
||||||
|
export const targets: (keyof typeof targetLabels)[] = Object.keys(
|
||||||
|
targetLabels
|
||||||
|
) as any
|
||||||
|
export type Target = (typeof targets)[number]
|
||||||
|
|
||||||
|
export const httpTargetLabels = {
|
||||||
|
curl: 'cURL',
|
||||||
|
httpie: 'HTTPie'
|
||||||
|
} as const
|
||||||
|
export const httpTargets: (keyof typeof httpTargetLabels)[] = Object.keys(
|
||||||
|
httpTargetLabels
|
||||||
|
) as any
|
||||||
|
export type HTTPTarget = (typeof httpTargets)[number]
|
||||||
|
|
||||||
|
export const mcpClientTargetLabels = {
|
||||||
|
any: 'Any MCP Client',
|
||||||
|
'claude-desktop': 'Claude Desktop',
|
||||||
|
raycast: 'Raycast',
|
||||||
|
cursor: 'Cursor',
|
||||||
|
windsurf: 'Windsurf',
|
||||||
|
cline: 'Cline',
|
||||||
|
goose: 'Goose'
|
||||||
|
} as const
|
||||||
|
export const mcpClientTargets: (keyof typeof mcpClientTargetLabels)[] =
|
||||||
|
Object.keys(mcpClientTargetLabels) as any
|
||||||
|
export type MCPClientTarget = (typeof mcpClientTargets)[number]
|
||||||
|
|
||||||
|
export const tsFrameworkTargetLabels = {
|
||||||
|
ai: 'Vercel AI SDK',
|
||||||
|
'openai-chat': 'OpenAI Chat',
|
||||||
|
'openai-responses': 'OpenAI Responses',
|
||||||
|
langchain: 'LangChain',
|
||||||
|
mastra: 'Mastra',
|
||||||
|
llamaindex: 'LlamaIndex',
|
||||||
|
'firebase-genkit': 'Firebase GenKit',
|
||||||
|
xsai: 'xsAI'
|
||||||
|
} as const
|
||||||
|
export const tsFrameworkTargets: (keyof typeof tsFrameworkTargetLabels)[] =
|
||||||
|
Object.keys(tsFrameworkTargetLabels) as any
|
||||||
|
export type TsFrameworkTarget = (typeof tsFrameworkTargets)[number]
|
||||||
|
|
||||||
|
export const pyFrameworkTargetLabels = {
|
||||||
|
openai: 'OpenAI',
|
||||||
|
langchain: 'LangChain',
|
||||||
|
llamaindex: 'LlamaIndex'
|
||||||
|
} as const
|
||||||
|
export const pyFrameworkTargets: (keyof typeof pyFrameworkTargetLabels)[] =
|
||||||
|
Object.keys(pyFrameworkTargetLabels) as any
|
||||||
|
export type PyFrameworkTarget = (typeof pyFrameworkTargets)[number]
|
||||||
|
|
||||||
|
export type DeveloperConfig = {
|
||||||
|
target: Target
|
||||||
|
mcpClientTarget: MCPClientTarget
|
||||||
|
tsFrameworkTarget: TsFrameworkTarget
|
||||||
|
pyFrameworkTarget: PyFrameworkTarget
|
||||||
|
httpTarget: HTTPTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultConfig: DeveloperConfig = {
|
||||||
|
target: 'typescript',
|
||||||
|
mcpClientTarget: 'any',
|
||||||
|
tsFrameworkTarget: 'ai',
|
||||||
|
pyFrameworkTarget: 'openai',
|
||||||
|
httpTarget: 'curl'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCodeForDeveloperConfig(opts: {
|
||||||
|
config: DeveloperConfig
|
||||||
|
project: Project
|
||||||
|
deployment: Deployment
|
||||||
|
identifier: string
|
||||||
|
tool?: string
|
||||||
|
}): string {
|
||||||
|
const { config } = opts
|
||||||
|
|
||||||
|
switch (config.target) {
|
||||||
|
case 'mcp':
|
||||||
|
return getCodeForMCPClientConfig(opts)
|
||||||
|
case 'typescript':
|
||||||
|
return getCodeForTSFrameworkConfig(opts)
|
||||||
|
case 'python':
|
||||||
|
return 'Python support is coming soon...'
|
||||||
|
// return getCodeForPythonFrameworkConfig(opts)
|
||||||
|
case 'http':
|
||||||
|
return getCodeForHTTPConfig(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCodeForMCPClientConfig({
|
||||||
|
identifier
|
||||||
|
}: {
|
||||||
|
identifier: string
|
||||||
|
}): string {
|
||||||
|
return `${gatewayBaseUrl}/${identifier}/mcp`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCodeForTSFrameworkConfig({
|
||||||
|
config,
|
||||||
|
identifier
|
||||||
|
}: {
|
||||||
|
config: DeveloperConfig
|
||||||
|
identifier: string
|
||||||
|
}): string {
|
||||||
|
switch (config.tsFrameworkTarget) {
|
||||||
|
case 'ai':
|
||||||
|
return `
|
||||||
|
import { createAISDKTools } from '@agentic/ai'
|
||||||
|
import { AgenticToolClient } from '@agentic/platform-tool-client'
|
||||||
|
import { openai } from '@ai-sdk/openai'
|
||||||
|
import { generateText } from 'ai'
|
||||||
|
|
||||||
|
const searchTool = await AgenticToolClient.fromIdentifier('${identifier}')
|
||||||
|
|
||||||
|
const result = await generateText({
|
||||||
|
model: openai('gpt-4o-mini'),
|
||||||
|
tools: createAISDKTools(searchTool),
|
||||||
|
toolChoice: 'required',
|
||||||
|
temperature: 0,
|
||||||
|
system: 'You are a helpful assistant. Be as concise as possible.',
|
||||||
|
prompt: 'What is the latest news about AI?'
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(result.toolResults[0])
|
||||||
|
`.trim()
|
||||||
|
|
||||||
|
case 'openai-chat':
|
||||||
|
return `
|
||||||
|
import { AgenticToolClient } from '@agentic/platform-tool-client'
|
||||||
|
import OpenAI from 'openai'
|
||||||
|
|
||||||
|
const openai = new OpenAI()
|
||||||
|
const searchTool = await AgenticToolClient.fromIdentifier('${identifier}')
|
||||||
|
|
||||||
|
const res = await openai.chat.completions.create({
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content: 'You are a helpful assistant. Be as concise as possible.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: 'What is the latest news about AI?'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
model: 'gpt-4o-mini',
|
||||||
|
temperature: 0,
|
||||||
|
tools: searchTool.functions.toolSpecs,
|
||||||
|
tool_choice: 'required'
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = res.choices[0]!.message!
|
||||||
|
const toolCall = message.tool_calls![0]!
|
||||||
|
|
||||||
|
const tool = searchTool.functions.get(toolCall.function.name)!
|
||||||
|
const toolResult = await tool(toolCall.function.arguments)
|
||||||
|
|
||||||
|
console.log(toolResult)
|
||||||
|
`.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// export function getCodeForPythonFrameworkConfig({
|
||||||
|
// config,
|
||||||
|
// project,
|
||||||
|
// deployment,
|
||||||
|
// tool
|
||||||
|
// }: {
|
||||||
|
// config: DeveloperConfig
|
||||||
|
// project: Project
|
||||||
|
// deployment: Deployment
|
||||||
|
// identifier: string
|
||||||
|
// tool?: string
|
||||||
|
// }): string {
|
||||||
|
// return ''
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function getCodeForHTTPConfig({
|
||||||
|
config,
|
||||||
|
identifier,
|
||||||
|
deployment,
|
||||||
|
tool
|
||||||
|
}: {
|
||||||
|
config: DeveloperConfig
|
||||||
|
deployment: Deployment
|
||||||
|
identifier: string
|
||||||
|
tool?: string
|
||||||
|
}): string {
|
||||||
|
tool ??= deployment.tools[0]?.name
|
||||||
|
assert(tool, 'tool is required')
|
||||||
|
// TODO: need a way of getting example tool args
|
||||||
|
|
||||||
|
const url = `${gatewayBaseUrl}/${identifier}/${tool}`
|
||||||
|
|
||||||
|
switch (config.httpTarget) {
|
||||||
|
case 'curl':
|
||||||
|
return `curl -X POST -H "Content-Type: application/json" -d '{"query": "example google search"}' ${url}`
|
||||||
|
|
||||||
|
case 'httpie':
|
||||||
|
return `http -j ${url} query='example google search'`
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,8 @@ export default [
|
||||||
files: [
|
files: [
|
||||||
'packages/cli/src/**/*.ts',
|
'packages/cli/src/**/*.ts',
|
||||||
'**/*.test.ts',
|
'**/*.test.ts',
|
||||||
'packages/fixtures/valid/**/*.ts'
|
'packages/fixtures/valid/**/*.ts',
|
||||||
|
'examples/**/*.ts'
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
SERPER_API_KEY=
|
||||||
|
|
||||||
|
AGENTIC_PROXY_SECRET=
|
|
@ -0,0 +1 @@
|
||||||
|
MCP_ORIGIN_URL=
|
|
@ -0,0 +1,17 @@
|
||||||
|
import 'dotenv/config'
|
||||||
|
|
||||||
|
import { defineConfig } from '@agentic/platform'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
name: 'search',
|
||||||
|
originUrl: process.env.MCP_ORIGIN_URL!,
|
||||||
|
originAdapter: {
|
||||||
|
type: 'mcp'
|
||||||
|
},
|
||||||
|
toolConfigs: [
|
||||||
|
{
|
||||||
|
name: 'search',
|
||||||
|
cacheControl: 'public, max-age=60, s-maxage=60 stale-while-revalidate=10'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"name": "@agentic/examples-search",
|
||||||
|
"private": true,
|
||||||
|
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git",
|
||||||
|
"directory": "examples/search"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"source": "./src/worker.ts",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "wrangler dev",
|
||||||
|
"deploy": "wrangler deploy --outdir dist --upload-source-maps",
|
||||||
|
"test": "run-s test:*",
|
||||||
|
"test:typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@agentic/platform": "workspace:*",
|
||||||
|
"@agentic/serper": "^7.6.7",
|
||||||
|
"@hono/mcp": "^0.1.0",
|
||||||
|
"@modelcontextprotocol/sdk": "catalog:",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
|
"hono": "catalog:",
|
||||||
|
"zod": "catalog:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@cloudflare/workers-types": "catalog:",
|
||||||
|
"wrangler": "catalog:"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const envSchema = z.object({
|
||||||
|
SERPER_API_KEY: z.string().nonempty()
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Env = z.infer<typeof envSchema>
|
||||||
|
|
||||||
|
export function parseEnv(inputEnv: Record<string, unknown>): Env {
|
||||||
|
return envSchema.parse(inputEnv)
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { SerperClient } from '@agentic/serper'
|
||||||
|
import { StreamableHTTPTransport } from '@hono/mcp'
|
||||||
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||||
|
import { Hono } from 'hono'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
import { type Env, parseEnv } from './env'
|
||||||
|
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
const mcpServer = new McpServer({
|
||||||
|
name: 'search',
|
||||||
|
version: '1.0.0'
|
||||||
|
})
|
||||||
|
|
||||||
|
let serper: SerperClient
|
||||||
|
|
||||||
|
mcpServer.registerTool(
|
||||||
|
'search',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'Uses Google Search to return the most relevant web pages for a given query. Useful for finding up-to-date news and information about any topic.',
|
||||||
|
inputSchema: z.object({
|
||||||
|
query: z.string().describe('Search query'),
|
||||||
|
num: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.default(5)
|
||||||
|
.optional()
|
||||||
|
.describe('Number of results to return'),
|
||||||
|
type: z
|
||||||
|
.enum(['search', 'images', 'videos', 'places', 'news', 'shopping'])
|
||||||
|
.default('search')
|
||||||
|
.optional()
|
||||||
|
.describe('Type of Google search to perform')
|
||||||
|
}).shape,
|
||||||
|
outputSchema: z.object({}).passthrough().shape
|
||||||
|
},
|
||||||
|
async (args, { _meta }) => {
|
||||||
|
// (_meta.agentic as any).agenticProxySecret
|
||||||
|
|
||||||
|
const result: any = await serper!.search({
|
||||||
|
q: args.query,
|
||||||
|
num: args.num,
|
||||||
|
type: args.type
|
||||||
|
})
|
||||||
|
|
||||||
|
delete result.topStories
|
||||||
|
delete result.peopleAlsoAsk
|
||||||
|
delete result.searchParameters
|
||||||
|
delete result.credits
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [],
|
||||||
|
structuredContent: result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
app.all('/mcp', async (c) => {
|
||||||
|
const transport = new StreamableHTTPTransport()
|
||||||
|
await mcpServer.connect(transport)
|
||||||
|
return transport.handleRequest(c)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async fetch(
|
||||||
|
request: Request,
|
||||||
|
env: Env,
|
||||||
|
ctx: ExecutionContext
|
||||||
|
): Promise<Response> {
|
||||||
|
const parsedEnv = parseEnv(env)
|
||||||
|
|
||||||
|
if (!serper) {
|
||||||
|
serper = new SerperClient({ apiKey: parsedEnv.SERPER_API_KEY })
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.fetch(request, parsedEnv, ctx)
|
||||||
|
}
|
||||||
|
} satisfies ExportedHandler<Env>
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "@fisch0920/config/tsconfig-node",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["@cloudflare/workers-types"]
|
||||||
|
},
|
||||||
|
"include": ["src", "*.config.ts"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* https://developers.cloudflare.com/workers/wrangler/configuration/
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"$schema": "node_modules/wrangler/config-schema.json",
|
||||||
|
"name": "agentic-mcp-search",
|
||||||
|
"main": "src/worker.ts",
|
||||||
|
"compatibility_date": "2025-05-25",
|
||||||
|
"compatibility_flags": ["nodejs_compat"],
|
||||||
|
"placement": { "mode": "smart" },
|
||||||
|
"upload_source_maps": true
|
||||||
|
}
|
|
@ -7,14 +7,14 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git"
|
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.12.1",
|
"packageManager": "pnpm@10.12.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo build --filter=!web",
|
"build": "turbo build --filter=!web",
|
||||||
"dev": "turbo dev --continue",
|
"dev": "turbo dev --continue --filter=!./examples/*",
|
||||||
"clean": "turbo clean",
|
"clean": "turbo clean",
|
||||||
"fix": "run-s fix:*",
|
"fix": "run-s fix:*",
|
||||||
"fix:format": "prettier --write \"**/*.{js,ts,tsx}\"",
|
"fix:format": "prettier --write \"**/*.{js,ts,tsx}\"",
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class AgenticApiClient {
|
||||||
apiKey?: string
|
apiKey?: string
|
||||||
ky?: KyInstance
|
ky?: KyInstance
|
||||||
onUpdateAuth?: OnUpdateAuthSessionFunction
|
onUpdateAuth?: OnUpdateAuthSessionFunction
|
||||||
}) {
|
} = {}) {
|
||||||
assert(apiBaseUrl, 'AgenticApiClient missing required "apiBaseUrl"')
|
assert(apiBaseUrl, 'AgenticApiClient missing required "apiBaseUrl"')
|
||||||
|
|
||||||
this.apiBaseUrl = apiBaseUrl
|
this.apiBaseUrl = apiBaseUrl
|
||||||
|
@ -655,7 +655,7 @@ export class AgenticApiClient {
|
||||||
.json()
|
.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a deployment by its public identifier. */
|
/** Gets a deployment by its identifier. */
|
||||||
async getDeploymentByIdentifier<
|
async getDeploymentByIdentifier<
|
||||||
TPopulate extends NonNullable<
|
TPopulate extends NonNullable<
|
||||||
OperationParameters<'getDeploymentByIdentifier'>['populate']
|
OperationParameters<'getDeploymentByIdentifier'>['populate']
|
||||||
|
@ -672,6 +672,23 @@ export class AgenticApiClient {
|
||||||
.json()
|
.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a public deployment by its identifier. */
|
||||||
|
async getPublicDeploymentByIdentifier<
|
||||||
|
TPopulate extends NonNullable<
|
||||||
|
OperationParameters<'getPublicDeploymentByIdentifier'>['populate']
|
||||||
|
>[number]
|
||||||
|
>(
|
||||||
|
searchParams: OperationParameters<'getPublicDeploymentByIdentifier'> & {
|
||||||
|
populate?: TPopulate[]
|
||||||
|
}
|
||||||
|
): Promise<PopulateDeployment<TPopulate>> {
|
||||||
|
return this.ky
|
||||||
|
.get(`v1/deployments/public/by-identifier`, {
|
||||||
|
searchParams: sanitizeSearchParams(searchParams)
|
||||||
|
})
|
||||||
|
.json()
|
||||||
|
}
|
||||||
|
|
||||||
/** Updates a deployment. */
|
/** Updates a deployment. */
|
||||||
async updateDeployment(
|
async updateDeployment(
|
||||||
deployment: OperationBody<'updateDeployment'>,
|
deployment: OperationBody<'updateDeployment'>,
|
||||||
|
|
|
@ -22,20 +22,17 @@
|
||||||
"test:unit": "vitest run"
|
"test:unit": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@agentic/core": "^7.6.7",
|
||||||
"@agentic/platform-core": "workspace:*",
|
"@agentic/platform-core": "workspace:*",
|
||||||
"@agentic/platform-openapi-utils": "workspace:*",
|
"@agentic/platform-openapi-utils": "workspace:*",
|
||||||
"@agentic/platform-types": "workspace:*",
|
"@agentic/platform-types": "workspace:*",
|
||||||
"@agentic/platform-validators": "workspace:*",
|
"@agentic/platform-validators": "workspace:*",
|
||||||
"@hono/zod-openapi": "catalog:",
|
|
||||||
"@modelcontextprotocol/sdk": "catalog:",
|
"@modelcontextprotocol/sdk": "catalog:",
|
||||||
"semver": "catalog:",
|
"semver": "catalog:",
|
||||||
"unconfig": "catalog:",
|
"unconfig": "catalog:"
|
||||||
"zod": "catalog:"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/semver": "catalog:",
|
"@types/semver": "catalog:"
|
||||||
"restore-cursor": "catalog:",
|
|
||||||
"zod-to-json-schema": "catalog:"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"name": "@agentic/platform-tool-client",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Tool client for the Agentic platform.",
|
||||||
|
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git",
|
||||||
|
"directory": "packages/tool-client"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"source": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "run-s test:*",
|
||||||
|
"test:typecheck": "tsc --noEmit",
|
||||||
|
"test:unit": "vitest run"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@agentic/core": "^7.6.7",
|
||||||
|
"@agentic/platform-api-client": "workspace:*",
|
||||||
|
"@agentic/platform-core": "workspace:*",
|
||||||
|
"@agentic/platform-types": "workspace:*",
|
||||||
|
"@agentic/platform-validators": "workspace:*",
|
||||||
|
"ky": "catalog:"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
## Project Identifier
|
||||||
|
|
||||||
|
`@namespace/project-name`
|
||||||
|
|
||||||
|
## Deployment Identifier
|
||||||
|
|
||||||
|
`${projectIdentifier}` => `${projectIdentifier}@latest`
|
||||||
|
`${projectIdentifier}@latest`
|
||||||
|
`${projectIdentifier}@dev`
|
||||||
|
`${projectIdentifier}@deploymentHash`
|
||||||
|
`${projectIdentifier}@version`
|
||||||
|
|
||||||
|
## Tool Identifier
|
||||||
|
|
||||||
|
`${deploymentIdentifier}/tool_name`
|
||||||
|
|
||||||
|
## Tool Names
|
||||||
|
|
||||||
|
- Must start with a letter or underscore
|
||||||
|
- Can include only letters, numbers, and underscores
|
||||||
|
- Use either camelCase or snake_case consistently across all tools
|
||||||
|
|
||||||
|
[OpenAI vs Anthropic vs Google vs MCP tool name restrictions](https://chatgpt.com/share/68419382-73a0-8007-afce-0ded7d9f05e7)
|
|
@ -0,0 +1,139 @@
|
||||||
|
import type { Deployment, Project } from '@agentic/platform-types'
|
||||||
|
import {
|
||||||
|
AIFunctionSet,
|
||||||
|
AIFunctionsProvider,
|
||||||
|
createAIFunction,
|
||||||
|
createJsonSchema
|
||||||
|
} from '@agentic/core'
|
||||||
|
import { AgenticApiClient } from '@agentic/platform-api-client'
|
||||||
|
import { assert } from '@agentic/platform-core'
|
||||||
|
import { parseDeploymentIdentifier } from '@agentic/platform-validators'
|
||||||
|
import defaultKy, { type KyInstance } from 'ky'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agentic tool client which makes it easy to use an Agentic tools product with
|
||||||
|
* all of the major TypeScript LLM SDKs.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const client = await AgenticToolClient.fromIdentifier('@agentic/search')
|
||||||
|
* const result = await client.functions.get('search').execute('search query')
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class AgenticToolClient extends AIFunctionsProvider {
|
||||||
|
readonly project: Project
|
||||||
|
readonly deployment: Deployment
|
||||||
|
readonly agenticGatewayBaseUrl: string
|
||||||
|
readonly ky: KyInstance
|
||||||
|
|
||||||
|
protected constructor({
|
||||||
|
project,
|
||||||
|
deployment,
|
||||||
|
deploymentIdentifier,
|
||||||
|
agenticGatewayBaseUrl,
|
||||||
|
ky
|
||||||
|
}: {
|
||||||
|
project: Project
|
||||||
|
deployment: Deployment
|
||||||
|
deploymentIdentifier: string
|
||||||
|
agenticGatewayBaseUrl: string
|
||||||
|
ky: KyInstance
|
||||||
|
}) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.project = project
|
||||||
|
this.deployment = deployment
|
||||||
|
this.agenticGatewayBaseUrl = agenticGatewayBaseUrl
|
||||||
|
this.ky = ky
|
||||||
|
|
||||||
|
this._functions = new AIFunctionSet(
|
||||||
|
deployment.tools.map((tool) => {
|
||||||
|
return createAIFunction({
|
||||||
|
name: tool.name,
|
||||||
|
description: tool.description ?? '',
|
||||||
|
inputSchema: createJsonSchema(tool.inputSchema),
|
||||||
|
execute: async (json) => {
|
||||||
|
return ky
|
||||||
|
.post(
|
||||||
|
`${agenticGatewayBaseUrl}/${deploymentIdentifier}/${tool.name}`,
|
||||||
|
{
|
||||||
|
json
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.json()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override get functions(): AIFunctionSet {
|
||||||
|
assert(this._functions)
|
||||||
|
return this._functions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Agentic tool client from a project or deployment identifier.
|
||||||
|
*
|
||||||
|
* You'll generally use a project identifier, which will automatically use
|
||||||
|
* that project's `latest` deployment, but if you want to target a specific
|
||||||
|
* version or preview deployment, you can use a fully-qualified deployment
|
||||||
|
* identifier.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const client1 = await AgenticToolClient.fromIdentifier('@agentic/search')
|
||||||
|
* const client2 = await AgenticToolClient.fromIdentifier('@agentic/search@v1.0.0')
|
||||||
|
* const client3 = await AgenticToolClient.fromIdentifier('@agentic/search@latest')
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
static async fromIdentifier(
|
||||||
|
projectOrDeploymentIdentifier: string,
|
||||||
|
{
|
||||||
|
agenticApiClient = new AgenticApiClient(),
|
||||||
|
agenticGatewayBaseUrl = 'https://gateway.agentic.so',
|
||||||
|
ky = defaultKy
|
||||||
|
}: {
|
||||||
|
agenticApiClient?: AgenticApiClient
|
||||||
|
agenticGatewayBaseUrl?: string
|
||||||
|
ky?: KyInstance
|
||||||
|
} = {}
|
||||||
|
): Promise<AgenticToolClient> {
|
||||||
|
const { projectIdentifier, deploymentIdentifier, deploymentVersion } =
|
||||||
|
parseDeploymentIdentifier(projectOrDeploymentIdentifier, {
|
||||||
|
strict: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const [project, rawDeployment] = await Promise.all([
|
||||||
|
agenticApiClient.getPublicProjectByIdentifier({
|
||||||
|
projectIdentifier,
|
||||||
|
populate:
|
||||||
|
deploymentVersion === 'latest' ? ['lastPublishedDeployment'] : []
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Only make 1 API call in the 95% case where the deployment version is
|
||||||
|
// set to the default value of `latest`.
|
||||||
|
deploymentVersion === 'latest'
|
||||||
|
? Promise.resolve(undefined)
|
||||||
|
: agenticApiClient.getPublicDeploymentByIdentifier({
|
||||||
|
deploymentIdentifier
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
const deployment =
|
||||||
|
deploymentVersion === 'latest'
|
||||||
|
? project?.lastPublishedDeployment
|
||||||
|
: rawDeployment
|
||||||
|
|
||||||
|
assert(project, `Project "${projectIdentifier}" not found`)
|
||||||
|
assert(deployment, `Deployment "${deploymentIdentifier}" not found`)
|
||||||
|
|
||||||
|
return new AgenticToolClient({
|
||||||
|
project,
|
||||||
|
deployment,
|
||||||
|
deploymentIdentifier,
|
||||||
|
agenticGatewayBaseUrl,
|
||||||
|
ky
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './agentic-tool-client'
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": "@fisch0920/config/tsconfig-node",
|
||||||
|
"include": ["src", "*.config.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
|
@ -140,6 +140,23 @@ export interface paths {
|
||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/v1/deployments/public/by-identifier": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/** @description Gets a public deployment by its identifier (eg, "@username/project-name@latest"). */
|
||||||
|
get: operations["getPublicDeploymentByIdentifier"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/v1/users/{userId}": {
|
"/v1/users/{userId}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
@ -428,7 +445,7 @@ export interface paths {
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
/** @description Gets a deployment by its public identifier */
|
/** @description Gets a deployment by its identifier (eg, "@username/project-name@latest"). */
|
||||||
get: operations["getDeploymentByIdentifier"];
|
get: operations["getDeploymentByIdentifier"];
|
||||||
put?: never;
|
put?: never;
|
||||||
post?: never;
|
post?: never;
|
||||||
|
@ -600,46 +617,6 @@ export interface components {
|
||||||
team?: components["schemas"]["Team"];
|
team?: components["schemas"]["Team"];
|
||||||
lastPublishedDeployment?: unknown;
|
lastPublishedDeployment?: unknown;
|
||||||
lastDeployment?: unknown;
|
lastDeployment?: unknown;
|
||||||
};
|
|
||||||
TeamMember: {
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
deletedAt?: string;
|
|
||||||
userId: string;
|
|
||||||
teamSlug: string;
|
|
||||||
teamId: string;
|
|
||||||
/** @enum {string} */
|
|
||||||
role: "user" | "admin";
|
|
||||||
confirmed: boolean;
|
|
||||||
confirmedAt?: string;
|
|
||||||
};
|
|
||||||
/** @description A Consumer represents a user who has subscribed to a Project and is used
|
|
||||||
* to track usage and billing.
|
|
||||||
*
|
|
||||||
* Consumers are linked to a corresponding Stripe Customer and Subscription.
|
|
||||||
* The Stripe customer will either be the user's default Stripe Customer if the
|
|
||||||
* project uses the default Agentic platform account, or a customer on the project
|
|
||||||
* owner's connected Stripe account if the project has Stripe Connect enabled. */
|
|
||||||
Consumer: {
|
|
||||||
/** @description Consumer id (e.g. "csmr_tz4a98xxat96iws9zmbrgj3a") */
|
|
||||||
id: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
deletedAt?: string;
|
|
||||||
token: string;
|
|
||||||
plan?: string;
|
|
||||||
activated: boolean;
|
|
||||||
source?: string;
|
|
||||||
/** @description User id (e.g. "user_tz4a98xxat96iws9zmbrgj3a") */
|
|
||||||
userId: string;
|
|
||||||
/** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */
|
|
||||||
projectId: string;
|
|
||||||
/** @description Deployment id (e.g. "depl_tz4a98xxat96iws9zmbrgj3a") */
|
|
||||||
deploymentId: string;
|
|
||||||
stripeStatus: string;
|
|
||||||
isStripeSubscriptionActive: boolean;
|
|
||||||
user?: components["schemas"]["User"];
|
|
||||||
project?: components["schemas"]["Project"];
|
|
||||||
deployment?: unknown;
|
deployment?: unknown;
|
||||||
};
|
};
|
||||||
/** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */
|
/** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */
|
||||||
|
@ -899,6 +876,47 @@ export interface components {
|
||||||
defaultRateLimit?: components["schemas"]["RateLimit"] & unknown;
|
defaultRateLimit?: components["schemas"]["RateLimit"] & unknown;
|
||||||
project?: unknown;
|
project?: unknown;
|
||||||
};
|
};
|
||||||
|
TeamMember: {
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt?: string;
|
||||||
|
userId: string;
|
||||||
|
teamSlug: string;
|
||||||
|
teamId: string;
|
||||||
|
/** @enum {string} */
|
||||||
|
role: "user" | "admin";
|
||||||
|
confirmed: boolean;
|
||||||
|
confirmedAt?: string;
|
||||||
|
};
|
||||||
|
/** @description A Consumer represents a user who has subscribed to a Project and is used
|
||||||
|
* to track usage and billing.
|
||||||
|
*
|
||||||
|
* Consumers are linked to a corresponding Stripe Customer and Subscription.
|
||||||
|
* The Stripe customer will either be the user's default Stripe Customer if the
|
||||||
|
* project uses the default Agentic platform account, or a customer on the project
|
||||||
|
* owner's connected Stripe account if the project has Stripe Connect enabled. */
|
||||||
|
Consumer: {
|
||||||
|
/** @description Consumer id (e.g. "csmr_tz4a98xxat96iws9zmbrgj3a") */
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt?: string;
|
||||||
|
token: string;
|
||||||
|
plan?: string;
|
||||||
|
activated: boolean;
|
||||||
|
source?: string;
|
||||||
|
/** @description User id (e.g. "user_tz4a98xxat96iws9zmbrgj3a") */
|
||||||
|
userId: string;
|
||||||
|
/** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */
|
||||||
|
projectId: string;
|
||||||
|
/** @description Deployment id (e.g. "depl_tz4a98xxat96iws9zmbrgj3a") */
|
||||||
|
deploymentId: string;
|
||||||
|
stripeStatus: string;
|
||||||
|
isStripeSubscriptionActive: boolean;
|
||||||
|
user?: components["schemas"]["User"];
|
||||||
|
project?: components["schemas"]["Project"];
|
||||||
|
deployment?: unknown;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* @description Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API.
|
* @description Origin adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools are defined: either an OpenAPI spec, an MCP server, or a raw HTTP REST API.
|
||||||
*
|
*
|
||||||
|
@ -1269,6 +1287,34 @@ export interface operations {
|
||||||
404: components["responses"]["404"];
|
404: components["responses"]["404"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
getPublicDeploymentByIdentifier: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
populate?: ("user" | "team" | "project") | ("user" | "team" | "project")[];
|
||||||
|
/** @description Public deployment identifier (e.g. "@namespace/project-name@{hash|version|latest}") */
|
||||||
|
deploymentIdentifier: components["schemas"]["DeploymentIdentifier"];
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description A deployment object */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["Deployment"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
400: components["responses"]["400"];
|
||||||
|
401: components["responses"]["401"];
|
||||||
|
403: components["responses"]["403"];
|
||||||
|
404: components["responses"]["404"];
|
||||||
|
};
|
||||||
|
};
|
||||||
getUser: {
|
getUser: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
|
@ -67,3 +67,47 @@ export type AdminConsumer = Simplify<
|
||||||
deployment?: Deployment
|
deployment?: Deployment
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
export type AgenticMcpRequestMetadata = {
|
||||||
|
agenticProxySecret: string
|
||||||
|
sessionId: string
|
||||||
|
isCustomerSubscriptionActive: boolean
|
||||||
|
|
||||||
|
customerId?: string
|
||||||
|
customerSubscriptionStatus?: string
|
||||||
|
customerSubscriptionPlan?: string
|
||||||
|
|
||||||
|
userId?: string
|
||||||
|
userEmail?: string
|
||||||
|
userUsername?: string
|
||||||
|
userName?: string
|
||||||
|
userCreatedAt?: string
|
||||||
|
userUpdatedAt?: string
|
||||||
|
|
||||||
|
deploymentId: string
|
||||||
|
deploymentIdentifier: string
|
||||||
|
projectId: string
|
||||||
|
projectIdentifier: string
|
||||||
|
|
||||||
|
ip?: string
|
||||||
|
} & (
|
||||||
|
| {
|
||||||
|
// If the customer has an active subscription, these fields are guaranteed
|
||||||
|
// to be present in the metadata.
|
||||||
|
isCustomerSubscriptionActive: true
|
||||||
|
|
||||||
|
customerId: string
|
||||||
|
customerSubscriptionStatus: string
|
||||||
|
|
||||||
|
userId: string
|
||||||
|
userEmail: string
|
||||||
|
userUsername: string
|
||||||
|
userCreatedAt: string
|
||||||
|
userUpdatedAt: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
// If the customer does not have an active subscription, then the customer
|
||||||
|
// fields may or may not be present.
|
||||||
|
isCustomerSubscriptionActive: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
export const namespaceBlacklist = new Set([
|
export const namespaceBlacklist = new Set([
|
||||||
// restricted because they would be confusing
|
// restricted because they would be confusing
|
||||||
'agentic',
|
|
||||||
'admin',
|
'admin',
|
||||||
'root',
|
'root',
|
||||||
'sudo',
|
'sudo',
|
||||||
|
|
969
pnpm-lock.yaml
969
pnpm-lock.yaml
Plik diff jest za duży
Load Diff
|
@ -1,6 +1,7 @@
|
||||||
packages:
|
packages:
|
||||||
- packages/*
|
- packages/*
|
||||||
- apps/*
|
- apps/*
|
||||||
|
- examples/*
|
||||||
- packages/fixtures/valid/*
|
- packages/fixtures/valid/*
|
||||||
|
|
||||||
catalog:
|
catalog:
|
||||||
|
@ -13,7 +14,7 @@ catalog:
|
||||||
'@fisch0920/config': ^1.1.2
|
'@fisch0920/config': ^1.1.2
|
||||||
'@fisch0920/drizzle-orm': ^0.43.7
|
'@fisch0920/drizzle-orm': ^0.43.7
|
||||||
'@fisch0920/drizzle-zod': ^0.7.9
|
'@fisch0920/drizzle-zod': ^0.7.9
|
||||||
'@hono/node-server': ^1.14.4
|
'@hono/node-server': 1.14.4
|
||||||
'@hono/sentry': ^1.2.2
|
'@hono/sentry': ^1.2.2
|
||||||
'@hono/zod-openapi': ^0.19.8
|
'@hono/zod-openapi': ^0.19.8
|
||||||
'@hono/zod-validator': ^0.7.0
|
'@hono/zod-validator': ^0.7.0
|
||||||
|
@ -52,7 +53,7 @@ catalog:
|
||||||
fastmcp: ^3.4.0
|
fastmcp: ^3.4.0
|
||||||
get-port: ^7.1.0
|
get-port: ^7.1.0
|
||||||
hash-object: ^5.0.1
|
hash-object: ^5.0.1
|
||||||
hono: ^4.8.1
|
hono: 4.8.1
|
||||||
is-relative-url: ^4.0.0
|
is-relative-url: ^4.0.0
|
||||||
knip: ^5.61.2
|
knip: ^5.61.2
|
||||||
ky: 1.8.1
|
ky: 1.8.1
|
||||||
|
@ -88,7 +89,7 @@ catalog:
|
||||||
unconfig: ^7.3.2
|
unconfig: ^7.3.2
|
||||||
vite-tsconfig-paths: ^5.1.4
|
vite-tsconfig-paths: ^5.1.4
|
||||||
vitest: ^3.2.4
|
vitest: ^3.2.4
|
||||||
wrangler: ^4.20.3
|
wrangler: ^4.21.0
|
||||||
zod: ^3.25.67
|
zod: ^3.25.67
|
||||||
zod-to-json-schema: ^3.24.5
|
zod-to-json-schema: ^3.24.5
|
||||||
zod-validation-error: ^3.5.2
|
zod-validation-error: ^3.5.2
|
||||||
|
|
Ładowanie…
Reference in New Issue