@axiom-lattice/gateway 3.0.2 → 3.0.4

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.
Files changed (155) hide show
  1. package/dist/index.js +37 -9
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +37 -9
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +9 -6
  6. package/.eslintrc.json +0 -22
  7. package/.turbo/turbo-build.log +0 -46
  8. package/AGENTS.md +0 -112
  9. package/CHANGELOG.md +0 -1190
  10. package/LOGGER_CONFIG.md +0 -135
  11. package/RESUME_STREAM_CONTENT_BASED.md +0 -326
  12. package/RESUME_STREAM_README.md +0 -388
  13. package/jest.config.js +0 -72
  14. package/public/sdk/README.md +0 -695
  15. package/public/sdk/dashboard-engine-skill.md +0 -1122
  16. package/public/sdk/dashboard-single-file-spec.md +0 -357
  17. package/public/sdk/data-query-sdk-skill.md +0 -307
  18. package/public/sdk/data-query-sdk.d.ts +0 -252
  19. package/public/sdk/data-query-sdk.js +0 -970
  20. package/public/sdk/occupancy-dashboard.html +0 -363
  21. package/public/sdk/test-dashboard.html +0 -690
  22. package/scripts/seed-lark-installation.ts +0 -54
  23. package/src/__tests__/__mocks__/e2b.ts +0 -1
  24. package/src/__tests__/a2a.test.ts +0 -428
  25. package/src/__tests__/channel-installations.test.ts +0 -199
  26. package/src/__tests__/data-query.test.ts +0 -77
  27. package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
  28. package/src/__tests__/setup.ts +0 -5
  29. package/src/__tests__/workspace.test.ts +0 -400
  30. package/src/channels/__tests__/routes.test.ts +0 -50
  31. package/src/channels/lark/LarkChannelAdapter.ts +0 -217
  32. package/src/channels/lark/README.md +0 -187
  33. package/src/channels/lark/__tests__/controller.test.ts +0 -136
  34. package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
  35. package/src/channels/lark/__tests__/parser.test.ts +0 -72
  36. package/src/channels/lark/__tests__/sender.test.ts +0 -37
  37. package/src/channels/lark/__tests__/verification.test.ts +0 -157
  38. package/src/channels/lark/controller.ts +0 -48
  39. package/src/channels/lark/mapping-service.ts +0 -138
  40. package/src/channels/lark/parser.ts +0 -68
  41. package/src/channels/lark/routes.ts +0 -22
  42. package/src/channels/lark/sender.ts +0 -58
  43. package/src/channels/lark/types.ts +0 -20
  44. package/src/channels/lark/verification.ts +0 -32
  45. package/src/channels/registry.ts +0 -20
  46. package/src/channels/routes.ts +0 -30
  47. package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
  48. package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
  49. package/src/channels/wechat/context-store.ts +0 -54
  50. package/src/channels/wechat/controller.ts +0 -59
  51. package/src/channels/wechat/routes.ts +0 -7
  52. package/src/channels/wechat/types.ts +0 -62
  53. package/src/channels/wechat/wechat-client.ts +0 -162
  54. package/src/config/local-a2a-templates.ts +0 -59
  55. package/src/config.ts +0 -124
  56. package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
  57. package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
  58. package/src/controllers/__tests__/mime.test.ts +0 -74
  59. package/src/controllers/__tests__/resources.test.ts +0 -122
  60. package/src/controllers/__tests__/run.test.ts +0 -115
  61. package/src/controllers/__tests__/tasks.test.ts +0 -215
  62. package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
  63. package/src/controllers/agent_task.ts +0 -72
  64. package/src/controllers/assistant.ts +0 -490
  65. package/src/controllers/auth.ts +0 -524
  66. package/src/controllers/channel-bindings.ts +0 -135
  67. package/src/controllers/channel-installations.ts +0 -409
  68. package/src/controllers/collections.ts +0 -176
  69. package/src/controllers/config.ts +0 -126
  70. package/src/controllers/connections.ts +0 -181
  71. package/src/controllers/data-query.ts +0 -236
  72. package/src/controllers/database-configs.ts +0 -440
  73. package/src/controllers/embeddings.ts +0 -22
  74. package/src/controllers/entries.ts +0 -167
  75. package/src/controllers/eval.ts +0 -513
  76. package/src/controllers/export-import.ts +0 -166
  77. package/src/controllers/health.ts +0 -36
  78. package/src/controllers/local-a2a-templates.ts +0 -18
  79. package/src/controllers/local-a2a.ts +0 -149
  80. package/src/controllers/mcp-configs.ts +0 -673
  81. package/src/controllers/memory.ts +0 -257
  82. package/src/controllers/menu-items.ts +0 -114
  83. package/src/controllers/metrics-configs.ts +0 -1061
  84. package/src/controllers/middleware-types.ts +0 -15
  85. package/src/controllers/models.ts +0 -199
  86. package/src/controllers/personal-assistant.ts +0 -191
  87. package/src/controllers/resources.ts +0 -477
  88. package/src/controllers/run.ts +0 -338
  89. package/src/controllers/sandbox.ts +0 -255
  90. package/src/controllers/schedules.ts +0 -336
  91. package/src/controllers/skills.ts +0 -256
  92. package/src/controllers/tasks.ts +0 -213
  93. package/src/controllers/tenants.ts +0 -121
  94. package/src/controllers/thread_status.ts +0 -66
  95. package/src/controllers/threads.ts +0 -260
  96. package/src/controllers/tools.ts +0 -139
  97. package/src/controllers/users.ts +0 -135
  98. package/src/controllers/workflow-tracking.ts +0 -693
  99. package/src/controllers/workspace.ts +0 -879
  100. package/src/export_registrations/a2a-api-key.registration.ts +0 -89
  101. package/src/export_registrations/agent.registration.ts +0 -80
  102. package/src/export_registrations/binding.registration.ts +0 -106
  103. package/src/export_registrations/channel-installation.registration.ts +0 -88
  104. package/src/export_registrations/collection.registration.ts +0 -85
  105. package/src/export_registrations/connection.registration.ts +0 -97
  106. package/src/export_registrations/database-config.registration.ts +0 -94
  107. package/src/export_registrations/eval.registration.ts +0 -334
  108. package/src/export_registrations/index.ts +0 -31
  109. package/src/export_registrations/mcp-config.registration.ts +0 -97
  110. package/src/export_registrations/menu.registration.ts +0 -91
  111. package/src/export_registrations/metrics-config.registration.ts +0 -94
  112. package/src/export_registrations/skill.registration.ts +0 -88
  113. package/src/export_registrations/task.registration.ts +0 -99
  114. package/src/index.ts +0 -497
  115. package/src/logger/Logger.ts +0 -186
  116. package/src/router/MessageContext.ts +0 -14
  117. package/src/router/MessageRouter.ts +0 -490
  118. package/src/router/middlewares/auditLogger.ts +0 -34
  119. package/src/router/middlewares/deduplication.ts +0 -38
  120. package/src/router/middlewares/index.ts +0 -3
  121. package/src/router/middlewares/rateLimit.ts +0 -46
  122. package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
  123. package/src/routes/a2a-bridge.ts +0 -266
  124. package/src/routes/a2a-test-connection.ts +0 -126
  125. package/src/routes/a2a.ts +0 -851
  126. package/src/routes/channel-bindings.ts +0 -11
  127. package/src/routes/channel-installations.ts +0 -33
  128. package/src/routes/index.ts +0 -640
  129. package/src/routes/menu-items.ts +0 -10
  130. package/src/routes/resource-routes.ts +0 -35
  131. package/src/routes/stt.ts +0 -102
  132. package/src/schemas/data-query.ts +0 -69
  133. package/src/schemas/index.ts +0 -852
  134. package/src/services/ExportImportService.ts +0 -296
  135. package/src/services/__tests__/ExportImportService.test.ts +0 -130
  136. package/src/services/__tests__/eval-runner.test.ts +0 -131
  137. package/src/services/__tests__/local-a2a.test.ts +0 -124
  138. package/src/services/agent_task_consumer.ts +0 -348
  139. package/src/services/collection_service.ts +0 -258
  140. package/src/services/eval-runner.ts +0 -283
  141. package/src/services/local-a2a.ts +0 -354
  142. package/src/services/queue_service.ts +0 -104
  143. package/src/services/sandbox_service.ts +0 -223
  144. package/src/services/skill_service.ts +0 -97
  145. package/src/services/supabase.ts +0 -60
  146. package/src/swagger.ts +0 -77
  147. package/src/types/index.ts +0 -118
  148. package/src/types/koa.d.ts +0 -7
  149. package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
  150. package/src/utils/__tests__/path-containment.test.ts +0 -35
  151. package/src/utils/a2a-url.ts +0 -20
  152. package/src/utils/local-a2a-runtime.ts +0 -107
  153. package/src/utils/mime.ts +0 -44
  154. package/src/utils/path-containment.ts +0 -38
  155. package/tsconfig.json +0 -26
