@mastra/factory 0.9.0 → 0.10.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +179 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/factory.d.ts +8 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +24 -8
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +1 -0
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/provenance.d.ts +7 -2
- package/dist/integrations/github/provenance.d.ts.map +1 -1
- package/dist/integrations/github/provenance.js +16 -2
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.d.ts +3 -0
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +165 -6
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +193 -127
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +50 -0
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +1 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/routes/attention.d.ts +17 -0
- package/dist/routes/attention.d.ts.map +1 -0
- package/dist/routes/attention.js +305 -0
- package/dist/routes/attention.js.map +1 -0
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +11 -2
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/surface.d.ts +2 -3
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +61 -43
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +35 -14
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/defaults.js +5 -5
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatch-errors.d.ts +13 -0
- package/dist/rules/dispatch-errors.d.ts.map +1 -0
- package/dist/rules/dispatch-errors.js +77 -0
- package/dist/rules/dispatch-errors.js.map +1 -0
- package/dist/rules/dispatcher.d.ts +9 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +118 -45
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +30 -14
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/terminal-cleanup.d.ts +1 -1
- package/dist/rules/terminal-cleanup.d.ts.map +1 -1
- package/dist/rules/terminal-cleanup.js +2 -2
- package/dist/rules/terminal-cleanup.js.map +1 -1
- package/dist/rules/transition-service.d.ts +2 -1
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +1 -1
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +2 -0
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +9 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/secret-encryption.d.ts +25 -0
- package/dist/secret-encryption.d.ts.map +1 -0
- package/dist/secret-encryption.js +96 -0
- package/dist/secret-encryption.js.map +1 -0
- package/dist/session/factory-session.d.ts +13 -3
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +15 -6
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/first-exec-capture.d.ts +16 -10
- package/dist/session/first-exec-capture.d.ts.map +1 -1
- package/dist/session/first-exec-capture.js +74 -14
- package/dist/session/first-exec-capture.js.map +1 -1
- package/dist/session/first-message-capture.d.ts +14 -10
- package/dist/session/first-message-capture.d.ts.map +1 -1
- package/dist/session/first-message-capture.js +17 -11
- package/dist/session/first-message-capture.js.map +1 -1
- package/dist/session/session-title.d.ts +7 -0
- package/dist/session/session-title.d.ts.map +1 -0
- package/dist/session/session-title.js +14 -0
- package/dist/session/session-title.js.map +1 -0
- package/dist/session/thread-title-mirror.d.ts +35 -0
- package/dist/session/thread-title-mirror.d.ts.map +1 -0
- package/dist/session/thread-title-mirror.js +51 -0
- package/dist/session/thread-title-mirror.js.map +1 -0
- package/dist/storage/domains/credentials/base.d.ts +3 -1
- package/dist/storage/domains/credentials/base.d.ts.map +1 -1
- package/dist/storage/domains/credentials/base.js +33 -15
- package/dist/storage/domains/credentials/base.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.d.ts +3 -1
- package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
- package/dist/storage/domains/custom-providers/base.js +45 -25
- package/dist/storage/domains/custom-providers/base.js.map +1 -1
- package/dist/storage/domains/intake/base.d.ts +6 -5
- package/dist/storage/domains/intake/base.d.ts.map +1 -1
- package/dist/storage/domains/intake/base.js +21 -13
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.d.ts +3 -1
- package/dist/storage/domains/integrations/base.d.ts.map +1 -1
- package/dist/storage/domains/integrations/base.js +28 -12
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +8 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +14 -2
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +7 -0
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +115 -12
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +497 -43
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/work-item-branch.d.ts +23 -0
- package/dist/work-item-branch.d.ts.map +1 -0
- package/dist/work-item-branch.js +39 -0
- package/dist/work-item-branch.js.map +1 -0
- package/factory-skills/factory-rereview/SKILL.md +2 -0
- package/factory-skills/factory-review/SKILL.md +2 -0
- package/package.json +7 -7
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { factoryAttentionKey, factoryDecisionAttentionIdentity } from "../storage/domains/work-items/base.js";
|
|
2
|
+
import { factoryDispatchFailureMetadata } from "../rules/dispatch-errors.js";
|
|
3
|
+
import { registerApiRoute } from "@mastra/core/server";
|
|
4
|
+
//#region src/routes/attention.ts
|
|
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;
|
|
6
|
+
const DEFAULT_PAGE_SIZE = 25;
|
|
7
|
+
const MAX_PAGE_SIZE = 50;
|
|
8
|
+
const MAX_RECEIPT_SCAN_PAGES = 4;
|
|
9
|
+
function factoryDecisionType(decision) {
|
|
10
|
+
return typeof decision.decision.type === "string" ? decision.decision.type.slice(0, 64) : "unknown";
|
|
11
|
+
}
|
|
12
|
+
function parseAttentionView(raw) {
|
|
13
|
+
if (!raw || raw === "open") return "open";
|
|
14
|
+
if (raw === "unread" || raw === "archived") return raw;
|
|
15
|
+
}
|
|
16
|
+
function parseAttentionLimit(raw) {
|
|
17
|
+
const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_PAGE_SIZE;
|
|
18
|
+
if (!Number.isFinite(parsed)) return DEFAULT_PAGE_SIZE;
|
|
19
|
+
return Math.max(1, Math.min(MAX_PAGE_SIZE, parsed));
|
|
20
|
+
}
|
|
21
|
+
function attentionIdentity(decision) {
|
|
22
|
+
return factoryDecisionAttentionIdentity(decision.id, decision.failureOccurrence);
|
|
23
|
+
}
|
|
24
|
+
function attentionKey(factoryProjectId, decision) {
|
|
25
|
+
return factoryAttentionKey(factoryProjectId, attentionIdentity(decision));
|
|
26
|
+
}
|
|
27
|
+
function failureOccurredAt(decision) {
|
|
28
|
+
return decision.completedAt ?? decision.updatedAt;
|
|
29
|
+
}
|
|
30
|
+
function encodeAttentionCursor(decision) {
|
|
31
|
+
return Buffer.from(JSON.stringify([failureOccurredAt(decision).toISOString(), decision.id]), "utf8").toString("base64url");
|
|
32
|
+
}
|
|
33
|
+
function parseAttentionCursor(raw) {
|
|
34
|
+
if (!raw) return void 0;
|
|
35
|
+
try {
|
|
36
|
+
const decoded = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
|
|
37
|
+
if (!Array.isArray(decoded) || decoded.length !== 2 || typeof decoded[0] !== "string" || typeof decoded[1] !== "string") return;
|
|
38
|
+
const occurredAt = new Date(decoded[0]);
|
|
39
|
+
if (Number.isNaN(occurredAt.getTime()) || !UUID_RE.test(decoded[1])) return void 0;
|
|
40
|
+
return {
|
|
41
|
+
occurredAt,
|
|
42
|
+
id: decoded[1]
|
|
43
|
+
};
|
|
44
|
+
} catch {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function parseFailureOccurrence(raw) {
|
|
49
|
+
if (!raw || !/^(0|[1-9]\d*)$/.test(raw)) return void 0;
|
|
50
|
+
const occurrence = Number(raw);
|
|
51
|
+
return Number.isSafeInteger(occurrence) ? occurrence : void 0;
|
|
52
|
+
}
|
|
53
|
+
function attentionTarget(decision, item) {
|
|
54
|
+
if (!item) return { kind: "rules" };
|
|
55
|
+
const role = typeof decision.decision.role === "string" ? decision.decision.role : void 0;
|
|
56
|
+
const session = role ? item.sessions[role] : void 0;
|
|
57
|
+
if (session) return {
|
|
58
|
+
kind: "thread",
|
|
59
|
+
sessionId: session.sessionId,
|
|
60
|
+
threadId: session.threadId
|
|
61
|
+
};
|
|
62
|
+
const review = item.externalSource?.integrationId === "github" && item.externalSource.type === "pull-request";
|
|
63
|
+
return {
|
|
64
|
+
kind: "work-item",
|
|
65
|
+
workItemId: item.id,
|
|
66
|
+
board: review ? "review" : "work"
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function attentionItem(factoryProjectId, decision, item, receipt) {
|
|
70
|
+
const failure = factoryDispatchFailureMetadata(decision.failureCode);
|
|
71
|
+
return {
|
|
72
|
+
key: attentionKey(factoryProjectId, decision),
|
|
73
|
+
kind: "automation-failed",
|
|
74
|
+
decisionId: decision.id,
|
|
75
|
+
occurrence: decision.failureOccurrence,
|
|
76
|
+
workItemId: decision.workItemId,
|
|
77
|
+
title: item?.title ?? failure.label,
|
|
78
|
+
detail: decision.lastError?.slice(0, 512) ?? failure.label,
|
|
79
|
+
decisionType: factoryDecisionType(decision),
|
|
80
|
+
failureCode: decision.failureCode,
|
|
81
|
+
canRetry: failure.canRetry,
|
|
82
|
+
occurredAt: failureOccurredAt(decision).toISOString(),
|
|
83
|
+
read: receipt !== void 0,
|
|
84
|
+
archived: receipt?.state === "archived",
|
|
85
|
+
target: attentionTarget(decision, item)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function receiptRoute(dependencies, verb, action) {
|
|
89
|
+
return registerApiRoute(`/web/factory/projects/:id/attention/automation-failed/:decisionId/:occurrence/${verb}`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
requiresAuth: false,
|
|
92
|
+
handler: async (context) => {
|
|
93
|
+
const resolved = await dependencies.resolveProject(context);
|
|
94
|
+
if ("response" in resolved) return resolved.response;
|
|
95
|
+
const decisionId = context.req.param("decisionId");
|
|
96
|
+
const failureOccurrence = parseFailureOccurrence(context.req.param("occurrence"));
|
|
97
|
+
if (!decisionId || !UUID_RE.test(decisionId) || failureOccurrence === void 0) return context.json({ error: "invalid_attention_item" }, 422);
|
|
98
|
+
await dependencies.workItems.ensureReady();
|
|
99
|
+
const receipt = await dependencies.workItems.setAttentionReceipt({
|
|
100
|
+
orgId: resolved.orgId,
|
|
101
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
102
|
+
userId: resolved.userId,
|
|
103
|
+
decisionId,
|
|
104
|
+
failureOccurrence,
|
|
105
|
+
action,
|
|
106
|
+
now: /* @__PURE__ */ new Date()
|
|
107
|
+
});
|
|
108
|
+
if (!receipt) return context.json({ error: "attention_item_not_current" }, 409);
|
|
109
|
+
return context.json({ receipt: {
|
|
110
|
+
key: factoryAttentionKey(resolved.factoryProjectId, receipt),
|
|
111
|
+
state: receipt.state,
|
|
112
|
+
readAt: receipt.readAt.toISOString(),
|
|
113
|
+
archivedAt: receipt.archivedAt?.toISOString() ?? null
|
|
114
|
+
} });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function buildAttentionRoutes(dependencies) {
|
|
119
|
+
const { workItems } = dependencies;
|
|
120
|
+
return [
|
|
121
|
+
registerApiRoute("/web/factory/projects/:id/attention", {
|
|
122
|
+
method: "GET",
|
|
123
|
+
requiresAuth: false,
|
|
124
|
+
handler: async (context) => {
|
|
125
|
+
const resolved = await dependencies.resolveProject(context);
|
|
126
|
+
if ("response" in resolved) return resolved.response;
|
|
127
|
+
const view = parseAttentionView(context.req.query("view"));
|
|
128
|
+
if (view === void 0) return context.json({ error: "invalid_attention_view" }, 400);
|
|
129
|
+
const cursorRaw = context.req.query("before");
|
|
130
|
+
const before = parseAttentionCursor(cursorRaw);
|
|
131
|
+
if (cursorRaw && !before) return context.json({ error: "invalid_cursor" }, 400);
|
|
132
|
+
await workItems.ensureReady();
|
|
133
|
+
const [failedCount, approvalCount, receiptCount, archivedCount, newestPage] = await Promise.all([
|
|
134
|
+
workItems.countDeferredDecisionsByStatuses({
|
|
135
|
+
orgId: resolved.orgId,
|
|
136
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
137
|
+
statuses: ["failed"]
|
|
138
|
+
}),
|
|
139
|
+
workItems.countDeferredDecisionsByStatuses({
|
|
140
|
+
orgId: resolved.orgId,
|
|
141
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
142
|
+
statuses: ["proposed"]
|
|
143
|
+
}),
|
|
144
|
+
workItems.countAttentionReceipts({
|
|
145
|
+
orgId: resolved.orgId,
|
|
146
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
147
|
+
userId: resolved.userId
|
|
148
|
+
}),
|
|
149
|
+
workItems.countAttentionReceipts({
|
|
150
|
+
orgId: resolved.orgId,
|
|
151
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
152
|
+
userId: resolved.userId,
|
|
153
|
+
state: "archived"
|
|
154
|
+
}),
|
|
155
|
+
workItems.listFailedDecisionPage({
|
|
156
|
+
orgId: resolved.orgId,
|
|
157
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
158
|
+
limit: 1
|
|
159
|
+
})
|
|
160
|
+
]);
|
|
161
|
+
const failureOpenCount = Math.max(0, failedCount - archivedCount);
|
|
162
|
+
const openCount = failureOpenCount + approvalCount;
|
|
163
|
+
const unreadCount = Math.max(0, failedCount - receiptCount);
|
|
164
|
+
const badgeCount = unreadCount + approvalCount;
|
|
165
|
+
const newestFailure = newestPage.decisions[0];
|
|
166
|
+
const newestReceipt = newestFailure ? (await workItems.listAttentionReceipts({
|
|
167
|
+
orgId: resolved.orgId,
|
|
168
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
169
|
+
userId: resolved.userId,
|
|
170
|
+
identities: [attentionIdentity(newestFailure)]
|
|
171
|
+
}))[0] : void 0;
|
|
172
|
+
const search = context.req.query("search")?.trim().toLowerCase().slice(0, 200);
|
|
173
|
+
const requestedLimit = parseAttentionLimit(context.req.query("limit"));
|
|
174
|
+
const visible = [];
|
|
175
|
+
let scanBefore = before;
|
|
176
|
+
let cursorDecision;
|
|
177
|
+
let continuationDecision;
|
|
178
|
+
let scannedPages = 0;
|
|
179
|
+
let hasMore = false;
|
|
180
|
+
scan: while (view === "open" && failureOpenCount > 0 || view === "unread" && unreadCount > 0 || view === "archived" && archivedCount > 0) {
|
|
181
|
+
const page = await workItems.listFailedDecisionPage({
|
|
182
|
+
orgId: resolved.orgId,
|
|
183
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
184
|
+
before: scanBefore,
|
|
185
|
+
limit: MAX_PAGE_SIZE
|
|
186
|
+
});
|
|
187
|
+
scannedPages += 1;
|
|
188
|
+
if (page.decisions.length === 0) break;
|
|
189
|
+
const receipts = await workItems.listAttentionReceipts({
|
|
190
|
+
orgId: resolved.orgId,
|
|
191
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
192
|
+
userId: resolved.userId,
|
|
193
|
+
identities: page.decisions.map(attentionIdentity)
|
|
194
|
+
});
|
|
195
|
+
const receiptByKey = new Map(receipts.map((receipt) => [factoryAttentionKey(resolved.factoryProjectId, receipt), receipt]));
|
|
196
|
+
const linkedItems = await workItems.listByIds({
|
|
197
|
+
orgId: resolved.orgId,
|
|
198
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
199
|
+
ids: page.decisions.flatMap((decision) => decision.workItemId ? [decision.workItemId] : [])
|
|
200
|
+
});
|
|
201
|
+
const itemById = new Map(linkedItems.map((item) => [item.id, item]));
|
|
202
|
+
for (const decision of page.decisions) {
|
|
203
|
+
const receipt = receiptByKey.get(attentionKey(resolved.factoryProjectId, decision));
|
|
204
|
+
if (view === "archived" ? receipt?.state !== "archived" : view === "unread" ? receipt : receipt?.state === "archived") continue;
|
|
205
|
+
const item = decision.workItemId ? itemById.get(decision.workItemId) : void 0;
|
|
206
|
+
if (search && item?.title.toLowerCase().includes(search) !== true && decision.lastError?.toLowerCase().includes(search) !== true && !factoryDecisionType(decision).toLowerCase().includes(search)) continue;
|
|
207
|
+
if (visible.length === requestedLimit) {
|
|
208
|
+
hasMore = true;
|
|
209
|
+
continuationDecision = cursorDecision;
|
|
210
|
+
break scan;
|
|
211
|
+
}
|
|
212
|
+
visible.push({
|
|
213
|
+
decision,
|
|
214
|
+
item,
|
|
215
|
+
receipt
|
|
216
|
+
});
|
|
217
|
+
if (visible.length === requestedLimit) cursorDecision = decision;
|
|
218
|
+
}
|
|
219
|
+
const lastScanned = page.decisions.at(-1);
|
|
220
|
+
if (!page.hasMore || !lastScanned) break;
|
|
221
|
+
if (scannedPages === MAX_RECEIPT_SCAN_PAGES) {
|
|
222
|
+
hasMore = true;
|
|
223
|
+
continuationDecision = lastScanned;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
scanBefore = {
|
|
227
|
+
occurredAt: failureOccurredAt(lastScanned),
|
|
228
|
+
id: lastScanned.id
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return context.json({
|
|
232
|
+
items: visible.map(({ decision, item, receipt }) => attentionItem(resolved.factoryProjectId, decision, item, receipt)),
|
|
233
|
+
openCount,
|
|
234
|
+
approvalCount,
|
|
235
|
+
badgeCount,
|
|
236
|
+
unreadCount,
|
|
237
|
+
latestOccurrenceKey: newestFailure ? attentionKey(resolved.factoryProjectId, newestFailure) : null,
|
|
238
|
+
latestOccurrenceAt: newestFailure ? failureOccurredAt(newestFailure).toISOString() : null,
|
|
239
|
+
latestOccurrenceUnread: newestFailure !== void 0 && newestReceipt === void 0,
|
|
240
|
+
hasMore,
|
|
241
|
+
...hasMore && continuationDecision ? { nextCursor: encodeAttentionCursor(continuationDecision) } : {}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}),
|
|
245
|
+
registerApiRoute("/web/factory/projects/:id/attention/read-all", {
|
|
246
|
+
method: "POST",
|
|
247
|
+
requiresAuth: false,
|
|
248
|
+
handler: async (context) => {
|
|
249
|
+
const resolved = await dependencies.resolveProject(context);
|
|
250
|
+
if ("response" in resolved) return resolved.response;
|
|
251
|
+
const cursorRaw = context.req.query("before");
|
|
252
|
+
const initialBefore = parseAttentionCursor(cursorRaw);
|
|
253
|
+
if (cursorRaw && !initialBefore) return context.json({ error: "invalid_cursor" }, 400);
|
|
254
|
+
await workItems.ensureReady();
|
|
255
|
+
let before = initialBefore;
|
|
256
|
+
let pages = 0;
|
|
257
|
+
let hasMore = false;
|
|
258
|
+
let nextCursor;
|
|
259
|
+
while (pages < MAX_RECEIPT_SCAN_PAGES) {
|
|
260
|
+
const page = await workItems.listFailedDecisionPage({
|
|
261
|
+
orgId: resolved.orgId,
|
|
262
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
263
|
+
before,
|
|
264
|
+
limit: MAX_PAGE_SIZE
|
|
265
|
+
});
|
|
266
|
+
pages += 1;
|
|
267
|
+
if (page.decisions.length === 0) break;
|
|
268
|
+
await workItems.markAttentionReceiptsRead({
|
|
269
|
+
orgId: resolved.orgId,
|
|
270
|
+
factoryProjectId: resolved.factoryProjectId,
|
|
271
|
+
userId: resolved.userId,
|
|
272
|
+
occurrences: page.decisions.map((decision) => ({
|
|
273
|
+
decisionId: decision.id,
|
|
274
|
+
failureOccurrence: decision.failureOccurrence
|
|
275
|
+
})),
|
|
276
|
+
now: /* @__PURE__ */ new Date()
|
|
277
|
+
});
|
|
278
|
+
const last = page.decisions.at(-1);
|
|
279
|
+
if (!page.hasMore || !last) break;
|
|
280
|
+
if (pages === MAX_RECEIPT_SCAN_PAGES) {
|
|
281
|
+
hasMore = true;
|
|
282
|
+
nextCursor = encodeAttentionCursor(last);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
before = {
|
|
286
|
+
occurredAt: failureOccurredAt(last),
|
|
287
|
+
id: last.id
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return context.json({
|
|
291
|
+
ok: true,
|
|
292
|
+
hasMore,
|
|
293
|
+
...nextCursor ? { nextCursor } : {}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}),
|
|
297
|
+
receiptRoute(dependencies, "read", "read"),
|
|
298
|
+
receiptRoute(dependencies, "archive", "archive"),
|
|
299
|
+
receiptRoute(dependencies, "restore", "restore")
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
//#endregion
|
|
303
|
+
export { buildAttentionRoutes, factoryDecisionType };
|
|
304
|
+
|
|
305
|
+
//# sourceMappingURL=attention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention.js","names":[],"sources":["../../src/routes/attention.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\n\nimport { factoryDispatchFailureMetadata } from '../rules/dispatch-errors.js';\nimport type {\n FactoryAttentionReceiptAction,\n FactoryAttentionReceiptRecord,\n FactoryDeferredDecisionRecord,\n WorkItemRow,\n WorkItemsStorage,\n} from '../storage/domains/work-items/base.js';\nimport { factoryAttentionKey, factoryDecisionAttentionIdentity } from '../storage/domains/work-items/base.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 DEFAULT_PAGE_SIZE = 25;\nconst MAX_PAGE_SIZE = 50;\n// Receipt filtering is bounded to 200 failed decisions per request; the response cursor resumes after the last scan.\nconst MAX_RECEIPT_SCAN_PAGES = 4;\n\ntype FactoryAttentionView = 'open' | 'unread' | 'archived';\n\ninterface ResolvedAttentionProject {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n}\n\ninterface AttentionRouteDependencies {\n workItems: WorkItemsStorage;\n resolveProject(context: unknown): Promise<ResolvedAttentionProject | { response: Response }>;\n}\n\nexport function factoryDecisionType(decision: FactoryDeferredDecisionRecord): string {\n return typeof decision.decision.type === 'string' ? decision.decision.type.slice(0, 64) : 'unknown';\n}\n\nfunction parseAttentionView(raw: string | undefined): FactoryAttentionView | undefined {\n if (!raw || raw === 'open') return 'open';\n if (raw === 'unread' || raw === 'archived') return raw;\n return undefined;\n}\n\nfunction parseAttentionLimit(raw: string | undefined): number {\n const parsed = raw ? Number.parseInt(raw, 10) : DEFAULT_PAGE_SIZE;\n if (!Number.isFinite(parsed)) return DEFAULT_PAGE_SIZE;\n return Math.max(1, Math.min(MAX_PAGE_SIZE, parsed));\n}\n\nfunction attentionIdentity(decision: FactoryDeferredDecisionRecord) {\n return factoryDecisionAttentionIdentity(decision.id, decision.failureOccurrence);\n}\n\nfunction attentionKey(factoryProjectId: string, decision: FactoryDeferredDecisionRecord): string {\n return factoryAttentionKey(factoryProjectId, attentionIdentity(decision));\n}\n\nfunction failureOccurredAt(decision: FactoryDeferredDecisionRecord): Date {\n return decision.completedAt ?? decision.updatedAt;\n}\n\nfunction encodeAttentionCursor(decision: FactoryDeferredDecisionRecord): string {\n return Buffer.from(JSON.stringify([failureOccurredAt(decision).toISOString(), decision.id]), 'utf8').toString(\n 'base64url',\n );\n}\n\nfunction parseAttentionCursor(raw: string | undefined): { occurredAt: Date; id: string } | undefined {\n if (!raw) return undefined;\n try {\n const decoded: unknown = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8'));\n if (\n !Array.isArray(decoded) ||\n decoded.length !== 2 ||\n typeof decoded[0] !== 'string' ||\n typeof decoded[1] !== 'string'\n ) {\n return undefined;\n }\n const occurredAt = new Date(decoded[0]);\n if (Number.isNaN(occurredAt.getTime()) || !UUID_RE.test(decoded[1])) return undefined;\n return { occurredAt, id: decoded[1] };\n } catch {\n return undefined;\n }\n}\n\nfunction parseFailureOccurrence(raw: string | undefined): number | undefined {\n if (!raw || !/^(0|[1-9]\\d*)$/.test(raw)) return undefined;\n const occurrence = Number(raw);\n return Number.isSafeInteger(occurrence) ? occurrence : undefined;\n}\n\nfunction attentionTarget(decision: FactoryDeferredDecisionRecord, item: WorkItemRow | undefined) {\n if (!item) return { kind: 'rules' as const };\n const role = typeof decision.decision.role === 'string' ? decision.decision.role : undefined;\n const session = role ? item.sessions[role] : undefined;\n if (session) {\n return {\n kind: 'thread' as const,\n sessionId: session.sessionId,\n threadId: session.threadId,\n };\n }\n const review = item.externalSource?.integrationId === 'github' && item.externalSource.type === 'pull-request';\n return {\n kind: 'work-item' as const,\n workItemId: item.id,\n board: review ? ('review' as const) : ('work' as const),\n };\n}\n\nfunction attentionItem(\n factoryProjectId: string,\n decision: FactoryDeferredDecisionRecord,\n item: WorkItemRow | undefined,\n receipt: FactoryAttentionReceiptRecord | undefined,\n) {\n const failure = factoryDispatchFailureMetadata(decision.failureCode);\n return {\n key: attentionKey(factoryProjectId, decision),\n kind: 'automation-failed' as const,\n decisionId: decision.id,\n occurrence: decision.failureOccurrence,\n workItemId: decision.workItemId,\n title: item?.title ?? failure.label,\n detail: decision.lastError?.slice(0, 512) ?? failure.label,\n decisionType: factoryDecisionType(decision),\n failureCode: decision.failureCode,\n canRetry: failure.canRetry,\n occurredAt: failureOccurredAt(decision).toISOString(),\n read: receipt !== undefined,\n archived: receipt?.state === 'archived',\n target: attentionTarget(decision, item),\n };\n}\n\nfunction receiptRoute(\n dependencies: AttentionRouteDependencies,\n verb: 'read' | 'archive' | 'restore',\n action: FactoryAttentionReceiptAction,\n): ApiRoute {\n return registerApiRoute(`/web/factory/projects/:id/attention/automation-failed/:decisionId/:occurrence/${verb}`, {\n method: 'POST',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const decisionId = context.req.param('decisionId');\n const failureOccurrence = parseFailureOccurrence(context.req.param('occurrence'));\n if (!decisionId || !UUID_RE.test(decisionId) || failureOccurrence === undefined) {\n return context.json({ error: 'invalid_attention_item' }, 422);\n }\n await dependencies.workItems.ensureReady();\n const receipt = await dependencies.workItems.setAttentionReceipt({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n decisionId,\n failureOccurrence,\n action,\n now: new Date(),\n });\n if (!receipt) return context.json({ error: 'attention_item_not_current' }, 409);\n return context.json({\n receipt: {\n key: factoryAttentionKey(resolved.factoryProjectId, receipt),\n state: receipt.state,\n readAt: receipt.readAt.toISOString(),\n archivedAt: receipt.archivedAt?.toISOString() ?? null,\n },\n });\n },\n });\n}\n\nexport function buildAttentionRoutes(dependencies: AttentionRouteDependencies): ApiRoute[] {\n const { workItems } = dependencies;\n return [\n registerApiRoute('/web/factory/projects/:id/attention', {\n method: 'GET',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const view = parseAttentionView(context.req.query('view'));\n if (view === undefined) return context.json({ error: 'invalid_attention_view' }, 400);\n const cursorRaw = context.req.query('before');\n const before = parseAttentionCursor(cursorRaw);\n if (cursorRaw && !before) return context.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n const [failedCount, approvalCount, receiptCount, archivedCount, newestPage] = await Promise.all([\n workItems.countDeferredDecisionsByStatuses({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: ['failed'],\n }),\n workItems.countDeferredDecisionsByStatuses({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n statuses: ['proposed'],\n }),\n workItems.countAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n }),\n workItems.countAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n state: 'archived',\n }),\n workItems.listFailedDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n limit: 1,\n }),\n ]);\n const failureOpenCount = Math.max(0, failedCount - archivedCount);\n const openCount = failureOpenCount + approvalCount;\n const unreadCount = Math.max(0, failedCount - receiptCount);\n const badgeCount = unreadCount + approvalCount;\n const newestFailure = newestPage.decisions[0];\n const newestReceipt = newestFailure\n ? (\n await workItems.listAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n identities: [attentionIdentity(newestFailure)],\n })\n )[0]\n : undefined;\n const search = context.req.query('search')?.trim().toLowerCase().slice(0, 200);\n const requestedLimit = parseAttentionLimit(context.req.query('limit'));\n const visible: Array<{\n decision: FactoryDeferredDecisionRecord;\n item: WorkItemRow | undefined;\n receipt: FactoryAttentionReceiptRecord | undefined;\n }> = [];\n let scanBefore = before;\n let cursorDecision: FactoryDeferredDecisionRecord | undefined;\n let continuationDecision: FactoryDeferredDecisionRecord | undefined;\n let scannedPages = 0;\n let hasMore = false;\n\n scan: while (\n (view === 'open' && failureOpenCount > 0) ||\n (view === 'unread' && unreadCount > 0) ||\n (view === 'archived' && archivedCount > 0)\n ) {\n const page = await workItems.listFailedDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n before: scanBefore,\n limit: MAX_PAGE_SIZE,\n });\n scannedPages += 1;\n if (page.decisions.length === 0) break;\n const receipts = await workItems.listAttentionReceipts({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n identities: page.decisions.map(attentionIdentity),\n });\n const receiptByKey = new Map(\n receipts.map(receipt => [factoryAttentionKey(resolved.factoryProjectId, receipt), receipt]),\n );\n const linkedItems = await workItems.listByIds({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n ids: page.decisions.flatMap(decision => (decision.workItemId ? [decision.workItemId] : [])),\n });\n const itemById = new Map(linkedItems.map(item => [item.id, item]));\n for (const decision of page.decisions) {\n const receipt = receiptByKey.get(attentionKey(resolved.factoryProjectId, decision));\n if (\n view === 'archived'\n ? receipt?.state !== 'archived'\n : view === 'unread'\n ? receipt\n : receipt?.state === 'archived'\n ) {\n continue;\n }\n const item = decision.workItemId ? itemById.get(decision.workItemId) : undefined;\n if (\n search &&\n item?.title.toLowerCase().includes(search) !== true &&\n decision.lastError?.toLowerCase().includes(search) !== true &&\n !factoryDecisionType(decision).toLowerCase().includes(search)\n ) {\n continue;\n }\n if (visible.length === requestedLimit) {\n hasMore = true;\n continuationDecision = cursorDecision;\n break scan;\n }\n visible.push({ decision, item, receipt });\n if (visible.length === requestedLimit) cursorDecision = decision;\n }\n const lastScanned = page.decisions.at(-1);\n if (!page.hasMore || !lastScanned) break;\n if (scannedPages === MAX_RECEIPT_SCAN_PAGES) {\n hasMore = true;\n continuationDecision = lastScanned;\n break;\n }\n scanBefore = { occurredAt: failureOccurredAt(lastScanned), id: lastScanned.id };\n }\n\n return context.json({\n items: visible.map(({ decision, item, receipt }) =>\n attentionItem(resolved.factoryProjectId, decision, item, receipt),\n ),\n openCount,\n approvalCount,\n badgeCount,\n unreadCount,\n latestOccurrenceKey: newestFailure ? attentionKey(resolved.factoryProjectId, newestFailure) : null,\n latestOccurrenceAt: newestFailure ? failureOccurredAt(newestFailure).toISOString() : null,\n latestOccurrenceUnread: newestFailure !== undefined && newestReceipt === undefined,\n hasMore,\n ...(hasMore && continuationDecision ? { nextCursor: encodeAttentionCursor(continuationDecision) } : {}),\n });\n },\n }),\n registerApiRoute('/web/factory/projects/:id/attention/read-all', {\n method: 'POST',\n requiresAuth: false,\n handler: async context => {\n const resolved = await dependencies.resolveProject(context);\n if ('response' in resolved) return resolved.response;\n const cursorRaw = context.req.query('before');\n const initialBefore = parseAttentionCursor(cursorRaw);\n if (cursorRaw && !initialBefore) return context.json({ error: 'invalid_cursor' }, 400);\n await workItems.ensureReady();\n let before = initialBefore;\n let pages = 0;\n let hasMore = false;\n let nextCursor: string | undefined;\n while (pages < MAX_RECEIPT_SCAN_PAGES) {\n const page = await workItems.listFailedDecisionPage({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n before,\n limit: MAX_PAGE_SIZE,\n });\n pages += 1;\n if (page.decisions.length === 0) break;\n await workItems.markAttentionReceiptsRead({\n orgId: resolved.orgId,\n factoryProjectId: resolved.factoryProjectId,\n userId: resolved.userId,\n occurrences: page.decisions.map(decision => ({\n decisionId: decision.id,\n failureOccurrence: decision.failureOccurrence,\n })),\n now: new Date(),\n });\n const last = page.decisions.at(-1);\n if (!page.hasMore || !last) break;\n if (pages === MAX_RECEIPT_SCAN_PAGES) {\n hasMore = true;\n nextCursor = encodeAttentionCursor(last);\n break;\n }\n before = { occurredAt: failureOccurredAt(last), id: last.id };\n }\n return context.json({ ok: true, hasMore, ...(nextCursor ? { nextCursor } : {}) });\n },\n }),\n receiptRoute(dependencies, 'read', 'read'),\n receiptRoute(dependencies, 'archive', 'archive'),\n receiptRoute(dependencies, 'restore', 'restore'),\n ];\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/routes/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;CACnD;AAYD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,KAAK,EAAE,YAAY,CAAC;IACpB,sCAAsC;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;IAClF,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,UAAU,aAAa;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAWtE;AAaD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAyBpE;AAmBD,qBAAa,YAAa,SAAQ,KAAK,CAAC,gBAAgB,CAAC;;IAgBvD,MAAM,IAAI,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/routes/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;CACnD;AAYD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,KAAK,EAAE,YAAY,CAAC;IACpB,sCAAsC;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;IAClF,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,UAAU,aAAa;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAWtE;AAaD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAyBpE;AAmBD,qBAAa,YAAa,SAAQ,KAAK,CAAC,gBAAgB,CAAC;;IAgBvD,MAAM,IAAI,QAAQ,EAAE;CAsLrB"}
|
package/dist/routes/intake.js
CHANGED
|
@@ -164,15 +164,24 @@ var IntakeRoutes = class extends Route {
|
|
|
164
164
|
})) return c.json({ error: "factory_project_not_found" }, 404);
|
|
165
165
|
}
|
|
166
166
|
await intake.ensureReady();
|
|
167
|
-
|
|
167
|
+
let auditFactoryProjectId = binding.factoryProjectId;
|
|
168
|
+
if (binding.factoryProjectId === null) auditFactoryProjectId = (await intake.clearBinding({
|
|
169
|
+
orgId: tenant.orgId,
|
|
170
|
+
integrationId: binding.integrationId,
|
|
171
|
+
sourceId: binding.sourceId
|
|
172
|
+
}))?.factoryProjectId ?? null;
|
|
173
|
+
else await intake.setBinding({
|
|
168
174
|
orgId: tenant.orgId,
|
|
169
175
|
userId: tenant.userId,
|
|
170
|
-
|
|
176
|
+
integrationId: binding.integrationId,
|
|
177
|
+
sourceId: binding.sourceId,
|
|
178
|
+
factoryProjectId: binding.factoryProjectId
|
|
171
179
|
});
|
|
172
180
|
await audit.emit({
|
|
173
181
|
context: loose(c),
|
|
174
182
|
input: {
|
|
175
183
|
action: "factory.intake.binding_updated",
|
|
184
|
+
...auditFactoryProjectId ? { factoryProjectId: auditFactoryProjectId } : {},
|
|
176
185
|
targets: [{
|
|
177
186
|
type: "intake_source",
|
|
178
187
|
id: `${binding.integrationId}:${binding.sourceId}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake.js","names":["#resolveTenant"],"sources":["../../src/routes/intake.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { Intake, IntakeItem } from '../capabilities/intake.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { IntakeConfig, IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nexport interface IntakeIntegration {\n id: string;\n intake: Pick<Intake, 'listSources' | 'listItems'>;\n}\n\ninterface AggregatedIntakeItem extends Omit<IntakeItem, 'source'> {\n integrationId: string;\n externalSource: {\n integrationId: string;\n type: string;\n externalId: string;\n url?: string;\n };\n}\n\nexport interface IntakeRoutesDeps extends RouteDependencies {\n audit: AuditEmitter;\n /** Intake selection domain handle. */\n intake: IntakeStorage;\n /** Factory project domain handle, used to validate binding targets. */\n projects?: { get(input: { orgId: string; id: string }): Promise<unknown | null> };\n integrations?: IntakeIntegration[];\n}\n\ninterface ParsedBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string | null;\n}\n\n/** Validate a binding request body, rejecting unknown shapes. */\nexport function parseIntakeBinding(body: unknown): ParsedBinding | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const { integrationId, sourceId, factoryProjectId } = body as Record<string, unknown>;\n const isId = (value: unknown) => typeof value === 'string' && value.length > 0 && value.length <= 256;\n if (!isId(integrationId) || !isId(sourceId)) return null;\n if (factoryProjectId !== null && !isId(factoryProjectId)) return null;\n return {\n integrationId: integrationId as string,\n sourceId: sourceId as string,\n factoryProjectId: factoryProjectId as string | null,\n };\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction sanitizeIdList(value: unknown): string[] | null | undefined {\n if (value === null) return null;\n if (!Array.isArray(value) || value.length > 200) return undefined;\n const ids = value.filter((item): item is string => typeof item === 'string' && item.length > 0 && item.length <= 256);\n return ids.length === value.length && new Set(ids).size === ids.length ? ids : undefined;\n}\n\n/** Validate a request body into an intake config, rejecting unknown shapes. */\nexport function parseIntakeConfig(body: unknown): IntakeConfig | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const entries = Object.entries(body);\n if (entries.length > 50) return null;\n\n // Null-prototype so an `__proto__` key lands as a real entry instead of silently\n // reassigning the prototype and disappearing from the validation below.\n const config: IntakeConfig = Object.create(null);\n for (const [integrationId, value] of entries) {\n if (\n !integrationId ||\n integrationId.length > 128 ||\n typeof value !== 'object' ||\n value === null ||\n Array.isArray(value)\n ) {\n return null;\n }\n const selection = value as { enabled?: unknown; sourceIds?: unknown };\n if (typeof selection.enabled !== 'boolean') return null;\n const sourceIds = sanitizeIdList(selection.sourceIds ?? null);\n if (sourceIds === undefined) return null;\n config[integrationId] = { enabled: selection.enabled, sourceIds };\n }\n return config;\n}\n\nfunction encodeCursor(cursors: Record<string, string>): string | null {\n return Object.keys(cursors).length > 0 ? Buffer.from(JSON.stringify(cursors)).toString('base64url') : null;\n}\n\nfunction decodeCursor(value: string | undefined): Record<string, string> | null {\n if (!value) return {};\n try {\n const parsed = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const entries = Object.entries(parsed);\n if (entries.some(([key, cursor]) => !key || typeof cursor !== 'string')) return null;\n return Object.fromEntries(entries) as Record<string, string>;\n } catch {\n return null;\n }\n}\n\nexport class IntakeRoutes extends Route<IntakeRoutesDeps> {\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'Intake configuration requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n const { audit, intake, projects, integrations = [] } = this.deps;\n const integrationIds = integrations.map(integration => integration.id);\n\n return [\n registerApiRoute('/web/intake/config', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n return c.json({ config });\n },\n }),\n registerApiRoute('/web/intake/config', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const config = parseIntakeConfig(body);\n if (!config) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n\n const registeredConfig: IntakeConfig = Object.create(null);\n for (const [integrationId, selection] of Object.entries(config)) {\n if (integrationIds.includes(integrationId)) {\n registeredConfig[integrationId] = selection;\n continue;\n }\n if (selection.enabled || selection.sourceIds?.length) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n }\n\n await intake.ensureReady();\n await intake.saveConfig({ ...tenant, config: registeredConfig });\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.config_updated',\n targets: [{ type: 'intake_config', id: tenant.orgId }],\n metadata: Object.fromEntries(\n Object.entries(registeredConfig).map(([integrationId, selection]) => [\n integrationId,\n { enabled: selection.enabled, sources: selection.sourceIds?.length ?? null },\n ]),\n ),\n },\n });\n return c.json({ config: registeredConfig });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const binding = parseIntakeBinding(body);\n if (!binding || !integrationIds.includes(binding.integrationId)) {\n return c.json({ error: 'invalid_binding' }, 400);\n }\n if (binding.factoryProjectId && projects) {\n const project = await projects.get({ orgId: tenant.orgId, id: binding.factoryProjectId });\n if (!project) return c.json({ error: 'factory_project_not_found' }, 404);\n }\n\n await intake.ensureReady();\n await intake.setBinding({ orgId: tenant.orgId, userId: tenant.userId, ...binding });\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.binding_updated',\n targets: [{ type: 'intake_source', id: `${binding.integrationId}:${binding.sourceId}` }],\n metadata: { factoryProjectId: binding.factoryProjectId },\n },\n });\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/sources', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const pages = await Promise.all(\n integrations.map(async integration => ({\n integrationId: integration.id,\n sources: await integration.intake.listSources(tenant),\n })),\n );\n return c.json({\n sources: pages.flatMap(page =>\n page.sources.map(source => ({ integrationId: page.integrationId, ...source })),\n ),\n });\n },\n }),\n registerApiRoute('/web/intake/items', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const cursors = decodeCursor(c.req.query('cursor'));\n if (!cursors) return c.json({ error: 'invalid_cursor' }, 400);\n\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n const items: AggregatedIntakeItem[] = [];\n const nextCursors: Record<string, string> = {};\n for (const integration of integrations) {\n const selection = config[integration.id];\n if (!selection?.enabled || !selection.sourceIds?.length) continue;\n const page = await integration.intake.listItems({\n ...tenant,\n sourceIds: selection.sourceIds,\n ...(cursors[integration.id] ? { cursor: cursors[integration.id] } : {}),\n });\n items.push(\n ...page.items.map(item => {\n const { source, ...candidate } = item;\n return {\n ...candidate,\n integrationId: integration.id,\n externalSource: { integrationId: integration.id, ...source },\n };\n }),\n );\n if (page.nextCursor) nextCursors[integration.id] = page.nextCursor;\n }\n return c.json({ items, nextCursor: encodeCursor(nextCursors) });\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"intake.js","names":["#resolveTenant"],"sources":["../../src/routes/intake.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { Intake, IntakeItem } from '../capabilities/intake.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { IntakeConfig, IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nexport interface IntakeIntegration {\n id: string;\n intake: Pick<Intake, 'listSources' | 'listItems'>;\n}\n\ninterface AggregatedIntakeItem extends Omit<IntakeItem, 'source'> {\n integrationId: string;\n externalSource: {\n integrationId: string;\n type: string;\n externalId: string;\n url?: string;\n };\n}\n\nexport interface IntakeRoutesDeps extends RouteDependencies {\n audit: AuditEmitter;\n /** Intake selection domain handle. */\n intake: IntakeStorage;\n /** Factory project domain handle, used to validate binding targets. */\n projects?: { get(input: { orgId: string; id: string }): Promise<unknown | null> };\n integrations?: IntakeIntegration[];\n}\n\ninterface ParsedBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string | null;\n}\n\n/** Validate a binding request body, rejecting unknown shapes. */\nexport function parseIntakeBinding(body: unknown): ParsedBinding | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const { integrationId, sourceId, factoryProjectId } = body as Record<string, unknown>;\n const isId = (value: unknown) => typeof value === 'string' && value.length > 0 && value.length <= 256;\n if (!isId(integrationId) || !isId(sourceId)) return null;\n if (factoryProjectId !== null && !isId(factoryProjectId)) return null;\n return {\n integrationId: integrationId as string,\n sourceId: sourceId as string,\n factoryProjectId: factoryProjectId as string | null,\n };\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction sanitizeIdList(value: unknown): string[] | null | undefined {\n if (value === null) return null;\n if (!Array.isArray(value) || value.length > 200) return undefined;\n const ids = value.filter((item): item is string => typeof item === 'string' && item.length > 0 && item.length <= 256);\n return ids.length === value.length && new Set(ids).size === ids.length ? ids : undefined;\n}\n\n/** Validate a request body into an intake config, rejecting unknown shapes. */\nexport function parseIntakeConfig(body: unknown): IntakeConfig | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const entries = Object.entries(body);\n if (entries.length > 50) return null;\n\n // Null-prototype so an `__proto__` key lands as a real entry instead of silently\n // reassigning the prototype and disappearing from the validation below.\n const config: IntakeConfig = Object.create(null);\n for (const [integrationId, value] of entries) {\n if (\n !integrationId ||\n integrationId.length > 128 ||\n typeof value !== 'object' ||\n value === null ||\n Array.isArray(value)\n ) {\n return null;\n }\n const selection = value as { enabled?: unknown; sourceIds?: unknown };\n if (typeof selection.enabled !== 'boolean') return null;\n const sourceIds = sanitizeIdList(selection.sourceIds ?? null);\n if (sourceIds === undefined) return null;\n config[integrationId] = { enabled: selection.enabled, sourceIds };\n }\n return config;\n}\n\nfunction encodeCursor(cursors: Record<string, string>): string | null {\n return Object.keys(cursors).length > 0 ? Buffer.from(JSON.stringify(cursors)).toString('base64url') : null;\n}\n\nfunction decodeCursor(value: string | undefined): Record<string, string> | null {\n if (!value) return {};\n try {\n const parsed = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const entries = Object.entries(parsed);\n if (entries.some(([key, cursor]) => !key || typeof cursor !== 'string')) return null;\n return Object.fromEntries(entries) as Record<string, string>;\n } catch {\n return null;\n }\n}\n\nexport class IntakeRoutes extends Route<IntakeRoutesDeps> {\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'Intake configuration requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n const { audit, intake, projects, integrations = [] } = this.deps;\n const integrationIds = integrations.map(integration => integration.id);\n\n return [\n registerApiRoute('/web/intake/config', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n return c.json({ config });\n },\n }),\n registerApiRoute('/web/intake/config', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const config = parseIntakeConfig(body);\n if (!config) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n\n const registeredConfig: IntakeConfig = Object.create(null);\n for (const [integrationId, selection] of Object.entries(config)) {\n if (integrationIds.includes(integrationId)) {\n registeredConfig[integrationId] = selection;\n continue;\n }\n if (selection.enabled || selection.sourceIds?.length) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n }\n\n await intake.ensureReady();\n await intake.saveConfig({ ...tenant, config: registeredConfig });\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.config_updated',\n targets: [{ type: 'intake_config', id: tenant.orgId }],\n metadata: Object.fromEntries(\n Object.entries(registeredConfig).map(([integrationId, selection]) => [\n integrationId,\n { enabled: selection.enabled, sources: selection.sourceIds?.length ?? null },\n ]),\n ),\n },\n });\n return c.json({ config: registeredConfig });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const binding = parseIntakeBinding(body);\n if (!binding || !integrationIds.includes(binding.integrationId)) {\n return c.json({ error: 'invalid_binding' }, 400);\n }\n if (binding.factoryProjectId && projects) {\n const project = await projects.get({ orgId: tenant.orgId, id: binding.factoryProjectId });\n if (!project) return c.json({ error: 'factory_project_not_found' }, 404);\n }\n\n await intake.ensureReady();\n let auditFactoryProjectId = binding.factoryProjectId;\n if (binding.factoryProjectId === null) {\n const previousBinding = await intake.clearBinding({\n orgId: tenant.orgId,\n integrationId: binding.integrationId,\n sourceId: binding.sourceId,\n });\n auditFactoryProjectId = previousBinding?.factoryProjectId ?? null;\n } else {\n await intake.setBinding({\n orgId: tenant.orgId,\n userId: tenant.userId,\n integrationId: binding.integrationId,\n sourceId: binding.sourceId,\n factoryProjectId: binding.factoryProjectId,\n });\n }\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.binding_updated',\n ...(auditFactoryProjectId ? { factoryProjectId: auditFactoryProjectId } : {}),\n targets: [{ type: 'intake_source', id: `${binding.integrationId}:${binding.sourceId}` }],\n metadata: { factoryProjectId: binding.factoryProjectId },\n },\n });\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/sources', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const pages = await Promise.all(\n integrations.map(async integration => ({\n integrationId: integration.id,\n sources: await integration.intake.listSources(tenant),\n })),\n );\n return c.json({\n sources: pages.flatMap(page =>\n page.sources.map(source => ({ integrationId: page.integrationId, ...source })),\n ),\n });\n },\n }),\n registerApiRoute('/web/intake/items', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const cursors = decodeCursor(c.req.query('cursor'));\n if (!cursors) return c.json({ error: 'invalid_cursor' }, 400);\n\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n const items: AggregatedIntakeItem[] = [];\n const nextCursors: Record<string, string> = {};\n for (const integration of integrations) {\n const selection = config[integration.id];\n if (!selection?.enabled || !selection.sourceIds?.length) continue;\n const page = await integration.intake.listItems({\n ...tenant,\n sourceIds: selection.sourceIds,\n ...(cursors[integration.id] ? { cursor: cursors[integration.id] } : {}),\n });\n items.push(\n ...page.items.map(item => {\n const { source, ...candidate } = item;\n return {\n ...candidate,\n integrationId: integration.id,\n externalSource: { integrationId: integration.id, ...source },\n };\n }),\n );\n if (page.nextCursor) nextCursors[integration.id] = page.nextCursor;\n }\n return c.json({ items, nextCursor: encodeCursor(nextCursors) });\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
|
package/dist/routes/surface.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { MemorySettingsStorage } from '../storage/domains/memory-settings/b
|
|
|
23
23
|
import type { ModelPacksStorage } from '../storage/domains/model-packs/base.js';
|
|
24
24
|
import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
|
|
25
25
|
import type { QueueHealthStorage } from '../storage/domains/queue-health/base.js';
|
|
26
|
-
import type
|
|
26
|
+
import { type SourceControlStorage } from '../storage/domains/source-control/base.js';
|
|
27
27
|
import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
28
28
|
import type { RouteAuth } from './route.js';
|
|
29
29
|
export interface IntegrationRegistration {
|
|
@@ -75,7 +75,6 @@ export interface FactoryApiRoutesDeps {
|
|
|
75
75
|
prepareBinding?: (input: FactoryBindingPreparationInput) => Promise<void>;
|
|
76
76
|
}) => void;
|
|
77
77
|
}
|
|
78
|
-
export declare function factoryRuleBranch(item: FactoryBindingPreparationInput['item']): string;
|
|
79
78
|
/**
|
|
80
79
|
* Start a factory run for a rule binding: ensure the source-control session the
|
|
81
80
|
* coordinator requires, then hand it to `prepare` along with the factory's
|
|
@@ -83,7 +82,7 @@ export declare function factoryRuleBranch(item: FactoryBindingPreparationInput['
|
|
|
83
82
|
* browser and no interactive user, so nothing else would catch a regression in
|
|
84
83
|
* what it forwards.
|
|
85
84
|
*/
|
|
86
|
-
export declare function prepareFactoryRuleBinding(github: GithubIntegration, coordinator: FactoryStartCoordinator, projects: FactoryProjectsStorage, input: FactoryBindingPreparationInput): Promise<void>;
|
|
85
|
+
export declare function prepareFactoryRuleBinding(github: GithubIntegration, coordinator: Pick<FactoryStartCoordinator, 'prepare'>, projects: FactoryProjectsStorage, input: FactoryBindingPreparationInput): Promise<void>;
|
|
87
86
|
/**
|
|
88
87
|
* Build the {@link IntegrationContext} handed to an integration when the
|
|
89
88
|
* factory collects its capabilities (routes, workers). One shape everywhere:
|
|
@@ -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,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAEtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/routes/surface.ts"],"names":[],"mappings":"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"}
|