feat: tiny lil baby kitten toes

pull/715/head
Travis Fischer 2025-05-27 00:35:19 +07:00
rodzic 23088eabb3
commit 31c6a54169
40 zmienionych plików z 1011 dodań i 135 usunięć

Wyświetl plik

@ -2,7 +2,7 @@
"name": "@agentic/platform-api",
"private": true,
"version": "0.1.0",
"description": "Agentic platform API.",
"description": "Internal Agentic platform API service.",
"author": "Travis Fischer <travis@transitivebullsh.it>",
"license": "UNLICENSED",
"repository": {

Wyświetl plik

@ -60,18 +60,24 @@ export function registerV1DeploymentsCreateDeployment(
const body = c.req.valid('json')
const teamMember = c.get('teamMember')
const logger = c.get('logger')
const { projectId } = body
// validatePricingPlans(ctx, pricingPlans)
const namespace = teamMember ? teamMember.teamSlug : user.username
const projectIdentifier = `${namespace}/${body.name}`
assert(
validators.projectIdentifier(projectIdentifier),
400,
`Invalid project identifier "${projectIdentifier}"`
)
const project = await db.query.projects.findFirst({
where: eq(schema.projects.id, projectId),
where: eq(schema.projects.identifier, projectIdentifier),
with: {
lastPublishedDeployment: true
}
})
assert(project, 404, `Project not found "${projectId}"`)
assert(project, 404, `Project not found "${projectIdentifier}"`)
await acl(c, project, { label: 'Project' })
const projectId = project.id
// TODO: investigate better short hash generation
const hash = sha256().slice(0, 8)

Wyświetl plik

@ -0,0 +1,57 @@
import { assert, parseZodSchema } from '@agentic/platform-core'
import { createRoute, type OpenAPIHono } from '@hono/zod-openapi'
import type { AuthenticatedEnv } from '@/lib/types'
import { schema } from '@/db'
import { acl } from '@/lib/acl'
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 deployment by its public identifier',
tags: ['deployments'],
operationId: 'getDeploymentByIdentifier',
method: 'get',
path: 'deployments/by-identifier',
security: openapiAuthenticatedSecuritySchemas,
request: {
query: deploymentIdentifierAndPopulateSchema
},
responses: {
200: {
description: 'A deployment object',
content: {
'application/json': {
schema: schema.deploymentSelectSchema
}
}
},
...openapiErrorResponses,
...openapiErrorResponse404
}
})
export function registerV1DeploymentsGetDeploymentByIdentifier(
app: OpenAPIHono<AuthenticatedEnv>
) {
return app.openapi(route, async (c) => {
const { deploymentIdentifier, populate = [] } = c.req.valid('query')
const deployment = await tryGetDeploymentByIdentifier(c, {
deploymentIdentifier,
with: {
...Object.fromEntries(populate.map((field) => [field, true]))
}
})
assert(deployment, 404, `Deployment not found "${deploymentIdentifier}"`)
await acl(c, deployment, { label: 'Deployment' })
return c.json(parseZodSchema(schema.deploymentSelectSchema, deployment))
})
}

Wyświetl plik

