@goodandready/dsh-moa 0.2.60 → 0.2.61

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/lib/routes.js CHANGED
@@ -1,586 +1,21 @@
1
1
  /**
2
- * DeepSeek Harness Mixture of Agents (MoA) — HTTP Route Handlers
2
+ * DeepSeek Harness Mixture of Agents (MoA) — HTTP Route Dispatcher
3
+ * Subroutes are modularized in lib/routes/ for line budget compliance.
3
4
  */
4
5
 
5
- import path from 'node:path'
6
- import os from 'node:os'
7
- import { getMoaHistory, getMoaLeaderboard, getMoaRunById, exportMoaHistory, isTestEnvironment } from './history.js'
8
- import { promoteCandidateWorkspace, readCandidateFiles, computeLineDiff, isSafeRelativePath, isLoopbackUrl } from './file-workspace.js'
9
- import { writeJson, readBody, isAllowedWorkspace, assertAuthorizedRequest } from './access.js'
10
- import { isSafeWriteRequest } from './updater.js'
11
- import { runMoAPipeline } from './moa-runner.js'
12
- import { generateMoABenchmarkReport } from './moa-report.js'
13
- import { resolvePresetForPrompt } from './moa-router.js'
14
- import { handleWorkbenchGet, handleWorkbenchAssemble } from './moa-workbench.js'
15
-
16
- let _modelsCache = { timestamp: 0, models: [] }
17
- const MODELS_CACHE_TTL = 60 * 1000
18
-
19
- export function registerMoaRoutes(ctx, { live, callLlm, liveCanvas, saveConfig, Config, plainConfig, webServerPort, timeMachineEngine, workspaceRoot }) {
20
-
21
- ctx.effect(() => {
22
- return ctx.webServer.register({
23
- kind: 'exact',
24
- path: '/dsh-moa/status',
25
- handler: (req, res) => {
26
- if (!assertAuthorizedRequest(req, res, ctx)) return
27
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
28
- try {
29
- const cfg = live()
30
- writeJson(res, 200, {
31
- ok: true,
32
- enabled: cfg.enabled !== false,
33
- defaultPreset: cfg.default_preset || 'default',
34
- presetsCount: (cfg.presets || []).length,
35
- })
36
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
37
- },
38
- })
39
- }, 'dsh-moa: status route')
40
-
41
- // Route: /dsh-moa/models
42
- ctx.effect(() => {
43
- return ctx.webServer.register({
44
- kind: 'exact',
45
- path: '/dsh-moa/models',
46
- handler: async (req, res) => {
47
- if (!assertAuthorizedRequest(req, res, ctx)) return
48
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
49
- if (_modelsCache.models.length > 0 && Date.now() - _modelsCache.timestamp < MODELS_CACHE_TTL) {
50
- writeJson(res, 200, { ok: true, models: _modelsCache.models, cached: true })
51
- return
52
- }
53
-
54
- const result = []
55
- const seen = new Set()
56
-
57
- const add = (p, m) => {
58
- if (!p || !m) return
59
- const key = `${p}:${m}`
60
- if (!seen.has(key)) {
61
- seen.add(key)
62
- result.push({ provider: p, model: m, label: key })
63
- }
64
- }
65
-
66
- if (ctx.llm) {
67
- try {
68
- if (typeof ctx.llm.listAvailableModels === 'function') {
69
- const list = await ctx.llm.listAvailableModels()
70
- for (const item of (list || [])) {
71
- if (item?.provider && item?.id) add(item.provider, item.id)
72
- }
73
- }
74
- } catch (e) {
75
- ctx.logger?.warn?.('[dsh-moa] ctx.llm.listAvailableModels error:', e)
76
- }
77
-
78
- try {
79
- if (typeof ctx.llm.listConfigurableProviders === 'function') {
80
- const cProvs = ctx.llm.listConfigurableProviders() || []
81
- const queries = cProvs.map(async (cp) => {
82
- const cpId = cp.provider || cp.id
83
- if (!cpId || typeof ctx.llm.listModels !== 'function') return
84
- try {
85
- const mList = await Promise.race([
86
- ctx.llm.listModels(cpId),
87
- new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 2000)),
88
- ])
89
- for (const m of (mList || [])) {
90
- const mId = typeof m === 'string' ? m : (m?.id || m?.model || m?.name)
91
- if (mId) add(cpId, mId)
92
- }
93
- } catch {
94
- /* provider query timeout or error handled best-effort */
95
- }
96
- })
97
- await Promise.allSettled(queries)
98
- }
99
- } catch (e) {
100
- ctx.logger?.warn?.('[dsh-moa] ctx.llm.listConfigurableProviders error:', e)
101
- }
102
- }
103
-
104
- // Query settings for configured providers
105
- try {
106
- let s = null
107
- try { s = ctx.get ? ctx.get('settings') : null } catch { s = null }
108
- const getSetting = (k) => { try { return s?.get?.(k) } catch { return undefined } }
109
-
110
- const def = getSetting('agent-default-model')
111
- if (def?.provider && def?.model) add(def.provider, def.model)
112
-
113
- const piAi = getSetting('llm-pi-ai')
114
- for (const [pName, pData] of Object.entries(piAi?.providers || {})) {
115
- for (const m of (pData?.models || [])) {
116
- const mId = typeof m === 'string' ? m : (m?.id || m?.model)
117
- if (mId) add(pName, mId)
118
- }
119
- }
120
-
121
- const sync = getSetting('dsh-model-sync')
122
- for (const [syncProv, syncModels] of Object.entries(sync?.modelCatalogs || {})) {
123
- for (const m of (syncModels || [])) {
124
- const mId = typeof m === 'string' ? m : (m?.id || m?.model)
125
- if (mId) add(syncProv, mId)
126
- }
127
- }
128
- } catch (e) {
129
- ctx.logger?.warn?.('[dsh-moa] Error reading settings:', e)
130
- }
131
-
132
- _modelsCache = { timestamp: Date.now(), models: result }
133
- writeJson(res, 200, { ok: true, models: result })
134
- },
135
- })
136
- }, 'dsh-moa: models route')
137
-
138
- // Route: /dsh-moa/presets
139
- ctx.effect(() => {
140
- return ctx.webServer.register({
141
- kind: 'exact',
142
- path: '/dsh-moa/presets',
143
- handler: async (req, res) => {
144
- if (!assertAuthorizedRequest(req, res, ctx)) return
145
- const cfg = live()
146
- if (req.method === 'GET') {
147
- writeJson(res, 200, {
148
- ok: true,
149
- defaultPreset: cfg.default_preset || 'default',
150
- presets: cfg.presets || [],
151
- })
152
- return
153
- }
154
-
155
- if (req.method === 'POST' || req.method === 'PUT') {
156
- if (!isSafeWriteRequest(req)) {
157
- writeJson(res, 403, { ok: false, error: 'Forbidden: cross-site request rejected' })
158
- return
159
- }
160
- try {
161
- const raw = await readBody(req)
162
- const payload = JSON.parse(raw)
163
- const newConfig = {
164
- ...cfg,
165
- enabled: typeof payload.enabled === 'boolean' ? payload.enabled : cfg.enabled !== false,
166
- default_preset: payload.default_preset || cfg.default_preset || 'default',
167
- presets: Array.isArray(payload.presets) ? payload.presets : (cfg.presets || []),
168
- ...(payload.prices !== undefined ? { prices: payload.prices } : {}),
169
- ...(payload.smart_routing_enabled !== undefined ? { smart_routing_enabled: payload.smart_routing_enabled } : {}),
170
- ...(payload.smart_routing_model !== undefined ? { smart_routing_model: payload.smart_routing_model } : {}),
171
- }
172
-
173
- // Validate the merged payload through the plugin schema before it
174
- // reaches the settings storage: malformed payloads must be
175
- // rejected with 400 instead of being persisted.
176
- let validated
177
- try {
178
- validated = Config(structuredClone(newConfig))
179
- } catch (verr) {
180
- writeJson(res, 400, { ok: false, error: 'Invalid presets payload: ' + (verr?.message || String(verr)) })
181
- return
182
- }
183
-
184
- // Persist via saveConfig (in-memory and DSH profile settings)
185
- let persistResult
186
- if (typeof saveConfig === 'function') {
187
- try { persistResult = await saveConfig(validated) }
188
- catch (perr) { return writeJson(res, 500, { ok: false, error: 'Settings persistence failed: ' + (perr?.message || String(perr)) }) }
189
- }
190
-
191
- const unwrapped = typeof plainConfig === 'function' ? plainConfig(validated) : validated
192
- writeJson(res, 200, {
193
- ok: true,
194
- persisted: persistResult?.persisted ?? false,
195
- enabled: unwrapped.enabled !== false,
196
- defaultPreset: unwrapped.default_preset,
197
- presets: unwrapped.presets,
198
- })
199
- } catch (err) { writeJson(res, 400, { ok: false, error: err?.message || String(err) }) }
200
- return
201
- }
202
-
203
- writeJson(res, 405, { ok: false, error: 'Method Not Allowed' })
204
- },
205
- })
206
- }, 'dsh-moa: presets route')
207
-
208
- // Route: /dsh-moa/history (#9)
209
- ctx.effect(() => {
210
- return ctx.webServer.register({
211
- kind: 'exact',
212
- path: '/dsh-moa/history',
213
- handler: async (req, res) => {
214
- if (!assertAuthorizedRequest(req, res, ctx)) return
215
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
216
- try {
217
- const u = new URL(req.url, 'http://localhost')
218
- const limit = Math.max(1, Math.min(100, parseInt(u.searchParams.get('limit') || '20', 10) || 20))
219
- const offset = Math.max(0, parseInt(u.searchParams.get('offset') || '0', 10) || 0)
220
- const data = getMoaHistory(limit, offset)
221
- writeJson(res, 200, { ok: true, ...data })
222
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
223
- },
224
- })
225
- }, 'dsh-moa: history route')
226
-
227
- // Route: /dsh-moa/leaderboard
228
- ctx.effect(() => {
229
- return ctx.webServer.register({
230
- kind: 'exact',
231
- path: '/dsh-moa/leaderboard',
232
- handler: async (req, res) => {
233
- if (!assertAuthorizedRequest(req, res, ctx)) return
234
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
235
- try {
236
- const url = new URL(req.url || '', 'http://127.0.0.1')
237
- const preset = url.searchParams.get('preset') || null
238
- const format = url.searchParams.get('format') || 'json'
239
-
240
- if (format === 'csv') {
241
- const csv = exportMoaHistory(undefined, { format: 'csv', preset })
242
- res.writeHead(200, {
243
- 'Content-Type': 'text/csv; charset=utf-8',
244
- 'Content-Disposition': 'attachment; filename="moa-leaderboard.csv"',
245
- })
246
- res.end(csv)
247
- return
248
- }
249
-
250
- const data = getMoaLeaderboard(undefined, { preset })
251
- writeJson(res, 200, { ok: true, ...data })
252
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
253
- },
254
- })
255
- }, 'dsh-moa: leaderboard route')
256
-
257
- // Route: /dsh-moa/promote (Candidate Override Action)
258
- ctx.effect(() => {
259
- return ctx.webServer.register({
260
- kind: 'exact',
261
- path: '/dsh-moa/promote',
262
- handler: async (req, res) => {
263
- if (!assertAuthorizedRequest(req, res, ctx)) return
264
- if (req.method !== 'POST') return writeJson(res, 405, { ok: false, error: 'POST required' })
265
- if (!isSafeWriteRequest(req)) {
266
- writeJson(res, 403, { ok: false, error: 'Forbidden: cross-site request rejected' })
267
- return
268
- }
269
- try {
270
- const raw = await readBody(req)
271
- const body = JSON.parse(raw || '{}')
272
- const candidateIndex = parseInt(body?.candidateIndex, 10)
273
- if (isNaN(candidateIndex) || candidateIndex < 1) {
274
- writeJson(res, 400, { ok: false, error: 'Valid candidateIndex required (>= 1)' })
275
- return
276
- }
277
- const runId = body?.runId
278
- let recordedRun = null
279
- if (runId) {
280
- recordedRun = getMoaRunById(runId)
281
- if (!recordedRun) {
282
- writeJson(res, 404, { ok: false, error: `Run '${runId}' not found` })
283
- return
284
- }
285
- } else {
286
- recordedRun = getMoaHistory(1, 0)?.runs?.[0] || null
287
- if (!recordedRun) {
288
- writeJson(res, 400, { ok: false, error: 'No recorded MoA run found' })
289
- return
290
- }
291
- }
292
- if (body?.cwd && path.resolve(body.cwd) !== path.resolve(recordedRun.cwd)) {
293
- writeJson(res, 403, { ok: false, error: 'Forbidden: cwd does not match recorded run cwd' })
294
- return
295
- }
296
- const effectiveCwd = recordedRun.cwd
297
- const promotedFiles = await promoteCandidateWorkspace(effectiveCwd, candidateIndex, {
298
- runId: recordedRun.id,
299
- keepMoa: true,
300
- port: ctx.webServer?.port,
301
- timeMachineEngine: timeMachineEngine || ctx.reflect?.get?.('timeMachineEngine', false),
302
- force: Boolean(body?.force),
303
- })
304
- writeJson(res, 200, { ok: true, candidateIndex, promotedFiles, checkpoint: promotedFiles?.checkpoint || null })
305
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
306
- },
307
- })
308
- }, 'dsh-moa: promote candidate route')
309
-
310
- // Route: /dsh-moa/diff (Candidate Diff Viewer)
311
- ctx.effect(() => {
312
- return ctx.webServer.register({
313
- kind: 'exact',
314
- path: '/dsh-moa/diff',
315
- handler: async (req, res) => {
316
- if (!assertAuthorizedRequest(req, res, ctx)) return
317
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
318
- try {
319
- const u = new URL(req.url, 'http://localhost')
320
- const rawRunId = u.searchParams.get('runId')
321
- const queryCwd = u.searchParams.get('cwd')
322
- let run = null
323
- if (rawRunId && rawRunId !== 'latest' && rawRunId !== 'active') {
324
- run = getMoaRunById(rawRunId)
325
- if (!run) {
326
- writeJson(res, 404, { ok: false, error: `Run '${rawRunId}' not found` })
327
- return
328
- }
329
- } else {
330
- run = getMoaHistory(1, 0)?.runs?.[0] || null
331
- if (!run) {
332
- writeJson(res, 404, { ok: false, error: 'No recorded MoA run found' })
333
- return
334
- }
335
- }
336
- if (queryCwd && path.resolve(queryCwd) !== path.resolve(run.cwd)) {
337
- writeJson(res, 403, { ok: false, error: 'Forbidden: cwd does not match recorded run cwd' })
338
- return
339
- }
340
- const runId = run.id
341
- const cwd = run.cwd
342
- const sanitizeTarget = (val, def) => {
343
- const clean = String(val || '').trim().replace(/[^a-zA-Z0-9_-]/g, '')
344
- return clean || def
345
- }
346
- const from = sanitizeTarget(u.searchParams.get('from'), '1')
347
- const to = sanitizeTarget(u.searchParams.get('to'), 'curator-synthesis')
348
- const rawFile = u.searchParams.get('file') || ''
349
- const file = (rawFile && isSafeRelativePath(rawFile)) ? rawFile : ''
350
-
351
- const fromFiles = await readCandidateFiles(cwd, from, { runId })
352
- const toFiles = await readCandidateFiles(cwd, to, { runId })
353
-
354
- // Collect all unique file paths
355
- const allPaths = Array.from(new Set([
356
- ...fromFiles.map((f) => f.relativePath),
357
- ...toFiles.map((f) => f.relativePath),
358
- ])).sort()
359
-
360
- const targetPath = file || allPaths[0] || ''
361
- const fromItem = fromFiles.find((f) => f.relativePath === targetPath)
362
- const toItem = toFiles.find((f) => f.relativePath === targetPath)
363
-
364
- const diff = targetPath
365
- ? computeLineDiff(fromItem?.content || '', toItem?.content || '')
366
- : []
367
-
368
- writeJson(res, 200, {
369
- ok: true,
370
- from,
371
- to,
372
- files: allPaths,
373
- selectedFile: targetPath,
374
- fromSize: fromItem?.size || 0,
375
- toSize: toItem?.size || 0,
376
- diff,
377
- })
378
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
379
- },
380
- })
381
- }, 'dsh-moa: candidate diff route')
382
-
383
- // Route: /dsh-moa/history/export
384
- ctx.effect(() => {
385
- return ctx.webServer.register({
386
- kind: 'exact',
387
- path: '/dsh-moa/history/export',
388
- handler: async (req, res) => {
389
- if (!assertAuthorizedRequest(req, res, ctx)) return
390
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
391
- try {
392
- const url = new URL(req.url || '', 'http://127.0.0.1')
393
- const preset = url.searchParams.get('preset') || null
394
- const format = url.searchParams.get('format') || 'json'
395
- const data = exportMoaHistory(undefined, { format, preset })
396
-
397
- const isCsv = format === 'csv'
398
- res.writeHead(200, {
399
- 'Content-Type': isCsv ? 'text/csv; charset=utf-8' : 'application/json; charset=utf-8',
400
- 'Content-Disposition': `attachment; filename="moa-history.${isCsv ? 'csv' : 'json'}"`,
401
- })
402
- res.end(data)
403
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
404
- },
405
- })
406
- }, 'dsh-moa: history export route')
407
-
408
- // Route: /dsh-moa/runs/<id> (run replay by id and interactive workbench)
409
- ctx.effect(() => {
410
- return ctx.webServer.register({
411
- kind: 'prefix',
412
- path: '/dsh-moa/runs',
413
- handler: (req, res) => {
414
- if (!assertAuthorizedRequest(req, res, ctx)) return
415
- const urlPath = req.url.split('?')[0]
416
- const parts = urlPath.split('/').filter(Boolean)
417
- const runId = parts[2] || ''
418
- if (parts[3] === 'workbench') {
419
- const targetCwd = workspaceRoot || ctx?.reflect?.get?.('baseDir', false) || ctx?.reflect?.get?.('workspaceDir', false) || process.cwd()
420
- if (req.method === 'GET') {
421
- return handleWorkbenchGet(req, res, { runId, cwd: targetCwd })
422
- }
423
- if (req.method === 'POST') {
424
- if (!isSafeWriteRequest(req)) {
425
- return writeJson(res, 403, { ok: false, error: 'Forbidden: cross-site request rejected' })
426
- }
427
- return handleWorkbenchAssemble(req, res, { runId, cwd: targetCwd, live, callLlm, webServerPort, timeMachineEngine })
428
- }
429
- return writeJson(res, 405, { ok: false, error: 'Method not allowed' })
430
- }
431
- if (req.method !== 'GET') return writeJson(res, 405, { ok: false, error: 'GET required' })
432
- const isReport = parts[3] === 'report'
433
- const run = runId ? getMoaRunById(runId) : null
434
- if (!run) {
435
- writeJson(res, 404, { ok: false, error: 'Run not found' })
436
- return
437
- }
438
- if (isReport) {
439
- const u = new URL(req.url, 'http://127.0.0.1')
440
- const format = u.searchParams.get('format') || 'md'
441
- const report = run.benchmarkReport || generateMoABenchmarkReport({
442
- runId: run.id,
443
- timestamp: run.timestamp,
444
- preset: run.preset,
445
- prompt: run.prompt,
446
- durationMs: run.durationMs,
447
- usage: { totalTokens: run.totalTokens },
448
- costUsd: run.totalCostUsd,
449
- candidates: run.candidates,
450
- winningIndex: run.winnerIndex,
451
- winningLabel: run.winnerModel,
452
- })
453
- if (format === 'json') {
454
- writeJson(res, 200, { ok: true, report: typeof report === 'object' ? (report.json || report) : report })
455
- return
456
- }
457
- const mdText = typeof report === 'string' ? report : (report.markdown || JSON.stringify(report, null, 2))
458
- res.writeHead(200, { 'Content-Type': 'text/markdown; charset=utf-8' })
459
- res.end(mdText)
460
- return
461
- }
462
- writeJson(res, 200, { ok: true, run })
463
- },
464
- })
465
- }, 'dsh-moa: run by id route')
466
-
467
- // Route: /dsh-moa/route-preset
468
- ctx.effect(() => {
469
- return ctx.webServer.register({
470
- kind: 'exact',
471
- path: '/dsh-moa/route-preset',
472
- handler: async (req, res) => {
473
- if (!assertAuthorizedRequest(req, res, ctx)) return
474
- if (req.method !== 'POST') return writeJson(res, 405, { ok: false, error: 'POST required' })
475
- if (!isSafeWriteRequest(req)) {
476
- writeJson(res, 403, { ok: false, error: 'Forbidden: cross-site request rejected' })
477
- return
478
- }
479
- try {
480
- const cfg = live()
481
- if (cfg.enabled === false) {
482
- writeJson(res, 400, { ok: false, error: 'MoA is disabled in settings' })
483
- return
484
- }
485
- const raw = await readBody(req)
486
- const body = JSON.parse(raw || '{}')
487
- const prompt = body?.prompt || ''
488
- const activePreset = (cfg.presets || []).find((p) => p.name === (cfg.default_preset || 'default')) || cfg.presets?.[0]
489
- const routingModel = (activePreset?.smart_routing_model?.model && activePreset.smart_routing_model)
490
- || (cfg.smart_routing_model?.model && cfg.smart_routing_model) || activePreset?.smart_routing_model || cfg.smart_routing_model
491
- const result = await resolvePresetForPrompt({
492
- prompt,
493
- presets: cfg.presets || [],
494
- defaultPreset: cfg.default_preset || 'default',
495
- routingModel,
496
- callLlm,
497
- enabled: cfg.enabled !== false,
498
- })
499
- writeJson(res, 200, { ok: true, ...result })
500
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
501
- },
502
- })
503
- }, 'dsh-moa: route preset endpoint')
504
-
505
- let _lastRunTimestamp = 0
506
- const RUN_MIN_INTERVAL_MS = 1000
507
-
508
- // Route: /dsh-moa/run
509
- ctx.effect(() => {
510
- return ctx.webServer.register({
511
- kind: 'exact',
512
- path: '/dsh-moa/run',
513
- handler: async (req, res) => {
514
- if (!assertAuthorizedRequest(req, res, ctx)) return
515
- if (req.method !== 'POST') return writeJson(res, 405, { ok: false, error: 'POST required' })
516
- if (!isSafeWriteRequest(req)) {
517
- writeJson(res, 403, { ok: false, error: 'Forbidden: cross-site request rejected' })
518
- return
519
- }
520
- const now = Date.now()
521
- if (now - _lastRunTimestamp < RUN_MIN_INTERVAL_MS) {
522
- writeJson(res, 429, { ok: false, error: 'Rate limit exceeded: please wait before starting another run' })
523
- return
524
- }
525
-
526
- try {
527
- const raw = await readBody(req)
528
- const payload = JSON.parse(raw)
529
- const prompt = payload.prompt || ''
530
- if (!prompt.trim()) {
531
- writeJson(res, 400, { ok: false, error: 'Empty prompt' })
532
- return
533
- }
534
- _lastRunTimestamp = now
535
-
536
- const cfg = live()
537
- if (cfg.enabled === false) {
538
- writeJson(res, 400, { ok: false, error: 'MoA is disabled in settings' })
539
- return
540
- }
541
- if (payload.timeMachineUrl && !isLoopbackUrl(payload.timeMachineUrl)) {
542
- writeJson(res, 400, { ok: false, error: 'Invalid timeMachineUrl: loopback host required' })
543
- return
544
- }
545
- const presetName = payload.preset || cfg.default_preset || 'default'
546
- const preset = (cfg.presets || []).find((p) => p.name === presetName) || cfg.presets?.[0]
547
-
548
- const baseWorkspaceRoot = workspaceRoot || ctx?.reflect?.get?.('baseDir', false) || ctx?.reflect?.get?.('workspaceDir', false) || process.cwd()
549
- const allowedRoots = [
550
- baseWorkspaceRoot,
551
- process.cwd(),
552
- cfg.workspace_root,
553
- ...(Array.isArray(cfg.allowed_workspaces) ? cfg.allowed_workspaces : (cfg.allowed_workspaces ? [cfg.allowed_workspaces] : [])),
554
- ...(isTestEnvironment() ? [os.tmpdir()] : []),
555
- ].filter(Boolean)
556
-
557
- const targetCwd = payload.cwd ? path.resolve(payload.cwd) : baseWorkspaceRoot
558
- if (payload.cwd && !isAllowedWorkspace(targetCwd, allowedRoots)) {
559
- writeJson(res, 403, { ok: false, error: 'Forbidden: cwd escapes allowed workspace boundary' })
560
- return
561
- }
562
-
563
- const result = await runMoAPipeline({
564
- userPrompt: prompt, messages: payload.messages || [], preset, callLlm,
565
- cwd: targetCwd, prices: cfg.prices || {}, liveCanvas,
566
- webServerPort: webServerPort || ctx.webServer?.port,
567
- timeMachineEngine: timeMachineEngine || ctx.reflect?.get?.('timeMachineEngine', false),
568
- timeMachineUrl: payload.timeMachineUrl, force: false,
569
- checkpoint: true,
570
- runId: payload.runId,
571
- })
572
-
573
- if (result?.skippedFiles > 0) {
574
- ctx.logger?.warn?.(`[dsh-moa] Workspace scan skipped ${result.skippedFiles} file(s): ${result.skippedList.join(', ')}`)
575
- }
576
- if (result?.error) {
577
- writeJson(res, 500, { ok: false, error: result.error?.message || String(result.error) })
578
- return
579
- }
580
-
581
- writeJson(res, 200, { ok: true, ...result })
582
- } catch (err) { writeJson(res, 500, { ok: false, error: err?.message || String(err) }) }
583
- },
584
- })
585
- }, 'dsh-moa: run pipeline route')
6
+ import { registerPresetRoutes } from './routes/presets.js'
7
+ import { registerHistoryRoutes } from './routes/history.js'
8
+ import { registerWorkbenchRoutes } from './routes/workbench.js'
9
+ import { registerPipelineRoutes } from './routes/pipeline.js'
10
+
11
+ export { registerPresetRoutes } from './routes/presets.js'
12
+ export { registerHistoryRoutes } from './routes/history.js'
13
+ export { registerWorkbenchRoutes } from './routes/workbench.js'
14
+ export { registerPipelineRoutes } from './routes/pipeline.js'
15
+
16
+ export function registerMoaRoutes(ctx, deps) {
17
+ registerPresetRoutes(ctx, deps)
18
+ registerHistoryRoutes(ctx, deps)
19
+ registerWorkbenchRoutes(ctx, deps)
20
+ registerPipelineRoutes(ctx, deps)
586
21
  }
@@ -0,0 +1,51 @@
1
+ import { logger } from '../logger.js'
2
+ import { bestEffort } from '../best-effort.js'
3
+ import { isLoopbackUrl } from './security.js'
4
+
5
+ export async function createPrePromotionCheckpoint(baseDir, candidateTarget, options = {}) {
6
+ if (!baseDir || options.checkpoint === false) return null
7
+ if (typeof options.checkpointFn === 'function') {
8
+ return (await bestEffort('custom checkpointFn', () => options.checkpointFn(baseDir, candidateTarget))) || null
9
+ }
10
+
11
+ const label = options.checkpointLabel || `moa-pre-promotion: candidate-${candidateTarget}`
12
+ if (options.timeMachineEngine && typeof options.timeMachineEngine.createSnapshot === 'function') {
13
+ const snap = await bestEffort('dsh-time-machine direct service createSnapshot', async () => {
14
+ return await options.timeMachineEngine.createSnapshot(label, { cwd: baseDir })
15
+ })
16
+ if (snap) return snap
17
+ }
18
+
19
+ const port = options.port || options.webServerPort || process.env.DSH_PORT || process.env.PORT
20
+ if (!port && !options.timeMachineUrl) {
21
+ logger.warn('[dsh-moa] Cannot create pre-promotion checkpoint: webServerPort is unknown and no timeMachineUrl provided')
22
+ return null
23
+ }
24
+
25
+ if (options.timeMachineUrl && !isLoopbackUrl(options.timeMachineUrl)) {
26
+ logger.warn(`[dsh-moa] Blocked non-loopback timeMachineUrl: ${options.timeMachineUrl}`)
27
+ return null
28
+ }
29
+
30
+ const endpoint = options.timeMachineUrl || `http://127.0.0.1:${port}/dsh-time-machine/create`
31
+ const snap = await bestEffort('dsh-time-machine pre-promotion checkpoint', async () => {
32
+ const res = await fetch(endpoint, {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify({ label, cwd: baseDir }),
36
+ signal: AbortSignal.timeout(options.timeoutMs ?? 1500),
37
+ redirect: 'error',
38
+ })
39
+ if (res.ok) {
40
+ const data = await res.json()
41
+ return data?.snapshot || data || null
42
+ }
43
+ return null
44
+ })
45
+ return snap || null
46
+ }
47
+
48
+ /**
49
+ * Promotes winning candidate workspace files into the base workspace.
50
+ * Serialized per baseDir and verifies symlink boundaries on promotion.
51
+ */