@mastra/factory 0.13.0-alpha.13 → 0.13.0-alpha.15
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/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +12 -4
- package/dist/boards/review.js.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +14 -0
- package/dist/factory.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/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 +620 -0
- package/dist/routes/contracts.d.ts.map +1 -0
- package/dist/routes/contracts.js +450 -0
- package/dist/routes/contracts.js.map +1 -0
- 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.map +1 -1
- package/dist/routes/supervisor.js +5 -4
- package/dist/routes/supervisor.js.map +1 -1
- package/dist/routes/surface.d.ts +3 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +1 -2
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts +2 -2
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +94 -235
- 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 +6 -2
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +17 -12
- package/dist/rules/dispatcher.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/work-items/base.d.ts +8 -4
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +20 -9
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/health.d.ts +2 -2
- package/dist/supervisor/health.d.ts.map +1 -1
- package/dist/supervisor/health.js +13 -12
- package/dist/supervisor/health.js.map +1 -1
- 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/package.json +7 -6
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createBoardRegistry } from "../boards/registry.js";
|
|
2
2
|
import "../boards/index.js";
|
|
3
3
|
import { FACTORY_PULL_REQUEST_RECONCILIATION_KEY, FACTORY_RULE_MATERIALIZATION_KEY, WorkItemRelationError, WorkItemUpdateConflictError } from "../storage/domains/work-items/base.js";
|
|
4
|
+
import { FACTORY_ROUTE_CONTRACTS } from "./contracts.js";
|
|
4
5
|
import { Route } from "./route.js";
|
|
5
6
|
import { factoryDispatchFailureMetadata } from "../rules/dispatch-errors.js";
|
|
6
7
|
import { FactoryStartTransitionError } from "../rules/start-coordinator.js";
|
|
@@ -19,133 +20,29 @@ function toWireWorkItem(item) {
|
|
|
19
20
|
metadata: publicWorkItemMetadata(item.metadata) ?? {}
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
|
-
/** Session ids of the listed cards
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
for (const item of items) for (const { sessionId } of Object.values(item.sessions)) if (
|
|
26
|
-
return [...
|
|
23
|
+
/** Session ids of the listed cards that the predicate picks, each once. */
|
|
24
|
+
function sessionIdsWhere(items, predicate) {
|
|
25
|
+
const picked = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const item of items) for (const { sessionId } of Object.values(item.sessions)) if (predicate(sessionId)) picked.add(sessionId);
|
|
27
|
+
return [...picked];
|
|
27
28
|
}
|
|
28
29
|
function loose(c) {
|
|
29
30
|
return c;
|
|
30
31
|
}
|
|
31
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
32
|
-
const MAX_STAGES = 16;
|
|
33
|
-
const MAX_STAGE_LENGTH = 64;
|
|
34
|
-
const MAX_METADATA_BYTES = 16 * 1024;
|
|
35
|
-
function isRecord(value) {
|
|
36
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
-
}
|
|
38
|
-
function validStages(value) {
|
|
39
|
-
return Array.isArray(value) && value.length > 0 && value.length <= MAX_STAGES && value.every((stage) => typeof stage === "string" && stage.length <= MAX_STAGE_LENGTH && /^[a-z0-9][a-z0-9_-]*$/i.test(stage)) && new Set(value).size === value.length;
|
|
40
|
-
}
|
|
41
|
-
function validMetadata(value) {
|
|
42
|
-
if (value === null) return true;
|
|
43
|
-
if (!isRecord(value)) return false;
|
|
44
|
-
try {
|
|
45
|
-
return JSON.stringify(value).length <= MAX_METADATA_BYTES;
|
|
46
|
-
} catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
32
|
function publicWorkItemMetadata(value) {
|
|
51
33
|
if (value === null) return null;
|
|
52
34
|
const { [FACTORY_RULE_MATERIALIZATION_KEY]: _materialization, [FACTORY_PULL_REQUEST_RECONCILIATION_KEY]: _reconciliation, ...metadata } = value;
|
|
53
35
|
return metadata;
|
|
54
36
|
}
|
|
55
|
-
function parseExternalSource(value) {
|
|
56
|
-
if (value === void 0 || value === null) return value;
|
|
57
|
-
if (!isRecord(value)) return void 0;
|
|
58
|
-
const { integrationId, type, externalId, url } = value;
|
|
59
|
-
if (typeof integrationId !== "string" || integrationId.length === 0 || integrationId.length > 128) return void 0;
|
|
60
|
-
if (typeof type !== "string" || type.length === 0 || type.length > 128) return void 0;
|
|
61
|
-
if (typeof externalId !== "string" || externalId.length === 0 || externalId.length > 512) return void 0;
|
|
62
|
-
if (url !== void 0 && (typeof url !== "string" || url.length > 2048)) return void 0;
|
|
63
|
-
return {
|
|
64
|
-
integrationId,
|
|
65
|
-
type,
|
|
66
|
-
externalId,
|
|
67
|
-
...url !== void 0 ? { url } : {}
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function parseParentWorkItemId(value) {
|
|
71
|
-
if (value === null) return null;
|
|
72
|
-
if (typeof value !== "string" || !UUID_RE.test(value)) return void 0;
|
|
73
|
-
return value;
|
|
74
|
-
}
|
|
75
|
-
function parseSessions(value) {
|
|
76
|
-
if (!isRecord(value)) return void 0;
|
|
77
|
-
const out = {};
|
|
78
|
-
for (const [role, session] of Object.entries(value)) {
|
|
79
|
-
if (!role || role.length > 64 || !isRecord(session)) return void 0;
|
|
80
|
-
const { sessionId, branch, threadId } = session;
|
|
81
|
-
if (typeof sessionId !== "string" || sessionId.length === 0 || sessionId.length > 512) return void 0;
|
|
82
|
-
if (typeof branch !== "string" || branch.length === 0 || branch.length > 512) return void 0;
|
|
83
|
-
if (typeof threadId !== "string" || threadId.length === 0 || threadId.length > 512) return void 0;
|
|
84
|
-
out[role] = {
|
|
85
|
-
sessionId,
|
|
86
|
-
branch,
|
|
87
|
-
threadId
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
return out;
|
|
91
|
-
}
|
|
92
37
|
/** Validate an untrusted create body. Unknown keys are dropped. */
|
|
93
38
|
function parseCreateWorkItem(body) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (typeof title !== "string" || title.trim().length === 0 || title.length > 500) return null;
|
|
97
|
-
if (board !== void 0 && (typeof board !== "string" || board.length === 0 || board.length > 128)) return null;
|
|
98
|
-
const hasParentWorkItemId = "parentWorkItemId" in body;
|
|
99
|
-
const parentWorkItemId = hasParentWorkItemId ? parseParentWorkItemId(body.parentWorkItemId) : void 0;
|
|
100
|
-
if (hasParentWorkItemId && parentWorkItemId === void 0) return null;
|
|
101
|
-
const parsedSource = parseExternalSource(externalSource);
|
|
102
|
-
if (externalSource !== void 0 && parsedSource === void 0) return null;
|
|
103
|
-
if (stages !== void 0 && !validStages(stages)) return null;
|
|
104
|
-
const parsedSessions = sessions === void 0 ? void 0 : parseSessions(sessions);
|
|
105
|
-
if (sessions !== void 0 && parsedSessions === void 0) return null;
|
|
106
|
-
let parsedMetadata;
|
|
107
|
-
if (metadata !== void 0) {
|
|
108
|
-
if (!validMetadata(metadata)) return null;
|
|
109
|
-
parsedMetadata = publicWorkItemMetadata(metadata);
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
title: title.trim(),
|
|
113
|
-
...board !== void 0 ? { board } : {},
|
|
114
|
-
...parsedSource !== void 0 ? { externalSource: parsedSource } : {},
|
|
115
|
-
...hasParentWorkItemId ? { parentWorkItemId: parentWorkItemId ?? null } : {},
|
|
116
|
-
...stages !== void 0 ? { stages } : {},
|
|
117
|
-
...parsedSessions !== void 0 ? { sessions: parsedSessions } : {},
|
|
118
|
-
...parsedMetadata !== void 0 ? { metadata: parsedMetadata } : {}
|
|
119
|
-
};
|
|
39
|
+
const parsed = FACTORY_ROUTE_CONTRACTS.workItemCreate.bodySchema.safeParse(body);
|
|
40
|
+
return parsed.success ? parsed.data : null;
|
|
120
41
|
}
|
|
121
42
|
/** Validate an untrusted patch body. Unknown keys are dropped. */
|
|
122
43
|
function parseUpdateWorkItem(body) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const hasParentWorkItemId = "parentWorkItemId" in body;
|
|
126
|
-
if (board === void 0 && title === void 0 && stages === void 0 && sessions === void 0 && metadata === void 0 && plansPreapproved === void 0 && !hasParentWorkItemId) return null;
|
|
127
|
-
if (board !== void 0 && (typeof board !== "string" || board.length === 0 || board.length > 128)) return null;
|
|
128
|
-
if (plansPreapproved !== void 0 && plansPreapproved !== true) return null;
|
|
129
|
-
const parentWorkItemId = hasParentWorkItemId ? parseParentWorkItemId(body.parentWorkItemId) : void 0;
|
|
130
|
-
if (hasParentWorkItemId && parentWorkItemId === void 0) return null;
|
|
131
|
-
if (title !== void 0 && (typeof title !== "string" || title.trim().length === 0 || title.length > 500)) return null;
|
|
132
|
-
if (stages !== void 0 && !validStages(stages)) return null;
|
|
133
|
-
const parsedSessions = sessions === void 0 ? void 0 : parseSessions(sessions);
|
|
134
|
-
if (sessions !== void 0 && parsedSessions === void 0) return null;
|
|
135
|
-
let parsedMetadata;
|
|
136
|
-
if (metadata !== void 0) {
|
|
137
|
-
if (!validMetadata(metadata)) return null;
|
|
138
|
-
parsedMetadata = publicWorkItemMetadata(metadata);
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
...board !== void 0 ? { board } : {},
|
|
142
|
-
...hasParentWorkItemId ? { parentWorkItemId: parentWorkItemId ?? null } : {},
|
|
143
|
-
...title !== void 0 ? { title: title.trim() } : {},
|
|
144
|
-
...stages !== void 0 ? { stages } : {},
|
|
145
|
-
...parsedSessions !== void 0 ? { sessions: parsedSessions } : {},
|
|
146
|
-
...parsedMetadata !== void 0 ? { metadata: parsedMetadata } : {},
|
|
147
|
-
...plansPreapproved === true ? { plansPreapproved: true } : {}
|
|
148
|
-
};
|
|
44
|
+
const parsed = FACTORY_ROUTE_CONTRACTS.workItemUpdate.bodySchema.safeParse(body);
|
|
45
|
+
return parsed.success ? parsed.data : null;
|
|
149
46
|
}
|
|
150
47
|
async function readJson(c) {
|
|
151
48
|
try {
|
|
@@ -158,92 +55,21 @@ async function readJson(c) {
|
|
|
158
55
|
function patchedFields(patch) {
|
|
159
56
|
return Object.keys(patch).filter((key) => patch[key] !== void 0);
|
|
160
57
|
}
|
|
161
|
-
function boundedText(value, max) {
|
|
162
|
-
if (typeof value !== "string") return void 0;
|
|
163
|
-
const normalized = value.trim();
|
|
164
|
-
return normalized.length > 0 && normalized.length <= max ? normalized : void 0;
|
|
165
|
-
}
|
|
166
58
|
function parseTransitionBody(body) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const stage = typeof body.stage === "string" && body.stage.length > 0 ? body.stage : void 0;
|
|
170
|
-
const requestId = boundedText(body.requestId, 256);
|
|
171
|
-
const cause = boundedText(body.cause, 256);
|
|
172
|
-
if (!board || !stage || !requestId || !UUID_RE.test(requestId) || !cause || !Number.isInteger(body.expectedRevision) || Number(body.expectedRevision) < 1) return null;
|
|
173
|
-
return {
|
|
174
|
-
board,
|
|
175
|
-
stage,
|
|
176
|
-
expectedRevision: Number(body.expectedRevision),
|
|
177
|
-
ingress: {
|
|
178
|
-
type: "human",
|
|
179
|
-
identity: requestId
|
|
180
|
-
},
|
|
181
|
-
cause,
|
|
182
|
-
...body.reenter === true ? { reenter: true } : {}
|
|
183
|
-
};
|
|
59
|
+
const parsed = FACTORY_ROUTE_CONTRACTS.workItemTransition.bodySchema.safeParse(body);
|
|
60
|
+
return parsed.success ? parsed.data : null;
|
|
184
61
|
}
|
|
185
62
|
function parseStartBody(body, tenant, factoryProjectId) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const sessionId = boundedText(body.sessionId, 256);
|
|
189
|
-
const threadTitle = boundedText(body.threadTitle, 512);
|
|
190
|
-
const kickoffKey = boundedText(body.kickoffKey, 256);
|
|
191
|
-
const role = boundedText(body.workItem.role, 32);
|
|
192
|
-
const id = boundedText(body.workItem.id, 64);
|
|
193
|
-
if (!input || !sessionId || !UUID_RE.test(sessionId) || !threadTitle || !kickoffKey || !UUID_RE.test(kickoffKey) || !id || !UUID_RE.test(id) || !role) return null;
|
|
194
|
-
return {
|
|
63
|
+
const parsed = FACTORY_ROUTE_CONTRACTS.workItemStart.bodySchema.safeParse(body);
|
|
64
|
+
return parsed.success ? {
|
|
195
65
|
...tenant,
|
|
196
66
|
factoryProjectId,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
kickoffKey,
|
|
200
|
-
workItem: {
|
|
201
|
-
id,
|
|
202
|
-
role,
|
|
203
|
-
input
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
const DECISION_STATUSES = /* @__PURE__ */ new Set([
|
|
208
|
-
"pending",
|
|
209
|
-
"proposed",
|
|
210
|
-
"dismissed",
|
|
211
|
-
"superseded",
|
|
212
|
-
"leased",
|
|
213
|
-
"retry",
|
|
214
|
-
"succeeded",
|
|
215
|
-
"failed"
|
|
216
|
-
]);
|
|
217
|
-
const DEFAULT_DECISION_PAGE_SIZE = 25;
|
|
218
|
-
const MAX_DECISION_PAGE_SIZE = 50;
|
|
219
|
-
function parseDecisionStatuses(raw) {
|
|
220
|
-
if (!raw) return void 0;
|
|
221
|
-
const statuses = [...new Set(raw.split(",").map((status) => status.trim()))].filter((status) => DECISION_STATUSES.has(status));
|
|
222
|
-
return statuses.length > 0 ? statuses : void 0;
|
|
223
|
-
}
|
|
224
|
-
function parseDecisionLimit(raw) {
|
|
225
|
-
const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_DECISION_PAGE_SIZE;
|
|
226
|
-
if (!Number.isFinite(parsed)) return DEFAULT_DECISION_PAGE_SIZE;
|
|
227
|
-
return Math.max(1, Math.min(MAX_DECISION_PAGE_SIZE, parsed));
|
|
67
|
+
...parsed.data
|
|
68
|
+
} : null;
|
|
228
69
|
}
|
|
229
70
|
function encodeDecisionCursor(decision) {
|
|
230
71
|
return Buffer.from(JSON.stringify([decision.createdAt.toISOString(), decision.id]), "utf8").toString("base64url");
|
|
231
72
|
}
|
|
232
|
-
function parseDecisionCursor(raw) {
|
|
233
|
-
if (!raw) return void 0;
|
|
234
|
-
try {
|
|
235
|
-
const decoded = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
236
|
-
if (!Array.isArray(decoded) || decoded.length !== 2 || typeof decoded[0] !== "string" || typeof decoded[1] !== "string") return;
|
|
237
|
-
const createdAt = new Date(decoded[0]);
|
|
238
|
-
if (Number.isNaN(createdAt.getTime()) || !UUID_RE.test(decoded[1])) return void 0;
|
|
239
|
-
return {
|
|
240
|
-
createdAt,
|
|
241
|
-
id: decoded[1]
|
|
242
|
-
};
|
|
243
|
-
} catch {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
73
|
/** A proposed transition names the seat its lane addresses, so the card can label what approving starts. */
|
|
248
74
|
function summaryRole(boards, decision) {
|
|
249
75
|
if (typeof decision.role === "string") return decision.role.slice(0, 32);
|
|
@@ -304,8 +130,9 @@ var WorkItemRoutes = class extends Route {
|
|
|
304
130
|
async #resolveProject(c) {
|
|
305
131
|
const tenant = await this.#resolveTenant(c);
|
|
306
132
|
if ("response" in tenant) return tenant;
|
|
307
|
-
const
|
|
308
|
-
if (!
|
|
133
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.projectGet.pathSchema.safeParse({ id: c.req.param("id") });
|
|
134
|
+
if (!parsedPath.success) return { response: c.json({ error: "Project not found" }, 404) };
|
|
135
|
+
const projectId = parsedPath.data.id;
|
|
309
136
|
const { projects } = this.deps;
|
|
310
137
|
await projects.ensureReady();
|
|
311
138
|
const project = await projects.get({
|
|
@@ -374,15 +201,20 @@ var WorkItemRoutes = class extends Route {
|
|
|
374
201
|
/** Releasing a parked run and dropping it: same request, opposite outcomes, both audited as consent. */
|
|
375
202
|
#proposalRoute({ verb, settle }) {
|
|
376
203
|
const { audit, workItems } = this.deps;
|
|
377
|
-
|
|
378
|
-
|
|
204
|
+
const contract = verb === "approve" ? FACTORY_ROUTE_CONTRACTS.decisionApprove : FACTORY_ROUTE_CONTRACTS.decisionDismiss;
|
|
205
|
+
return registerApiRoute(contract.path, {
|
|
206
|
+
method: contract.method,
|
|
379
207
|
requiresAuth: false,
|
|
380
208
|
handler: async (c) => {
|
|
381
209
|
const context = loose(c);
|
|
382
210
|
const resolved = await this.#resolveProject(context);
|
|
383
211
|
if ("response" in resolved) return resolved.response;
|
|
384
|
-
const
|
|
385
|
-
|
|
212
|
+
const parsedPath = contract.pathSchema.safeParse({
|
|
213
|
+
id: resolved.factoryProjectId,
|
|
214
|
+
decisionId: context.req.param("decisionId")
|
|
215
|
+
});
|
|
216
|
+
if (!parsedPath.success) return c.json({ error: "invalid_decision_id" }, 422);
|
|
217
|
+
const { decisionId } = parsedPath.data;
|
|
386
218
|
await workItems.ensureReady();
|
|
387
219
|
const now = /* @__PURE__ */ new Date();
|
|
388
220
|
const decision = await settle(resolved.orgId, resolved.factoryProjectId, decisionId, now, resolved.userId);
|
|
@@ -413,8 +245,8 @@ var WorkItemRoutes = class extends Route {
|
|
|
413
245
|
routes() {
|
|
414
246
|
const { audit, workItems, queueHealth, transitionService, startCoordinator, liveSessions } = this.deps;
|
|
415
247
|
return [
|
|
416
|
-
registerApiRoute(
|
|
417
|
-
method:
|
|
248
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.workItemList.path, {
|
|
249
|
+
method: FACTORY_ROUTE_CONTRACTS.workItemList.method,
|
|
418
250
|
requiresAuth: false,
|
|
419
251
|
handler: async (c) => {
|
|
420
252
|
const resolved = await this.#resolveProject(loose(c));
|
|
@@ -426,17 +258,24 @@ var WorkItemRoutes = class extends Route {
|
|
|
426
258
|
});
|
|
427
259
|
return c.json({
|
|
428
260
|
workItems: items.map(toWireWorkItem),
|
|
429
|
-
runningSessionIds:
|
|
261
|
+
runningSessionIds: sessionIdsWhere(items, (sessionId) => liveSessions.isRunning(sessionId)),
|
|
262
|
+
parkedSessionIds: sessionIdsWhere(items, (sessionId) => liveSessions.parked(sessionId) !== void 0)
|
|
430
263
|
});
|
|
431
264
|
}
|
|
432
265
|
}),
|
|
433
|
-
registerApiRoute(
|
|
434
|
-
method:
|
|
266
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.metricsGet.path, {
|
|
267
|
+
method: FACTORY_ROUTE_CONTRACTS.metricsGet.method,
|
|
435
268
|
requiresAuth: false,
|
|
436
269
|
handler: async (c) => {
|
|
437
|
-
const
|
|
270
|
+
const context = loose(c);
|
|
271
|
+
const resolved = await this.#resolveProject(context);
|
|
438
272
|
if ("response" in resolved) return resolved.response;
|
|
439
|
-
const
|
|
273
|
+
const query = FACTORY_ROUTE_CONTRACTS.metricsGet.querySchema.safeParse({
|
|
274
|
+
from: context.req.query("from"),
|
|
275
|
+
to: context.req.query("to")
|
|
276
|
+
});
|
|
277
|
+
if (!query.success) return c.json({ error: "invalid_metrics_range" }, 400);
|
|
278
|
+
const { windowStart, windowEnd } = parseMetricsRange(query.data.from, query.data.to, /* @__PURE__ */ new Date());
|
|
440
279
|
await workItems.ensureReady();
|
|
441
280
|
const items = await workItems.list({
|
|
442
281
|
orgId: resolved.orgId,
|
|
@@ -448,8 +287,8 @@ var WorkItemRoutes = class extends Route {
|
|
|
448
287
|
}) });
|
|
449
288
|
}
|
|
450
289
|
}),
|
|
451
|
-
registerApiRoute(
|
|
452
|
-
method:
|
|
290
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.healthThresholdsGet.path, {
|
|
291
|
+
method: FACTORY_ROUTE_CONTRACTS.healthThresholdsGet.method,
|
|
453
292
|
requiresAuth: false,
|
|
454
293
|
handler: async (c) => {
|
|
455
294
|
const resolved = await this.#resolveProject(loose(c));
|
|
@@ -459,23 +298,29 @@ var WorkItemRoutes = class extends Route {
|
|
|
459
298
|
return c.json({ thresholds: thresholdsOrDefault(stored) });
|
|
460
299
|
}
|
|
461
300
|
}),
|
|
462
|
-
registerApiRoute(
|
|
463
|
-
method:
|
|
301
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.decisionList.path, {
|
|
302
|
+
method: FACTORY_ROUTE_CONTRACTS.decisionList.method,
|
|
464
303
|
requiresAuth: false,
|
|
465
304
|
handler: async (c) => {
|
|
466
305
|
const context = loose(c);
|
|
467
306
|
const resolved = await this.#resolveProject(context);
|
|
468
307
|
if ("response" in resolved) return resolved.response;
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
308
|
+
const query = FACTORY_ROUTE_CONTRACTS.decisionList.querySchema.safeParse({
|
|
309
|
+
statuses: context.req.query("statuses"),
|
|
310
|
+
before: context.req.query("before"),
|
|
311
|
+
limit: context.req.query("limit")
|
|
312
|
+
});
|
|
313
|
+
if (!query.success) {
|
|
314
|
+
const field = query.error.issues[0]?.path[0];
|
|
315
|
+
return c.json({ error: field === "before" ? "invalid_cursor" : "invalid_decision_query" }, 400);
|
|
316
|
+
}
|
|
472
317
|
await workItems.ensureReady();
|
|
473
318
|
const page = await workItems.listDeferredDecisionPage({
|
|
474
319
|
orgId: resolved.orgId,
|
|
475
320
|
factoryProjectId: resolved.factoryProjectId,
|
|
476
|
-
statuses:
|
|
477
|
-
before,
|
|
478
|
-
limit:
|
|
321
|
+
statuses: query.data.statuses,
|
|
322
|
+
before: query.data.before,
|
|
323
|
+
limit: query.data.limit
|
|
479
324
|
});
|
|
480
325
|
const last = page.decisions.at(-1);
|
|
481
326
|
return c.json({
|
|
@@ -487,6 +332,7 @@ var WorkItemRoutes = class extends Route {
|
|
|
487
332
|
...buildAttentionRoutes({
|
|
488
333
|
workItems,
|
|
489
334
|
comments: this.deps.comments,
|
|
335
|
+
liveSessions,
|
|
490
336
|
resolveProject: (context) => this.#resolveProject(loose(context))
|
|
491
337
|
}),
|
|
492
338
|
...buildSupervisorRoutes({
|
|
@@ -502,15 +348,19 @@ var WorkItemRoutes = class extends Route {
|
|
|
502
348
|
verb: "dismiss",
|
|
503
349
|
settle: workItems.dismissDeferredDecision.bind(workItems)
|
|
504
350
|
}),
|
|
505
|
-
registerApiRoute(
|
|
506
|
-
method:
|
|
351
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.decisionRetry.path, {
|
|
352
|
+
method: FACTORY_ROUTE_CONTRACTS.decisionRetry.method,
|
|
507
353
|
requiresAuth: false,
|
|
508
354
|
handler: async (c) => {
|
|
509
355
|
const context = loose(c);
|
|
510
356
|
const resolved = await this.#resolveProject(context);
|
|
511
357
|
if ("response" in resolved) return resolved.response;
|
|
512
|
-
const
|
|
513
|
-
|
|
358
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.decisionRetry.pathSchema.safeParse({
|
|
359
|
+
id: resolved.factoryProjectId,
|
|
360
|
+
decisionId: context.req.param("decisionId")
|
|
361
|
+
});
|
|
362
|
+
if (!parsedPath.success) return c.json({ error: "invalid_decision_id" }, 422);
|
|
363
|
+
const { decisionId } = parsedPath.data;
|
|
514
364
|
await workItems.ensureReady();
|
|
515
365
|
const current = await workItems.getDeferredDecision(resolved.orgId, resolved.factoryProjectId, decisionId);
|
|
516
366
|
if (!current || current.status !== "failed" || !factoryDispatchFailureMetadata(current.failureCode).canRetry) return c.json({ error: "decision_not_retryable" }, 409);
|
|
@@ -519,8 +369,8 @@ var WorkItemRoutes = class extends Route {
|
|
|
519
369
|
return c.json({ decision: decisionSummary(this.#boards, decision) });
|
|
520
370
|
}
|
|
521
371
|
}),
|
|
522
|
-
registerApiRoute(
|
|
523
|
-
method:
|
|
372
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.workItemCreate.path, {
|
|
373
|
+
method: FACTORY_ROUTE_CONTRACTS.workItemCreate.method,
|
|
524
374
|
requiresAuth: false,
|
|
525
375
|
handler: async (c) => {
|
|
526
376
|
const resolved = await this.#resolveProject(loose(c));
|
|
@@ -627,15 +477,20 @@ var WorkItemRoutes = class extends Route {
|
|
|
627
477
|
}
|
|
628
478
|
}
|
|
629
479
|
}),
|
|
630
|
-
registerApiRoute(
|
|
631
|
-
method:
|
|
480
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.workItemTransition.path, {
|
|
481
|
+
method: FACTORY_ROUTE_CONTRACTS.workItemTransition.method,
|
|
632
482
|
requiresAuth: false,
|
|
633
483
|
handler: async (c) => {
|
|
634
484
|
const resolved = await this.#resolveProject(loose(c));
|
|
635
485
|
if ("response" in resolved) return resolved.response;
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
486
|
+
const context = loose(c);
|
|
487
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.workItemTransition.pathSchema.safeParse({
|
|
488
|
+
id: resolved.factoryProjectId,
|
|
489
|
+
workItemId: context.req.param("workItemId")
|
|
490
|
+
});
|
|
491
|
+
if (!parsedPath.success) return c.json({ error: "Work item not found" }, 404);
|
|
492
|
+
const { workItemId } = parsedPath.data;
|
|
493
|
+
const parsed = parseTransitionBody(await readJson(context));
|
|
639
494
|
if (!parsed) return c.json({ error: "invalid_transition_request" }, 400);
|
|
640
495
|
if (!transitionService) return c.json({ error: "factory_transition_unavailable" }, 503);
|
|
641
496
|
await workItems.ensureReady();
|
|
@@ -680,8 +535,8 @@ var WorkItemRoutes = class extends Route {
|
|
|
680
535
|
return c.json({ result }, result.code === "stale" ? 409 : 422);
|
|
681
536
|
}
|
|
682
537
|
}),
|
|
683
|
-
registerApiRoute(
|
|
684
|
-
method:
|
|
538
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.workItemStart.path, {
|
|
539
|
+
method: FACTORY_ROUTE_CONTRACTS.workItemStart.method,
|
|
685
540
|
requiresAuth: false,
|
|
686
541
|
handler: async (c) => {
|
|
687
542
|
const resolved = await this.#resolveProject(loose(c));
|
|
@@ -720,15 +575,17 @@ var WorkItemRoutes = class extends Route {
|
|
|
720
575
|
return c.json({ prepared }, 202);
|
|
721
576
|
}
|
|
722
577
|
}),
|
|
723
|
-
registerApiRoute(
|
|
724
|
-
method:
|
|
578
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.workItemUpdate.path, {
|
|
579
|
+
method: FACTORY_ROUTE_CONTRACTS.workItemUpdate.method,
|
|
725
580
|
requiresAuth: false,
|
|
726
581
|
handler: async (c) => {
|
|
727
582
|
const tenant = await this.#resolveTenant(loose(c));
|
|
728
583
|
if ("response" in tenant) return tenant.response;
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
584
|
+
const context = loose(c);
|
|
585
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.workItemUpdate.pathSchema.safeParse({ id: context.req.param("id") });
|
|
586
|
+
if (!parsedPath.success) return c.json({ error: "Work item not found" }, 404);
|
|
587
|
+
const { id } = parsedPath.data;
|
|
588
|
+
const body = await readJson(context);
|
|
732
589
|
if (body === void 0) return c.json({ error: "Invalid JSON body" }, 400);
|
|
733
590
|
const patch = parseUpdateWorkItem(body);
|
|
734
591
|
if (!patch) return c.json({ error: "invalid_work_item_patch" }, 400);
|
|
@@ -777,14 +634,16 @@ var WorkItemRoutes = class extends Route {
|
|
|
777
634
|
}
|
|
778
635
|
}
|
|
779
636
|
}),
|
|
780
|
-
registerApiRoute(
|
|
781
|
-
method:
|
|
637
|
+
registerApiRoute(FACTORY_ROUTE_CONTRACTS.workItemDelete.path, {
|
|
638
|
+
method: FACTORY_ROUTE_CONTRACTS.workItemDelete.method,
|
|
782
639
|
requiresAuth: false,
|
|
783
640
|
handler: async (c) => {
|
|
784
641
|
const tenant = await this.#resolveTenant(loose(c));
|
|
785
642
|
if ("response" in tenant) return tenant.response;
|
|
786
|
-
const
|
|
787
|
-
|
|
643
|
+
const context = loose(c);
|
|
644
|
+
const parsedPath = FACTORY_ROUTE_CONTRACTS.workItemDelete.pathSchema.safeParse({ id: context.req.param("id") });
|
|
645
|
+
if (!parsedPath.success) return c.json({ error: "Work item not found" }, 404);
|
|
646
|
+
const { id } = parsedPath.data;
|
|
788
647
|
await workItems.ensureReady();
|
|
789
648
|
const deleted = await workItems.delete({
|
|
790
649
|
orgId: tenant.orgId,
|