@mastra/factory 0.13.0-alpha.9 → 0.13.0
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/README.md +232 -10
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +29 -4
- package/dist/auth.js.map +1 -1
- package/dist/boards/define-board.d.ts +36 -2
- package/dist/boards/define-board.d.ts.map +1 -1
- package/dist/boards/define-board.js +58 -1
- package/dist/boards/define-board.js.map +1 -1
- package/dist/boards/index.d.ts +4 -2
- package/dist/boards/index.d.ts.map +1 -1
- package/dist/boards/index.js +2 -1
- package/dist/boards/index.js.map +1 -1
- package/dist/boards/relocate.d.ts +17 -0
- package/dist/boards/relocate.d.ts.map +1 -0
- package/dist/boards/relocate.js +53 -0
- package/dist/boards/relocate.js.map +1 -0
- package/dist/boards/review.d.ts +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +17 -4
- package/dist/boards/review.js.map +1 -1
- package/dist/boards/semantics.d.ts +28 -0
- package/dist/boards/semantics.d.ts.map +1 -0
- package/dist/boards/semantics.js +41 -0
- package/dist/boards/semantics.js.map +1 -0
- package/dist/boards/work-tool-rules.d.ts +8 -0
- package/dist/boards/work-tool-rules.d.ts.map +1 -0
- package/dist/boards/work-tool-rules.js +20 -0
- package/dist/boards/work-tool-rules.js.map +1 -0
- package/dist/boards/work.d.ts +1 -1
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +13 -0
- package/dist/boards/work.js.map +1 -1
- package/dist/capabilities/intake.d.ts +2 -0
- package/dist/capabilities/intake.d.ts.map +1 -1
- package/dist/factory.d.ts +5 -6
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +41 -12
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +7 -4
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.d.ts +2 -2
- package/dist/integrations/github/default-rules.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.js +2 -2
- package/dist/integrations/github/default-rules.js.map +1 -1
- package/dist/integrations/github/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/github/issue-reconciler.js +4 -3
- package/dist/integrations/github/issue-reconciler.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +6 -2
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +96 -15
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +6 -3
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +21 -2
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/issue-reconciler.d.ts +2 -0
- package/dist/integrations/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/issue-reconciler.js +1 -3
- package/dist/integrations/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/linear/default-rules.d.ts +2 -2
- package/dist/integrations/linear/default-rules.d.ts.map +1 -1
- package/dist/integrations/linear/default-rules.js +2 -2
- package/dist/integrations/linear/default-rules.js.map +1 -1
- package/dist/integrations/linear/integration.js +1 -0
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/linear/issue-reconciler.js +9 -4
- package/dist/integrations/linear/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/reconciliation-config.d.ts.map +1 -1
- package/dist/integrations/linear/reconciliation-config.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +19 -26
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/linear/rules.d.ts +7 -2
- package/dist/integrations/linear/rules.d.ts.map +1 -1
- package/dist/integrations/linear/rules.js +20 -8
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +25 -4
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +1 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +5 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +5 -2
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/emoji-shortcodes.generated.d.ts +8 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.d.ts.map +1 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.js +1985 -0
- package/dist/integrations/slack/emoji-shortcodes.generated.js.map +1 -0
- package/dist/integrations/slack/emoji.d.ts +3 -0
- package/dist/integrations/slack/emoji.d.ts.map +1 -0
- package/dist/integrations/slack/emoji.js +12 -0
- package/dist/integrations/slack/emoji.js.map +1 -0
- package/dist/integrations/slack/integration.js +1 -1
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +16 -2
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/attention-activity.d.ts +2 -3
- package/dist/routes/attention-activity.d.ts.map +1 -1
- package/dist/routes/attention-activity.js +12 -3
- package/dist/routes/attention-activity.js.map +1 -1
- package/dist/routes/attention-parked.d.ts +22 -0
- package/dist/routes/attention-parked.d.ts.map +1 -0
- package/dist/routes/attention-parked.js +136 -0
- package/dist/routes/attention-parked.js.map +1 -0
- package/dist/routes/attention-providers.js +8 -8
- package/dist/routes/attention-providers.js.map +1 -1
- package/dist/routes/attention.d.ts +2 -0
- package/dist/routes/attention.d.ts.map +1 -1
- package/dist/routes/attention.js +47 -118
- package/dist/routes/attention.js.map +1 -1
- package/dist/routes/contracts.d.ts +627 -0
- package/dist/routes/contracts.d.ts.map +1 -0
- package/dist/routes/contracts.js +468 -0
- package/dist/routes/contracts.js.map +1 -0
- package/dist/routes/intake.d.ts +18 -0
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +265 -15
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +26 -76
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/supervisor.d.ts +2 -0
- package/dist/routes/supervisor.d.ts.map +1 -1
- package/dist/routes/supervisor.js +7 -6
- package/dist/routes/supervisor.js.map +1 -1
- package/dist/routes/surface.d.ts +7 -4
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +17 -11
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/telemetry.d.ts +13 -0
- package/dist/routes/telemetry.d.ts.map +1 -0
- package/dist/routes/telemetry.js +60 -0
- package/dist/routes/telemetry.js.map +1 -0
- package/dist/routes/work-items.d.ts +3 -3
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +134 -250
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/dispatch-errors.d.ts +2 -2
- package/dist/rules/dispatch-errors.d.ts.map +1 -1
- package/dist/rules/dispatch-errors.js +10 -6
- package/dist/rules/dispatch-errors.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +9 -2
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +76 -23
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/index.d.ts +4 -5
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +3 -4
- package/dist/rules/processor.d.ts +3 -3
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +24 -31
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.d.ts +1 -3
- package/dist/rules/resolve.d.ts.map +1 -1
- package/dist/rules/resolve.js +1 -4
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +3 -1
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +20 -10
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +7 -7
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +21 -25
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +16 -11
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts +12 -2
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +29 -20
- package/dist/rules/validation.js.map +1 -1
- package/dist/session/live-sessions.d.ts +33 -3
- package/dist/session/live-sessions.d.ts.map +1 -1
- package/dist/session/live-sessions.js +67 -5
- package/dist/session/live-sessions.js.map +1 -1
- package/dist/storage/domains/intake/base.d.ts +54 -1
- package/dist/storage/domains/intake/base.d.ts.map +1 -1
- package/dist/storage/domains/intake/base.js +136 -4
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +22 -7
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +41 -26
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/health-worker.d.ts +2 -0
- package/dist/supervisor/health-worker.d.ts.map +1 -1
- package/dist/supervisor/health-worker.js +3 -1
- package/dist/supervisor/health-worker.js.map +1 -1
- package/dist/supervisor/health.d.ts +12 -14
- package/dist/supervisor/health.d.ts.map +1 -1
- package/dist/supervisor/health.js +24 -52
- package/dist/supervisor/health.js.map +1 -1
- package/dist/supervisor/instructions.d.ts +1 -1
- package/dist/supervisor/instructions.d.ts.map +1 -1
- package/dist/supervisor/instructions.js +10 -8
- package/dist/supervisor/instructions.js.map +1 -1
- package/dist/supervisor/read-tools.d.ts +2 -0
- package/dist/supervisor/read-tools.d.ts.map +1 -1
- package/dist/supervisor/read-tools.js +2 -2
- package/dist/supervisor/read-tools.js.map +1 -1
- package/dist/supervisor/write-tools.d.ts.map +1 -1
- package/dist/supervisor/write-tools.js +6 -4
- package/dist/supervisor/write-tools.js.map +1 -1
- package/dist/telemetry-types.d.ts +7 -0
- package/dist/telemetry-types.d.ts.map +1 -0
- package/dist/telemetry-types.js +24 -0
- package/dist/telemetry-types.js.map +1 -0
- package/dist/telemetry.d.ts +9 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +47 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/work-item-branch.d.ts +2 -0
- package/dist/work-item-branch.d.ts.map +1 -1
- package/dist/work-item-branch.js +6 -1
- package/dist/work-item-branch.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +3 -1
- package/dist/workspace.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +30 -10
- package/package.json +12 -10
- package/dist/rules/defaults.d.ts +0 -9
- package/dist/rules/defaults.d.ts.map +0 -1
- package/dist/rules/defaults.js +0 -50
- package/dist/rules/defaults.js.map +0 -1
package/dist/routes/projects.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { FACTORY_ROUTE_CONTRACTS } from "./contracts.js";
|
|
1
2
|
import { Route } from "./route.js";
|
|
2
3
|
import { registerApiRoute } from "@mastra/core/server";
|
|
3
4
|
//#region src/routes/projects.ts
|
|
4
5
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
6
|
const MAX_NAME_LENGTH = 200;
|
|
6
|
-
const MAX_DESCRIPTION_LENGTH = 2e3;
|
|
7
7
|
const MAX_REPOSITORY_COMMAND_LENGTH = 2e3;
|
|
8
8
|
const MAX_BRANCH_LENGTH = 255;
|
|
9
9
|
const MAX_SANDBOX_PROVIDER_LENGTH = 100;
|
|
@@ -19,58 +19,6 @@ async function readJson(context) {
|
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function parseCreateInput(value) {
|
|
23
|
-
if (!value || typeof value !== "object") return null;
|
|
24
|
-
const input = value;
|
|
25
|
-
if (typeof input.name !== "string") return null;
|
|
26
|
-
const name = input.name.trim();
|
|
27
|
-
if (!name || name.length > MAX_NAME_LENGTH) return null;
|
|
28
|
-
if (input.description !== void 0 && input.description !== null && typeof input.description !== "string") return null;
|
|
29
|
-
const description = typeof input.description === "string" ? input.description.trim() || null : null;
|
|
30
|
-
if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;
|
|
31
|
-
return {
|
|
32
|
-
name,
|
|
33
|
-
description
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function parseUpdateInput(value) {
|
|
37
|
-
if (!value || typeof value !== "object") return null;
|
|
38
|
-
const input = value;
|
|
39
|
-
const patch = {};
|
|
40
|
-
if (input.name !== void 0) {
|
|
41
|
-
if (typeof input.name !== "string") return null;
|
|
42
|
-
const name = input.name.trim();
|
|
43
|
-
if (!name || name.length > MAX_NAME_LENGTH) return null;
|
|
44
|
-
patch.name = name;
|
|
45
|
-
}
|
|
46
|
-
if (input.description !== void 0) {
|
|
47
|
-
if (input.description !== null && typeof input.description !== "string") return null;
|
|
48
|
-
const description = typeof input.description === "string" ? input.description.trim() || null : null;
|
|
49
|
-
if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;
|
|
50
|
-
patch.description = description;
|
|
51
|
-
}
|
|
52
|
-
if (input.defaultModelId !== void 0) {
|
|
53
|
-
const defaultModelId = parseOptionalString(input.defaultModelId, {
|
|
54
|
-
maxLength: MAX_NAME_LENGTH,
|
|
55
|
-
nullable: true
|
|
56
|
-
});
|
|
57
|
-
if (defaultModelId === false) return null;
|
|
58
|
-
patch.defaultModelId = defaultModelId ?? null;
|
|
59
|
-
}
|
|
60
|
-
if (input.slackWorkItemsEnabled !== void 0) {
|
|
61
|
-
if (typeof input.slackWorkItemsEnabled !== "boolean") return null;
|
|
62
|
-
patch.slackWorkItemsEnabled = input.slackWorkItemsEnabled;
|
|
63
|
-
}
|
|
64
|
-
if (input.autoRunEnabled !== void 0) {
|
|
65
|
-
if (typeof input.autoRunEnabled !== "boolean") return null;
|
|
66
|
-
patch.autoRunEnabled = input.autoRunEnabled;
|
|
67
|
-
}
|
|
68
|
-
if (input.autoApprovePlans !== void 0) {
|
|
69
|
-
if (typeof input.autoApprovePlans !== "boolean") return null;
|
|
70
|
-
patch.autoApprovePlans = input.autoApprovePlans;
|
|
71
|
-
}
|
|
72
|
-
return Object.keys(patch).length > 0 ? patch : null;
|
|
73
|
-
}
|
|
74
22
|
function parseConnectionInput(value) {
|
|
75
23
|
if (!value || typeof value !== "object") return null;
|
|
76
24
|
const input = value;
|
|
@@ -250,8 +198,8 @@ var ProjectRoutes = class extends Route {
|
|
|
250
198
|
}
|
|
251
199
|
routes() {
|
|
252
200
|
return [
|
|
253
|
-
registerApiRoute(
|
|
254
|
-
method:
|
|
201
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectList.path, {
|
|
202
|
+
method: FACTORY_ROUTE_CONTRACTS.projectList.method,
|
|
255
203
|
requiresAuth: false,
|
|
256
204
|
handler: async (routeContext) => {
|
|
257
205
|
const context = loose(routeContext);
|
|
@@ -260,64 +208,66 @@ var ProjectRoutes = class extends Route {
|
|
|
260
208
|
return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });
|
|
261
209
|
}
|
|
262
210
|
}),
|
|
263
|
-
registerApiRoute(
|
|
264
|
-
method:
|
|
211
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectCreate.path, {
|
|
212
|
+
method: FACTORY_ROUTE_CONTRACTS.projectCreate.method,
|
|
265
213
|
requiresAuth: false,
|
|
266
214
|
handler: async (routeContext) => {
|
|
267
215
|
const context = loose(routeContext);
|
|
268
216
|
const tenant = await this.#resolveTenant(context);
|
|
269
217
|
if ("response" in tenant) return tenant.response;
|
|
270
|
-
const
|
|
271
|
-
if (!
|
|
218
|
+
const parsed = FACTORY_ROUTE_CONTRACTS.projectCreate.bodySchema.safeParse(await readJson(context));
|
|
219
|
+
if (!parsed.success) return context.json({ error: "invalid_project" }, 400);
|
|
272
220
|
const project = await (await this.#projects()).create({
|
|
273
221
|
orgId: tenant.orgId,
|
|
274
222
|
userId: tenant.userId,
|
|
275
|
-
input
|
|
223
|
+
input: parsed.data
|
|
276
224
|
});
|
|
277
225
|
return context.json({ project }, 201);
|
|
278
226
|
}
|
|
279
227
|
}),
|
|
280
|
-
registerApiRoute(
|
|
281
|
-
method:
|
|
228
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectGet.path, {
|
|
229
|
+
method: FACTORY_ROUTE_CONTRACTS.projectGet.method,
|
|
282
230
|
requiresAuth: false,
|
|
283
231
|
handler: async (routeContext) => {
|
|
284
232
|
const context = loose(routeContext);
|
|
285
233
|
const tenant = await this.#resolveTenant(context);
|
|
286
234
|
if ("response" in tenant) return tenant.response;
|
|
287
|
-
const
|
|
288
|
-
if (!
|
|
235
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.projectGet.pathSchema.safeParse({ id: context.req.param("id") });
|
|
236
|
+
if (!parsedPath.success) return context.json({ error: "Project not found" }, 404);
|
|
237
|
+
const { id } = parsedPath.data;
|
|
289
238
|
const project = await this.#project(tenant.orgId, id);
|
|
290
239
|
return project ? context.json({ project }) : context.json({ error: "Project not found" }, 404);
|
|
291
240
|
}
|
|
292
241
|
}),
|
|
293
|
-
registerApiRoute(
|
|
294
|
-
method:
|
|
242
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectUpdate.path, {
|
|
243
|
+
method: FACTORY_ROUTE_CONTRACTS.projectUpdate.method,
|
|
295
244
|
requiresAuth: false,
|
|
296
245
|
handler: async (routeContext) => {
|
|
297
246
|
const context = loose(routeContext);
|
|
298
247
|
const tenant = await this.#resolveTenant(context);
|
|
299
248
|
if ("response" in tenant) return tenant.response;
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
302
|
-
const
|
|
303
|
-
if (!
|
|
249
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.projectUpdate.pathSchema.safeParse({ id: context.req.param("id") });
|
|
250
|
+
if (!parsedPath.success) return context.json({ error: "Project not found" }, 404);
|
|
251
|
+
const parsedBody = FACTORY_ROUTE_CONTRACTS.projectUpdate.bodySchema.safeParse(await readJson(context));
|
|
252
|
+
if (!parsedBody.success) return context.json({ error: "invalid_project" }, 400);
|
|
304
253
|
const project = await (await this.#projects()).update({
|
|
305
254
|
orgId: tenant.orgId,
|
|
306
|
-
id,
|
|
307
|
-
input
|
|
255
|
+
id: parsedPath.data.id,
|
|
256
|
+
input: parsedBody.data
|
|
308
257
|
});
|
|
309
258
|
return project ? context.json({ project }) : context.json({ error: "Project not found" }, 404);
|
|
310
259
|
}
|
|
311
260
|
}),
|
|
312
|
-
registerApiRoute(
|
|
313
|
-
method:
|
|
261
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectDelete.path, {
|
|
262
|
+
method: FACTORY_ROUTE_CONTRACTS.projectDelete.method,
|
|
314
263
|
requiresAuth: false,
|
|
315
264
|
handler: async (routeContext) => {
|
|
316
265
|
const context = loose(routeContext);
|
|
317
266
|
const tenant = await this.#resolveTenant(context);
|
|
318
267
|
if ("response" in tenant) return tenant.response;
|
|
319
|
-
const
|
|
320
|
-
if (!
|
|
268
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.projectDelete.pathSchema.safeParse({ id: context.req.param("id") });
|
|
269
|
+
if (!parsedPath.success) return context.json({ error: "Project not found" }, 404);
|
|
270
|
+
const { id } = parsedPath.data;
|
|
321
271
|
if (!await this.#project(tenant.orgId, id)) return context.json({ error: "Project not found" }, 404);
|
|
322
272
|
for (const handle of await this.#handles()) for (const connection of await handle.connections.list({
|
|
323
273
|
orgId: tenant.orgId,
|
|
@@ -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 SourceControlRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/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 if (input.autoApprovePlans !== undefined) {\n if (typeof input.autoApprovePlans !== 'boolean') return null;\n patch.autoApprovePlans = input.autoApprovePlans;\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 repository?: { externalId: string; slug: 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 const repositoryId =\n typeof input.repositoryId === 'string' && UUID_RE.test(input.repositoryId) ? input.repositoryId : undefined;\n const repositoryInput = input.repository;\n let repository: { externalId: string; slug: string } | undefined;\n if (repositoryInput && typeof repositoryInput === 'object') {\n const candidate = repositoryInput as Record<string, unknown>;\n const externalId = parseOptionalString(candidate.externalId, { maxLength: MAX_NAME_LENGTH });\n const slug = parseOptionalString(candidate.slug, { maxLength: MAX_NAME_LENGTH });\n if (typeof externalId !== 'string' || typeof slug !== 'string') return null;\n repository = { externalId, slug };\n }\n if (!repositoryId && !repository) 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 ? { repositoryId } : {}),\n ...(repository ? { repository } : {}),\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 /** Validate and persist a repository selected from a provider-backed listing. */\n resolveRepository?: (input: {\n integrationId: string;\n orgId: string;\n installationId: string;\n externalId: string;\n slug: string;\n }) => Promise<SourceControlRepository | null>;\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 /** Work-items domain — retired sessions drop the refs work items hold on them. */\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences'>;\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 ...(this.deps.workItems ? { workItems: this.deps.workItems } : {}),\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 = input.repositoryId\n ? await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId })\n : input.repository && this.deps.resolveRepository\n ? await this.deps.resolveRepository({\n integrationId: found.connection.integrationId,\n orgId: tenant.orgId,\n installationId: found.connection.installationId,\n ...input.repository,\n })\n : null;\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const { repositoryId: _repositoryId, repository: _repository, ...linkInput } = input;\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n repositoryId: repository.id,\n ...linkInput,\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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"}
|
|
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 { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type {\n ProjectRepository,\n SourceControlRepository,\n SourceControlStorage,\n SourceControlStorageHandle,\n UpdateProjectRepositoryInput,\n} from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { FACTORY_ROUTE_CONTRACTS } from './contracts.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_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 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 repository?: { externalId: string; slug: 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 const repositoryId =\n typeof input.repositoryId === 'string' && UUID_RE.test(input.repositoryId) ? input.repositoryId : undefined;\n const repositoryInput = input.repository;\n let repository: { externalId: string; slug: string } | undefined;\n if (repositoryInput && typeof repositoryInput === 'object') {\n const candidate = repositoryInput as Record<string, unknown>;\n const externalId = parseOptionalString(candidate.externalId, { maxLength: MAX_NAME_LENGTH });\n const slug = parseOptionalString(candidate.slug, { maxLength: MAX_NAME_LENGTH });\n if (typeof externalId !== 'string' || typeof slug !== 'string') return null;\n repository = { externalId, slug };\n }\n if (!repositoryId && !repository) 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 ? { repositoryId } : {}),\n ...(repository ? { repository } : {}),\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 /** Validate and persist a repository selected from a provider-backed listing. */\n resolveRepository?: (input: {\n integrationId: string;\n orgId: string;\n installationId: string;\n externalId: string;\n slug: string;\n }) => Promise<SourceControlRepository | null>;\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 /** Work-items domain — retired sessions drop the refs work items hold on them. */\n workItems?: Pick<WorkItemsStorage, 'clearSessionReferences'>;\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 ...(this.deps.workItems ? { workItems: this.deps.workItems } : {}),\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(FACTORY_ROUTE_CONTRACTS.projectList.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectList.method,\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(FACTORY_ROUTE_CONTRACTS.projectCreate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectCreate.method,\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 parsed = FACTORY_ROUTE_CONTRACTS.projectCreate.bodySchema.safeParse(await readJson(context));\n if (!parsed.success) return context.json({ error: 'invalid_project' }, 400);\n const project = await (\n await this.#projects()\n ).create({ orgId: tenant.orgId, userId: tenant.userId, input: parsed.data });\n return context.json({ project }, 201);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectGet.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectGet.method,\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 parsedPath = FACTORY_ROUTE_CONTRACTS.projectGet.pathSchema.safeParse({ id: context.req.param('id') });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const { id } = parsedPath.data;\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(FACTORY_ROUTE_CONTRACTS.projectUpdate.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectUpdate.method,\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 parsedPath = FACTORY_ROUTE_CONTRACTS.projectUpdate.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const parsedBody = FACTORY_ROUTE_CONTRACTS.projectUpdate.bodySchema.safeParse(await readJson(context));\n if (!parsedBody.success) return context.json({ error: 'invalid_project' }, 400);\n const project = await (\n await this.#projects()\n ).update({ orgId: tenant.orgId, id: parsedPath.data.id, input: parsedBody.data });\n return project ? context.json({ project }) : context.json({ error: 'Project not found' }, 404);\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.projectDelete.path, {\n method: FACTORY_ROUTE_CONTRACTS.projectDelete.method,\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 parsedPath = FACTORY_ROUTE_CONTRACTS.projectDelete.pathSchema.safeParse({\n id: context.req.param('id'),\n });\n if (!parsedPath.success) return context.json({ error: 'Project not found' }, 404);\n const { id } = parsedPath.data;\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 = input.repositoryId\n ? await found.handle.repositories.get({ orgId: tenant.orgId, id: input.repositoryId })\n : input.repository && this.deps.resolveRepository\n ? await this.deps.resolveRepository({\n integrationId: found.connection.integrationId,\n orgId: tenant.orgId,\n installationId: found.connection.installationId,\n ...input.repository,\n })\n : null;\n if (!repository || repository.installationId !== found.connection.installationId)\n return context.json({ error: 'Source-control repository not found' }, 404);\n const { repositoryId: _repositoryId, repository: _repository, ...linkInput } = input;\n const projectRepository = await found.handle.projectRepositories.link({\n orgId: tenant.orgId,\n connectionId,\n createdByUserId: tenant.userId,\n repositoryId: repository.id,\n ...linkInput,\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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"}
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* owns.
|
|
8
8
|
*/
|
|
9
9
|
import type { ApiRoute } from '@mastra/core/server';
|
|
10
|
+
import type { BoardRegistry } from '../boards/index.js';
|
|
10
11
|
import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
11
12
|
interface SupervisorRouteDependencies {
|
|
12
13
|
workItems: WorkItemsStorage;
|
|
14
|
+
boards: BoardRegistry;
|
|
13
15
|
resolveProject(context: unknown): Promise<{
|
|
14
16
|
orgId: string;
|
|
15
17
|
userId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/routes/supervisor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/routes/supervisor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAK9E,UAAU,2BAA2B;IACnC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,cAAc,CACZ,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;CAClG;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,2BAA2B,GAAG,QAAQ,EAAE,CA+B3F"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { FACTORY_ROUTE_CONTRACTS } from "./contracts.js";
|
|
1
2
|
import { supervisorResourceId, supervisorThreadId } from "../supervisor/session.js";
|
|
2
3
|
import { runFactoryHealthCheck } from "../supervisor/health.js";
|
|
3
4
|
import { registerApiRoute } from "@mastra/core/server";
|
|
4
5
|
//#region src/routes/supervisor.ts
|
|
5
6
|
function buildSupervisorRoutes(dependencies) {
|
|
6
|
-
const { workItems } = dependencies;
|
|
7
|
-
return [registerApiRoute(
|
|
8
|
-
method:
|
|
7
|
+
const { workItems, boards } = dependencies;
|
|
8
|
+
return [registerApiRoute(FACTORY_ROUTE_CONTRACTS.supervisorSession.path, {
|
|
9
|
+
method: FACTORY_ROUTE_CONTRACTS.supervisorSession.method,
|
|
9
10
|
requiresAuth: false,
|
|
10
11
|
handler: async (context) => {
|
|
11
12
|
const resolved = await dependencies.resolveProject(context);
|
|
@@ -16,14 +17,14 @@ function buildSupervisorRoutes(dependencies) {
|
|
|
16
17
|
factoryProjectId: resolved.factoryProjectId
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
|
-
}), registerApiRoute(
|
|
20
|
-
method:
|
|
20
|
+
}), registerApiRoute(FACTORY_ROUTE_CONTRACTS.supervisorHealth.path, {
|
|
21
|
+
method: FACTORY_ROUTE_CONTRACTS.supervisorHealth.method,
|
|
21
22
|
requiresAuth: false,
|
|
22
23
|
handler: async (context) => {
|
|
23
24
|
const resolved = await dependencies.resolveProject(context);
|
|
24
25
|
if ("response" in resolved) return resolved.response;
|
|
25
26
|
await workItems.ensureReady();
|
|
26
|
-
const report = await runFactoryHealthCheck(workItems, resolved);
|
|
27
|
+
const report = await runFactoryHealthCheck(workItems, boards, resolved);
|
|
27
28
|
return context.json(report);
|
|
28
29
|
}
|
|
29
30
|
})];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.js","names":[],"sources":["../../src/routes/supervisor.ts"],"sourcesContent":["/**\n * Supervisor routes: the per-factory supervisor session address and the\n * deterministic health check the supervisor page pins above its transcript.\n *\n * Mounted from {@link WorkItemRoutes} so they share its tenant + project\n * resolution: a caller can only address a supervisor for a project their org\n * owns.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\n\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { runFactoryHealthCheck } from '../supervisor/health.js';\nimport { supervisorResourceId, supervisorThreadId } from '../supervisor/session.js';\n\ninterface SupervisorRouteDependencies {\n workItems: WorkItemsStorage;\n resolveProject(\n context: unknown,\n ): Promise<{ orgId: string; userId: string; factoryProjectId: string } | { response: Response }>;\n}\n\nexport function buildSupervisorRoutes(dependencies: SupervisorRouteDependencies): ApiRoute[] {\n const { workItems } = dependencies;\n return [\n // The session is addressed deterministically and created lazily by the\n // agent controller on first reach (see hydrateSupervisorSession), so\n // \"ensure\" only has to hand back the address once ownership is verified.\n registerApiRoute(
|
|
1
|
+
{"version":3,"file":"supervisor.js","names":[],"sources":["../../src/routes/supervisor.ts"],"sourcesContent":["/**\n * Supervisor routes: the per-factory supervisor session address and the\n * deterministic health check the supervisor page pins above its transcript.\n *\n * Mounted from {@link WorkItemRoutes} so they share its tenant + project\n * resolution: a caller can only address a supervisor for a project their org\n * owns.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\n\nimport type { BoardRegistry } from '../boards/index.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { runFactoryHealthCheck } from '../supervisor/health.js';\nimport { supervisorResourceId, supervisorThreadId } from '../supervisor/session.js';\nimport { FACTORY_ROUTE_CONTRACTS } from './contracts.js';\n\ninterface SupervisorRouteDependencies {\n workItems: WorkItemsStorage;\n boards: BoardRegistry;\n resolveProject(\n context: unknown,\n ): Promise<{ orgId: string; userId: string; factoryProjectId: string } | { response: Response }>;\n}\n\nexport function buildSupervisorRoutes(dependencies: SupervisorRouteDependencies): ApiRoute[] {\n const { workItems, boards } = dependencies;\n return [\n // The session is addressed deterministically and created lazily by the\n // agent controller on first reach (see hydrateSupervisorSession), so\n // \"ensure\" only has to hand back the address once ownership is verified.\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.supervisorSession.path, {\n method: FACTORY_ROUTE_CONTRACTS.supervisorSession.method,\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n return context.json({\n sessionId: supervisorResourceId(resolved.factoryProjectId),\n threadId: supervisorThreadId(resolved.factoryProjectId),\n factoryProjectId: resolved.factoryProjectId,\n });\n },\n }),\n registerApiRoute(FACTORY_ROUTE_CONTRACTS.supervisorHealth.path, {\n method: FACTORY_ROUTE_CONTRACTS.supervisorHealth.method,\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n await workItems.ensureReady();\n const report = await runFactoryHealthCheck(workItems, boards, resolved);\n return context.json(report);\n },\n }),\n ];\n}\n"],"mappings":";;;;;AA0BA,SAAgB,sBAAsB,cAAuD;CAC3F,MAAM,EAAE,WAAW,WAAW;CAC9B,OAAO,CAIL,iBAAiB,wBAAwB,kBAAkB,MAAM;EAC/D,QAAQ,wBAAwB,kBAAkB;EAClD,cAAc;EACd,SAAS,OAAM,YAAW;GACxB,MAAM,WAAW,MAAM,aAAa,eAAe,OAAO;GAC1D,IAAI,cAAc,UAAU,OAAO,SAAS;GAC5C,OAAO,QAAQ,KAAK;IAClB,WAAW,qBAAqB,SAAS,gBAAgB;IACzD,UAAU,mBAAmB,SAAS,gBAAgB;IACtD,kBAAkB,SAAS;GAC7B,CAAC;EACH;CACF,CAAC,GACD,iBAAiB,wBAAwB,iBAAiB,MAAM;EAC9D,QAAQ,wBAAwB,iBAAiB;EACjD,cAAc;EACd,SAAS,OAAM,YAAW;GACxB,MAAM,WAAW,MAAM,aAAa,eAAe,OAAO;GAC1D,IAAI,cAAc,UAAU,OAAO,SAAS;GAC5C,MAAM,UAAU,YAAY;GAC5B,MAAM,SAAS,MAAM,sBAAsB,WAAW,QAAQ,QAAQ;GACtE,OAAO,QAAQ,KAAK,MAAM;EAC5B;CACF,CAAC,CACH;AACF"}
|
package/dist/routes/surface.d.ts
CHANGED
|
@@ -9,8 +9,8 @@ import type { GithubIntegration } from '../integrations/github/integration.js';
|
|
|
9
9
|
import type { FactoryBindingPreparationInput } from '../rules/dispatcher.js';
|
|
10
10
|
import { FactoryStartCoordinator } from '../rules/start-coordinator.js';
|
|
11
11
|
import { FactoryTransitionService } from '../rules/transition-service.js';
|
|
12
|
-
import type { FactoryRules } from '../rules/types.js';
|
|
13
12
|
import type { MastraFactorySandboxConfig } from '../sandbox/session-sandbox.js';
|
|
13
|
+
import type { LiveSessions } from '../session/live-sessions.js';
|
|
14
14
|
import type { StateSigner } from '../state-signing.js';
|
|
15
15
|
import type { AuditEmitter } from '../storage/domains/audit/domain.js';
|
|
16
16
|
import type { ChannelIdentityStorage } from '../storage/domains/channel-identity/base.js';
|
|
@@ -36,6 +36,8 @@ export interface IntegrationRegistration {
|
|
|
36
36
|
export interface FactoryApiRoutesDeps {
|
|
37
37
|
controllerId: string;
|
|
38
38
|
controller: AgentController<MastraCodeState>;
|
|
39
|
+
/** Registry of the sessions this process holds, owned by the host so it can watch them too. */
|
|
40
|
+
liveSessions: LiveSessions;
|
|
39
41
|
/** Request-auth seam threaded from the host (no service locator). */
|
|
40
42
|
auth: RouteAuth;
|
|
41
43
|
/** Optional user directory for resolving persisted owners to display profiles. */
|
|
@@ -70,7 +72,7 @@ export interface FactoryApiRoutesDeps {
|
|
|
70
72
|
factoryReady: boolean;
|
|
71
73
|
knowledgeEnabled: boolean;
|
|
72
74
|
/** Resolved Factory rule set, threaded from the host (no service locator). */
|
|
73
|
-
|
|
75
|
+
configVersion: string;
|
|
74
76
|
/** Boards installed for this Factory instance. */
|
|
75
77
|
boardRegistry: BoardRegistry;
|
|
76
78
|
/** Work-item feed service, handed to integrations that ingest platform messages. */
|
|
@@ -89,7 +91,7 @@ export interface FactoryApiRoutesDeps {
|
|
|
89
91
|
* browser and no interactive user, so nothing else would catch a regression in
|
|
90
92
|
* what it forwards.
|
|
91
93
|
*/
|
|
92
|
-
export declare function prepareFactoryRuleBinding(github: GithubIntegration, coordinator: Pick<FactoryStartCoordinator, 'prepare'>, projects: FactoryProjectsStorage, input: FactoryBindingPreparationInput): Promise<void>;
|
|
94
|
+
export declare function prepareFactoryRuleBinding(github: GithubIntegration, coordinator: Pick<FactoryStartCoordinator, 'prepare'>, projects: FactoryProjectsStorage, boards: BoardRegistry, input: FactoryBindingPreparationInput): Promise<void>;
|
|
93
95
|
/**
|
|
94
96
|
* Build the {@link IntegrationContext} handed to an integration when the
|
|
95
97
|
* factory collects its capabilities (routes, workers). One shape everywhere:
|
|
@@ -99,7 +101,8 @@ export declare function prepareFactoryRuleBinding(github: GithubIntegration, coo
|
|
|
99
101
|
export declare function buildIntegrationContext(deps: Pick<FactoryApiRoutesDeps, 'controller' | 'publicOrigin' | 'auth' | 'sandbox' | 'users' | 'factoryStorage' | 'integrationStorage' | 'sourceControlStorage'> & {
|
|
100
102
|
stateSigner: StateSigner;
|
|
101
103
|
emitAudit?: AuditEmitter['emit'];
|
|
102
|
-
|
|
104
|
+
configVersion: string;
|
|
105
|
+
boardRegistry: BoardRegistry;
|
|
103
106
|
factoryReady: boolean;
|
|
104
107
|
/** Work-item feed service, so a channel integration can ingest platform messages. */
|
|
105
108
|
feed: CommentsDomain;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
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
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { boardForWorkItem } from "../boards/semantics.js";
|
|
1
2
|
import { workItemBranch, workItemBranchSource, workItemThreadTitle } from "../work-item-branch.js";
|
|
2
|
-
import
|
|
3
|
+
import "../boards/index.js";
|
|
3
4
|
import { MaterializeError } from "../integrations/github/sandbox.js";
|
|
4
5
|
import { isAgentActor } from "../storage/domains/work-items/base.js";
|
|
5
6
|
import { getGithubFeatureDiagnostics } from "../integrations/github/config.js";
|
|
@@ -8,7 +9,6 @@ import { FactoryDispatchError } from "../rules/dispatch-errors.js";
|
|
|
8
9
|
import { FactorySourceSessionResolutionError, ensureFactorySourceSession, resolveFactoryDefaultModelId, resolveFactoryProjectForSession } from "../session/factory-session.js";
|
|
9
10
|
import { FactoryStartCoordinator } from "../rules/start-coordinator.js";
|
|
10
11
|
import { FactoryTransitionService } from "../rules/transition-service.js";
|
|
11
|
-
import { LiveSessions } from "../session/live-sessions.js";
|
|
12
12
|
import { SourceControlConnectionNotFoundError } from "../storage/domains/source-control/base.js";
|
|
13
13
|
import { ConfigRoutes } from "./config.js";
|
|
14
14
|
import { buildFsRoutes } from "./fs.js";
|
|
@@ -101,7 +101,7 @@ async function reuseBoundSession(sourceControl, input) {
|
|
|
101
101
|
* browser and no interactive user, so nothing else would catch a regression in
|
|
102
102
|
* what it forwards.
|
|
103
103
|
*/
|
|
104
|
-
async function prepareFactoryRuleBinding(github, coordinator, projects, input) {
|
|
104
|
+
async function prepareFactoryRuleBinding(github, coordinator, projects, boards, input) {
|
|
105
105
|
try {
|
|
106
106
|
const source = workItemBranchSource(input.item.externalSource);
|
|
107
107
|
const branch = workItemBranch({
|
|
@@ -109,8 +109,10 @@ async function prepareFactoryRuleBinding(github, coordinator, projects, input) {
|
|
|
109
109
|
source,
|
|
110
110
|
metadata: input.item.metadata
|
|
111
111
|
});
|
|
112
|
-
const
|
|
113
|
-
const
|
|
112
|
+
const board = boards.get(boardForWorkItem(input.item));
|
|
113
|
+
const currentStage = input.item.stages.length === 1 ? input.item.stages[0] : void 0;
|
|
114
|
+
const currentKind = currentStage === void 0 ? void 0 : board?.phaseKind(currentStage);
|
|
115
|
+
const destinationStage = currentKind === void 0 ? void 0 : currentKind === "resting" ? board?.phaseForRole(input.role) : currentStage;
|
|
114
116
|
if (!destinationStage) throw new FactoryDispatchError("unsupported_provider_item", `Factory skill invocation has no destination lane (role "${input.role}", stages [${input.item.stages.join(", ")}]).`);
|
|
115
117
|
const repositorySlug = typeof input.item.metadata?.repository === "string" ? input.item.metadata.repository : void 0;
|
|
116
118
|
const approver = input.record.approvedBy ?? void 0;
|
|
@@ -184,9 +186,10 @@ function buildIntegrationContext(deps, integrationId) {
|
|
|
184
186
|
workItems: deps.domains.workItems,
|
|
185
187
|
feed: deps.feed
|
|
186
188
|
} : {},
|
|
187
|
-
...deps.factoryReady ? {
|
|
188
|
-
|
|
189
|
-
workItems: deps.domains.workItems
|
|
189
|
+
...deps.factoryReady ? { runtime: {
|
|
190
|
+
configVersion: deps.configVersion,
|
|
191
|
+
workItems: deps.domains.workItems,
|
|
192
|
+
boards: deps.boardRegistry
|
|
190
193
|
} } : {},
|
|
191
194
|
...deps.emitAudit ? { hooks: { emitAudit: deps.emitAudit } } : {}
|
|
192
195
|
};
|
|
@@ -285,13 +288,14 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
285
288
|
const absentStubs = ["github", "linear"].filter((id) => !registrations.some(({ integration }) => integration.id === id)).flatMap((id) => disabledIntegrationStatusRoutes(deps, id));
|
|
286
289
|
const slackAbsentStubs = registrations.some(({ integration }) => integration.id === "slack") ? [] : absentSlackChannelAccountsRoutes();
|
|
287
290
|
const transitionService = deps.factoryReady ? deps.factoryTransitionService ?? new FactoryTransitionService({
|
|
288
|
-
|
|
291
|
+
configVersion: deps.configVersion,
|
|
292
|
+
boards: deps.boardRegistry,
|
|
289
293
|
storage: deps.domains.workItems
|
|
290
294
|
}) : void 0;
|
|
291
295
|
const startCoordinator = transitionService ? new FactoryStartCoordinator(deps.controller, deps.domains.workItems, transitionService, githubIntegration?.sourceControlStorage, deps.domains.memorySettings) : void 0;
|
|
292
296
|
if (transitionService && startCoordinator) deps.onFactoryRuntime?.({
|
|
293
297
|
transitionService,
|
|
294
|
-
...githubIntegration ? { prepareBinding: (input) => prepareFactoryRuleBinding(githubIntegration, startCoordinator, deps.domains.projects, input) } : {}
|
|
298
|
+
...githubIntegration ? { prepareBinding: (input) => prepareFactoryRuleBinding(githubIntegration, startCoordinator, deps.domains.projects, deps.boardRegistry, input) } : {}
|
|
295
299
|
});
|
|
296
300
|
return [
|
|
297
301
|
...buildFsRoutes({
|
|
@@ -338,6 +342,8 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
338
342
|
audit: deps.audit,
|
|
339
343
|
intake: deps.domains.intake,
|
|
340
344
|
projects: deps.domains.projects,
|
|
345
|
+
boardRegistry: deps.boardRegistry,
|
|
346
|
+
...deps.factoryReady ? { workItems: deps.domains.workItems } : {},
|
|
341
347
|
integrations: (deps.integrations ?? []).flatMap(({ integration }) => integration.intake ? [{
|
|
342
348
|
id: integration.id,
|
|
343
349
|
intake: integration.intake
|
|
@@ -358,7 +364,7 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
358
364
|
queueHealth: deps.domains.queueHealth,
|
|
359
365
|
transitionService,
|
|
360
366
|
startCoordinator,
|
|
361
|
-
liveSessions:
|
|
367
|
+
liveSessions: deps.liveSessions
|
|
362
368
|
}).routes() : []
|
|
363
369
|
];
|
|
364
370
|
}
|