@mastra/factory 0.8.0-alpha.14 → 0.8.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +30 -2
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +8 -0
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +3 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/reconcile-worker.d.ts +5 -0
- package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
- package/dist/integrations/github/reconcile-worker.js +7 -0
- package/dist/integrations/github/reconcile-worker.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +7 -3
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +31 -1
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +12 -6
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +7 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +32 -3
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/routes/config.d.ts +8 -6
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +91 -56
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/projects.d.ts +9 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +8 -0
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/surface.d.ts +5 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +2 -0
- package/dist/routes/surface.js.map +1 -1
- package/dist/sandbox/base-checkpoint-triggers.d.ts +77 -0
- package/dist/sandbox/base-checkpoint-triggers.d.ts.map +1 -0
- package/dist/sandbox/base-checkpoint-triggers.js +137 -0
- package/dist/sandbox/base-checkpoint-triggers.js.map +1 -0
- package/dist/sandbox/base-checkpoint.d.ts +59 -0
- package/dist/sandbox/base-checkpoint.d.ts.map +1 -0
- package/dist/sandbox/base-checkpoint.js +145 -0
- package/dist/sandbox/base-checkpoint.js.map +1 -0
- package/dist/sandbox/fleet.d.ts +32 -0
- package/dist/sandbox/fleet.d.ts.map +1 -1
- package/dist/sandbox/fleet.js +39 -10
- package/dist/sandbox/fleet.js.map +1 -1
- package/dist/session/checkpoint-capture.d.ts +3 -2
- package/dist/session/checkpoint-capture.d.ts.map +1 -1
- package/dist/session/checkpoint-capture.js +3 -3
- package/dist/session/checkpoint-capture.js.map +1 -1
- package/dist/session/model-pack-hydration.d.ts +55 -0
- package/dist/session/model-pack-hydration.d.ts.map +1 -0
- package/dist/session/model-pack-hydration.js +56 -0
- package/dist/session/model-pack-hydration.js.map +1 -0
- package/dist/storage/domains/model-packs/base.d.ts +26 -0
- package/dist/storage/domains/model-packs/base.d.ts.map +1 -1
- package/dist/storage/domains/model-packs/base.js +102 -13
- package/dist/storage/domains/model-packs/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +28 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +70 -14
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +9 -1
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +8 -0
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/workspace.d.ts +31 -2
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +224 -53
- package/dist/workspace.js.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projects.js","names":["#versionControlIntegrationIds","#projects","#sourceControl","#handles","#project","#findConnection","#findProjectRepository","#repositoryPayload","#retireProjectRepositorySessions","#resolveTenant"],"sources":["../../src/routes/projects.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { SessionRetirementCoordinator } from '../sandbox/session-retirement.js';\nimport type {\n CreateFactoryProjectInput,\n FactoryProjectsStorage,\n UpdateFactoryProjectInput,\n} from '../storage/domains/projects/base.js';\nimport type {\n ProjectRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_NAME_LENGTH = 200;\nconst MAX_DESCRIPTION_LENGTH = 2_000;\nconst MAX_REPOSITORY_COMMAND_LENGTH = 2_000;\nconst MAX_BRANCH_LENGTH = 255;\nconst MAX_SANDBOX_PROVIDER_LENGTH = 100;\nconst MAX_SANDBOX_WORKDIR_LENGTH = 1_000;\nconst CONTROL_CHAR_RE = /[\\0-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/;\n\nfunction loose(context: unknown): Context {\n return context as Context;\n}\n\nasync function readJson(context: Context): Promise<unknown | undefined> {\n try {\n return await context.req.json();\n } catch {\n return undefined;\n }\n}\n\nfunction parseCreateInput(value: unknown): CreateFactoryProjectInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.name !== 'string') return null;\n const name = input.name.trim();\n if (!name || name.length > MAX_NAME_LENGTH) return null;\n if (input.description !== undefined && input.description !== null && typeof input.description !== 'string')\n return null;\n const description = typeof input.description === 'string' ? input.description.trim() || null : null;\n if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;\n return { name, description };\n}\n\nfunction parseUpdateInput(value: unknown): UpdateFactoryProjectInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateFactoryProjectInput = {};\n if (input.name !== undefined) {\n if (typeof input.name !== 'string') return null;\n const name = input.name.trim();\n if (!name || name.length > MAX_NAME_LENGTH) return null;\n patch.name = name;\n }\n if (input.description !== undefined) {\n if (input.description !== null && typeof input.description !== 'string') return null;\n const description = typeof input.description === 'string' ? input.description.trim() || null : null;\n if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;\n patch.description = description;\n }\n if (input.defaultModelId !== undefined) {\n const defaultModelId = parseOptionalString(input.defaultModelId, { maxLength: MAX_NAME_LENGTH, nullable: true });\n if (defaultModelId === false) return null;\n patch.defaultModelId = defaultModelId ?? null;\n }\n if (input.slackWorkItemsEnabled !== undefined) {\n if (typeof input.slackWorkItemsEnabled !== 'boolean') return null;\n patch.slackWorkItemsEnabled = input.slackWorkItemsEnabled;\n }\n if (input.autoRunEnabled !== undefined) {\n if (typeof input.autoRunEnabled !== 'boolean') return null;\n patch.autoRunEnabled = input.autoRunEnabled;\n }\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\nfunction parseConnectionInput(value: unknown): { integrationId: string; installationId: string } | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.integrationId !== 'string' || !input.integrationId.trim()) return null;\n if (typeof input.installationId !== 'string' || !UUID_RE.test(input.installationId)) return null;\n return { integrationId: input.integrationId.trim(), installationId: input.installationId };\n}\n\nfunction parseOptionalString(\n value: unknown,\n { maxLength, nullable = false }: { maxLength: number; nullable?: boolean },\n): string | null | undefined | false {\n if (value === undefined) return undefined;\n if (value === null) return nullable ? null : false;\n if (typeof value !== 'string') return false;\n const normalized = value.trim();\n if (!normalized) return nullable ? null : false;\n if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;\n return normalized;\n}\n\nfunction parseRepositoryLinkInput(value: unknown): {\n repositoryId: string;\n branch: string | null;\n sandboxProvider: string;\n sandboxWorkdir: string;\n setupCommand: string | null;\n teardownCommand: string | null;\n} | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.repositoryId !== 'string' || !UUID_RE.test(input.repositoryId)) return null;\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n typeof sandboxProvider !== 'string' ||\n typeof sandboxWorkdir !== 'string' ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n return {\n repositoryId: input.repositoryId,\n branch: branch ?? null,\n sandboxProvider,\n sandboxWorkdir,\n setupCommand: setupCommand ?? null,\n teardownCommand: teardownCommand ?? null,\n };\n}\n\nfunction parseRepositoryUpdateInput(value: unknown): UpdateProjectRepositoryInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateProjectRepositoryInput = {};\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n sandboxProvider === false ||\n sandboxProvider === null ||\n sandboxWorkdir === false ||\n sandboxWorkdir === null ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n if (branch !== undefined) patch.branch = branch;\n if (sandboxProvider !== undefined) patch.sandboxProvider = sandboxProvider;\n if (sandboxWorkdir !== undefined) patch.sandboxWorkdir = sandboxWorkdir;\n if (setupCommand !== undefined) patch.setupCommand = setupCommand;\n if (teardownCommand !== undefined) patch.teardownCommand = teardownCommand;\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\nexport interface ProjectRoutesDeps extends RouteDependencies {\n /** Factory projects domain backing the CRUD surface. */\n projects: FactoryProjectsStorage;\n /** Source-control domain the connection/repository routes fan out over. */\n sourceControl: SourceControlStorage;\n /** Integration ids allowed as source-control connection targets. */\n versionControlIntegrationIds?: string[];\n /** Shared lifecycle for retiring sessions before their owning records are deleted. */\n sessionRetirement?: SessionRetirementCoordinator;\n}\n\nexport class ProjectRoutes extends Route<ProjectRoutesDeps> {\n readonly #versionControlIntegrationIds: Set<string>;\n\n constructor(deps: ProjectRoutesDeps) {\n super(deps);\n this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);\n }\n\n async #projects(): Promise<FactoryProjectsStorage> {\n await this.deps.projects.ensureReady();\n return this.deps.projects;\n }\n\n async #sourceControl(): Promise<SourceControlStorage> {\n await this.deps.sourceControl.ensureReady();\n return this.deps.sourceControl;\n }\n\n async #handles(): Promise<SourceControlStorageHandle[]> {\n const storage = await this.#sourceControl();\n return [...this.#versionControlIntegrationIds].map(integrationId => storage.forIntegration(integrationId));\n }\n\n async #project(orgId: string, id: string) {\n return (await this.#projects()).get({ orgId, id });\n }\n\n async #findConnection({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const connection = await handle.connections.get({ orgId, id });\n if (connection?.factoryProjectId === projectId) return { handle, connection };\n }\n return null;\n }\n\n async #findProjectRepository({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const projectRepository = await handle.projectRepositories.get({ orgId, id });\n if (!projectRepository) continue;\n const connection = await handle.connections.get({ orgId, id: projectRepository.connectionId });\n if (connection?.factoryProjectId === projectId) return { handle, connection, projectRepository };\n }\n return null;\n }\n\n async #repositoryPayload(handle: SourceControlStorageHandle, orgId: string, projectRepository: ProjectRepository) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n return { ...projectRepository, repository };\n }\n\n async #retireProjectRepositorySessions(\n handle: SourceControlStorageHandle,\n orgId: string,\n projectRepositoryId: string,\n ): Promise<boolean> {\n const sessions = await handle.sessions.listByProjectRepository({ projectRepositoryId });\n if (sessions.length === 0) return true;\n if (!this.deps.sessionRetirement) return false;\n await this.deps.sessionRetirement.retireProjectRepositorySessions({\n sourceControl: handle,\n orgId,\n projectRepositoryId,\n });\n return true;\n }\n\n async #resolveTenant(context: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(context);\n const tenant = this.deps.auth.tenant(context);\n if (!tenant) return { response: context.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: context.json(\n { error: 'organization_required', message: 'Factory projects require an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/factory/projects', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const input = parseCreateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project' }, 400);\n const project = await (await this.#projects()).create({ orgId: tenant.orgId, userId: tenant.userId, input });\n return context.json({ project }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, id);\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n const input = parseUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project' }, 400);\n const project = await (await this.#projects()).update({ orgId: tenant.orgId, id, input });\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n if (!(await this.#project(tenant.orgId, id))) return context.json({ error: 'Project not found' }, 404);\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({ orgId: tenant.orgId, factoryProjectId: id })) {\n for (const projectRepository of await handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId: connection.id,\n })) {\n if (!(await this.#retireProjectRepositorySessions(handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await handle.connections.delete({ orgId: tenant.orgId, id: connection.id });\n }\n }\n await (await this.#projects()).delete({ orgId: tenant.orgId, id });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const connections = [];\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n })) {\n const installation = await handle.installations.get({\n orgId: tenant.orgId,\n id: connection.installationId,\n });\n // Skip orphaned connections whose installation was pruned (e.g.\n // the user uninstalled the GitHub App). Otherwise\n // `projectRepositories.list` throws `requireConnection` and the\n // whole endpoint 500s, which hangs the web UI on the page\n // loader for every project. New code cascade-deletes these on\n // installation removal, but this defensive skip lets already-\n // orphaned rows in existing databases self-heal on read.\n if (!installation) continue;\n const links = await handle.projectRepositories.list({ orgId: tenant.orgId, connectionId: connection.id });\n connections.push({\n ...connection,\n installation,\n repositories: await Promise.all(links.map(link => this.#repositoryPayload(handle, tenant.orgId, link))),\n });\n }\n }\n return context.json({ connections });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const input = parseConnectionInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_source_control_connection' }, 400);\n if (!this.#versionControlIntegrationIds.has(input.integrationId))\n return context.json({ error: 'Source-control integration not found' }, 404);\n const handle = (await this.#sourceControl()).forIntegration(input.integrationId);\n if (!(await handle.installations.get({ orgId: tenant.orgId, id: input.installationId })))\n return context.json({ error: 'Source-control installation not found' }, 404);\n const connection = await handle.connections.create({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n installationId: input.installationId,\n createdByUserId: tenant.userId,\n });\n return context.json({ connection }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n for (const projectRepository of await found.handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId,\n })) {\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await found.handle.connections.delete({ orgId: tenant.orgId, id: connectionId });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId/repositories', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n const input = parseRepositoryLinkInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const repository = await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId });\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n ...input,\n });\n return context.json(\n { projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) },\n 201,\n );\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n const input = parseRepositoryUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const projectRepository = await found.handle.projectRepositories.update({\n orgId: tenant.orgId,\n id: projectRepositoryId,\n input,\n });\n return context.json({\n projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository!),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepositoryId))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n await found.handle.projectRepositories.unlink({ orgId: tenant.orgId, id: projectRepositoryId });\n return context.body(null, 204);\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"projects.js","names":["#versionControlIntegrationIds","#projects","#sourceControl","#handles","#project","#findConnection","#findProjectRepository","#repositoryPayload","#retireProjectRepositorySessions","#resolveTenant"],"sources":["../../src/routes/projects.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { SessionRetirementCoordinator } from '../sandbox/session-retirement.js';\nimport type {\n CreateFactoryProjectInput,\n FactoryProjectsStorage,\n UpdateFactoryProjectInput,\n} from '../storage/domains/projects/base.js';\nimport type {\n ProjectRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_NAME_LENGTH = 200;\nconst MAX_DESCRIPTION_LENGTH = 2_000;\nconst MAX_REPOSITORY_COMMAND_LENGTH = 2_000;\nconst MAX_BRANCH_LENGTH = 255;\nconst MAX_SANDBOX_PROVIDER_LENGTH = 100;\nconst MAX_SANDBOX_WORKDIR_LENGTH = 1_000;\nconst CONTROL_CHAR_RE = /[\\0-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/;\n\nfunction loose(context: unknown): Context {\n return context as Context;\n}\n\nasync function readJson(context: Context): Promise<unknown | undefined> {\n try {\n return await context.req.json();\n } catch {\n return undefined;\n }\n}\n\nfunction parseCreateInput(value: unknown): CreateFactoryProjectInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.name !== 'string') return null;\n const name = input.name.trim();\n if (!name || name.length > MAX_NAME_LENGTH) return null;\n if (input.description !== undefined && input.description !== null && typeof input.description !== 'string')\n return null;\n const description = typeof input.description === 'string' ? input.description.trim() || null : null;\n if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;\n return { name, description };\n}\n\nfunction parseUpdateInput(value: unknown): UpdateFactoryProjectInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateFactoryProjectInput = {};\n if (input.name !== undefined) {\n if (typeof input.name !== 'string') return null;\n const name = input.name.trim();\n if (!name || name.length > MAX_NAME_LENGTH) return null;\n patch.name = name;\n }\n if (input.description !== undefined) {\n if (input.description !== null && typeof input.description !== 'string') return null;\n const description = typeof input.description === 'string' ? input.description.trim() || null : null;\n if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;\n patch.description = description;\n }\n if (input.defaultModelId !== undefined) {\n const defaultModelId = parseOptionalString(input.defaultModelId, { maxLength: MAX_NAME_LENGTH, nullable: true });\n if (defaultModelId === false) return null;\n patch.defaultModelId = defaultModelId ?? null;\n }\n if (input.slackWorkItemsEnabled !== undefined) {\n if (typeof input.slackWorkItemsEnabled !== 'boolean') return null;\n patch.slackWorkItemsEnabled = input.slackWorkItemsEnabled;\n }\n if (input.autoRunEnabled !== undefined) {\n if (typeof input.autoRunEnabled !== 'boolean') return null;\n patch.autoRunEnabled = input.autoRunEnabled;\n }\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\nfunction parseConnectionInput(value: unknown): { integrationId: string; installationId: string } | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.integrationId !== 'string' || !input.integrationId.trim()) return null;\n if (typeof input.installationId !== 'string' || !UUID_RE.test(input.installationId)) return null;\n return { integrationId: input.integrationId.trim(), installationId: input.installationId };\n}\n\nfunction parseOptionalString(\n value: unknown,\n { maxLength, nullable = false }: { maxLength: number; nullable?: boolean },\n): string | null | undefined | false {\n if (value === undefined) return undefined;\n if (value === null) return nullable ? null : false;\n if (typeof value !== 'string') return false;\n const normalized = value.trim();\n if (!normalized) return nullable ? null : false;\n if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;\n return normalized;\n}\n\nfunction parseRepositoryLinkInput(value: unknown): {\n repositoryId: string;\n branch: string | null;\n sandboxProvider: string;\n sandboxWorkdir: string;\n setupCommand: string | null;\n teardownCommand: string | null;\n} | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n if (typeof input.repositoryId !== 'string' || !UUID_RE.test(input.repositoryId)) return null;\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n typeof sandboxProvider !== 'string' ||\n typeof sandboxWorkdir !== 'string' ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n return {\n repositoryId: input.repositoryId,\n branch: branch ?? null,\n sandboxProvider,\n sandboxWorkdir,\n setupCommand: setupCommand ?? null,\n teardownCommand: teardownCommand ?? null,\n };\n}\n\nfunction parseRepositoryUpdateInput(value: unknown): UpdateProjectRepositoryInput | null {\n if (!value || typeof value !== 'object') return null;\n const input = value as Record<string, unknown>;\n const patch: UpdateProjectRepositoryInput = {};\n const branch = parseOptionalString(input.branch, { maxLength: MAX_BRANCH_LENGTH, nullable: true });\n const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });\n const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });\n const setupCommand = parseOptionalString(input.setupCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n const teardownCommand = parseOptionalString(input.teardownCommand, {\n maxLength: MAX_REPOSITORY_COMMAND_LENGTH,\n nullable: true,\n });\n if (\n branch === false ||\n sandboxProvider === false ||\n sandboxProvider === null ||\n sandboxWorkdir === false ||\n sandboxWorkdir === null ||\n setupCommand === false ||\n teardownCommand === false\n )\n return null;\n if (branch !== undefined) patch.branch = branch;\n if (sandboxProvider !== undefined) patch.sandboxProvider = sandboxProvider;\n if (sandboxWorkdir !== undefined) patch.sandboxWorkdir = sandboxWorkdir;\n if (setupCommand !== undefined) patch.setupCommand = setupCommand;\n if (teardownCommand !== undefined) patch.teardownCommand = teardownCommand;\n return Object.keys(patch).length > 0 ? patch : null;\n}\n\nexport interface ProjectRoutesDeps extends RouteDependencies {\n /** Factory projects domain backing the CRUD surface. */\n projects: FactoryProjectsStorage;\n /** Source-control domain the connection/repository routes fan out over. */\n sourceControl: SourceControlStorage;\n /** Integration ids allowed as source-control connection targets. */\n versionControlIntegrationIds?: string[];\n /**\n * Fire-and-forget hook invoked after a repository is linked to a project —\n * kicks the initial base-checkpoint build. Must never throw.\n */\n onProjectRepositoryLinked?: (args: { orgId: string; projectRepository: ProjectRepository }) => void;\n /** Shared lifecycle for retiring sessions before their owning records are deleted. */\n sessionRetirement?: SessionRetirementCoordinator;\n}\n\nexport class ProjectRoutes extends Route<ProjectRoutesDeps> {\n readonly #versionControlIntegrationIds: Set<string>;\n\n constructor(deps: ProjectRoutesDeps) {\n super(deps);\n this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);\n }\n\n async #projects(): Promise<FactoryProjectsStorage> {\n await this.deps.projects.ensureReady();\n return this.deps.projects;\n }\n\n async #sourceControl(): Promise<SourceControlStorage> {\n await this.deps.sourceControl.ensureReady();\n return this.deps.sourceControl;\n }\n\n async #handles(): Promise<SourceControlStorageHandle[]> {\n const storage = await this.#sourceControl();\n return [...this.#versionControlIntegrationIds].map(integrationId => storage.forIntegration(integrationId));\n }\n\n async #project(orgId: string, id: string) {\n return (await this.#projects()).get({ orgId, id });\n }\n\n async #findConnection({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const connection = await handle.connections.get({ orgId, id });\n if (connection?.factoryProjectId === projectId) return { handle, connection };\n }\n return null;\n }\n\n async #findProjectRepository({ orgId, projectId, id }: { orgId: string; projectId: string; id: string }) {\n for (const handle of await this.#handles()) {\n const projectRepository = await handle.projectRepositories.get({ orgId, id });\n if (!projectRepository) continue;\n const connection = await handle.connections.get({ orgId, id: projectRepository.connectionId });\n if (connection?.factoryProjectId === projectId) return { handle, connection, projectRepository };\n }\n return null;\n }\n\n async #repositoryPayload(handle: SourceControlStorageHandle, orgId: string, projectRepository: ProjectRepository) {\n const repository = await handle.repositories.get({ orgId, id: projectRepository.repositoryId });\n return { ...projectRepository, repository };\n }\n\n async #retireProjectRepositorySessions(\n handle: SourceControlStorageHandle,\n orgId: string,\n projectRepositoryId: string,\n ): Promise<boolean> {\n const sessions = await handle.sessions.listByProjectRepository({ projectRepositoryId });\n if (sessions.length === 0) return true;\n if (!this.deps.sessionRetirement) return false;\n await this.deps.sessionRetirement.retireProjectRepositorySessions({\n sourceControl: handle,\n orgId,\n projectRepositoryId,\n });\n return true;\n }\n\n async #resolveTenant(context: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(context);\n const tenant = this.deps.auth.tenant(context);\n if (!tenant) return { response: context.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: context.json(\n { error: 'organization_required', message: 'Factory projects require an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/factory/projects', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const input = parseCreateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project' }, 400);\n const project = await (await this.#projects()).create({ orgId: tenant.orgId, userId: tenant.userId, input });\n return context.json({ project }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n const project = await this.#project(tenant.orgId, id);\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n const input = parseUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project' }, 400);\n const project = await (await this.#projects()).update({ orgId: tenant.orgId, id, input });\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute('/web/factory/projects/:id', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const id = context.req.param('id');\n if (!id || !UUID_RE.test(id)) return context.json({ error: 'Project not found' }, 404);\n if (!(await this.#project(tenant.orgId, id))) return context.json({ error: 'Project not found' }, 404);\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({ orgId: tenant.orgId, factoryProjectId: id })) {\n for (const projectRepository of await handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId: connection.id,\n })) {\n if (!(await this.#retireProjectRepositorySessions(handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await handle.connections.delete({ orgId: tenant.orgId, id: connection.id });\n }\n }\n await (await this.#projects()).delete({ orgId: tenant.orgId, id });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const connections = [];\n for (const handle of await this.#handles()) {\n for (const connection of await handle.connections.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n })) {\n const installation = await handle.installations.get({\n orgId: tenant.orgId,\n id: connection.installationId,\n });\n // Skip orphaned connections whose installation was pruned (e.g.\n // the user uninstalled the GitHub App). Otherwise\n // `projectRepositories.list` throws `requireConnection` and the\n // whole endpoint 500s, which hangs the web UI on the page\n // loader for every project. New code cascade-deletes these on\n // installation removal, but this defensive skip lets already-\n // orphaned rows in existing databases self-heal on read.\n if (!installation) continue;\n const links = await handle.projectRepositories.list({ orgId: tenant.orgId, connectionId: connection.id });\n connections.push({\n ...connection,\n installation,\n repositories: await Promise.all(links.map(link => this.#repositoryPayload(handle, tenant.orgId, link))),\n });\n }\n }\n return context.json({ connections });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n if (!projectId || !UUID_RE.test(projectId) || !(await this.#project(tenant.orgId, projectId)))\n return context.json({ error: 'Project not found' }, 404);\n const input = parseConnectionInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_source_control_connection' }, 400);\n if (!this.#versionControlIntegrationIds.has(input.integrationId))\n return context.json({ error: 'Source-control integration not found' }, 404);\n const handle = (await this.#sourceControl()).forIntegration(input.integrationId);\n if (!(await handle.installations.get({ orgId: tenant.orgId, id: input.installationId })))\n return context.json({ error: 'Source-control installation not found' }, 404);\n const connection = await handle.connections.create({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n installationId: input.installationId,\n createdByUserId: tenant.userId,\n });\n return context.json({ connection }, 201);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n for (const projectRepository of await found.handle.projectRepositories.list({\n orgId: tenant.orgId,\n connectionId,\n })) {\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepository.id))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n }\n await found.handle.connections.delete({ orgId: tenant.orgId, id: connectionId });\n return context.body(null, 204);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/source-control-connections/:connectionId/repositories', {\n method: 'POST',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const connectionId = context.req.param('connectionId');\n if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId))\n return context.json({ error: 'Source-control connection not found' }, 404);\n const found = await this.#findConnection({ orgId: tenant.orgId, projectId, id: connectionId });\n if (!found) return context.json({ error: 'Source-control connection not found' }, 404);\n const input = parseRepositoryLinkInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const repository = await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId });\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n ...input,\n });\n try {\n this.deps.onProjectRepositoryLinked?.({ orgId: tenant.orgId, projectRepository });\n } catch (error) {\n console.warn('[factory] onProjectRepositoryLinked failed after a successful repository link:', error);\n }\n return context.json(\n { projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) },\n 201,\n );\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'PATCH',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n const input = parseRepositoryUpdateInput(await readJson(context));\n if (!input) return context.json({ error: 'invalid_project_repository' }, 400);\n const projectRepository = await found.handle.projectRepositories.update({\n orgId: tenant.orgId,\n id: projectRepositoryId,\n input,\n });\n return context.json({\n projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository!),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/repositories/:projectRepositoryId', {\n method: 'DELETE',\n requiresAuth: false,\n handler: async routeContext => {\n const context = loose(routeContext);\n const tenant = await this.#resolveTenant(context);\n if ('response' in tenant) return tenant.response;\n const projectId = context.req.param('id');\n const projectRepositoryId = context.req.param('projectRepositoryId');\n if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId))\n return context.json({ error: 'Project repository not found' }, 404);\n const found = await this.#findProjectRepository({ orgId: tenant.orgId, projectId, id: projectRepositoryId });\n if (!found) return context.json({ error: 'Project repository not found' }, 404);\n if (!(await this.#retireProjectRepositorySessions(found.handle, tenant.orgId, projectRepositoryId))) {\n return context.json({ error: 'session_retirement_unavailable' }, 409);\n }\n await found.handle.projectRepositories.unlink({ orgId: tenant.orgId, id: projectRepositoryId });\n return context.body(null, 204);\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
|
package/dist/routes/surface.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { FactoryBindingPreparationInput } from '../rules/dispatcher.js';
|
|
|
9
9
|
import { FactoryStartCoordinator } from '../rules/start-coordinator.js';
|
|
10
10
|
import { FactoryTransitionService } from '../rules/transition-service.js';
|
|
11
11
|
import type { FactoryRules } from '../rules/types.js';
|
|
12
|
+
import type { BaseCheckpointTriggers } from '../sandbox/base-checkpoint-triggers.js';
|
|
12
13
|
import type { SandboxFleet } from '../sandbox/fleet.js';
|
|
13
14
|
import type { StateSigner } from '../state-signing.js';
|
|
14
15
|
import type { AuditEmitter } from '../storage/domains/audit/domain.js';
|
|
@@ -42,6 +43,8 @@ export interface FactoryApiRoutesDeps {
|
|
|
42
43
|
stateSigner?: StateSigner;
|
|
43
44
|
/** Sandbox fleet constructed by the factory (disabled when no machine). */
|
|
44
45
|
fleet: SandboxFleet;
|
|
46
|
+
/** Base-checkpoint trigger surface, when the factory constructed one. */
|
|
47
|
+
baseCheckpoints?: BaseCheckpointTriggers;
|
|
45
48
|
/** Root factory storage backend (distributed locks, app-db diagnostics). */
|
|
46
49
|
factoryStorage?: FactoryStorage;
|
|
47
50
|
integrationStorage: IntegrationStorage;
|
|
@@ -99,6 +102,8 @@ export declare function buildIntegrationContext(deps: Pick<FactoryApiRoutesDeps,
|
|
|
99
102
|
* `routes()`, `channels()`, and `workers()` all see the same context shape.
|
|
100
103
|
*/
|
|
101
104
|
sourceControlOwnerId?: string;
|
|
105
|
+
/** Base-checkpoint trigger surface, when the factory constructed one. */
|
|
106
|
+
baseCheckpoints?: BaseCheckpointTriggers;
|
|
102
107
|
}, integrationId: string): IntegrationContext;
|
|
103
108
|
/**
|
|
104
109
|
* Assemble the custom `/web/*` API routes as Mastra `server.apiRoutes`:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/surface.js
CHANGED
|
@@ -116,6 +116,7 @@ function buildIntegrationContext(deps, integrationId) {
|
|
|
116
116
|
return {
|
|
117
117
|
auth: deps.auth,
|
|
118
118
|
fleet: deps.fleet,
|
|
119
|
+
...deps.baseCheckpoints ? { baseCheckpoints: deps.baseCheckpoints } : {},
|
|
119
120
|
factoryStorage: deps.factoryStorage,
|
|
120
121
|
baseUrl: deps.publicOrigin,
|
|
121
122
|
controller: deps.controller,
|
|
@@ -254,6 +255,7 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
254
255
|
authStorage: deps.authStorage,
|
|
255
256
|
modelCredentials: deps.domains.modelCredentials,
|
|
256
257
|
modelPacks: deps.domains.modelPacks,
|
|
258
|
+
sourceControlSessions: deps.sourceControlStorage.forIntegration("github").sessions,
|
|
257
259
|
memorySettings: deps.domains.memorySettings,
|
|
258
260
|
customProviders: deps.domains.customProviders,
|
|
259
261
|
features: { knowledge: deps.knowledgeEnabled },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.js","names":[],"sources":["../../src/routes/surface.ts"],"sourcesContent":["import type { AuthStorage } from '@mastra/code-sdk/auth/storage';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { FactoryStorage } from '@mastra/core/storage';\n\nimport type { FactoryIntegration, IntegrationContext } from '../integrations/base.js';\nimport { getGithubFeatureDiagnostics } from '../integrations/github/config.js';\nimport type { GithubIntegration } from '../integrations/github/integration.js';\nimport type { FactoryBindingPreparationInput } from '../rules/dispatcher.js';\nimport { FactoryStartCoordinator } from '../rules/start-coordinator.js';\nimport { FactoryTransitionService } from '../rules/transition-service.js';\nimport type { FactoryRules } from '../rules/types.js';\nimport { isFactoryRuleStage } from '../rules/types.js';\nimport type { SandboxFleet } from '../sandbox/fleet.js';\nimport { ensureFactorySourceSession, resolveFactoryDefaultModelId } from '../session/factory-session.js';\nimport { LiveSessions } from '../session/live-sessions.js';\nimport type { StateSigner } from '../state-signing.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { ChannelIdentityStorage } from '../storage/domains/channel-identity/base.js';\nimport type { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';\nimport type { CustomProvidersStorage } from '../storage/domains/custom-providers/base.js';\nimport type { FilesystemStorage } from '../storage/domains/filesystem/base.js';\nimport type { IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { IntegrationStorage } from '../storage/domains/integrations/base.js';\nimport type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';\nimport type { ModelPacksStorage } from '../storage/domains/model-packs/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';\nimport type { SourceControlStorage } from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { ConfigRoutes } from './config.js';\nimport { invalidateCustomProvidersSnapshots } from './custom-provider-source.js';\nimport { buildFsRoutes } from './fs.js';\nimport { IntakeRoutes } from './intake.js';\nimport { KnowledgeRoutes } from './knowledge.js';\nimport { OAuthRoutes } from './oauth.js';\nimport type { RouteAuth } from './route.js';\nimport { SkillRoutes } from './skills.js';\nimport { invalidateTenantCredentialSnapshots } from './tenant-credentials.js';\nimport { WorkItemRoutes } from './work-items.js';\n\nexport interface IntegrationRegistration {\n integration: FactoryIntegration;\n ready: boolean;\n ensureReady: () => Promise<void>;\n}\n\nexport interface FactoryApiRoutesDeps {\n controllerId: string;\n controller: AgentController<MastraCodeState>;\n /** Request-auth seam threaded from the host (no service locator). */\n auth: RouteAuth;\n authStorage: AuthStorage;\n audit: AuditEmitter;\n fsRoot?: string;\n publicOrigin: string;\n stateSigner?: StateSigner;\n /** Sandbox fleet constructed by the factory (disabled when no machine). */\n fleet: SandboxFleet;\n /** Root factory storage backend (distributed locks, app-db diagnostics). */\n factoryStorage?: FactoryStorage;\n integrationStorage: IntegrationStorage;\n sourceControlStorage: SourceControlStorage;\n /** App-table domain handles, registered and owned by `MastraFactory.prepare()`. */\n domains: {\n intake: IntakeStorage;\n modelCredentials: ModelCredentialsStorage;\n memorySettings: MemorySettingsStorage;\n customProviders: CustomProvidersStorage;\n filesystem: FilesystemStorage;\n modelPacks: ModelPacksStorage;\n projects: FactoryProjectsStorage;\n queueHealth: QueueHealthStorage;\n workItems: WorkItemsStorage;\n channelIdentity: ChannelIdentityStorage;\n };\n integrations?: IntegrationRegistration[];\n intakeReady: boolean;\n factoryReady: boolean;\n knowledgeEnabled: boolean;\n /** Resolved Factory rule set, threaded from the host (no service locator). */\n rules: FactoryRules;\n factoryTransitionService?: FactoryTransitionService;\n sessionRetirement?: import('../sandbox/session-retirement.js').SessionRetirementCoordinator;\n onFactoryRuntime?: (runtime: {\n transitionService: FactoryTransitionService;\n prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n }) => void;\n}\n\nfunction guardIntegrationRoutes({\n integration,\n ready,\n ensureReady,\n routes,\n}: IntegrationRegistration & { routes: ApiRoute[] }): ApiRoute[] {\n if (ready) return routes;\n return routes.map(route => {\n if ('handler' in route) {\n const handler = route.handler;\n return {\n ...route,\n handler: async (context: Parameters<typeof handler>[0]) => {\n try {\n await ensureReady();\n } catch {\n return context.json(\n { error: 'integration_unavailable', message: `${integration.id} integration is unavailable.` },\n 503,\n );\n }\n return handler(context, async () => {});\n },\n };\n }\n\n const createHandler = route.createHandler;\n return {\n ...route,\n createHandler: async (args: Parameters<typeof createHandler>[0]) => {\n const handler = await createHandler(args);\n return async (context: Parameters<typeof handler>[0]) => {\n try {\n await ensureReady();\n } catch {\n return context.json(\n { error: 'integration_unavailable', message: `${integration.id} integration is unavailable.` },\n 503,\n );\n }\n return handler(context);\n };\n },\n };\n });\n}\n\nexport function factoryRuleBranch(item: FactoryBindingPreparationInput['item']): string {\n const metadata = item.metadata ?? {};\n const issueNumber = metadata.githubIssueNumber ?? metadata.number;\n if (\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'issue' &&\n typeof issueNumber === 'number'\n ) {\n return `factory/issue-${issueNumber}`;\n }\n const pullRequestNumber = metadata.githubPullRequestNumber ?? metadata.number;\n if (\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'pull-request' &&\n typeof pullRequestNumber === 'number'\n ) {\n return `factory/pr-${pullRequestNumber}`;\n }\n if (item.externalSource?.integrationId === 'linear' && typeof metadata.identifier === 'string') {\n return `factory/linear-${metadata.identifier.toLowerCase()}`;\n }\n throw new Error('Factory skill invocation requires a supported issue or pull request identifier.');\n}\n\n/**\n * Start a factory run for a rule binding: ensure the source-control session the\n * coordinator requires, then hand it to `prepare` along with the factory's\n * default model. Exported for tests — this is the autonomous entry point with no\n * browser and no interactive user, so nothing else would catch a regression in\n * what it forwards.\n */\nexport async function prepareFactoryRuleBinding(\n github: GithubIntegration,\n coordinator: FactoryStartCoordinator,\n projects: FactoryProjectsStorage,\n input: FactoryBindingPreparationInput,\n): Promise<void> {\n const branch = factoryRuleBranch(input.item);\n const repositorySlug =\n typeof input.item.metadata?.repository === 'string' ? input.item.metadata.repository : undefined;\n const preparedSession = await ensureFactorySourceSession({\n sourceControl: github.sourceControlStorage,\n orgId: input.record.orgId,\n factoryProjectId: input.record.factoryProjectId,\n repositorySlug,\n branch,\n });\n const destinationStage = input.item.stages.length === 1 ? input.item.stages[0] : undefined;\n if (!isFactoryRuleStage(destinationStage))\n throw new Error('Factory skill invocation requires one exclusive board stage.');\n\n await coordinator.prepare({\n orgId: input.record.orgId,\n userId: preparedSession.userId,\n factoryProjectId: input.record.factoryProjectId,\n sessionId: preparedSession.sessionId,\n defaultModelId: await resolveFactoryDefaultModelId(projects, input.record.factoryProjectId),\n threadTitle: `${input.role === 'review' ? 'PR' : 'Issue'}: ${input.item.title}`,\n kickoffKey: input.record.id,\n destinationStage,\n workItem: {\n id: input.item.id,\n role: input.role,\n input: {\n externalSource: input.item.externalSource,\n parentWorkItemId: input.item.parentWorkItemId,\n title: input.item.title,\n stages: ['intake'],\n sessions: input.item.sessions,\n metadata: input.item.metadata,\n },\n },\n });\n}\n\n/**\n * Build the {@link IntegrationContext} handed to an integration when the\n * factory collects its capabilities (routes, workers). One shape everywhere:\n * `assembleFactoryApiRoutes` uses it per registration, and `MastraFactory` uses it\n * when collecting integration workers at finalize.\n */\nexport function buildIntegrationContext(\n deps: Pick<\n FactoryApiRoutesDeps,\n 'controller' | 'publicOrigin' | 'auth' | 'fleet' | 'factoryStorage' | 'integrationStorage' | 'sourceControlStorage'\n > & {\n stateSigner: StateSigner;\n emitAudit?: AuditEmitter['emit'];\n rules: FactoryRules;\n factoryReady: boolean;\n domains: Pick<\n FactoryApiRoutesDeps['domains'],\n 'projects' | 'intake' | 'workItems' | 'channelIdentity' | 'memorySettings'\n >;\n /**\n * Stable id of the registered source-control-owning integration (today:\n * `'github'` when registered). Every call site must derive and pass it so\n * `routes()`, `channels()`, and `workers()` all see the same context shape.\n */\n sourceControlOwnerId?: string;\n },\n integrationId: string,\n): IntegrationContext {\n return {\n auth: deps.auth,\n fleet: deps.fleet,\n factoryStorage: deps.factoryStorage,\n baseUrl: deps.publicOrigin,\n controller: deps.controller,\n stateSigner: deps.stateSigner,\n storage: {\n generic: deps.integrationStorage.forIntegration(integrationId),\n sourceControl: deps.sourceControlStorage.forIntegration(integrationId),\n ...(deps.sourceControlOwnerId\n ? { sourceControlOwner: deps.sourceControlStorage.forIntegration(deps.sourceControlOwnerId) }\n : {}),\n projects: deps.domains.projects,\n intake: deps.domains.intake,\n channelIdentity: deps.domains.channelIdentity,\n memorySettings: deps.domains.memorySettings,\n },\n ...(deps.factoryReady ? { rules: { config: deps.rules, workItems: deps.domains.workItems } } : {}),\n ...(deps.emitAudit ? { hooks: { emitAudit: deps.emitAudit } } : {}),\n };\n}\n\n/**\n * Disabled-status stub for the well-known integration ids. The SPA polls\n * `/web/github/status` and `/web/linear/status` unconditionally, so when an\n * integration is absent (or not ready) the status contract must still hold.\n * Unknown custom ids get no stub — the SPA doesn't poll them.\n */\nfunction disabledIntegrationStatusRoutes(deps: FactoryApiRoutesDeps, id: string, configured = false): ApiRoute[] {\n if (id === 'github') {\n return [\n registerApiRoute('/web/github/status', {\n method: 'GET',\n requiresAuth: false,\n handler: c =>\n c.json({\n enabled: false,\n connected: false,\n installations: [],\n reason: 'missing_config',\n diagnostics: getGithubFeatureDiagnostics({\n github: undefined,\n auth: deps.auth,\n appDbConfigured: deps.factoryStorage !== undefined,\n stateSigner: deps.stateSigner,\n fleet: deps.fleet,\n }),\n }),\n }),\n ];\n }\n if (id === 'linear') {\n return [\n registerApiRoute('/web/linear/status', {\n method: 'GET',\n requiresAuth: false,\n handler: c =>\n c.json({\n enabled: false,\n connected: false,\n workspace: null,\n reason: 'missing_config',\n diagnostics: {\n linearAppConfigured: configured,\n factoryAuthEnabled: deps.auth.enabled(),\n appDbConfigured: true,\n },\n }),\n }),\n ];\n }\n return [];\n}\n\n/**\n * Stub for `GET /web/channel-accounts` when NO Slack integration is\n * registered. The SPA's Connections section polls the path unconditionally;\n * without a stub the SPA fallback serves HTML, which the UI can only read as\n * \"old server / unknown\". The machine-readable reason lets it say the truth:\n * the integration isn't registered.\n *\n * Mounted only for ABSENT slack — a registered integration owns the path via\n * its connect routes (or, when the state signer is unstable, gets no routes\n * at all and the UI falls back to the generic copy). Static payload, leaks\n * nothing → no auth needed, same posture as the github/linear stubs.\n */\nfunction absentSlackChannelAccountsRoutes(): ApiRoute[] {\n return [\n registerApiRoute('/web/channel-accounts', {\n method: 'GET',\n requiresAuth: false,\n handler: c => c.json({ accounts: [], canConnect: false, reason: 'not_registered' }),\n }),\n ];\n}\n\n/**\n * Assemble the custom `/web/*` API routes as Mastra `server.apiRoutes`:\n * - fs browser routes (project picker), confined to `fsRoot`\n * - config routes (provider/API-key/model-pack/OM management)\n * - every registered integration's `routes()` surface (full set when ready,\n * disabled-status stub otherwise), plus stubs for absent known ids\n */\nexport function assembleFactoryApiRoutes(deps: FactoryApiRoutesDeps): ApiRoute[] {\n const emitAudit: AuditEmitter['emit'] = args => deps.audit.emit(args);\n const registrations = deps.integrations ?? [];\n const githubRegistration = registrations.find(({ integration }) => integration.id === 'github');\n const githubStorage = githubRegistration ? deps.sourceControlStorage.forIntegration('github') : undefined;\n const githubIntegration = githubRegistration?.integration as GithubIntegration | undefined;\n\n const integrationRoutes = registrations.flatMap(registration => {\n const { integration } = registration;\n if (!deps.stateSigner) return disabledIntegrationStatusRoutes(deps, integration.id, true);\n const context = buildIntegrationContext(\n {\n ...deps,\n stateSigner: deps.stateSigner,\n emitAudit,\n ...(githubRegistration ? { sourceControlOwnerId: 'github' } : {}),\n },\n integration.id,\n );\n return guardIntegrationRoutes({ ...registration, routes: integration.routes(context) });\n });\n // Absent known integrations still get their disabled-status stub.\n const absentStubs = ['github', 'linear']\n .filter(id => !registrations.some(({ integration }) => integration.id === id))\n .flatMap(id => disabledIntegrationStatusRoutes(deps, id));\n // Absent slack gets the channel-accounts not-registered stub (registered\n // slack owns the path via its own connect routes).\n const slackAbsentStubs = registrations.some(({ integration }) => integration.id === 'slack')\n ? []\n : absentSlackChannelAccountsRoutes();\n\n const transitionService = deps.factoryReady\n ? (deps.factoryTransitionService ??\n new FactoryTransitionService({ rules: deps.rules, storage: deps.domains.workItems }))\n : undefined;\n const startCoordinator = transitionService\n ? new FactoryStartCoordinator(\n deps.controller,\n deps.domains.workItems,\n transitionService,\n githubIntegration?.sourceControlStorage,\n deps.domains.memorySettings,\n )\n : undefined;\n if (transitionService && startCoordinator) {\n deps.onFactoryRuntime?.({\n transitionService,\n ...(githubIntegration\n ? {\n prepareBinding: (input: FactoryBindingPreparationInput) =>\n prepareFactoryRuleBinding(githubIntegration, startCoordinator, deps.domains.projects, input),\n }\n : {}),\n });\n }\n\n return [\n ...buildFsRoutes({\n root: deps.fsRoot,\n sessionFs: {\n auth: deps.auth,\n fleet: deps.fleet,\n sessions: deps.sourceControlStorage.forIntegration('github').sessions,\n filesystem: deps.domains.filesystem,\n },\n }),\n ...new ConfigRoutes({\n auth: deps.auth,\n controller: deps.controller,\n authStorage: deps.authStorage,\n modelCredentials: deps.domains.modelCredentials,\n modelPacks: deps.domains.modelPacks,\n memorySettings: deps.domains.memorySettings,\n customProviders: deps.domains.customProviders,\n features: { knowledge: deps.knowledgeEnabled },\n onCredentialsChanged: invalidateTenantCredentialSnapshots,\n onCustomProvidersChanged: invalidateCustomProvidersSnapshots,\n }).routes(),\n ...new OAuthRoutes({\n auth: deps.auth,\n authStorage: deps.authStorage,\n modelCredentials: deps.domains.modelCredentials,\n onCredentialsChanged: invalidateTenantCredentialSnapshots,\n }).routes(),\n ...new SkillRoutes({\n auth: deps.auth,\n controllerId: deps.controllerId,\n controller: deps.controller,\n sourceControlStorage: githubStorage,\n ensureSourceControlReady: githubRegistration?.ensureReady,\n }).routes(),\n ...integrationRoutes,\n ...absentStubs,\n ...slackAbsentStubs,\n ...(deps.intakeReady\n ? new IntakeRoutes({\n auth: deps.auth,\n audit: deps.audit,\n intake: deps.domains.intake,\n projects: deps.domains.projects,\n integrations: (deps.integrations ?? []).flatMap(({ integration }) =>\n integration.intake ? [{ id: integration.id, intake: integration.intake }] : [],\n ),\n }).routes()\n : []),\n ...(deps.factoryReady && deps.knowledgeEnabled\n ? new KnowledgeRoutes({\n auth: deps.auth,\n projects: deps.domains.projects,\n knowledge: async () => deps.factoryStorage?.getMastraStorage().getStore('knowledge'),\n }).routes()\n : []),\n ...(deps.factoryReady\n ? new WorkItemRoutes({\n auth: deps.auth,\n audit: deps.audit,\n projects: deps.domains.projects,\n workItems: deps.domains.workItems,\n queueHealth: deps.domains.queueHealth,\n transitionService,\n startCoordinator,\n liveSessions: new LiveSessions(deps.controller),\n }).routes()\n : []),\n ];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"surface.js","names":[],"sources":["../../src/routes/surface.ts"],"sourcesContent":["import type { AuthStorage } from '@mastra/code-sdk/auth/storage';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { FactoryStorage } from '@mastra/core/storage';\n\nimport type { FactoryIntegration, IntegrationContext } from '../integrations/base.js';\nimport { getGithubFeatureDiagnostics } from '../integrations/github/config.js';\nimport type { GithubIntegration } from '../integrations/github/integration.js';\nimport type { FactoryBindingPreparationInput } from '../rules/dispatcher.js';\nimport { FactoryStartCoordinator } from '../rules/start-coordinator.js';\nimport { FactoryTransitionService } from '../rules/transition-service.js';\nimport type { FactoryRules } from '../rules/types.js';\nimport { isFactoryRuleStage } from '../rules/types.js';\nimport type { BaseCheckpointTriggers } from '../sandbox/base-checkpoint-triggers.js';\nimport type { SandboxFleet } from '../sandbox/fleet.js';\nimport { ensureFactorySourceSession, resolveFactoryDefaultModelId } from '../session/factory-session.js';\nimport { LiveSessions } from '../session/live-sessions.js';\nimport type { StateSigner } from '../state-signing.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { ChannelIdentityStorage } from '../storage/domains/channel-identity/base.js';\nimport type { ModelCredentialsStorage } from '../storage/domains/credentials/base.js';\nimport type { CustomProvidersStorage } from '../storage/domains/custom-providers/base.js';\nimport type { FilesystemStorage } from '../storage/domains/filesystem/base.js';\nimport type { IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { IntegrationStorage } from '../storage/domains/integrations/base.js';\nimport type { MemorySettingsStorage } from '../storage/domains/memory-settings/base.js';\nimport type { ModelPacksStorage } from '../storage/domains/model-packs/base.js';\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';\nimport type { SourceControlStorage } from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { ConfigRoutes } from './config.js';\nimport { invalidateCustomProvidersSnapshots } from './custom-provider-source.js';\nimport { buildFsRoutes } from './fs.js';\nimport { IntakeRoutes } from './intake.js';\nimport { KnowledgeRoutes } from './knowledge.js';\nimport { OAuthRoutes } from './oauth.js';\nimport type { RouteAuth } from './route.js';\nimport { SkillRoutes } from './skills.js';\nimport { invalidateTenantCredentialSnapshots } from './tenant-credentials.js';\nimport { WorkItemRoutes } from './work-items.js';\n\nexport interface IntegrationRegistration {\n integration: FactoryIntegration;\n ready: boolean;\n ensureReady: () => Promise<void>;\n}\n\nexport interface FactoryApiRoutesDeps {\n controllerId: string;\n controller: AgentController<MastraCodeState>;\n /** Request-auth seam threaded from the host (no service locator). */\n auth: RouteAuth;\n authStorage: AuthStorage;\n audit: AuditEmitter;\n fsRoot?: string;\n publicOrigin: string;\n stateSigner?: StateSigner;\n /** Sandbox fleet constructed by the factory (disabled when no machine). */\n fleet: SandboxFleet;\n /** Base-checkpoint trigger surface, when the factory constructed one. */\n baseCheckpoints?: BaseCheckpointTriggers;\n /** Root factory storage backend (distributed locks, app-db diagnostics). */\n factoryStorage?: FactoryStorage;\n integrationStorage: IntegrationStorage;\n sourceControlStorage: SourceControlStorage;\n /** App-table domain handles, registered and owned by `MastraFactory.prepare()`. */\n domains: {\n intake: IntakeStorage;\n modelCredentials: ModelCredentialsStorage;\n memorySettings: MemorySettingsStorage;\n customProviders: CustomProvidersStorage;\n filesystem: FilesystemStorage;\n modelPacks: ModelPacksStorage;\n projects: FactoryProjectsStorage;\n queueHealth: QueueHealthStorage;\n workItems: WorkItemsStorage;\n channelIdentity: ChannelIdentityStorage;\n };\n integrations?: IntegrationRegistration[];\n intakeReady: boolean;\n factoryReady: boolean;\n knowledgeEnabled: boolean;\n /** Resolved Factory rule set, threaded from the host (no service locator). */\n rules: FactoryRules;\n factoryTransitionService?: FactoryTransitionService;\n sessionRetirement?: import('../sandbox/session-retirement.js').SessionRetirementCoordinator;\n onFactoryRuntime?: (runtime: {\n transitionService: FactoryTransitionService;\n prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;\n }) => void;\n}\n\nfunction guardIntegrationRoutes({\n integration,\n ready,\n ensureReady,\n routes,\n}: IntegrationRegistration & { routes: ApiRoute[] }): ApiRoute[] {\n if (ready) return routes;\n return routes.map(route => {\n if ('handler' in route) {\n const handler = route.handler;\n return {\n ...route,\n handler: async (context: Parameters<typeof handler>[0]) => {\n try {\n await ensureReady();\n } catch {\n return context.json(\n { error: 'integration_unavailable', message: `${integration.id} integration is unavailable.` },\n 503,\n );\n }\n return handler(context, async () => {});\n },\n };\n }\n\n const createHandler = route.createHandler;\n return {\n ...route,\n createHandler: async (args: Parameters<typeof createHandler>[0]) => {\n const handler = await createHandler(args);\n return async (context: Parameters<typeof handler>[0]) => {\n try {\n await ensureReady();\n } catch {\n return context.json(\n { error: 'integration_unavailable', message: `${integration.id} integration is unavailable.` },\n 503,\n );\n }\n return handler(context);\n };\n },\n };\n });\n}\n\nexport function factoryRuleBranch(item: FactoryBindingPreparationInput['item']): string {\n const metadata = item.metadata ?? {};\n const issueNumber = metadata.githubIssueNumber ?? metadata.number;\n if (\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'issue' &&\n typeof issueNumber === 'number'\n ) {\n return `factory/issue-${issueNumber}`;\n }\n const pullRequestNumber = metadata.githubPullRequestNumber ?? metadata.number;\n if (\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'pull-request' &&\n typeof pullRequestNumber === 'number'\n ) {\n return `factory/pr-${pullRequestNumber}`;\n }\n if (item.externalSource?.integrationId === 'linear' && typeof metadata.identifier === 'string') {\n return `factory/linear-${metadata.identifier.toLowerCase()}`;\n }\n throw new Error('Factory skill invocation requires a supported issue or pull request identifier.');\n}\n\n/**\n * Start a factory run for a rule binding: ensure the source-control session the\n * coordinator requires, then hand it to `prepare` along with the factory's\n * default model. Exported for tests — this is the autonomous entry point with no\n * browser and no interactive user, so nothing else would catch a regression in\n * what it forwards.\n */\nexport async function prepareFactoryRuleBinding(\n github: GithubIntegration,\n coordinator: FactoryStartCoordinator,\n projects: FactoryProjectsStorage,\n input: FactoryBindingPreparationInput,\n): Promise<void> {\n const branch = factoryRuleBranch(input.item);\n const repositorySlug =\n typeof input.item.metadata?.repository === 'string' ? input.item.metadata.repository : undefined;\n const preparedSession = await ensureFactorySourceSession({\n sourceControl: github.sourceControlStorage,\n orgId: input.record.orgId,\n factoryProjectId: input.record.factoryProjectId,\n repositorySlug,\n branch,\n });\n const destinationStage = input.item.stages.length === 1 ? input.item.stages[0] : undefined;\n if (!isFactoryRuleStage(destinationStage))\n throw new Error('Factory skill invocation requires one exclusive board stage.');\n\n await coordinator.prepare({\n orgId: input.record.orgId,\n userId: preparedSession.userId,\n factoryProjectId: input.record.factoryProjectId,\n sessionId: preparedSession.sessionId,\n defaultModelId: await resolveFactoryDefaultModelId(projects, input.record.factoryProjectId),\n threadTitle: `${input.role === 'review' ? 'PR' : 'Issue'}: ${input.item.title}`,\n kickoffKey: input.record.id,\n destinationStage,\n workItem: {\n id: input.item.id,\n role: input.role,\n input: {\n externalSource: input.item.externalSource,\n parentWorkItemId: input.item.parentWorkItemId,\n title: input.item.title,\n stages: ['intake'],\n sessions: input.item.sessions,\n metadata: input.item.metadata,\n },\n },\n });\n}\n\n/**\n * Build the {@link IntegrationContext} handed to an integration when the\n * factory collects its capabilities (routes, workers). One shape everywhere:\n * `assembleFactoryApiRoutes` uses it per registration, and `MastraFactory` uses it\n * when collecting integration workers at finalize.\n */\nexport function buildIntegrationContext(\n deps: Pick<\n FactoryApiRoutesDeps,\n 'controller' | 'publicOrigin' | 'auth' | 'fleet' | 'factoryStorage' | 'integrationStorage' | 'sourceControlStorage'\n > & {\n stateSigner: StateSigner;\n emitAudit?: AuditEmitter['emit'];\n rules: FactoryRules;\n factoryReady: boolean;\n domains: Pick<\n FactoryApiRoutesDeps['domains'],\n 'projects' | 'intake' | 'workItems' | 'channelIdentity' | 'memorySettings'\n >;\n /**\n * Stable id of the registered source-control-owning integration (today:\n * `'github'` when registered). Every call site must derive and pass it so\n * `routes()`, `channels()`, and `workers()` all see the same context shape.\n */\n sourceControlOwnerId?: string;\n /** Base-checkpoint trigger surface, when the factory constructed one. */\n baseCheckpoints?: BaseCheckpointTriggers;\n },\n integrationId: string,\n): IntegrationContext {\n return {\n auth: deps.auth,\n fleet: deps.fleet,\n ...(deps.baseCheckpoints ? { baseCheckpoints: deps.baseCheckpoints } : {}),\n factoryStorage: deps.factoryStorage,\n baseUrl: deps.publicOrigin,\n controller: deps.controller,\n stateSigner: deps.stateSigner,\n storage: {\n generic: deps.integrationStorage.forIntegration(integrationId),\n sourceControl: deps.sourceControlStorage.forIntegration(integrationId),\n ...(deps.sourceControlOwnerId\n ? { sourceControlOwner: deps.sourceControlStorage.forIntegration(deps.sourceControlOwnerId) }\n : {}),\n projects: deps.domains.projects,\n intake: deps.domains.intake,\n channelIdentity: deps.domains.channelIdentity,\n memorySettings: deps.domains.memorySettings,\n },\n ...(deps.factoryReady ? { rules: { config: deps.rules, workItems: deps.domains.workItems } } : {}),\n ...(deps.emitAudit ? { hooks: { emitAudit: deps.emitAudit } } : {}),\n };\n}\n\n/**\n * Disabled-status stub for the well-known integration ids. The SPA polls\n * `/web/github/status` and `/web/linear/status` unconditionally, so when an\n * integration is absent (or not ready) the status contract must still hold.\n * Unknown custom ids get no stub — the SPA doesn't poll them.\n */\nfunction disabledIntegrationStatusRoutes(deps: FactoryApiRoutesDeps, id: string, configured = false): ApiRoute[] {\n if (id === 'github') {\n return [\n registerApiRoute('/web/github/status', {\n method: 'GET',\n requiresAuth: false,\n handler: c =>\n c.json({\n enabled: false,\n connected: false,\n installations: [],\n reason: 'missing_config',\n diagnostics: getGithubFeatureDiagnostics({\n github: undefined,\n auth: deps.auth,\n appDbConfigured: deps.factoryStorage !== undefined,\n stateSigner: deps.stateSigner,\n fleet: deps.fleet,\n }),\n }),\n }),\n ];\n }\n if (id === 'linear') {\n return [\n registerApiRoute('/web/linear/status', {\n method: 'GET',\n requiresAuth: false,\n handler: c =>\n c.json({\n enabled: false,\n connected: false,\n workspace: null,\n reason: 'missing_config',\n diagnostics: {\n linearAppConfigured: configured,\n factoryAuthEnabled: deps.auth.enabled(),\n appDbConfigured: true,\n },\n }),\n }),\n ];\n }\n return [];\n}\n\n/**\n * Stub for `GET /web/channel-accounts` when NO Slack integration is\n * registered. The SPA's Connections section polls the path unconditionally;\n * without a stub the SPA fallback serves HTML, which the UI can only read as\n * \"old server / unknown\". The machine-readable reason lets it say the truth:\n * the integration isn't registered.\n *\n * Mounted only for ABSENT slack — a registered integration owns the path via\n * its connect routes (or, when the state signer is unstable, gets no routes\n * at all and the UI falls back to the generic copy). Static payload, leaks\n * nothing → no auth needed, same posture as the github/linear stubs.\n */\nfunction absentSlackChannelAccountsRoutes(): ApiRoute[] {\n return [\n registerApiRoute('/web/channel-accounts', {\n method: 'GET',\n requiresAuth: false,\n handler: c => c.json({ accounts: [], canConnect: false, reason: 'not_registered' }),\n }),\n ];\n}\n\n/**\n * Assemble the custom `/web/*` API routes as Mastra `server.apiRoutes`:\n * - fs browser routes (project picker), confined to `fsRoot`\n * - config routes (provider/API-key/model-pack/OM management)\n * - every registered integration's `routes()` surface (full set when ready,\n * disabled-status stub otherwise), plus stubs for absent known ids\n */\nexport function assembleFactoryApiRoutes(deps: FactoryApiRoutesDeps): ApiRoute[] {\n const emitAudit: AuditEmitter['emit'] = args => deps.audit.emit(args);\n const registrations = deps.integrations ?? [];\n const githubRegistration = registrations.find(({ integration }) => integration.id === 'github');\n const githubStorage = githubRegistration ? deps.sourceControlStorage.forIntegration('github') : undefined;\n const githubIntegration = githubRegistration?.integration as GithubIntegration | undefined;\n\n const integrationRoutes = registrations.flatMap(registration => {\n const { integration } = registration;\n if (!deps.stateSigner) return disabledIntegrationStatusRoutes(deps, integration.id, true);\n const context = buildIntegrationContext(\n {\n ...deps,\n stateSigner: deps.stateSigner,\n emitAudit,\n ...(githubRegistration ? { sourceControlOwnerId: 'github' } : {}),\n },\n integration.id,\n );\n return guardIntegrationRoutes({ ...registration, routes: integration.routes(context) });\n });\n // Absent known integrations still get their disabled-status stub.\n const absentStubs = ['github', 'linear']\n .filter(id => !registrations.some(({ integration }) => integration.id === id))\n .flatMap(id => disabledIntegrationStatusRoutes(deps, id));\n // Absent slack gets the channel-accounts not-registered stub (registered\n // slack owns the path via its own connect routes).\n const slackAbsentStubs = registrations.some(({ integration }) => integration.id === 'slack')\n ? []\n : absentSlackChannelAccountsRoutes();\n\n const transitionService = deps.factoryReady\n ? (deps.factoryTransitionService ??\n new FactoryTransitionService({ rules: deps.rules, storage: deps.domains.workItems }))\n : undefined;\n const startCoordinator = transitionService\n ? new FactoryStartCoordinator(\n deps.controller,\n deps.domains.workItems,\n transitionService,\n githubIntegration?.sourceControlStorage,\n deps.domains.memorySettings,\n )\n : undefined;\n if (transitionService && startCoordinator) {\n deps.onFactoryRuntime?.({\n transitionService,\n ...(githubIntegration\n ? {\n prepareBinding: (input: FactoryBindingPreparationInput) =>\n prepareFactoryRuleBinding(githubIntegration, startCoordinator, deps.domains.projects, input),\n }\n : {}),\n });\n }\n\n return [\n ...buildFsRoutes({\n root: deps.fsRoot,\n sessionFs: {\n auth: deps.auth,\n fleet: deps.fleet,\n sessions: deps.sourceControlStorage.forIntegration('github').sessions,\n filesystem: deps.domains.filesystem,\n },\n }),\n ...new ConfigRoutes({\n auth: deps.auth,\n controller: deps.controller,\n authStorage: deps.authStorage,\n modelCredentials: deps.domains.modelCredentials,\n modelPacks: deps.domains.modelPacks,\n sourceControlSessions: deps.sourceControlStorage.forIntegration('github').sessions,\n memorySettings: deps.domains.memorySettings,\n customProviders: deps.domains.customProviders,\n features: { knowledge: deps.knowledgeEnabled },\n onCredentialsChanged: invalidateTenantCredentialSnapshots,\n onCustomProvidersChanged: invalidateCustomProvidersSnapshots,\n }).routes(),\n ...new OAuthRoutes({\n auth: deps.auth,\n authStorage: deps.authStorage,\n modelCredentials: deps.domains.modelCredentials,\n onCredentialsChanged: invalidateTenantCredentialSnapshots,\n }).routes(),\n ...new SkillRoutes({\n auth: deps.auth,\n controllerId: deps.controllerId,\n controller: deps.controller,\n sourceControlStorage: githubStorage,\n ensureSourceControlReady: githubRegistration?.ensureReady,\n }).routes(),\n ...integrationRoutes,\n ...absentStubs,\n ...slackAbsentStubs,\n ...(deps.intakeReady\n ? new IntakeRoutes({\n auth: deps.auth,\n audit: deps.audit,\n intake: deps.domains.intake,\n projects: deps.domains.projects,\n integrations: (deps.integrations ?? []).flatMap(({ integration }) =>\n integration.intake ? [{ id: integration.id, intake: integration.intake }] : [],\n ),\n }).routes()\n : []),\n ...(deps.factoryReady && deps.knowledgeEnabled\n ? new KnowledgeRoutes({\n auth: deps.auth,\n projects: deps.domains.projects,\n knowledge: async () => deps.factoryStorage?.getMastraStorage().getStore('knowledge'),\n }).routes()\n : []),\n ...(deps.factoryReady\n ? new WorkItemRoutes({\n auth: deps.auth,\n audit: deps.audit,\n projects: deps.domains.projects,\n workItems: deps.domains.workItems,\n queueHealth: deps.domains.queueHealth,\n transitionService,\n startCoordinator,\n liveSessions: new LiveSessions(deps.controller),\n }).routes()\n : []),\n ];\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger surface for base-checkpoint builds.
|
|
3
|
+
*
|
|
4
|
+
* Three entry points feed the {@link BaseCheckpointBuilder}:
|
|
5
|
+
* - repo connect: the projects route calls `onProjectRepositoryLinked` right
|
|
6
|
+
* after `projectRepositories.link()` succeeds — the initial build.
|
|
7
|
+
* - webhooks: `onWebhookEvent` classifies merged PRs and pushes targeting the
|
|
8
|
+
* repository's default branch and requests a rebuild for every factory
|
|
9
|
+
* project linked to that external repository.
|
|
10
|
+
* - reconcile sweep: `sweep()` walks configured repositories and rebuilds any
|
|
11
|
+
* whose checkpoint is missing or whose setup command changed since it was
|
|
12
|
+
* built (webhooks can be missed; setup edits invalidate metadata).
|
|
13
|
+
*
|
|
14
|
+
* Every trigger is fire-and-forget relative to its caller: classification
|
|
15
|
+
* errors log, builds coalesce inside the builder, and nothing here ever blocks
|
|
16
|
+
* webhook dispatch, route responses, or the sweep lease.
|
|
17
|
+
*/
|
|
18
|
+
import type { IMastraLogger } from '@mastra/core/logger';
|
|
19
|
+
import type { RepositoryAccess } from '../capabilities/version-control.js';
|
|
20
|
+
import type { ParsedGithubWebhook } from '../integrations/github/webhook.js';
|
|
21
|
+
import type { ProjectRepository, SourceControlStorageHandle } from '../storage/domains/source-control/base.js';
|
|
22
|
+
import type { BaseCheckpointBuilder } from './base-checkpoint.js';
|
|
23
|
+
import type { SandboxFleet } from './fleet.js';
|
|
24
|
+
/** The slice of the GitHub integration the triggers need. */
|
|
25
|
+
export interface BaseCheckpointGithubSource {
|
|
26
|
+
readonly sourceControlStorage: SourceControlStorageHandle;
|
|
27
|
+
readonly versionControl?: {
|
|
28
|
+
getRepositoryAccess(input: {
|
|
29
|
+
orgId: string;
|
|
30
|
+
repositoryId: string;
|
|
31
|
+
}): Promise<RepositoryAccess>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface BaseCheckpointTriggers {
|
|
35
|
+
/** Kick the initial build after a repo is linked to a factory project. */
|
|
36
|
+
onProjectRepositoryLinked(args: {
|
|
37
|
+
orgId: string;
|
|
38
|
+
projectRepository: ProjectRepository;
|
|
39
|
+
}): void;
|
|
40
|
+
/** Classify a webhook and request rebuilds for default-branch updates. */
|
|
41
|
+
onWebhookEvent(parsed: ParsedGithubWebhook): void;
|
|
42
|
+
/** Rebuild stale/missing checkpoints for all configured repositories. */
|
|
43
|
+
sweep(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* True when the webhook is a merged pull request into — or a push to — the
|
|
47
|
+
* repository's default branch. Returns the external id pair used to find the
|
|
48
|
+
* linked factory projects, or null when the event is not a rebuild trigger.
|
|
49
|
+
*/
|
|
50
|
+
export declare function classifyDefaultBranchUpdate(parsed: ParsedGithubWebhook): {
|
|
51
|
+
installationExternalId: string;
|
|
52
|
+
repositoryExternalId: string;
|
|
53
|
+
} | null;
|
|
54
|
+
/**
|
|
55
|
+
* Rebuild sweep age bound: even when webhooks are healthy, a checkpoint older
|
|
56
|
+
* than this is rebuilt so a missed default-branch webhook cannot pin sessions
|
|
57
|
+
* to an old commit indefinitely.
|
|
58
|
+
*/
|
|
59
|
+
export declare const BASE_CHECKPOINT_MAX_AGE_MS: number;
|
|
60
|
+
/**
|
|
61
|
+
* A checkpoint is stale when absent, built against a different setup command,
|
|
62
|
+
* or older than {@link BASE_CHECKPOINT_MAX_AGE_MS}.
|
|
63
|
+
*/
|
|
64
|
+
export declare function baseCheckpointIsStale(projectRepository: ProjectRepository, now?: Date): boolean;
|
|
65
|
+
export declare function createBaseCheckpointTriggers(options: {
|
|
66
|
+
builder: BaseCheckpointBuilder;
|
|
67
|
+
fleet: SandboxFleet;
|
|
68
|
+
github: BaseCheckpointGithubSource;
|
|
69
|
+
logger?: IMastraLogger;
|
|
70
|
+
}): BaseCheckpointTriggers;
|
|
71
|
+
/**
|
|
72
|
+
* Wrap a webhook ingest callback so every parsed event also feeds the
|
|
73
|
+
* base-checkpoint triggers. Trigger dispatch is fire-and-forget; the wrapped
|
|
74
|
+
* ingest keeps its original awaited semantics.
|
|
75
|
+
*/
|
|
76
|
+
export declare function withBaseCheckpointWebhookTrigger(ingest: ((event: ParsedGithubWebhook) => Promise<unknown>) | undefined, triggers: BaseCheckpointTriggers | undefined): ((event: ParsedGithubWebhook) => Promise<unknown>) | undefined;
|
|
77
|
+
//# sourceMappingURL=base-checkpoint-triggers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-checkpoint-triggers.d.ts","sourceRoot":"","sources":["../../src/sandbox/base-checkpoint-triggers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,KAAK,EAEV,iBAAiB,EACjB,0BAA0B,EAC3B,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,qBAAqB,EAAqB,MAAM,sBAAsB,CAAC;AAErF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,6DAA6D;AAC7D,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,oBAAoB,EAAE,0BAA0B,CAAC;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,mBAAmB,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;KAChG,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,yBAAyB,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,iBAAiB,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/F,0EAA0E;IAC1E,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClD,yEAAyE;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,mBAAmB,GAC1B;IAAE,sBAAsB,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqBzE;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,QAAsB,CAAC;AAK9D;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAK3G;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,OAAO,EAAE,qBAAqB,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,0BAA0B,CAAC;IACnC,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,GAAG,sBAAsB,CA6EzB;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,EACtE,QAAQ,EAAE,sBAAsB,GAAG,SAAS,GAC3C,CAAC,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAMhE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { hashSetupCommand } from "./base-checkpoint.js";
|
|
2
|
+
//#region src/sandbox/base-checkpoint-triggers.ts
|
|
3
|
+
/**
|
|
4
|
+
* True when the webhook is a merged pull request into — or a push to — the
|
|
5
|
+
* repository's default branch. Returns the external id pair used to find the
|
|
6
|
+
* linked factory projects, or null when the event is not a rebuild trigger.
|
|
7
|
+
*/
|
|
8
|
+
function classifyDefaultBranchUpdate(parsed) {
|
|
9
|
+
const payload = parsed.payload;
|
|
10
|
+
const repository = asRecord(payload.repository);
|
|
11
|
+
const installationId = idString(asRecord(payload.installation)?.id);
|
|
12
|
+
const repositoryId = idString(repository?.id);
|
|
13
|
+
const defaultBranch = typeof repository?.default_branch === "string" ? repository.default_branch : null;
|
|
14
|
+
if (!installationId || !repositoryId || !defaultBranch) return null;
|
|
15
|
+
if (parsed.event === "pull_request" && payload.action === "closed") {
|
|
16
|
+
const pullRequest = asRecord(payload.pull_request);
|
|
17
|
+
const base = asRecord(pullRequest?.base);
|
|
18
|
+
if (pullRequest?.merged === true && base?.ref === defaultBranch) return {
|
|
19
|
+
installationExternalId: installationId,
|
|
20
|
+
repositoryExternalId: repositoryId
|
|
21
|
+
};
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (parsed.event === "push" && payload.ref === `refs/heads/${defaultBranch}`) return {
|
|
25
|
+
installationExternalId: installationId,
|
|
26
|
+
repositoryExternalId: repositoryId
|
|
27
|
+
};
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Rebuild sweep age bound: even when webhooks are healthy, a checkpoint older
|
|
32
|
+
* than this is rebuilt so a missed default-branch webhook cannot pin sessions
|
|
33
|
+
* to an old commit indefinitely.
|
|
34
|
+
*/
|
|
35
|
+
const BASE_CHECKPOINT_MAX_AGE_MS = 1440 * 60 * 1e3;
|
|
36
|
+
/** How many sweep-triggered checkpoint builds may run at once. */
|
|
37
|
+
const SWEEP_BUILD_CONCURRENCY = 3;
|
|
38
|
+
/**
|
|
39
|
+
* A checkpoint is stale when absent, built against a different setup command,
|
|
40
|
+
* or older than {@link BASE_CHECKPOINT_MAX_AGE_MS}.
|
|
41
|
+
*/
|
|
42
|
+
function baseCheckpointIsStale(projectRepository, now = /* @__PURE__ */ new Date()) {
|
|
43
|
+
const checkpoint = projectRepository.baseCheckpoint;
|
|
44
|
+
if (!checkpoint) return true;
|
|
45
|
+
if (checkpoint.setupCommandHash !== hashSetupCommand(projectRepository.setupCommand)) return true;
|
|
46
|
+
return now.getTime() - checkpoint.builtAt.getTime() > BASE_CHECKPOINT_MAX_AGE_MS;
|
|
47
|
+
}
|
|
48
|
+
function createBaseCheckpointTriggers(options) {
|
|
49
|
+
const { builder, fleet, github, logger } = options;
|
|
50
|
+
const storage = github.sourceControlStorage;
|
|
51
|
+
const jobFor = async (orgId, projectRepository) => {
|
|
52
|
+
const getRepositoryAccess = github.versionControl?.getRepositoryAccess.bind(github.versionControl);
|
|
53
|
+
if (!fleet.enabled || !getRepositoryAccess) return null;
|
|
54
|
+
const repository = await storage.repositories.get({
|
|
55
|
+
orgId,
|
|
56
|
+
id: projectRepository.repositoryId
|
|
57
|
+
});
|
|
58
|
+
if (!repository) return null;
|
|
59
|
+
return {
|
|
60
|
+
projectRepositoryId: projectRepository.id,
|
|
61
|
+
repoFullName: repository.slug,
|
|
62
|
+
defaultBranch: repository.defaultBranch,
|
|
63
|
+
setupCommand: projectRepository.setupCommand,
|
|
64
|
+
workdir: fleet.computeWorkdir(repository.slug),
|
|
65
|
+
getToken: async () => {
|
|
66
|
+
const token = (await getRepositoryAccess({
|
|
67
|
+
orgId,
|
|
68
|
+
repositoryId: repository.id
|
|
69
|
+
})).authorization?.token;
|
|
70
|
+
if (!token) throw new Error(`No installation token available for ${repository.slug}`);
|
|
71
|
+
return token;
|
|
72
|
+
},
|
|
73
|
+
storage
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
const request = async (orgId, projectRepository) => {
|
|
77
|
+
const job = await jobFor(orgId, projectRepository);
|
|
78
|
+
if (job) await builder.request(job);
|
|
79
|
+
};
|
|
80
|
+
const requestTargets = async (targets) => {
|
|
81
|
+
for (const target of targets) await request(target.orgId, target.projectRepository);
|
|
82
|
+
};
|
|
83
|
+
const warn = (context, error) => {
|
|
84
|
+
logger?.warn?.(`Base-checkpoint trigger failed (${context}): ${error instanceof Error ? error.message : String(error)}`);
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
onProjectRepositoryLinked({ orgId, projectRepository }) {
|
|
88
|
+
request(orgId, projectRepository).catch((error) => warn("repo-connect", error));
|
|
89
|
+
},
|
|
90
|
+
onWebhookEvent(parsed) {
|
|
91
|
+
const key = classifyDefaultBranchUpdate(parsed);
|
|
92
|
+
if (!key) return;
|
|
93
|
+
storage.projectRepositories.listByExternalRepository(key).then((targets) => requestTargets(targets)).catch((error) => warn("webhook", error));
|
|
94
|
+
},
|
|
95
|
+
async sweep() {
|
|
96
|
+
try {
|
|
97
|
+
const keys = await storage.projectRepositories.listConfiguredExternalKeys();
|
|
98
|
+
const stale = [];
|
|
99
|
+
for (const key of keys) {
|
|
100
|
+
const targets = await storage.projectRepositories.listByExternalRepository(key);
|
|
101
|
+
stale.push(...targets.filter((target) => baseCheckpointIsStale(target.projectRepository)));
|
|
102
|
+
}
|
|
103
|
+
const queue = [...stale];
|
|
104
|
+
const workers = Array.from({ length: Math.min(SWEEP_BUILD_CONCURRENCY, queue.length) }, async () => {
|
|
105
|
+
for (let target = queue.shift(); target; target = queue.shift()) await request(target.orgId, target.projectRepository).catch((error) => warn("sweep-build", error));
|
|
106
|
+
});
|
|
107
|
+
await Promise.all(workers);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
warn("sweep", error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Wrap a webhook ingest callback so every parsed event also feeds the
|
|
116
|
+
* base-checkpoint triggers. Trigger dispatch is fire-and-forget; the wrapped
|
|
117
|
+
* ingest keeps its original awaited semantics.
|
|
118
|
+
*/
|
|
119
|
+
function withBaseCheckpointWebhookTrigger(ingest, triggers) {
|
|
120
|
+
if (!triggers) return ingest;
|
|
121
|
+
return async (event) => {
|
|
122
|
+
triggers.onWebhookEvent(event);
|
|
123
|
+
return ingest ? ingest(event) : void 0;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function asRecord(value) {
|
|
127
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
128
|
+
}
|
|
129
|
+
function idString(value) {
|
|
130
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
131
|
+
if (typeof value === "string" && value) return value;
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { BASE_CHECKPOINT_MAX_AGE_MS, baseCheckpointIsStale, classifyDefaultBranchUpdate, createBaseCheckpointTriggers, withBaseCheckpointWebhookTrigger };
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=base-checkpoint-triggers.js.map
|