@@ -1,513 +0,0 @@
1
- import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
2
- import { getStoreLattice } from "@axiom-lattice/core";
3
- import { EvalStore } from "@axiom-lattice/protocols";
4
- import { v4 as uuidv4 } from "uuid";
5
- import { evalRunner, EvalStreamEvent } from "../services/eval-runner";
6
-
7
- function getTenantId(request: FastifyRequest): string {
8
- const userTenantId = (request as any).user?.tenantId;
9
- if (userTenantId) {
10
- return userTenantId;
11
- }
12
- return (request.headers["x-tenant-id"] as string) || "default";
13
- }
14
-
15
- function getEvalStore(): EvalStore {
16
- return getStoreLattice("default", "eval").store as EvalStore;
17
- }
18
-
19
- export async function createProject(
20
- request: FastifyRequest<{ Body: Record<string, unknown> }>,
21
- reply: FastifyReply
22
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
23
- try {
24
- const tenantId = getTenantId(request);
25
- const store = getEvalStore();
26
- const id = uuidv4();
27
- const data = request.body as any;
28
- const serverCfg = (data.targetServerConfig || {}) as Record<string, unknown>;
29
- const judgeCfg = (data.judgeModelConfig || {}) as Record<string, unknown>;
30
- const modelKey = (judgeCfg.modelKey as string) || "";
31
- if (modelKey) {
32
- const { modelLatticeManager } = await import("@axiom-lattice/core");
33
- if (!modelLatticeManager.hasLattice(modelKey)) {
34
- return reply.status(400).send({ success: false, message: `Judge model "${modelKey}" is not registered` });
35
- }
36
- }
37
- const project = await store.createProject(tenantId, id, {
38
- name: data.name,
39
- description: data.description,
40
- version: data.version,
41
- judgeModelConfig: {
42
- modelKey,
43
- displayName: (judgeCfg.displayName as string) || "",
44
- },
45
- targetServerConfig: {
46
- base_url: (serverCfg.base_url as string) || "",
47
- api_key: (serverCfg.api_key as string) || "",
48
- },
49
- concurrency: data.concurrency ?? 1,
50
- reportConfig: data.reportConfig,
51
- });
52
- return reply.status(201).send({
53
- success: true,
54
- message: "Successfully created project",
55
- data: project,
56
- });
57
- } catch (err: unknown) {
58
- const message = err instanceof Error ? err.message : "Failed to create project";
59
- return reply.status(500).send({ success: false, message });
60
- }
61
- }
62
-
63
- export async function listProjects(
64
- request: FastifyRequest,
65
- reply: FastifyReply
66
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
67
- try {
68
- const tenantId = getTenantId(request);
69
- const store = getEvalStore();
70
- let projects = await store.getProjectsByTenant(tenantId);
71
-
72
- // Seed a default project for new tenants
73
- if (projects.length === 0) {
74
- try {
75
- const { modelLatticeManager } = await import("@axiom-lattice/core");
76
- const models = modelLatticeManager.getAllLattices();
77
- const first = models[0];
78
- const judgeModel = first
79
- ? { modelKey: first.key }
80
- : {};
81
-
82
- const host = request.hostname || "localhost";
83
- const baseUrl = `http://${host}:${process.env.PORT || 4001}`;
84
-
85
- await store.createProject(tenantId, uuidv4(), {
86
- name: "Default",
87
- description: "Built-in project for testing eval against this server",
88
- judgeModelConfig: judgeModel,
89
- targetServerConfig: { base_url: baseUrl, api_key: "" },
90
- concurrency: 3,
91
- });
92
- projects = await store.getProjectsByTenant(tenantId);
93
- } catch { /* seed failure is non-fatal */ }
94
- }
95
-
96
- return {
97
- success: true,
98
- message: "Ok",
99
- data: { records: projects, total: projects.length },
100
- };
101
- } catch (err: unknown) {
102
- const message = err instanceof Error ? err.message : "Failed to list projects";
103
- return reply.status(500).send({ success: false, message });
104
- }
105
- }
106
-
107
- export async function getProject(
108
- request: FastifyRequest<{ Params: { pid: string } }>,
109
- reply: FastifyReply
110
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
111
- try {
112
- const tenantId = getTenantId(request);
113
- const store = getEvalStore();
114
- const { pid } = request.params;
115
- const project = await store.getProjectById(tenantId, pid);
116
- if (!project) {
117
- return reply.status(404).send({ success: false, message: "Project not found" });
118
- }
119
- const suites = await store.getSuitesByProject(tenantId, pid);
120
- return {
121
- success: true,
122
- message: "Ok",
123
- data: { project, suites },
124
- };
125
- } catch (err: unknown) {
126
- const message = err instanceof Error ? err.message : "Failed to get project";
127
- return reply.status(500).send({ success: false, message });
128
- }
129
- }
130
-
131
- export async function updateProject(
132
- request: FastifyRequest<{ Params: { pid: string }; Body: Record<string, unknown> }>,
133
- reply: FastifyReply
134
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
135
- try {
136
- const tenantId = getTenantId(request);
137
- const store = getEvalStore();
138
- const { pid } = request.params;
139
- const existing = await store.getProjectById(tenantId, pid);
140
- if (!existing) {
141
- return reply.status(404).send({ success: false, message: "Project not found" });
142
- }
143
- const body = request.body as Record<string, unknown>;
144
- const updateData: Record<string, unknown> = { ...body };
145
- // Strip in_process from user input — it is server-controlled
146
- const serverCfg = (body.targetServerConfig || {}) as Record<string, unknown>;
147
- if (body.targetServerConfig !== undefined) {
148
- updateData.targetServerConfig = {
149
- base_url: (serverCfg.base_url as string) || "",
150
- api_key: (serverCfg.api_key as string) || "",
151
- };
152
- }
153
- // Only allow modelKey/displayName — never raw provider/apiKey/baseURL
154
- if (body.judgeModelConfig !== undefined) {
155
- const judgeCfg = (body.judgeModelConfig || {}) as Record<string, unknown>;
156
- const modelKey = (judgeCfg.modelKey as string) || "";
157
- if (modelKey) {
158
- const { modelLatticeManager } = await import("@axiom-lattice/core");
159
- if (!modelLatticeManager.hasLattice(modelKey)) {
160
- return reply.status(400).send({ success: false, message: `Judge model "${modelKey}" is not registered` });
161
- }
162
- }
163
- updateData.judgeModelConfig = {
164
- modelKey,
165
- displayName: (judgeCfg.displayName as string) || "",
166
- };
167
- }
168
- const updated = await store.updateProject(tenantId, pid, updateData);
169
- return {
170
- success: true,
171
- message: "Successfully updated project",
172
- data: updated,
173
- };
174
- } catch (err: unknown) {
175
- const message = err instanceof Error ? err.message : "Failed to update project";
176
- return reply.status(500).send({ success: false, message });
177
- }
178
- }
179
-
180
- export async function deleteProject(
181
- request: FastifyRequest<{ Params: { pid: string } }>,
182
- reply: FastifyReply
183
- ): Promise<{ success: boolean; message: string }> {
184
- try {
185
- const tenantId = getTenantId(request);
186
- const store = getEvalStore();
187
- const { pid } = request.params;
188
- const existing = await store.getProjectById(tenantId, pid);
189
- if (!existing) {
190
- return reply.status(404).send({ success: false, message: "Project not found" });
191
- }
192
- const deleted = await store.deleteProject(tenantId, pid);
193
- if (!deleted) {
194
- return reply.status(500).send({ success: false, message: "Failed to delete project" });
195
- }
196
- return { success: true, message: "Successfully deleted project" };
197
- } catch (err: unknown) {
198
- const message = err instanceof Error ? err.message : "Failed to delete project";
199
- return reply.status(500).send({ success: false, message });
200
- }
201
- }
202
-
203
- export async function createSuite(
204
- request: FastifyRequest<{ Params: { pid: string }; Body: Record<string, unknown> }>,
205
- reply: FastifyReply
206
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
207
- try {
208
- const tenantId = getTenantId(request);
209
- const store = getEvalStore();
210
- const { pid } = request.params;
211
- const project = await store.getProjectById(tenantId, pid);
212
- if (!project) {
213
- return reply.status(404).send({ success: false, message: "Project not found" });
214
- }
215
- const id = uuidv4();
216
- const data = request.body as any;
217
- const suite = await store.createSuite(tenantId, pid, id, { name: data.name });
218
- return reply.status(201).send({
219
- success: true,
220
- message: "Successfully created suite",
221
- data: suite,
222
- });
223
- } catch (err: unknown) {
224
- const message = err instanceof Error ? err.message : "Failed to create suite";
225
- return reply.status(500).send({ success: false, message });
226
- }
227
- }
228
-
229
- export async function updateSuite(
230
- request: FastifyRequest<{ Params: { pid: string; sid: string }; Body: Record<string, unknown> }>,
231
- reply: FastifyReply
232
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
233
- try {
234
- const tenantId = getTenantId(request);
235
- const store = getEvalStore();
236
- const { sid } = request.params;
237
- const existing = await store.getSuiteById(tenantId, sid);
238
- if (!existing) {
239
- return reply.status(404).send({ success: false, message: "Suite not found" });
240
- }
241
- const updated = await store.updateSuite(tenantId, sid, request.body as any);
242
- return {
243
- success: true,
244
- message: "Successfully updated suite",
245
- data: updated,
246
- };
247
- } catch (err: unknown) {
248
- const message = err instanceof Error ? err.message : "Failed to update suite";
249
- return reply.status(500).send({ success: false, message });
250
- }
251
- }
252
-
253
- export async function deleteSuite(
254
- request: FastifyRequest<{ Params: { pid: string; sid: string } }>,
255
- reply: FastifyReply
256
- ): Promise<{ success: boolean; message: string }> {
257
- try {
258
- const tenantId = getTenantId(request);
259
- const store = getEvalStore();
260
- const { sid } = request.params;
261
- const existing = await store.getSuiteById(tenantId, sid);
262
- if (!existing) {
263
- return reply.status(404).send({ success: false, message: "Suite not found" });
264
- }
265
- const deleted = await store.deleteSuite(tenantId, sid);
266
- if (!deleted) {
267
- return reply.status(500).send({ success: false, message: "Failed to delete suite" });
268
- }
269
- return { success: true, message: "Successfully deleted suite" };
270
- } catch (err: unknown) {
271
- const message = err instanceof Error ? err.message : "Failed to delete suite";
272
- return reply.status(500).send({ success: false, message });
273
- }
274
- }
275
-
276
- export async function createCase(
277
- request: FastifyRequest<{ Params: { pid: string; sid: string }; Body: Record<string, unknown> }>,
278
- reply: FastifyReply
279
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
280
- try {
281
- const tenantId = getTenantId(request);
282
- const store = getEvalStore();
283
- const { sid } = request.params;
284
- const suite = await store.getSuiteById(tenantId, sid);
285
- if (!suite) {
286
- return reply.status(404).send({ success: false, message: "Suite not found" });
287
- }
288
- const id = uuidv4();
289
- const data = request.body as any;
290
- const created = await store.createCase(tenantId, sid, id, {
291
- inputMessage: data.inputMessage,
292
- inputFiles: data.inputFiles,
293
- steps: data.steps ?? [],
294
- outputType: data.outputType ?? "message_content",
295
- contentAssertion: data.contentAssertion ?? "",
296
- rubrics: data.rubrics,
297
- });
298
- return reply.status(201).send({
299
- success: true,
300
- message: "Successfully created case",
301
- data: created,
302
- });
303
- } catch (err: unknown) {
304
- const message = err instanceof Error ? err.message : "Failed to create case";
305
- return reply.status(500).send({ success: false, message });
306
- }
307
- }
308
-
309
- export async function listCasesForSuite(
310
- request: FastifyRequest<{ Params: { pid: string; sid: string } }>,
311
- reply: FastifyReply
312
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
313
- try {
314
- const tenantId = getTenantId(request);
315
- const store = getEvalStore();
316
- const { sid } = request.params;
317
- const cases = await store.getCasesBySuite(tenantId, sid);
318
- return {
319
- success: true,
320
- message: "Ok",
321
- data: { records: cases, total: cases.length },
322
- };
323
- } catch (err: unknown) {
324
- const message = err instanceof Error ? err.message : "Failed to list cases";
325
- return reply.status(500).send({ success: false, message });
326
- }
327
- }
328
-
329
- export async function updateCase(
330
- request: FastifyRequest<{ Params: { pid: string; sid: string; cid: string }; Body: Record<string, unknown> }>,
331
- reply: FastifyReply
332
- ): Promise<{ success: boolean; message: string; data?: unknown }> {
333
- try {
334
- const tenantId = getTenantId(request);
335
- const store = getEvalStore();
336
- const { cid } = request.params;
337
- const existing = await store.getCaseById(tenantId, cid);
338
- if (!existing) {
339
- return reply.status(404).send({ success: false, message: "Case not found" });
340
- }
341
- const updated = await store.updateCase(tenantId, cid, request.body as any);
342
- return {
343
- success: true,
344
- message: "Successfully updated case",
345
- data: updated,
346
- };
347
- } catch (err: unknown) {
348
- const message = err instanceof Error ? err.message : "Failed to update case";
349
- return reply.status(500).send({ success: false, message });
350
- }
351
- }
352
-
353
- export async function deleteCase(
354
- request: FastifyRequest<{ Params: { pid: string; sid: string; cid: string } }>,
355
- reply: FastifyReply
356
- ): Promise<{ success: boolean; message: string }> {
357
- try {
358
- const tenantId = getTenantId(request);
359
- const store = getEvalStore();
360
- const { cid } = request.params;
361
- const existing = await store.getCaseById(tenantId, cid);
362
- if (!existing) {
363
- return reply.status(404).send({ success: false, message: "Case not found" });
364
- }
365
- const deleted = await store.deleteCase(tenantId, cid);
366
- if (!deleted) {
367
- return reply.status(500).send({ success: false, message: "Failed to delete case" });
368
- }
369
- return { success: true, message: "Successfully deleted case" };
370
- } catch (err: unknown) {
371
- const message = err instanceof Error ? err.message : "Failed to delete case";
372
- return reply.status(500).send({ success: false, message });
373
- }
374
- }
375
-
376
- export function registerEvalRoutes(app: FastifyInstance): void {
377
- app.post("/api/eval/projects", createProject);
378
- app.get("/api/eval/projects", listProjects);
379
- app.get("/api/eval/projects/:pid", getProject);
380
- app.put("/api/eval/projects/:pid", updateProject);
381
- app.delete("/api/eval/projects/:pid", deleteProject);
382
-
383
- app.post("/api/eval/projects/:pid/suites", createSuite);
384
- app.put("/api/eval/projects/:pid/suites/:sid", updateSuite);
385
- app.delete("/api/eval/projects/:pid/suites/:sid", deleteSuite);
386
-
387
- app.post("/api/eval/projects/:pid/suites/:sid/cases", createCase);
388
- app.get("/api/eval/projects/:pid/suites/:sid/cases", listCasesForSuite);
389
- app.put("/api/eval/projects/:pid/suites/:sid/cases/:cid", updateCase);
390
- app.delete("/api/eval/projects/:pid/suites/:sid/cases/:cid", deleteCase);
391
-
392
- // POST /api/eval/projects/:pid/runs — trigger run
393
- app.post("/api/eval/projects/:pid/runs", async (request, reply) => {
394
- try {
395
- const tenantId = getTenantId(request);
396
- const { pid } = request.params as { pid: string };
397
- const body = (request.body ?? {}) as { suiteIds?: string[] };
398
- const runId = await evalRunner.startRun(tenantId, pid, body?.suiteIds);
399
- reply.status(202).send({ success: true, message: "Run started", data: { run_id: runId } });
400
- } catch (err) {
401
- const msg = (err as Error).message;
402
- const code = msg === "Project not found" ? 404
403
- : msg.includes("already in progress") ? 409
404
- : msg.includes("No suites match") ? 400
405
- : 500;
406
- reply.status(code).send({ success: false, message: msg });
407
- }
408
- });
409
-
410
- // GET /api/eval/runs — list runs
411
- app.get("/api/eval/runs", async (request, reply) => {
412
- try {
413
- const tenantId = getTenantId(request);
414
- const query = request.query as { project_id?: string; status?: string };
415
- const runs = await getEvalStore().getRunsByTenant(tenantId, { projectId: query.project_id, status: query.status });
416
- reply.send({ success: true, message: "Ok", data: { records: runs, total: runs.length } });
417
- } catch (err) {
418
- reply.status(500).send({ success: false, message: (err as Error).message });
419
- }
420
- });
421
-
422
- // GET /api/eval/runs/:rid — get run detail with results
423
- app.get("/api/eval/runs/:rid", async (request, reply) => {
424
- try {
425
- const tenantId = getTenantId(request);
426
- const { rid } = request.params as { rid: string };
427
- const run = await getEvalStore().getRunById(tenantId, rid);
428
- if (!run) return reply.status(404).send({ success: false, message: "Run not found" });
429
- const results = await getEvalStore().getResultsByRun(tenantId, rid);
430
- reply.send({ success: true, message: "Ok", data: { ...run, results } });
431
- } catch (err) {
432
- reply.status(500).send({ success: false, message: (err as Error).message });
433
- }
434
- });
435
-
436
- // GET /api/eval/runs/:rid/stream — SSE progress stream
437
- app.get("/api/eval/runs/:rid/stream", async (request, reply) => {
438
- reply.hijack();
439
- reply.raw.writeHead(200, {
440
- "Content-Type": "text/event-stream",
441
- "Cache-Control": "no-cache",
442
- Connection: "keep-alive",
443
- "Access-Control-Allow-Origin": "*",
444
- });
445
-
446
- const { rid } = request.params as { rid: string };
447
- const emitter = evalRunner.getEventEmitter();
448
- const eventKey = `run:${rid}`;
449
-
450
- if (!evalRunner.isRunning(rid)) {
451
- const tenantId = getTenantId(request);
452
- const run = await getEvalStore().getRunById(tenantId, rid);
453
- if (run) {
454
- const eventType = run.status === "completed" ? "completed" : "error";
455
- reply.raw.write(`event: ${eventType}\ndata: ${JSON.stringify({ passed: run.passedCases, failed: run.failedCases, avgScore: run.avgScore })}\n\n`);
456
- }
457
- reply.raw.end();
458
- return;
459
- }
460
-
461
- const handler = (event: EvalStreamEvent): void => {
462
- reply.raw.write(`event: ${event.type}\ndata: ${JSON.stringify(event.data)}\n\n`);
463
- if (event.type === "completed" || event.type === "error") {
464
- emitter.off(eventKey, handler);
465
- reply.raw.end();
466
- }
467
- };
468
-
469
- emitter.on(eventKey, handler);
470
-
471
- request.raw.on("close", () => {
472
- emitter.off(eventKey, handler);
473
- });
474
- });
475
-
476
- // POST /api/eval/runs/:rid/abort — abort run
477
- app.post("/api/eval/runs/:rid/abort", async (request, reply) => {
478
- try {
479
- const { rid } = request.params as { rid: string };
480
- const aborted = await evalRunner.abortRun(rid);
481
- if (!aborted) return reply.status(404).send({ success: false, message: "Run not found or not running" });
482
- reply.send({ success: true, message: "Run aborted" });
483
- } catch (err) {
484
- reply.status(500).send({ success: false, message: (err as Error).message });
485
- }
486
- });
487
-
488
- // DELETE /api/eval/runs/:rid — delete run and results
489
- app.delete("/api/eval/runs/:rid", async (request, reply) => {
490
- try {
491
- const tenantId = getTenantId(request);
492
- const { rid } = request.params as { rid: string };
493
- const deleted = await getEvalStore().deleteRun(tenantId, rid);
494
- if (!deleted) return reply.status(404).send({ success: false, message: "Run not found" });
495
- reply.send({ success: true, message: "Run deleted" });
496
- } catch (err) {
497
- reply.status(500).send({ success: false, message: (err as Error).message });
498
- }
499
- });
500
-
501
- // GET /api/eval/reports/projects/:pid — aggregated report
502
- app.get("/api/eval/reports/projects/:pid", async (request, reply) => {
503
- try {
504
- const tenantId = getTenantId(request);
505
- const { pid } = request.params as { pid: string };
506
- const report = await getEvalStore().getProjectReport(tenantId, pid);
507
- if (!report) return reply.status(404).send({ success: false, message: "Project not found" });
508
- reply.send({ success: true, message: "Ok", data: report });
509
- } catch (err) {
510
- reply.status(500).send({ success: false, message: (err as Error).message });
511
- }
512
- });
513
- }
@@ -1,166 +0,0 @@
1
- import type { FastifyRequest, FastifyReply } from 'fastify';
2
- import { ExportImportService } from '../services/ExportImportService';
3
- import { ExportableEntityRegistry } from '@axiom-lattice/core';
4
-
5
- const service = new ExportImportService();
6
-
7
- function getTenantId(request: FastifyRequest): string {
8
- return (request as any).user?.tenantId || (request.headers['x-tenant-id'] as string) || 'default';
9
- }
10
-
11
- /** GET /api/tenants/exportable-types */
12
- export async function getExportableTypes(
13
- _request: FastifyRequest,
14
- reply: FastifyReply,
15
- ): Promise<void> {
16
- const types = service.listExportableTypes();
17
- return reply.send({ success: true, data: types });
18
- }
19
-
20
- /** POST /api/tenants/:tenantId/export */
21
- export async function exportConfig(
22
- request: FastifyRequest<{ Params: { tenantId: string }; Body: { entityTypes: string[]; entityIds?: Record<string, string[]> } }>,
23
- reply: FastifyReply,
24
- ): Promise<void> {
25
- const tenantId = getTenantId(request) || request.params.tenantId;
26
- const { entityTypes, entityIds } = request.body;
27
-
28
- if (!Array.isArray(entityTypes) || entityTypes.length === 0) {
29
- return reply.status(400).send({ success: false, message: 'entityTypes must be a non-empty array' });
30
- }
31
-
32
- const preview = await service.exportPreview(tenantId, entityTypes);
33
-
34
- if (preview.missingDependencies.length > 0 || preview.cascadeAdditions.length > 0) {
35
- return reply.send({
36
- success: true,
37
- data: {
38
- needsConfirmation: true,
39
- missingDependencies: preview.missingDependencies,
40
- cascadeAdditions: preview.cascadeAdditions,
41
- },
42
- });
43
- }
44
-
45
- const { jobId, bundle } = await service.export(tenantId, entityTypes, entityIds);
46
- return reply.send({
47
- success: true,
48
- data: {
49
- jobId,
50
- summary: Object.fromEntries(
51
- Object.entries(bundle.entities).map(([type, entities]) => [type, entities.length]),
52
- ),
53
- },
54
- });
55
- }
56
-
57
- /** POST /api/tenants/:tenantId/export/confirm */
58
- export async function exportConfigConfirm(
59
- request: FastifyRequest<{ Params: { tenantId: string }; Body: { entityTypes: string[]; entityIds?: Record<string, string[]> } }>,
60
- reply: FastifyReply,
61
- ): Promise<void> {
62
- const tenantId = getTenantId(request) || request.params.tenantId;
63
- const { entityTypes, entityIds } = request.body;
64
-
65
- const { jobId, bundle } = await service.export(tenantId, entityTypes, entityIds);
66
- return reply.send({
67
- success: true,
68
- data: {
69
- jobId,
70
- summary: Object.fromEntries(
71
- Object.entries(bundle.entities).map(([type, entities]) => [type, entities.length]),
72
- ),
73
- },
74
- });
75
- }
76
-
77
- /** POST /api/tenants/:tenantId/export/entities */
78
- export async function previewEntities(
79
- request: FastifyRequest<{ Params: { tenantId: string }; Body: { entityTypes: string[] } }>,
80
- reply: FastifyReply,
81
- ): Promise<void> {
82
- const tenantId = getTenantId(request) || request.params.tenantId;
83
- const { entityTypes } = request.body;
84
-
85
- try {
86
- const registry = ExportableEntityRegistry.getInstance();
87
- const result: Record<string, Array<{ id: string; name: string; description?: string }>> = {};
88
-
89
- for (const type of entityTypes) {
90
- const def = registry.get(type);
91
- const entities = await def.listForExport(tenantId);
92
- result[type] = entities.map((e) => ({
93
- id: e.data.id as string,
94
- name: (e.data.name as string) || (e.data.id as string),
95
- description: e.data.description as string | undefined,
96
- }));
97
- }
98
-
99
- return reply.send({ success: true, data: result });
100
- } catch (err) {
101
- return reply.status(400).send({ success: false, message: (err as Error).message });
102
- }
103
- }
104
-
105
- /** GET /api/tenants/:tenantId/export/:jobId/download */
106
- export async function downloadExport(
107
- request: FastifyRequest<{ Params: { tenantId: string; jobId: string } }>,
108
- reply: FastifyReply,
109
- ): Promise<void> {
110
- const tenantId = getTenantId(request) || request.params.tenantId;
111
- const bundle = service.getExportJob(request.params.jobId, tenantId);
112
-
113
- if (!bundle) {
114
- return reply.status(404).send({ success: false, message: 'Export job not found or expired' });
115
- }
116
-
117
- return reply
118
- .header('Content-Type', 'application/json')
119
- .header('Content-Disposition', `attachment; filename="tenant-export-${bundle.sourceTenantId}.json"`)
120
- .send({ success: true, data: bundle });
121
- }
122
-
123
- /** POST /api/tenants/:tenantId/import/preview */
124
- export async function importPreview(
125
- request: FastifyRequest<{ Params: { tenantId: string }; Body: { bundle: any } }>,
126
- reply: FastifyReply,
127
- ): Promise<void> {
128
- const tenantId = getTenantId(request) || request.params.tenantId;
129
- const { bundle } = request.body;
130
-
131
- if (!bundle) {
132
- return reply.status(400).send({ success: false, message: 'No bundle provided in request body' });
133
- }
134
-
135
- try {
136
- const preview = await service.previewImport(tenantId, bundle);
137
- return reply.send({ success: true, data: preview });
138
- } catch (err) {
139
- return reply.status(400).send({
140
- success: false,
141
- message: `Invalid export file: ${(err as Error).message}`,
142
- });
143
- }
144
- }
145
-
146
- /** POST /api/tenants/:tenantId/import/apply */
147
- export async function importApply(
148
- request: FastifyRequest<{
149
- Params: { tenantId: string };
150
- Body: { bundle: any; resolutions: Record<string, any> };
151
- }>,
152
- reply: FastifyReply,
153
- ): Promise<void> {
154
- const tenantId = getTenantId(request) || request.params.tenantId;
155
-
156
- try {
157
- const { bundle, resolutions } = request.body;
158
- const result = await service.applyImport(tenantId, bundle, resolutions);
159
- return reply.send({ success: true, data: result });
160
- } catch (err) {
161
- return reply.status(400).send({
162
- success: false,
163
- message: `Import failed: ${(err as Error).message}`,
164
- });
165
- }
166
- }