kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: work on e2e gateway tests
rodzic
99e86f12e5
commit
004b745897
|
@ -4,6 +4,7 @@ import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
|
||||||
import type { AuthenticatedEnv } from '@/lib/types'
|
import type { AuthenticatedEnv } from '@/lib/types'
|
||||||
import { db, eq, schema } from '@/db'
|
import { db, eq, schema } from '@/db'
|
||||||
import { aclAdmin } from '@/lib/acl-admin'
|
import { aclAdmin } from '@/lib/acl-admin'
|
||||||
|
import { setPublicCacheControl } from '@/lib/cache-control'
|
||||||
import {
|
import {
|
||||||
openapiAuthenticatedSecuritySchemas,
|
openapiAuthenticatedSecuritySchemas,
|
||||||
openapiErrorResponse404,
|
openapiErrorResponse404,
|
||||||
|
@ -58,15 +59,9 @@ export function registerV1AdminConsumersGetConsumerByToken(
|
||||||
!consumer.activated ||
|
!consumer.activated ||
|
||||||
!consumer.isStripeSubscriptionActive
|
!consumer.isStripeSubscriptionActive
|
||||||
) {
|
) {
|
||||||
c.res.headers.set(
|
setPublicCacheControl(c.res, '1s')
|
||||||
'cache-control',
|
|
||||||
'public, max-age=1, s-maxage=1 stale-while-revalidate=1'
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
c.res.headers.set(
|
setPublicCacheControl(c.res, '1m')
|
||||||
'cache-control',
|
|
||||||
'public, max-age=120, s-maxage=120, stale-while-revalidate=10'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json(parseZodSchema(schema.consumerSelectSchema, consumer))
|
return c.json(parseZodSchema(schema.consumerSelectSchema, consumer))
|
||||||
|
|
|
@ -62,20 +62,6 @@ export function registerV1AdminDeploymentsGetDeploymentByIdentifier(
|
||||||
|
|
||||||
const hasPopulateProject = populate.includes('project')
|
const hasPopulateProject = populate.includes('project')
|
||||||
|
|
||||||
// TODO
|
|
||||||
// TODO: switch from published to publishedAt?
|
|
||||||
// if (deployment.published) {
|
|
||||||
// c.res.headers.set(
|
|
||||||
// 'cache-control',
|
|
||||||
// 'public, max-age=1, s-maxage=1 stale-while-revalidate=1'
|
|
||||||
// )
|
|
||||||
// } else {
|
|
||||||
// c.res.headers.set(
|
|
||||||
// 'cache-control',
|
|
||||||
// 'public, max-age=120, s-maxage=120, stale-while-revalidate=10'
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
return c.json(
|
return c.json(
|
||||||
parseZodSchema(schema.deploymentAdminSelectSchema, {
|
parseZodSchema(schema.deploymentAdminSelectSchema, {
|
||||||
...deployment,
|
...deployment,
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { assert } from '@agentic/platform-core'
|
||||||
|
|
||||||
|
export type PublicCacheControlLevels = '1s' | '10s' | '1m' | '1h' | '1d'
|
||||||
|
|
||||||
|
const publicCacheControlLevelsMap: Record<PublicCacheControlLevels, string> = {
|
||||||
|
'1s': 'public, max-age=1, s-maxage=1 stale-while-revalidate=0',
|
||||||
|
'10s': 'public, max-age=10, s-maxage=10 stale-while-revalidate=1',
|
||||||
|
'1m': 'public, max-age=60, s-maxage=60 stale-while-revalidate=10',
|
||||||
|
'1h': 'public, max-age=3600, s-maxage=3600, stale-while-revalidate=300',
|
||||||
|
'1d': 'public, max-age=86400, s-maxage=86400, stale-while-revalidate=3600'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPublicCacheControl(
|
||||||
|
res: Response,
|
||||||
|
level: PublicCacheControlLevels
|
||||||
|
) {
|
||||||
|
const cacheControl = publicCacheControlLevelsMap[level]
|
||||||
|
assert(cacheControl, `Invalid cache control level "${level}"`)
|
||||||
|
|
||||||
|
res.headers.set('cache-control', cacheControl)
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import {
|
||||||
type RawDeployment,
|
type RawDeployment,
|
||||||
schema
|
schema
|
||||||
} from '@/db'
|
} from '@/db'
|
||||||
|
import { setPublicCacheControl } from '@/lib/cache-control'
|
||||||
import { ensureAuthUser } from '@/lib/ensure-auth-user'
|
import { ensureAuthUser } from '@/lib/ensure-auth-user'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,6 +45,7 @@ export async function tryGetDeploymentByIdentifier(
|
||||||
where: eq(schema.deployments.id, deploymentIdentifier)
|
where: eq(schema.deployments.id, deploymentIdentifier)
|
||||||
})
|
})
|
||||||
assert(deployment, 404, `Deployment not found "${deploymentIdentifier}"`)
|
assert(deployment, 404, `Deployment not found "${deploymentIdentifier}"`)
|
||||||
|
setPublicCacheControl(ctx.res, '1h')
|
||||||
return deployment
|
return deployment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +70,7 @@ export async function tryGetDeploymentByIdentifier(
|
||||||
where: eq(schema.deployments.identifier, deploymentIdentifier)
|
where: eq(schema.deployments.identifier, deploymentIdentifier)
|
||||||
})
|
})
|
||||||
assert(deployment, 404, `Deployment not found "${deploymentIdentifier}"`)
|
assert(deployment, 404, `Deployment not found "${deploymentIdentifier}"`)
|
||||||
|
setPublicCacheControl(ctx.res, '1h')
|
||||||
|
|
||||||
return deployment
|
return deployment
|
||||||
} else if (version) {
|
} else if (version) {
|
||||||
|
@ -93,6 +96,7 @@ export async function tryGetDeploymentByIdentifier(
|
||||||
404,
|
404,
|
||||||
`Deployment not found "${project.lastPublishedDeploymentId}"`
|
`Deployment not found "${project.lastPublishedDeploymentId}"`
|
||||||
)
|
)
|
||||||
|
setPublicCacheControl(ctx.res, '10s')
|
||||||
|
|
||||||
return deployment
|
return deployment
|
||||||
} else if (version === 'dev') {
|
} else if (version === 'dev') {
|
||||||
|
@ -111,6 +115,7 @@ export async function tryGetDeploymentByIdentifier(
|
||||||
404,
|
404,
|
||||||
`Deployment not found "${project.lastDeploymentId}"`
|
`Deployment not found "${project.lastDeploymentId}"`
|
||||||
)
|
)
|
||||||
|
setPublicCacheControl(ctx.res, '10s')
|
||||||
|
|
||||||
return deployment
|
return deployment
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,6 +131,7 @@ export async function tryGetDeploymentByIdentifier(
|
||||||
404,
|
404,
|
||||||
`Deployment not found "${projectIdentifier}@${version}"`
|
`Deployment not found "${projectIdentifier}@${version}"`
|
||||||
)
|
)
|
||||||
|
setPublicCacheControl(ctx.res, '1h')
|
||||||
|
|
||||||
return deployment
|
return deployment
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,7 @@
|
||||||
# a local .env file in order to run this project.
|
# a local .env file in order to run this project.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
AGENTIC_API_BASE_URL=
|
||||||
AGENTIC_API_ACCESS_TOKEN=
|
AGENTIC_API_ACCESS_TOKEN=
|
||||||
|
|
||||||
|
AGENTIC_GATEWAY_BASE_URL=
|
||||||
|
|
|
@ -17,13 +17,15 @@
|
||||||
"test:typecheck": "tsc --noEmit",
|
"test:typecheck": "tsc --noEmit",
|
||||||
"test:e2e": "vitest run"
|
"test:e2e": "vitest run"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ky": "catalog:",
|
||||||
|
"p-map": "catalog:"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@agentic/platform": "workspace:*",
|
"@agentic/platform": "workspace:*",
|
||||||
"@agentic/platform-api-client": "workspace:*",
|
"@agentic/platform-api-client": "workspace:*",
|
||||||
"@agentic/platform-core": "workspace:*",
|
"@agentic/platform-core": "workspace:*",
|
||||||
"@agentic/platform-fixtures": "workspace:*"
|
"@agentic/platform-fixtures": "workspace:*",
|
||||||
},
|
"fast-content-type-parse": "^3.0.0"
|
||||||
"dependencies": {
|
|
||||||
"p-map": "catalog:"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,64 @@
|
||||||
|
import contentType from 'fast-content-type-parse'
|
||||||
|
import defaultKy from 'ky'
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from 'vitest'
|
||||||
|
|
||||||
test(
|
import { env } from './env'
|
||||||
`${fixture}`,
|
import { fixtures } from './fixtures'
|
||||||
{
|
|
||||||
timeout: 60_000
|
const ky = defaultKy.extend({
|
||||||
},
|
prefixUrl: env.AGENTIC_GATEWAY_BASE_URL,
|
||||||
async () => {
|
|
||||||
'dev/test-basic-openapi@8d1a4900'
|
// Disable automatic retries for testing.
|
||||||
}
|
retry: 0
|
||||||
)
|
})
|
||||||
|
|
||||||
|
for (const [i, fixture] of fixtures.entries()) {
|
||||||
|
const method = fixture.request?.method ?? 'GET'
|
||||||
|
const {
|
||||||
|
status = 200,
|
||||||
|
snapshot = true,
|
||||||
|
contentType: expectedContentType = 'application/json',
|
||||||
|
headers: expectedHeaders,
|
||||||
|
body: expectedBody
|
||||||
|
} = fixture.response ?? {}
|
||||||
|
|
||||||
|
test(
|
||||||
|
`${i}) ${method} ${fixture.path}`,
|
||||||
|
{
|
||||||
|
timeout: fixture.timeout ?? 60_000
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const res = await ky(fixture.path, fixture.request)
|
||||||
|
expect(res.status).toBe(status)
|
||||||
|
|
||||||
|
const { type } = contentType.safeParse(
|
||||||
|
res.headers.get('content-type') ?? ''
|
||||||
|
)
|
||||||
|
expect(type).toBe(expectedContentType)
|
||||||
|
|
||||||
|
if (expectedHeaders) {
|
||||||
|
for (const [key, value] of Object.entries(expectedHeaders)) {
|
||||||
|
expect(res.headers.get(key)).toBe(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: any
|
||||||
|
|
||||||
|
if (type.includes('json')) {
|
||||||
|
body = await res.json()
|
||||||
|
} else if (type.includes('text')) {
|
||||||
|
body = await res.text()
|
||||||
|
} else {
|
||||||
|
body = await res.arrayBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedBody) {
|
||||||
|
expect(body).toEqual(expectedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot) {
|
||||||
|
expect(body).toMatchSnapshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -3,13 +3,22 @@ import 'dotenv/config'
|
||||||
import { parseZodSchema } from '@agentic/platform-core'
|
import { parseZodSchema } from '@agentic/platform-core'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
// TODO: derive AGENTIC_API_BASE_URL and AGENTIC_GATEWAY_BASE_URL based on
|
||||||
|
// environment.
|
||||||
|
|
||||||
export const envSchema = z.object({
|
export const envSchema = z.object({
|
||||||
NODE_ENV: z
|
NODE_ENV: z
|
||||||
.enum(['development', 'test', 'production'])
|
.enum(['development', 'test', 'production'])
|
||||||
.default('development'),
|
.default('development'),
|
||||||
|
|
||||||
AGENTIC_API_BASE_URL: z.string().url().optional(),
|
AGENTIC_API_BASE_URL: z.string().url().optional(),
|
||||||
AGENTIC_API_ACCESS_TOKEN: z.string().nonempty()
|
AGENTIC_API_ACCESS_TOKEN: z.string().nonempty(),
|
||||||
|
|
||||||
|
AGENTIC_GATEWAY_BASE_URL: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.optional()
|
||||||
|
.default('http://localhost:8787')
|
||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line no-process-env
|
// eslint-disable-next-line no-process-env
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
export type E2ETestFixture = {
|
||||||
|
path: string
|
||||||
|
|
||||||
|
/** @default 60_000 milliseconds */
|
||||||
|
timeout?: number
|
||||||
|
|
||||||
|
request?: {
|
||||||
|
/** @default 'GET' */
|
||||||
|
method?: 'GET' | 'POST'
|
||||||
|
searchParams?: Record<string, string | number | boolean>
|
||||||
|
headers?: Record<string, string>
|
||||||
|
json?: Record<string, unknown>
|
||||||
|
body?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
response?: {
|
||||||
|
/** @default 200 */
|
||||||
|
status?: number
|
||||||
|
/** @default 'application/json' */
|
||||||
|
contentType?: string
|
||||||
|
headers?: Record<string, string>
|
||||||
|
body?: any
|
||||||
|
validate?: (body: any) => void
|
||||||
|
/** @default true */
|
||||||
|
snapshot?: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fixtures: E2ETestFixture[] = [
|
||||||
|
{
|
||||||
|
path: 'dev/test-basic-openapi/getPost',
|
||||||
|
request: {
|
||||||
|
searchParams: {
|
||||||
|
postId: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dev/test-basic-openapi@8d1a4900/getPost?postId=1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'test-basic-openapi/getPost',
|
||||||
|
request: {
|
||||||
|
searchParams: {
|
||||||
|
postId: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'test-basic-openapi@8d1a4900/getPost',
|
||||||
|
request: {
|
||||||
|
searchParams: {
|
||||||
|
postId: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dev/test-basic-openapi@8d1a4900/getPost',
|
||||||
|
request: {
|
||||||
|
searchParams: {
|
||||||
|
postId: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dev/test-basic-openapi/getPost',
|
||||||
|
request: {
|
||||||
|
method: 'POST',
|
||||||
|
json: {
|
||||||
|
postId: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -39,6 +39,7 @@
|
||||||
"eventid": "catalog:",
|
"eventid": "catalog:",
|
||||||
"fast-content-type-parse": "^3.0.0",
|
"fast-content-type-parse": "^3.0.0",
|
||||||
"hono": "catalog:",
|
"hono": "catalog:",
|
||||||
|
"ky": "catalog:",
|
||||||
"plur": "^5.1.0",
|
"plur": "^5.1.0",
|
||||||
"type-fest": "catalog:"
|
"type-fest": "catalog:"
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function cfValidateJsonSchemaObject<
|
||||||
errorMessage
|
errorMessage
|
||||||
}: {
|
}: {
|
||||||
schema: any
|
schema: any
|
||||||
data: unknown
|
data: Record<string, unknown>
|
||||||
errorMessage?: string
|
errorMessage?: string
|
||||||
}): T {
|
}): T {
|
||||||
// Special-case check for required fields to give better error messages
|
// Special-case check for required fields to give better error messages
|
||||||
|
|
|
@ -26,6 +26,7 @@ export async function createRequestForOpenAPIOperation({
|
||||||
|
|
||||||
let incomingRequestParams: Record<string, any> = {}
|
let incomingRequestParams: Record<string, any> = {}
|
||||||
if (request.method === 'GET') {
|
if (request.method === 'GET') {
|
||||||
|
// TODO: coerce data types to match input schema since all values will be strings
|
||||||
incomingRequestParams = Object.fromEntries(
|
incomingRequestParams = Object.fromEntries(
|
||||||
new URL(request.url).searchParams.entries()
|
new URL(request.url).searchParams.entries()
|
||||||
)
|
)
|
||||||
|
@ -43,6 +44,8 @@ export async function createRequestForOpenAPIOperation({
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Validate incoming request params against the tool's input JSON schema
|
// TODO: Validate incoming request params against the tool's input JSON schema
|
||||||
|
// TODO: we want to coerce data types to match the schema for booleans, dates, etc
|
||||||
|
// Currently, these will fail if given as body params, for instance, on the origin server.
|
||||||
cfValidateJsonSchemaObject({
|
cfValidateJsonSchemaObject({
|
||||||
schema: tool.inputSchema,
|
schema: tool.inputSchema,
|
||||||
data: incomingRequestParams,
|
data: incomingRequestParams,
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import type { Context } from './types'
|
import type { Context } from './types'
|
||||||
|
|
||||||
const cache = caches.default
|
|
||||||
|
|
||||||
export async function fetchCache(
|
export async function fetchCache(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
{
|
{
|
||||||
|
@ -15,7 +13,7 @@ export async function fetchCache(
|
||||||
let response: Response | undefined
|
let response: Response | undefined
|
||||||
|
|
||||||
if (cacheKey) {
|
if (cacheKey) {
|
||||||
response = await cache.match(cacheKey)
|
response = await ctx.cache.match(cacheKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
|
@ -26,7 +24,7 @@ export async function fetchCache(
|
||||||
if (response.headers.has('Cache-Control')) {
|
if (response.headers.has('Cache-Control')) {
|
||||||
// Note that cloudflare's `cache` should respect response headers.
|
// Note that cloudflare's `cache` should respect response headers.
|
||||||
ctx.waitUntil(
|
ctx.waitUntil(
|
||||||
cache.put(cacheKey, response.clone()).catch((err) => {
|
ctx.cache.put(cacheKey, response.clone()).catch((err) => {
|
||||||
console.warn('cache put error', cacheKey, err)
|
console.warn('cache put error', cacheKey, err)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,6 +16,7 @@ export type Context = ExecutionContext & {
|
||||||
req: Request
|
req: Request
|
||||||
env: AgenticEnv
|
env: AgenticEnv
|
||||||
client: AgenticApiClient
|
client: AgenticApiClient
|
||||||
|
cache: Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResolvedOriginRequest {
|
export interface ResolvedOriginRequest {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { AgenticApiClient } from '@agentic/platform-api-client'
|
import { AgenticApiClient } from '@agentic/platform-api-client'
|
||||||
import { assert, parseZodSchema } from '@agentic/platform-core'
|
import { assert, parseZodSchema } from '@agentic/platform-core'
|
||||||
|
import defaultKy from 'ky'
|
||||||
|
|
||||||
import type { Context } from './lib/types'
|
import type { Context } from './lib/types'
|
||||||
import { type AgenticEnv, envSchema } from './lib/env'
|
import { type AgenticEnv, envSchema } from './lib/env'
|
||||||
|
@ -44,9 +45,40 @@ export default {
|
||||||
gatewayTimespan = now - gatewayStartTime
|
gatewayTimespan = now - gatewayStartTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cache = caches.default
|
||||||
const client = new AgenticApiClient({
|
const client = new AgenticApiClient({
|
||||||
apiBaseUrl: env.AGENTIC_API_BASE_URL,
|
apiBaseUrl: env.AGENTIC_API_BASE_URL,
|
||||||
apiKey: env.AGENTIC_API_KEY
|
apiKey: env.AGENTIC_API_KEY,
|
||||||
|
ky: defaultKy.extend({
|
||||||
|
hooks: {
|
||||||
|
// NOTE: The order of the `beforeRequest` hook matters, and it only
|
||||||
|
// works alongside the one in AgenticApiClient because that one's body
|
||||||
|
// should never be run. This only works because we're using `apiKey`
|
||||||
|
// authentication, which is a lil hacky since it's actually a long-
|
||||||
|
// lived access token.
|
||||||
|
beforeRequest: [
|
||||||
|
async (request) => {
|
||||||
|
// Check the cache first before making a request to Agentic's
|
||||||
|
// backend API.
|
||||||
|
return cache.match(request)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
afterResponse: [
|
||||||
|
async (request, _options, response) => {
|
||||||
|
if (response.headers.has('Cache-Control')) {
|
||||||
|
// Asynchronously update the cache with the response from
|
||||||
|
// Agentic's backend API.
|
||||||
|
inputCtx.waitUntil(
|
||||||
|
cache.put(request, response.clone()).catch((err) => {
|
||||||
|
console.warn('cache put error', request, err)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// NOTE: We have to mutate the given ExecutionContext because spreading it
|
// NOTE: We have to mutate the given ExecutionContext because spreading it
|
||||||
|
@ -55,6 +87,7 @@ export default {
|
||||||
ctx.req = inputReq
|
ctx.req = inputReq
|
||||||
ctx.env = env
|
ctx.env = env
|
||||||
ctx.client = client
|
ctx.client = client
|
||||||
|
ctx.cache = cache
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (inputReq.method === 'OPTIONS') {
|
if (inputReq.method === 'OPTIONS') {
|
||||||
|
|
|
@ -348,6 +348,9 @@ importers:
|
||||||
|
|
||||||
apps/e2e:
|
apps/e2e:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
ky:
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 1.8.1
|
||||||
p-map:
|
p-map:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 7.0.3
|
version: 7.0.3
|
||||||
|
@ -364,6 +367,9 @@ importers:
|
||||||
'@agentic/platform-fixtures':
|
'@agentic/platform-fixtures':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/fixtures
|
version: link:../../packages/fixtures
|
||||||
|
fast-content-type-parse:
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
|
|
||||||
apps/gateway:
|
apps/gateway:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -400,6 +406,9 @@ importers:
|
||||||
hono:
|
hono:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 4.7.10
|
version: 4.7.10
|
||||||
|
ky:
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 1.8.1
|
||||||
plur:
|
plur:
|
||||||
specifier: ^5.1.0
|
specifier: ^5.1.0
|
||||||
version: 5.1.0
|
version: 5.1.0
|
||||||
|
|
Ładowanie…
Reference in New Issue