@ -14,7 +14,7 @@ import {
import { deploymentIdParamsSchema, populateDeploymentSchema } from './schemas'
const route = createRoute({
description: 'Gets a deployment',
description: 'Gets a deployment by its ID',
tags: ['deployments'],
operationId: 'getDeployment',
method: 'get',

Wyświetl plik

@ -1,6 +1,7 @@
import { z } from '@hono/zod-openapi'
import {
deploymentIdentifierSchema,
deploymentIdSchema,
deploymentRelationsSchema,
paginationSchema,
@ -29,6 +30,15 @@ export const populateDeploymentSchema = z.object({
populate: z.array(deploymentRelationsSchema).default([]).optional()
})
export const deploymentIdentifierQuerySchema = z.object({
deploymentIdentifier: deploymentIdentifierSchema
})
export const deploymentIdentifierAndPopulateSchema = z.object({
...populateDeploymentSchema.shape,
...deploymentIdentifierQuerySchema.shape
})
export const paginationAndPopulateAndFilterDeploymentSchema = z.object({
...paginationSchema.shape,
...populateDeploymentSchema.shape,

Wyświetl plik

@ -13,6 +13,7 @@ import { registerV1ConsumersRefreshConsumerToken } from './consumers/refresh-con
import { registerV1ConsumersUpdateConsumer } from './consumers/update-consumer'
import { registerV1DeploymentsCreateDeployment } from './deployments/create-deployment'
import { registerV1DeploymentsGetDeployment } from './deployments/get-deployment'
import { registerV1DeploymentsGetDeploymentByIdentifier } from './deployments/get-deployment-by-identifier'
import { registerV1DeploymentsListDeployments } from './deployments/list-deployments'
import { registerV1DeploymentsPublishDeployment } from './deployments/publish-deployment'
import { registerV1DeploymentsUpdateDeployment } from './deployments/update-deployment'
@ -33,6 +34,9 @@ import { registerV1UsersGetUser } from './users/get-user'
import { registerV1UsersUpdateUser } from './users/update-user'
import { registerV1StripeWebhook } from './webhooks/stripe-webhook'
// Note that the order of some of these routes is important because of
// wildcards, so be careful when updating them or adding new routes.
export const apiV1 = new OpenAPIHono({
defaultHook: (result, ctx) => {
if (!result.success) {
@ -92,6 +96,7 @@ registerV1ConsumersRefreshConsumerToken(privateRouter)
registerV1ProjectsListConsumers(privateRouter)
// Deployments
registerV1DeploymentsGetDeploymentByIdentifier(privateRouter) // must be before `registerV1DeploymentsGetDeployment`
registerV1DeploymentsGetDeployment(privateRouter)
registerV1DeploymentsCreateDeployment(privateRouter)
registerV1DeploymentsUpdateDeployment(privateRouter)

Wyświetl plik

@ -134,6 +134,6 @@ export const authRouter = issuer({
500,
`Authentication error for provider "${provider}": Unexpected error initializing user`
)
return ctx.subject('user', pick(user, 'id'))
return ctx.subject('user', pick(user, 'id', 'username'))
}
})

Wyświetl plik

@ -87,7 +87,7 @@ export function stripeId<U extends string, T extends Readonly<[U, ...U[]]>>(
}
/**
* `namespace/projectName`
* `namespace/project-name`
*/
export function projectIdentifier<
U extends string,
@ -99,7 +99,7 @@ export function projectIdentifier<
}
/**
* `namespace/projectName@hash`
* `namespace/project-name@hash`
*/
export function deploymentIdentifier<
U extends string,

Wyświetl plik

@ -1,10 +1,7 @@
import {
agenticProjectConfigSchema,
type DeploymentOriginAdapter,
deploymentOriginAdapterSchema,
pricingIntervalListSchema,
type PricingPlanList,
pricingPlanListSchema
type PricingPlanList
} from '@agentic/platform-schemas'
import { validators } from '@agentic/platform-validators'
import { relations } from '@fisch0920/drizzle-orm'
@ -26,7 +23,6 @@ import {
userIdSchema
} from '../schemas'
import {
createInsertSchema,
createSelectSchema,
createUpdateSchema,
deploymentIdentifier,
@ -37,7 +33,7 @@ import {
timestamps,
userId
} from './common'
import { projects } from './project'
import { projects, projectSelectSchema } from './project'
import { teams } from './team'
import { users } from './user'
@ -149,27 +145,37 @@ export const deploymentSelectSchema = createSelectSchema(deployments, {
message: 'Invalid deployment hash'
}),
originAdapter: deploymentOriginAdapterSchema,
pricingPlans: pricingPlanListSchema,
pricingIntervals: pricingIntervalListSchema
version: agenticProjectConfigSchema.shape.version,
description: agenticProjectConfigSchema.shape.description,
readme: agenticProjectConfigSchema.shape.readme,
iconUrl: agenticProjectConfigSchema.shape.iconUrl,
sourceUrl: agenticProjectConfigSchema.shape.sourceUrl,
originAdapter: agenticProjectConfigSchema.shape.originAdapter,
pricingPlans: agenticProjectConfigSchema.shape.pricingPlans,
pricingIntervals: agenticProjectConfigSchema.shape.pricingIntervals
})
.omit({
originUrl: true
})
// .extend({
// user: z
// .lazy(() => userSelectSchema)
// .optional()
// .openapi('User', { type: 'object' }),
.extend({
// user: z
// .lazy(() => userSelectSchema)
// .optional()
// .openapi('User', { type: 'object' }),
// team: z
// .lazy(() => teamSelectSchema)
// .optional()
// .openapi('Team', { type: 'object' }),
// team: z
// .lazy(() => teamSelectSchema)
// .optional()
// .openapi('Team', { type: 'object' }),
// // TODO: Circular references make this schema less than ideal
// project: z.object({}).optional().openapi('Project', { type: 'object' })
// })
project: z
.lazy(() => projectSelectSchema)
.optional()
.openapi('Project', { type: 'object' })
// TODO: Circular references make this schema less than ideal
// project: z.object({}).optional().openapi('Project', { type: 'object' })
})
.strip()
.describe(
`A Deployment is a single, immutable instance of a Project. Each deployment contains pricing plans, origin server config (OpenAPI or MCP server), tool definitions, and metadata.
@ -178,21 +184,7 @@ Deployments are private to a developer or team until they are published, at whic
)
.openapi('Deployment')
export const deploymentInsertSchema = createInsertSchema(deployments, {
projectId: projectIdSchema
})
.omit({
id: true,
createdAt: true,
updatedAt: true,
hash: true,
userId: true,
teamId: true
})
.extend({
...agenticProjectConfigSchema.shape
})
.strict()
export const deploymentInsertSchema = agenticProjectConfigSchema.strict()
// TODO: Deployments should be immutable, so we should not allow updates aside
// from publishing. But editing a project's description should be possible from

Wyświetl plik

@ -48,6 +48,7 @@ export const projectIdentifierSchema = z
.refine((id) => validators.projectIdentifier(id), {
message: 'Invalid project identifier'
})
.describe('Public project identifier (e.g. "namespace/project-name")')
.openapi('ProjectIdentifier')
export const deploymentIdentifierSchema = z
@ -55,6 +56,9 @@ export const deploymentIdentifierSchema = z
.refine((id) => validators.deploymentIdentifier(id), {
message: 'Invalid deployment identifier'
})
.describe(
'Public deployment identifier (e.g. "namespace/project-name@{hash|version|latest}")'
)
.openapi('DeploymentIdentifier')
export const usernameSchema = z

Wyświetl plik

@ -1,10 +1,4 @@
import { subjectSchemas } from '@agentic/platform-schemas'
import { createSubjects } from '@openauthjs/openauth/subject'
import { z } from 'zod'
import { userIdSchema } from '@/db'
export const subjects = createSubjects({
user: z.object({
id: userIdSchema
})
})
export const subjects = createSubjects(subjectSchemas)

Wyświetl plik

@ -31,7 +31,7 @@ export async function publishDeployment(
version: rawVersion
})
// TODO: enforce certain semver constraints
// TODO: enforce additional semver constraints
// - pricing changes require major version update
// - deployment shouldn't already be published?
// - any others?

Wyświetl plik

@ -25,7 +25,8 @@
"test:unit": "turbo test:unit",
"pretest": "run-s build",
"preinstall": "npx only-allow pnpm",
"prepare": "simple-git-hooks"
"prepare": "simple-git-hooks",
"knip": "knip"
},
"devDependencies": {
"@fisch0920/config": "catalog:",
@ -34,6 +35,7 @@
"dotenv": "catalog:",
"eslint": "catalog:",
"eslint-plugin-drizzle": "^0.2.3",
"knip": "^5.58.1",
"lint-staged": "catalog:",
"npm-run-all2": "catalog:",
"only-allow": "catalog:",

Wyświetl plik

@ -24,6 +24,7 @@
},
"dependencies": {
"@agentic/platform-core": "workspace:*",
"@agentic/platform-schemas": "workspace:*",
"@openauthjs/openauth": "^0.4.3",
"ky": "catalog:",
"type-fest": "catalog:",

Wyświetl plik

@ -176,7 +176,7 @@ export class AgenticApiClient {
async createTeam(
team: OperationBody<'createTeam'>,
{ ...searchParams }: OperationParameters<'createTeam'>
{ ...searchParams }: OperationParameters<'createTeam'> = {}
): Promise<OperationResponse<'createTeam'>> {
return this.ky.post('v1/teams', { json: team, searchParams }).json()
}
@ -251,7 +251,7 @@ export class AgenticApiClient {
async createProject(
project: OperationBody<'createProject'>,
{ ...searchParams }: OperationParameters<'createProject'>
{ ...searchParams }: OperationParameters<'createProject'> = {}
): Promise<OperationResponse<'createProject'>> {
return this.ky.post('v1/projects', { json: project, searchParams }).json()
}
@ -302,7 +302,7 @@ export class AgenticApiClient {
async createConsumer(
consumer: OperationBody<'createConsumer'>,
{ ...searchParams }: OperationParameters<'createConsumer'>
{ ...searchParams }: OperationParameters<'createConsumer'> = {}
): Promise<OperationResponse<'createConsumer'>> {
return this.ky.post('v1/consumers', { json: consumer, searchParams }).json()
}
@ -344,6 +344,16 @@ export class AgenticApiClient {
.json()
}
async getDeploymentByIdentifier(
searchParams: OperationParameters<'getDeploymentByIdentifier'>
): Promise<OperationResponse<'getDeploymentByIdentifier'>> {
return this.ky
.get(`v1/deployments/by-identifier`, {
searchParams: sanitizeSearchParams(searchParams)
})
.json()
}
async updateDeployment(
deployment: OperationBody<'updateDeployment'>,
{ deploymentId, ...searchParams }: OperationParameters<'updateDeployment'>
@ -368,7 +378,7 @@ export class AgenticApiClient {
async createDeployment(
deployment: OperationBody<'createDeployment'>,
{ ...searchParams }: OperationParameters<'createDeployment'>
{ ...searchParams }: OperationParameters<'createDeployment'> = {}
): Promise<OperationResponse<'createDeployment'>> {
return this.ky
.post('v1/deployments', { json: deployment, searchParams })

Wyświetl plik

@ -236,6 +236,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/v1/deployments/by-identifier": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** @description Gets a deployment by its public identifier */
get: operations["getDeploymentByIdentifier"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/v1/deployments/{deploymentId}": {
parameters: {
query?: never;
@ -243,7 +260,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** @description Gets a deployment */
/** @description Gets a deployment by its ID */
get: operations["getDeployment"];
put?: never;
/** @description Updates a deployment. */
@ -345,6 +362,7 @@ export interface components {
confirmed: boolean;
confirmedAt?: string;
};
/** @description Public project identifier (e.g. "namespace/project-name") */
ProjectIdentifier: string;
/** @description The frequency at which a subscription is billed. */
PricingInterval: "day" | "week" | "month" | "year";
@ -401,10 +419,17 @@ export interface components {
stripeStatus?: string;
isStripeSubscriptionActive: boolean;
};
/** @description Public deployment identifier (e.g. "namespace/project-name@{hash|version|latest}") */
DeploymentIdentifier: string;
/** @description Deployment origin API adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools / services are defined: either as an OpenAPI spec, an MCP server, or as a raw HTTP REST API.
/**
* @description Deployment origin API adapter is used to configure the origin API server downstream from Agentic's API gateway. It specifies whether the origin API server denoted by `originUrl` is hosted externally or deployed internally to Agentic's infrastructure. It also specifies the format for how origin tools / services are defined: either as an OpenAPI spec, an MCP server, or as a raw HTTP REST API.
*
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so. */
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
* @default {
* "location": "external",
* "type": "raw"
* }
*/
DeploymentOriginAdapter: {
/** @enum {string} */
type: "openapi";
@ -475,8 +500,6 @@ export interface components {
trialPeriodDays?: number;
lineItems: components["schemas"]["PricingPlanLineItem"][];
};
/** @description List of billing intervals for subscriptions. */
PricingIntervalList: components["schemas"]["PricingInterval"][];
/** @description A Deployment is a single, immutable instance of a Project. Each deployment contains pricing plans, origin server config (OpenAPI or MCP server), tool definitions, and metadata.
*
* Deployments are private to a developer or team until they are published, at which point they are accessible to any customers with access to the parent Project. */
@ -488,11 +511,22 @@ export interface components {
deletedAt?: string;
identifier: components["schemas"]["DeploymentIdentifier"];
hash: string;
/** @description Optional semantic version of the project as a semver string. Ex: 1.0.0, 0.0.1, 5.0.1, etc. */
version?: string;
published: boolean;
description: string;
readme: string;
/** @description A short description of the project. */
description?: string;
/** @description A readme documenting the project (supports GitHub-flavored markdown). */
readme?: string;
/**
* Format: uri
* @description Optional logo image URL to use for the project. Logos should have a square aspect ratio.
*/
iconUrl?: string;
/**
* Format: uri
* @description Optional URL to the source code of the project (eg, GitHub repo).
*/
sourceUrl?: string;
/** @description User id (e.g. "user_tz4a98xxat96iws9zmbrgj3a") */
userId: string;
@ -500,10 +534,36 @@ export interface components {
teamId?: string;
/** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */
projectId: string;
originAdapter: components["schemas"]["DeploymentOriginAdapter"];
/** @description List of PricingPlans */
originAdapter?: components["schemas"]["DeploymentOriginAdapter"];
/**
* @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project.
* @default [
* {
* "name": "Free",
* "slug": "free",
* "lineItems": [
* {
* "slug": "base",
* "usageType": "licensed",
* "amount": 0
* }
* ]
* }
* ]
*/
pricingPlans: components["schemas"]["PricingPlan"][];
pricingIntervals: components["schemas"]["PricingIntervalList"];
/**
* @description Optional list of billing intervals to enable in the pricingPlans.
*
* Defaults to a single monthly interval `['month']`.
*
* To add support for annual pricing plans, for example, you can use: `['month', 'year']`.
* @default [
* "month"
* ]
*/
pricingIntervals: components["schemas"]["PricingInterval"][];
project?: components["schemas"]["Project"];
};
};
responses: {
@ -1174,6 +1234,34 @@ export interface operations {
404: components["responses"]["404"];
};
};
getDeploymentByIdentifier: {
parameters: {
query: {
populate?: ("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"];
};
};
getDeployment: {
parameters: {
query?: {
@ -1280,10 +1368,10 @@ export interface operations {
requestBody: {
content: {
"application/json": {
deletedAt?: string;
identifier: string;
/** @description Name of the project. */
name: string;
/** @description Optional semantic version of the project as a semver string. Ex: 1.0.0, 0.0.1, 5.0.1, etc. */
version?: string;
published?: boolean;
/** @description A short description of the project. */
description?: string;
/** @description A readme documenting the project (supports GitHub-flavored markdown). */
@ -1298,8 +1386,6 @@ export interface operations {
* @description Optional URL to the source code of the project (eg, GitHub repo).
*/
sourceUrl?: string;
/** @description Project id (e.g. "proj_tz4a98xxat96iws9zmbrgj3a") */
projectId: string;
/**
* Format: uri
* @description Required base URL of the externally hosted origin API server. Must be a valid `https` URL.
@ -1307,9 +1393,9 @@ export interface operations {
* NOTE: Agentic currently only supports `external` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.
*/
originUrl: string;
originAdapter?: components["schemas"]["DeploymentOriginAdapter"] & unknown;
originAdapter?: components["schemas"]["DeploymentOriginAdapter"];
/**
* @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing.your project.
* @description List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project.
* @default [
* {
* "name": "Free",
@ -1325,9 +1411,17 @@ export interface operations {
* ]
*/
pricingPlans?: components["schemas"]["PricingPlan"][];
pricingIntervals?: components["schemas"]["PricingIntervalList"] & unknown;
/** @description Name of the project. */
name: string;
/**
* @description Optional list of billing intervals to enable in the pricingPlans.
*
* Defaults to a single monthly interval `['month']`.
*
* To add support for annual pricing plans, for example, you can use: `['month', 'year']`.
* @default [
* "month"
* ]
*/
pricingIntervals?: components["schemas"]["PricingInterval"][];
};
};
};

Wyświetl plik

@ -1,10 +1,4 @@
import { subjectSchemas } from '@agentic/platform-schemas'
import { createSubjects } from '@openauthjs/openauth/subject'
import { z } from 'zod'
// TODO: share this with the API server
export const subjects = createSubjects({
user: z.object({
id: z.string()
})
})
export type AuthUser = z.infer<typeof subjects.user>
export const subjects = createSubjects(subjectSchemas)

Wyświetl plik

@ -1,7 +1,7 @@
import type { AuthUser } from '@agentic/platform-schemas'
import type { Tokens as AuthTokens } from '@openauthjs/openauth/client'
import type { components } from './openapi'
import type { AuthUser } from './subjects'
export type Consumer = components['schemas']['Consumer']
export type Project = components['schemas']['Project']
@ -26,7 +26,7 @@ export type PricingPlanName = components['schemas']['name']
export type PricingPlanSlug = components['schemas']['slug']
export type PricingPlanLabel = components['schemas']['label']
export type { AuthUser } from './subjects'
export type { AuthUser } from '@agentic/platform-schemas'
export type {
AuthorizeResult,
Tokens as AuthTokens

Wyświetl plik

@ -31,6 +31,7 @@
"@agentic/platform-api-client": "workspace:*",
"@agentic/platform-core": "workspace:*",
"@agentic/platform-schemas": "workspace:*",
"@clack/prompts": "^0.11.0",
"@hono/node-server": "^1.14.1",
"commander": "^14.0.0",
"conf": "^13.1.0",

Wyświetl plik

@ -0,0 +1,3 @@
# TODO
- add default error handling to commands

Wyświetl plik

@ -0,0 +1,59 @@
import { Command } from 'commander'
import { oraPromise } from 'ora'
import type { Context } from '../types'
import { loadAgenticConfig } from '../lib/load-agentic-config'
import { AuthStore } from '../lib/store'
export function registerDeployCommand({ client, program, logger }: Context) {
const command = new Command('deploy')
.description('Creates a new deployment.')
.option(
'-c, --cwd <dir>',
'The directory to load the Agentic project config from (defaults to cwd). This directory must contain an "agentic.config.{ts,js,json}" project file.'
)
.option('-d, --debug', 'Print out the parsed agentic config and return.')
.option('-p, --publish', 'Publishes the deployment after creating it.')
.action(async (opts) => {
AuthStore.requireAuth()
// Load the Agentic project config, parse, and validate it. This will also
// validate any origin adapter config such as OpenAPI or MCP specs and
// embed them if they point to local files or URLs. Note that the server
// also performs validation; this is just a client-side convenience for
// failing fast and sharing 99% of the validation code between server and
// client.
const config = await oraPromise(
loadAgenticConfig({
cwd: opts.cwd
}),
{
text: `Loading Agentic config from ${opts.cwd}`,
successText: `Agentic config loaded successfully.`,
failText: 'Failed to load Agentic config.'
}
)
if (opts.debug) {
logger.log(config)
return
}
// Create the deployment on the backend, validate it, and optionally
// publish it.
const deployment = await oraPromise(
client.createDeployment(config, {
publish: !!opts.publish
}),
{
text: `Creating deployment for project "${config.name}"`,
successText: `Deployment created successfully.`,
failText: 'Failed to create deployment.'
}
)
logger.log(deployment)
})
program.addCommand(command)
}

Wyświetl plik

@ -1,21 +1,57 @@
import { Command } from 'commander'
// import ora from 'ora'
export const publish = new Command('publish')
.description('Publishes a deployment')
.argument('[deploymentId]', 'deployment ID')
.action(async (_opts) => {
// const spinner = ora('Publishing deployment').start()
// try {
// const deployment = await client.publishDeployment(
// { version: '1.0.0' },
// { deploymentId }
// )
// spinner.succeed('Deployment published successfully')
// console.log(JSON.stringify(deployment, null, 2))
// } catch (err) {
// spinner.fail('Failed to publish deployment')
// console.error(err)
// process.exit(1)
// }
})
import type { Context } from '../types'
import { resolveDeployment } from '../lib/resolve-deployment'
import { AuthStore } from '../lib/store'
export function registerPublishCommand({ client, program, logger }: Context) {
const command = new Command('publish')
.description('Publishes a deployment')
.argument('[deploymentIdentifier]', 'Deployment identifier')
.option(
'-c, --cwd <dir>',
'The directory to load the Agentic project config from (defaults to cwd). This directory must contain an "agentic.config.{ts,js,json}" project file.'
)
.action(async (deploymentIdentifier, opts) => {
AuthStore.requireAuth()
if (deploymentIdentifier) {
// TODO: parseFaasIdentifier
}
const deployment = await resolveDeployment({
client,
deploymentIdentifier,
fuzzyDeploymentIdentifierVersion: 'dev',
cwd: opts.cwd,
populate: ['project']
})
if (deployment.published) {
logger.error(
deploymentIdentifier
? `Deployment "${deploymentIdentifier}" is already published`
: `Latest deployment "${deployment.identifier}" is already published`
)
return
}
// TODO
// const version = deployment.version
// // TODO: prompt user for version or bump
// await client.publishDeployment(
// {
// version
// },
// {
// deploymentId: deployment.id
// }
// )
logger.log(deployment)
})
program.addCommand(command)
}

Wyświetl plik

@ -2,11 +2,15 @@ import { Command } from 'commander'
import inquirer from 'inquirer'
import ora from 'ora'
import { AuthStore } from '../lib/store'
export const rm = new Command('rm')
.description('Removes deployments')
.argument('[deploymentIds...]', 'deployment IDs to remove')
.option('-y, --yes', 'Skip confirmation')
.action(async (deploymentIds: string[], options: { yes?: boolean }) => {
AuthStore.requireAuth()
if (!deploymentIds.length) {
console.error('Please provide at least one deployment ID')
process.exit(1)

Wyświetl plik

@ -1,15 +1,13 @@
import { Command } from 'commander'
import type { Context } from '../types'
import { AuthStore } from '../lib/store'
export function registerWhoAmICommand({ client, program, logger }: Context) {
const command = new Command('whoami')
.description('Displays info about the current user')
.action(async () => {
if (!client.isAuthenticated) {
logger.log('Not signed in')
return
}
AuthStore.requireAuth()
const res = await client.getMe()
logger.log(res)

Wyświetl plik

@ -4,6 +4,7 @@ import { AgenticApiClient } from '@agentic/platform-api-client'
import { Command } from 'commander'
import restoreCursor from 'restore-cursor'
import { registerDeployCommand } from './commands/deploy'
import { registerSigninCommand } from './commands/signin'
import { registerSignoutCommand } from './commands/signout'
import { registerWhoAmICommand } from './commands/whoami'
@ -50,6 +51,9 @@ async function main() {
} else {
console.log(...args)
}
},
error: (...args: any[]) => {
console.error(...args)
}
}
@ -63,6 +67,7 @@ async function main() {
registerSigninCommand(ctx)
registerWhoAmICommand(ctx)
registerSignoutCommand(ctx)
registerDeployCommand(ctx)
program.parse()
}

Wyświetl plik

@ -8,7 +8,7 @@ export async function loadAgenticConfig({
cwd
}: {
cwd?: string
}): Promise<AgenticProjectConfig> {
} = {}): Promise<AgenticProjectConfig> {
const { config } = await loadConfig({
cwd,
sources: [

Wyświetl plik

@ -0,0 +1,35 @@
import type { AgenticApiClient, Deployment } from '@agentic/platform-api-client'
import { loadAgenticConfig } from './load-agentic-config'
import { AuthStore } from './store'
export async function resolveDeployment({
client,
deploymentIdentifier,
fuzzyDeploymentIdentifierVersion = 'latest',
cwd,
populate
}: {
client: AgenticApiClient
deploymentIdentifier?: string
fuzzyDeploymentIdentifierVersion?: 'dev' | 'latest'
cwd?: string
populate?: ('user' | 'team' | 'project')[]
}): Promise<Deployment> {
if (!deploymentIdentifier) {
const config = await loadAgenticConfig({ cwd })
// TODO: team support
const auth = AuthStore.getAuth()
const namespace = auth.user.username
deploymentIdentifier = `${namespace}/${config.name}@${fuzzyDeploymentIdentifierVersion}`
}
const deployment = await client.getDeploymentByIdentifier({
deploymentIdentifier,
populate
})
return deployment
}

Wyświetl plik

@ -17,7 +17,7 @@ export const AuthStore = {
requireAuth() {
assert(
this.isAuthenticated(),
'Command requires authentication. Please login first.'
'This command requires authentication. Please login first.'
)
},

Wyświetl plik

@ -6,6 +6,7 @@ export type Context = {
program: Command
logger: {
log: (...args: any[]) => void
error: (...args: any[]) => void
}
}

Wyświetl plik

@ -28,10 +28,12 @@
"@agentic/platform-validators": "workspace:*",
"@hono/zod-openapi": "^0.19.6",
"ms": "^2.1.3",
"semver": "^7.7.2",
"zod": "catalog:"
},
"devDependencies": {
"@types/ms": "^2.1.0",
"@types/semver": "^7.7.0",
"restore-cursor": "catalog:",
"zod-to-json-schema": "^3.24.5"
},

Wyświetl plik

@ -17,6 +17,22 @@ exports[`rateLimitSchema invalid 1`] = `
`;
exports[`rateLimitSchema invalid 2`] = `
[ZodError: [
{
"code": "too_small",
"minimum": 0,
"type": "number",
"inclusive": false,
"exact": false,
"message": "Number must be greater than 0",
"path": [
"interval"
]
}
]]
`;
exports[`rateLimitSchema invalid 3`] = `
[ZodError: [
{
"code": "custom",
@ -29,7 +45,7 @@ exports[`rateLimitSchema invalid 2`] = `
]]
`;
exports[`rateLimitSchema invalid 3`] = `
exports[`rateLimitSchema invalid 4`] = `
[ZodError: [
{
"code": "too_small",

Wyświetl plik

@ -41,6 +41,17 @@ export const agenticProjectConfigSchema = z
*/
name: z.string().nonempty().describe('Name of the project.'),
/**
* Optional semantic version of the project as a semver string.
*/
version: z
.string()
.nonempty()
.describe(
'Optional semantic version of the project as a semver string. Ex: 1.0.0, 0.0.1, 5.0.1, etc.'
)
.optional(),
/** Optional short description of the project. */
description: z
.string()
@ -70,10 +81,10 @@ export const agenticProjectConfigSchema = z
sourceUrl: z
.string()
.url()
.optional()
.describe(
'Optional URL to the source code of the project (eg, GitHub repo).'
),
)
.optional(),
/** Required origin API HTTPS base URL */
originUrl: z.string().url()
@ -81,7 +92,14 @@ export const agenticProjectConfigSchema = z
NOTE: Agentic currently only supports \`external\` API servers. If you'd like to host your API or MCP server on Agentic's infrastructure, please reach out to support@agentic.so.`),
/** Optional origin API config */
/**
* Optional deployment origin API adapter used to configure the origin API
* server downstream from Agentic's API gateway. It specifies whether the
* origin API server denoted by \`originUrl\` is hosted externally or deployed
* internally to Agentic's infrastructure. It also specifies the format
* for how origin tools / services are defined: either as an OpenAPI spec,
* an MCP server, or as a raw HTTP REST API.
*/
originAdapter: deploymentOriginAdapterSchema.optional().default({
location: 'external',
type: 'raw'
@ -90,7 +108,7 @@ NOTE: Agentic currently only supports \`external\` API servers. If you'd like to
/** Optional subscription pricing config */
pricingPlans: pricingPlanListSchema
.describe(
'List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing.your project.'
'List of PricingPlans configuring which Stripe subscriptions should be available for the project. Defaults to a single free plan which is useful for developing and testing your project.'
)
.optional()
.default([defaultFreePricingPlan]),

Wyświetl plik

@ -1,6 +1,7 @@
export * from './agentic-project-config-schema'
export * from './define-config'
export * from './schemas'
export * from './subjects'
export * from './utils'
export * from './validate-agentic-project-config'
export * from './validate-origin-adapter'

Wyświetl plik

@ -119,7 +119,6 @@ export const pricingIntervalListSchema = z
message: 'Must contain at least one pricing interval'
})
.describe('List of billing intervals for subscriptions.')
.openapi('PricingIntervalList')
/**
* Internal PricingPlanLineItem hash

Wyświetl plik

@ -0,0 +1,9 @@
import { z } from 'zod'
export const subjectSchemas = {
user: z.object({
id: z.string(),
username: z.string()
})
}
export type AuthUser = z.infer<(typeof subjectSchemas)['user']>

Wyświetl plik

@ -1,6 +1,7 @@
import type { ZodTypeDef } from 'zod'
import { assert, type Logger, parseZodSchema } from '@agentic/platform-core'
import { validators } from '@agentic/platform-validators'
import { clean as cleanSemver, valid as isValidSemver } from 'semver'
import type { PricingPlanLineItem } from './schemas'
import {
@ -57,6 +58,22 @@ export async function validateAgenticProjectConfig(
})
}
if (config.version) {
const version = cleanSemver(config.version)
assert(
version,
`Invalid semver version "${config.version}" for project "${name}"`
)
assert(
isValidSemver(version),
`Invalid semver version "${version}" for project "${name}"`
)
// Update the config with the normalized semver version
config.version = version
}
{
// Validate pricing interval
const pricingIntervalsSet = new Set(pricingIntervals)

Wyświetl plik

@ -1,5 +1,244 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`username/project-name success 1`] = `
{
"projectIdentifier": "abc/hello-world",
"servicePath": "/",
"version": "latest",
}
`;
exports[`username/project-name success 2`] = `
{
"projectIdentifier": "a16z/foo-bar",
"servicePath": "/",
"version": "latest",
}
`;
exports[`username/project-name success 3`] = `
{
"projectIdentifier": "foodoo/foo-bar",
"servicePath": "/",
"version": "latest",
}
`;
exports[`username/project-name success 4`] = `
{
"projectIdentifier": "u/foobar123-yo",
"servicePath": "/",
"version": "latest",
}
`;
exports[`username/project-name success 5`] = `
{
"projectIdentifier": "abc/hello-world",
"servicePath": "/",
"version": "latest",
}
`;
exports[`username/project-name/servicePath success 1`] = `
{
"projectIdentifier": "u/foo-bar",
"servicePath": "/foo",
"version": "latest",
}
`;
exports[`username/project-name/servicePath success 2`] = `
{
"projectIdentifier": "a/foo-bar",
"servicePath": "/foo_123",
"version": "latest",
}
`;
exports[`username/project-name/servicePath success 3`] = `
{
"projectIdentifier": "foo/foobar123-yo",
"servicePath": "/foo_bar_BAR_901",
"version": "latest",
}
`;
exports[`username/project-name/servicePath success 4`] = `
{
"projectIdentifier": "foo/foobar123-yo",
"servicePath": "/foo/bar/123/456",
"version": "latest",
}
`;
exports[`username/project-name@deployment success 1`] = `
{
"deploymentHash": "3d2e0fd5",
"deploymentIdentifier": "abc/hello-world@3d2e0fd5",
"projectIdentifier": "abc/hello-world",
"servicePath": "/",
}
`;
exports[`username/project-name@deployment success 2`] = `
{
"projectIdentifier": "a16z/foo-bar",
"servicePath": "/",
"version": "f673db32c",
}
`;
exports[`username/project-name@deployment success 3`] = `
{
"projectIdentifier": "foodoo/foo-bar",
"servicePath": "/",
"version": "f673db32c",
}
`;
exports[`username/project-name@deployment success 4`] = `
{
"deploymentHash": "673db32c",
"deploymentIdentifier": "u/foobar123-yo@673db32c",
"projectIdentifier": "u/foobar123-yo",
"servicePath": "/",
}
`;
exports[`username/project-name@deployment success 5`] = `
{
"deploymentHash": "01234567",
"deploymentIdentifier": "username/foo-bar@01234567",
"projectIdentifier": "username/foo-bar",
"servicePath": "/",
}
`;
exports[`username/project-name@deployment/servicePath success 1`] = `
{
"deploymentHash": "01234567",
"deploymentIdentifier": "username/foo-bar@01234567",
"projectIdentifier": "username/foo-bar",
"servicePath": "/foo",
}
`;
exports[`username/project-name@deployment/servicePath success 2`] = `
{
"deploymentHash": "abc123lz",
"deploymentIdentifier": "username/foo-bar@abc123lz",
"projectIdentifier": "username/foo-bar",
"servicePath": "/foo",
}
`;
exports[`username/project-name@deployment/servicePath success 3`] = `
{
"deploymentHash": "01234567",
"deploymentIdentifier": "username/foobar123-yo@01234567",
"projectIdentifier": "username/foobar123-yo",
"servicePath": "/foo_bar_BAR_901",
}
`;
exports[`username/project-name@deployment/servicePath success 4`] = `
{
"deploymentHash": "01234567",
"deploymentIdentifier": "username/foobar@01234567",
"projectIdentifier": "username/foobar",
"servicePath": "/foo/bar/123/456",
}
`;
exports[`username/project-name@version success 1`] = `
{
"projectIdentifier": "abc/hello-world",
"servicePath": "/",
"version": "1.0.3",
}
`;
exports[`username/project-name@version success 2`] = `
{
"projectIdentifier": "a16z/foo-bar",
"servicePath": "/",
"version": "latest",
}
`;
exports[`username/project-name@version success 3`] = `
{
"projectIdentifier": "a16z/foo-bar",
"servicePath": "/",
"version": "dev",
}
`;
exports[`username/project-name@version success 4`] = `
{
"projectIdentifier": "foodoo/foo-bar",
"servicePath": "/",
"version": "1.0.1",
}
`;
exports[`username/project-name@version success 5`] = `
{
"projectIdentifier": "u/foobar123-yo",
"servicePath": "/",
"version": "3.2.2234",
}
`;
exports[`username/project-name@version success 6`] = `
{
"projectIdentifier": "username/foo-bar",
"servicePath": "/",
"version": "1.0.3",
}
`;
exports[`username/project-name@version/servicePath success 1`] = `
{
"projectIdentifier": "username/foo-bar",
"servicePath": "/foo",
"version": "latest",
}
`;
exports[`username/project-name@version/servicePath success 2`] = `
{
"projectIdentifier": "username/foo-bar",
"servicePath": "/foo",
"version": "dev",
}
`;
exports[`username/project-name@version/servicePath success 3`] = `
{
"projectIdentifier": "username/foo-bar",
"servicePath": "/foo",
"version": "1.0.0",
}
`;
exports[`username/project-name@version/servicePath success 4`] = `
{
"projectIdentifier": "username/foobar123-yo",
"servicePath": "/foo_bar_BAR_901",
"version": "0.0.1",
}
`;
exports[`username/project-name@version/servicePath success 5`] = `
{
"projectIdentifier": "username/foobar123-yo",
"servicePath": "/foo/bar/123-456",
"version": "0.0.1",
}
`;
exports[`username/projectName success 1`] = `
{
"projectIdentifier": "abc/hello-world",

Wyświetl plik

@ -15,14 +15,14 @@ function error(value: string) {
expect(result).toBeUndefined()
}
test('username/projectName@deployment/servicePath success', () => {
test('username/project-name@deployment/servicePath success', () => {
success('username/foo-bar@01234567/foo')
success('username/foo-bar@abc123lz/foo')
success('username/foobar123-yo@01234567/foo_bar_BAR_901')
success('username/foobar@01234567/foo/bar/123/456')
})
test('username/projectName@deployment/servicePath error', () => {
test('username/project-name@deployment/servicePath error', () => {
error('foo-bar@01234567/foo')
error('%/foo-bar@01234567/foo')
error('user/foo^bar@01234567/foo')
@ -30,7 +30,7 @@ test('username/projectName@deployment/servicePath error', () => {
error('username/Foo-Bar@01234567/foo')
})
test('username/projectName@version/servicePath success', () => {
test('username/project-name@version/servicePath success', () => {
success('username/foo-bar@latest/foo')
success('username/foo-bar@dev/foo')
success('username/foo-bar@1.0.0/foo')
@ -38,7 +38,7 @@ test('username/projectName@version/servicePath success', () => {
success('username/foobar123-yo@0.0.1/foo/bar/123-456')
})
test('username/projectName@version/servicePath error', () => {
test('username/project-name@version/servicePath error', () => {
error('foo_bar@latest/foo')
error('username/foo-bar@1.0.0/foo@')
error('username/foo-bar@/foo')
@ -46,14 +46,14 @@ test('username/projectName@version/servicePath error', () => {
error('username/fooBar123-yo@0.0.1/foo/bar/123-456')
})
test('username/projectName/servicePath success', () => {
test('username/project-name/servicePath success', () => {
success('u/foo-bar/foo')
success('a/foo-bar/foo_123')
success('foo/foobar123-yo/foo_bar_BAR_901')
success('foo/foobar123-yo/foo/bar/123/456')
})
test('username/projectName/servicePath error', () => {
test('username/project-name/servicePath error', () => {
error('@/foo_bar/foo')
error('foo-bar/foo\\/')
error('user/_/foo')
@ -61,7 +61,7 @@ test('username/projectName/servicePath error', () => {
error('u/FOO-bar/foo')
})
test('username/projectName@deployment success', () => {
test('username/project-name@deployment success', () => {
success('abc/hello-world@3d2e0fd5')
success('a16z/foo-bar@f673db32c')
success('foodoo/foo-bar@f673db32c')
@ -69,7 +69,7 @@ test('username/projectName@deployment success', () => {
success('username/foo-bar@01234567/')
})
test('username/projectName@deployment error', () => {
test('username/project-name@deployment error', () => {
error('/hello-world@3d2e0fd5')
error('foo-bar@f673db32c')
error('foodoo/foo@bar@f673db32c')
@ -77,7 +77,7 @@ test('username/projectName@deployment error', () => {
error('abc/Hello-World@3d2e0fd5')
})
test('username/projectName@version success', () => {
test('username/project-name@version success', () => {
success('abc/hello-world@1.0.3')
success('a16z/foo-bar@latest')
success('a16z/foo-bar@dev')
@ -86,7 +86,7 @@ test('username/projectName@version success', () => {
success('username/foo-bar@1.0.3/')
})
test('username/projectName@version error', () => {
test('username/project-name@version error', () => {
error('/hello-world@3d2e0fd5')
error('foo-bar@f673db32c')
error('foodoo/foo@bar@f673db32c@')
@ -94,7 +94,7 @@ test('username/projectName@version error', () => {
error('abc/hello-World@1.0.3')
})
test('username/projectName success', () => {
test('username/project-name success', () => {
success('abc/hello-world')
success('a16z/foo-bar')
success('foodoo/foo-bar')
@ -102,7 +102,7 @@ test('username/projectName success', () => {
success('abc/hello-world/')
})
test('username/projectName error', () => {
test('username/project-name error', () => {
error('/hello-world')
error('foo-barc')
error('foodoo/foo@bar@')

Wyświetl plik

@ -3,17 +3,17 @@
import type { ParsedFaasIdentifier } from './types'
// namespace/projectName@deploymentHash/servicePath
// namespace/project-name@deploymentHash/servicePath
// project@deploymentHash/servicePath
const projectDeploymentServiceRe =
/^([a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64})@([a-z0-9]{8})(\/[a-zA-Z0-9\-._~%!$&'()*+,;=:/]*)?$/
// namespace/projectName@version/servicePath
// namespace/project-name@version/servicePath
// project@version/servicePath
const projectVersionServiceRe =
/^([a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64})@([^/?@]+)(\/[a-zA-Z0-9\-._~%!$&'()*+,;=:/]*)?$/
// namespace/projectName/servicePath
// namespace/project-name/servicePath
// project/servicePath (latest version)
const projectServiceRe =
/^([a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64})(\/[a-zA-Z0-9\-._~%!$&'()*+,;=:/]*)?$/

Wyświetl plik

@ -89,6 +89,9 @@ importers:
eslint-plugin-drizzle:
specifier: ^0.2.3
version: 0.2.3(eslint@9.26.0(jiti@2.4.2))
knip:
specifier: ^5.58.1
version: 5.58.1(@types/node@22.15.18)(typescript@5.8.3)
lint-staged:
specifier: 'catalog:'
version: 16.0.0
@ -213,6 +216,9 @@ importers:
'@agentic/platform-core':
specifier: workspace:*
version: link:../core
'@agentic/platform-schemas':
specifier: workspace:*
version: link:../schemas
'@openauthjs/openauth':
specifier: ^0.4.3
version: 0.4.3(arctic@2.3.4)(hono@4.7.9)
@ -241,6 +247,9 @@ importers:
'@agentic/platform-schemas':
specifier: workspace:*
version: link:../schemas
'@clack/prompts':
specifier: ^0.11.0
version: 0.11.0
'@hono/node-server':
specifier: ^1.14.1
version: 1.14.1(hono@4.7.9)
@ -346,6 +355,9 @@ importers:
ms:
specifier: ^2.1.3
version: 2.1.3
semver:
specifier: ^7.7.2
version: 7.7.2
zod:
specifier: 'catalog:'
version: 3.24.4
@ -353,6 +365,9 @@ importers:
'@types/ms':
specifier: ^2.1.0
version: 2.1.0
'@types/semver':
specifier: ^7.7.0
version: 7.7.0
restore-cursor:
specifier: 'catalog:'
version: 5.1.0
@ -391,6 +406,12 @@ packages:
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
engines: {node: '>=6.9.0'}
'@clack/core@0.5.0':
resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==}
'@clack/prompts@0.11.0':
resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==}
'@commander-js/extra-typings@14.0.0':
resolution: {integrity: sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==}
peerDependencies:
@ -399,6 +420,15 @@ packages:
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
'@emnapi/core@1.4.3':
resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==}
'@emnapi/runtime@1.4.3':
resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
'@emnapi/wasi-threads@1.0.2':
resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==}
'@esbuild-kit/core-utils@3.3.2':
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
deprecated: 'Merged into tsx: https://tsx.is'
@ -910,6 +940,9 @@ packages:
resolution: {integrity: sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==}
engines: {node: '>=18'}
'@napi-rs/wasm-runtime@0.2.10':
resolution: {integrity: sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==}
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
@ -1245,6 +1278,71 @@ packages:
'@oslojs/jwt@0.2.0':
resolution: {integrity: sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==}
'@oxc-resolver/binding-darwin-arm64@9.0.2':
resolution: {integrity: sha512-MVyRgP2gzJJtAowjG/cHN3VQXwNLWnY+FpOEsyvDepJki1SdAX/8XDijM1yN6ESD1kr9uhBKjGelC6h3qtT+rA==}
cpu: [arm64]
os: [darwin]
'@oxc-resolver/binding-darwin-x64@9.0.2':
resolution: {integrity: sha512-7kV0EOFEZ3sk5Hjy4+bfA6XOQpCwbDiDkkHN4BHHyrBHsXxUR05EcEJPPL1WjItefg+9+8hrBmoK0xRoDs41+A==}
cpu: [x64]
os: [darwin]
'@oxc-resolver/binding-freebsd-x64@9.0.2':
resolution: {integrity: sha512-6OvkEtRXrt8sJ4aVfxHRikjain9nV1clIsWtJ1J3J8NG1ZhjyJFgT00SCvqxbK+pzeWJq6XzHyTCN78ML+lY2w==}
cpu: [x64]
os: [freebsd]
'@oxc-resolver/binding-linux-arm-gnueabihf@9.0.2':
resolution: {integrity: sha512-aYpNL6o5IRAUIdoweW21TyLt54Hy/ZS9tvzNzF6ya1ckOQ8DLaGVPjGpmzxdNja9j/bbV6aIzBH7lNcBtiOTkQ==}
cpu: [arm]
os: [linux]
'@oxc-resolver/binding-linux-arm64-gnu@9.0.2':
resolution: {integrity: sha512-RGFW4vCfKMFEIzb9VCY0oWyyY9tR1/o+wDdNePhiUXZU4SVniRPQaZ1SJ0sUFI1k25pXZmzQmIP6cBmazi/Dew==}
cpu: [arm64]
os: [linux]
'@oxc-resolver/binding-linux-arm64-musl@9.0.2':
resolution: {integrity: sha512-lxx/PibBfzqYvut2Y8N2D0Ritg9H8pKO+7NUSJb9YjR/bfk2KRmP8iaUz3zB0JhPtf/W3REs65oKpWxgflGToA==}
cpu: [arm64]
os: [linux]
'@oxc-resolver/binding-linux-riscv64-gnu@9.0.2':
resolution: {integrity: sha512-yD28ptS/OuNhwkpXRPNf+/FvrO7lwURLsEbRVcL1kIE0GxNJNMtKgIE4xQvtKDzkhk6ZRpLho5VSrkkF+3ARTQ==}
cpu: [riscv64]
os: [linux]
'@oxc-resolver/binding-linux-s390x-gnu@9.0.2':
resolution: {integrity: sha512-WBwEJdspoga2w+aly6JVZeHnxuPVuztw3fPfWrei2P6rNM5hcKxBGWKKT6zO1fPMCB4sdDkFohGKkMHVV1eryQ==}
cpu: [s390x]
os: [linux]
'@oxc-resolver/binding-linux-x64-gnu@9.0.2':
resolution: {integrity: sha512-a2z3/cbOOTUq0UTBG8f3EO/usFcdwwXnCejfXv42HmV/G8GjrT4fp5+5mVDoMByH3Ce3iVPxj1LmS6OvItKMYQ==}
cpu: [x64]
os: [linux]
'@oxc-resolver/binding-linux-x64-musl@9.0.2':
resolution: {integrity: sha512-bHZF+WShYQWpuswB9fyxcgMIWVk4sZQT0wnwpnZgQuvGTZLkYJ1JTCXJMtaX5mIFHf69ngvawnwPIUA4Feil0g==}
cpu: [x64]
os: [linux]
'@oxc-resolver/binding-wasm32-wasi@9.0.2':
resolution: {integrity: sha512-I5cSgCCh5nFozGSHz+PjIOfrqW99eUszlxKLgoNNzQ1xQ2ou9ZJGzcZ94BHsM9SpyYHLtgHljmOZxCT9bgxYNA==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@oxc-resolver/binding-win32-arm64-msvc@9.0.2':
resolution: {integrity: sha512-5IhoOpPr38YWDWRCA5kP30xlUxbIJyLAEsAK7EMyUgqygBHEYLkElaKGgS0X5jRXUQ6l5yNxuW73caogb2FYaw==}
cpu: [arm64]
os: [win32]
'@oxc-resolver/binding-win32-x64-msvc@9.0.2':
resolution: {integrity: sha512-Qc40GDkaad9rZksSQr2l/V9UubigIHsW69g94Gswc2sKYB3XfJXfIfyV8WTJ67u6ZMXsZ7BH1msSC6Aen75mCg==}
cpu: [x64]
os: [win32]
'@paralleldrive/cuid2@2.2.2':
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
@ -1415,6 +1513,9 @@ packages:
'@total-typescript/ts-reset@0.6.1':
resolution: {integrity: sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==}
'@tybys/wasm-util@0.9.0':
resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
'@types/aws-lambda@8.10.149':
resolution: {integrity: sha512-NXSZIhfJjnXqJgtS7IwutqIF/SOy1Wz5Px4gUY1RWITp3AYTyuJS4xaXr/bIJY1v15XMzrJ5soGnPM+7uigZjA==}
@ -2401,6 +2502,9 @@ packages:
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
fd-package-json@1.2.0:
resolution: {integrity: sha512-45LSPmWf+gC5tdCQMNH4s9Sr00bIkiD9aN7dc5hqkrEw1geRYyDQS1v1oMHAW3ysfxfndqGsrDREHHjNNbKUfA==}
fdir@6.4.4:
resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
peerDependencies:
@ -2444,6 +2548,11 @@ packages:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
formatly@0.2.3:
resolution: {integrity: sha512-WH01vbXEjh9L3bqn5V620xUAWs32CmK4IzWRRY6ep5zpa/mrisL4d9+pRVuETORVDTQw8OycSO1WC68PL51RaA==}
engines: {node: '>=18.3.0'}
hasBin: true
forwarded-parse@2.1.2:
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
@ -2883,6 +2992,14 @@ packages:
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
knip@5.58.1:
resolution: {integrity: sha512-9FKnuGhGFZ8wDgcjEYQ6btMmtGHa7dkvNU2ZHuMcv2NaxL7xDeFXZKjETbaiJBjWnLytrDXrh7a58lGXcE0Zfw==}
engines: {node: '>=18.18.0'}
hasBin: true
peerDependencies:
'@types/node': '>=18'
typescript: '>=5.0.4'
ky@1.8.1:
resolution: {integrity: sha512-7Bp3TpsE+L+TARSnnDpk3xg8Idi8RwSLdj6CMbNWoOARIrGrbuLGusV0dYwbZOm4bB3jHNxSw8Wk/ByDqJEnDw==}
engines: {node: '>=18'}
@ -3160,6 +3277,9 @@ packages:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
oxc-resolver@9.0.2:
resolution: {integrity: sha512-w838ygc1p7rF+7+h5vR9A+Y9Fc4imy6C3xPthCMkdFUgFvUWkmABeNB8RBDQ6+afk44Q60/UMMQ+gfDUW99fBA==}
p-all@5.0.0:
resolution: {integrity: sha512-pofqu/1FhCVa+78xNAptCGc9V45exFz2pvBRyIvgXkNM0Rh18Py7j8pQuSjA+zpabI46v9hRjNWmL9EAFcEbpw==}
engines: {node: '>=16'}
@ -3558,6 +3678,9 @@ packages:
resolution: {integrity: sha512-N+goiLxlkHJlyaYEglFypzVNMaNplPAk5syu0+OPp/Bk6dwVoXF6FfOw2vO0Dp+JHsBaI+w6cm8TnFl2Hw6tDA==}
hasBin: true
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
slash@5.1.0:
resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
engines: {node: '>=14.16'}
@ -3570,6 +3693,10 @@ packages:
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
engines: {node: '>=18'}
smol-toml@1.3.4:
resolution: {integrity: sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==}
engines: {node: '>= 18'}
sort-keys@5.1.0:
resolution: {integrity: sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==}
engines: {node: '>=12'}
@ -3677,6 +3804,10 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
strip-json-comments@5.0.1:
resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==}
engines: {node: '>=14.16'}
stripe@18.1.0:
resolution: {integrity: sha512-MLDiniPTHqcfIT3anyBPmOEcaiDhYa7/jRaNypQ3Rt2SJnayQZBvVbFghIziUCZdltGAndm/ZxVOSw6uuSCDig==}
engines: {node: '>=12.*'}
@ -4027,6 +4158,9 @@ packages:
jsdom:
optional: true
walk-up-path@3.0.1:
resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==}
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
@ -4154,12 +4288,39 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
'@clack/core@0.5.0':
dependencies:
picocolors: 1.1.1
sisteransi: 1.0.5
'@clack/prompts@0.11.0':
dependencies:
'@clack/core': 0.5.0
picocolors: 1.1.1
sisteransi: 1.0.5
'@commander-js/extra-typings@14.0.0(commander@14.0.0)':
dependencies:
commander: 14.0.0
'@drizzle-team/brocli@0.10.2': {}
'@emnapi/core@1.4.3':
dependencies:
'@emnapi/wasi-threads': 1.0.2
tslib: 2.8.1
optional: true
'@emnapi/runtime@1.4.3':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/wasi-threads@1.0.2':
dependencies:
tslib: 2.8.1
optional: true
'@esbuild-kit/core-utils@3.3.2':
dependencies:
esbuild: 0.18.20
@ -4483,6 +4644,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@napi-rs/wasm-runtime@0.2.10':
dependencies:
'@emnapi/core': 1.4.3
'@emnapi/runtime': 1.4.3
'@tybys/wasm-util': 0.9.0
optional: true
'@noble/hashes@1.8.0': {}
'@nodelib/fs.scandir@2.1.5':
@ -4913,6 +5081,47 @@ snapshots:
dependencies:
'@oslojs/encoding': 0.4.1
'@oxc-resolver/binding-darwin-arm64@9.0.2':
optional: true
'@oxc-resolver/binding-darwin-x64@9.0.2':
optional: true
'@oxc-resolver/binding-freebsd-x64@9.0.2':
optional: true
'@oxc-resolver/binding-linux-arm-gnueabihf@9.0.2':
optional: true
'@oxc-resolver/binding-linux-arm64-gnu@9.0.2':
optional: true
'@oxc-resolver/binding-linux-arm64-musl@9.0.2':
optional: true
'@oxc-resolver/binding-linux-riscv64-gnu@9.0.2':
optional: true
'@oxc-resolver/binding-linux-s390x-gnu@9.0.2':
optional: true
'@oxc-resolver/binding-linux-x64-gnu@9.0.2':
optional: true
'@oxc-resolver/binding-linux-x64-musl@9.0.2':
optional: true
'@oxc-resolver/binding-wasm32-wasi@9.0.2':
dependencies:
'@napi-rs/wasm-runtime': 0.2.10
optional: true
'@oxc-resolver/binding-win32-arm64-msvc@9.0.2':
optional: true
'@oxc-resolver/binding-win32-x64-msvc@9.0.2':
optional: true
'@paralleldrive/cuid2@2.2.2':
dependencies:
'@noble/hashes': 1.8.0
@ -5084,6 +5293,11 @@ snapshots:
'@total-typescript/ts-reset@0.6.1': {}
'@tybys/wasm-util@0.9.0':
dependencies:
tslib: 2.8.1
optional: true
'@types/aws-lambda@8.10.149': {}
'@types/connect@3.4.38':
@ -6221,6 +6435,10 @@ snapshots:
dependencies:
reusify: 1.1.0
fd-package-json@1.2.0:
dependencies:
walk-up-path: 3.0.1
fdir@6.4.4(picomatch@4.0.2):
optionalDependencies:
picomatch: 4.0.2
@ -6267,6 +6485,10 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
formatly@0.2.3:
dependencies:
fd-package-json: 1.2.0
forwarded-parse@2.1.2: {}
forwarded@0.2.0: {}
@ -6688,6 +6910,24 @@ snapshots:
dependencies:
json-buffer: 3.0.1
knip@5.58.1(@types/node@22.15.18)(typescript@5.8.3):
dependencies:
'@nodelib/fs.walk': 1.2.8
'@types/node': 22.15.18
fast-glob: 3.3.3
formatly: 0.2.3
jiti: 2.4.2
js-yaml: 4.1.0
minimist: 1.2.8
oxc-resolver: 9.0.2
picocolors: 1.1.1
picomatch: 4.0.2
smol-toml: 1.3.4
strip-json-comments: 5.0.1
typescript: 5.8.3
zod: 3.24.4
zod-validation-error: 3.4.1(zod@3.24.4)
ky@1.8.1: {}
kysely@0.28.2:
@ -7000,6 +7240,22 @@ snapshots:
object-keys: 1.1.1
safe-push-apply: 1.0.0
oxc-resolver@9.0.2:
optionalDependencies:
'@oxc-resolver/binding-darwin-arm64': 9.0.2
'@oxc-resolver/binding-darwin-x64': 9.0.2
'@oxc-resolver/binding-freebsd-x64': 9.0.2
'@oxc-resolver/binding-linux-arm-gnueabihf': 9.0.2
'@oxc-resolver/binding-linux-arm64-gnu': 9.0.2
'@oxc-resolver/binding-linux-arm64-musl': 9.0.2
'@oxc-resolver/binding-linux-riscv64-gnu': 9.0.2
'@oxc-resolver/binding-linux-s390x-gnu': 9.0.2
'@oxc-resolver/binding-linux-x64-gnu': 9.0.2
'@oxc-resolver/binding-linux-x64-musl': 9.0.2
'@oxc-resolver/binding-wasm32-wasi': 9.0.2
'@oxc-resolver/binding-win32-arm64-msvc': 9.0.2
'@oxc-resolver/binding-win32-x64-msvc': 9.0.2
p-all@5.0.0:
dependencies:
p-map: 6.0.0
@ -7413,6 +7669,8 @@ snapshots:
simple-git-hooks@2.13.0: {}
sisteransi@1.0.5: {}
slash@5.1.0: {}
slice-ansi@5.0.0:
@ -7425,6 +7683,8 @@ snapshots:
ansi-styles: 6.2.1
is-fullwidth-code-point: 5.0.0
smol-toml@1.3.4: {}
sort-keys@5.1.0:
dependencies:
is-plain-obj: 4.1.0
@ -7554,6 +7814,8 @@ snapshots:
strip-json-comments@3.1.1: {}
strip-json-comments@5.0.1: {}
stripe@18.1.0(@types/node@22.15.18):
dependencies:
qs: 6.14.0
@ -7903,6 +8165,8 @@ snapshots:
- tsx
- yaml
walk-up-path@3.0.1: {}
wcwidth@1.0.1:
dependencies:
defaults: 1.0.4