@dypai-ai/mcp 1.7.8 → 1.7.9
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/package.json +1 -1
- package/src/tools/sync/push.js +96 -2
package/package.json
CHANGED
package/src/tools/sync/push.js
CHANGED
|
@@ -223,6 +223,69 @@ async function refreshBackendSourceBaseline(projectId, rootDir, sourceCheckpoint
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
async function readComputePolicy(rootDir) {
|
|
227
|
+
try {
|
|
228
|
+
const raw = await readFile(join(rootDir, "compute.json"), "utf8")
|
|
229
|
+
const parsed = JSON.parse(raw)
|
|
230
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
231
|
+
throw new Error("dypai/compute.json must contain an object")
|
|
232
|
+
}
|
|
233
|
+
return parsed
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (error?.code === "ENOENT") return null
|
|
236
|
+
if (error instanceof SyntaxError) {
|
|
237
|
+
throw new Error("dypai/compute.json is not valid JSON")
|
|
238
|
+
}
|
|
239
|
+
throw error
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function prepareEndpointPayloadsForCompute(projectId, rootDir, names, local, mapsCtx) {
|
|
244
|
+
if (!names.length) {
|
|
245
|
+
return { ok: true, payloadsByName: {}, compute: { artifacts: 0, uploaded: 0, registered: 0 } }
|
|
246
|
+
}
|
|
247
|
+
const payloads = names.map((name) => {
|
|
248
|
+
const entry = local.byName[name]
|
|
249
|
+
if (!entry) throw new Error(`Compute preparation is missing local endpoint '${name}'`)
|
|
250
|
+
return localToCanonical(entry, mapsCtx)
|
|
251
|
+
})
|
|
252
|
+
const computePolicy = await readComputePolicy(rootDir)
|
|
253
|
+
const result = await api.post(
|
|
254
|
+
`/api/engine/${projectId}/backend/snapshot/prepare-push-payloads`,
|
|
255
|
+
{
|
|
256
|
+
payloads,
|
|
257
|
+
...(computePolicy ? { compute_policy: computePolicy } : {}),
|
|
258
|
+
},
|
|
259
|
+
)
|
|
260
|
+
if (!result || result.ok !== true || !Array.isArray(result.payloads)) {
|
|
261
|
+
throw new Error("Core API returned an invalid Compute preparation response")
|
|
262
|
+
}
|
|
263
|
+
const payloadsByName = {}
|
|
264
|
+
for (const payload of result.payloads) {
|
|
265
|
+
const name = typeof payload?.name === "string" ? payload.name : ""
|
|
266
|
+
if (!name || payloadsByName[name]) {
|
|
267
|
+
throw new Error("Core API returned duplicate or unnamed Compute payloads")
|
|
268
|
+
}
|
|
269
|
+
payloadsByName[name] = payload
|
|
270
|
+
}
|
|
271
|
+
const missing = names.filter((name) => !payloadsByName[name])
|
|
272
|
+
if (missing.length) {
|
|
273
|
+
throw new Error(`Core API omitted Compute payloads: ${missing.slice(0, 5).join(", ")}`)
|
|
274
|
+
}
|
|
275
|
+
return { ok: true, payloadsByName, compute: result.compute || {} }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function canonicalPreparedEndpoint(name, local, preparedByName, mapsCtx) {
|
|
279
|
+
const prepared = preparedByName[name]
|
|
280
|
+
if (!prepared) return localToCanonical(local.byName[name], mapsCtx)
|
|
281
|
+
const entry = local.byName[name]
|
|
282
|
+
return localToCanonical({
|
|
283
|
+
source: "flow",
|
|
284
|
+
group: entry?.group || prepared.group_name || null,
|
|
285
|
+
pushPayload: prepared,
|
|
286
|
+
}, mapsCtx)
|
|
287
|
+
}
|
|
288
|
+
|
|
226
289
|
/**
|
|
227
290
|
* Run an async worker over an array with bounded concurrency. Workers never
|
|
228
291
|
* throw (they handle their own errors via the push errors[] accumulator),
|
|
@@ -947,6 +1010,33 @@ export const dypaiPushTool = {
|
|
|
947
1010
|
// pushing 50 endpoints doesn't take 50 * 200ms = 10s — drops to ~2s.
|
|
948
1011
|
// Group operations are sequential because they're fast (a handful of
|
|
949
1012
|
// groups typically) and we need mapsCtx mutations to be race-free.
|
|
1013
|
+
const endpointNamesToWrite = [...new Set([
|
|
1014
|
+
...plan.create.map((item) => item.name),
|
|
1015
|
+
...plan.update.map((item) => item.name),
|
|
1016
|
+
])]
|
|
1017
|
+
let computePreparation
|
|
1018
|
+
try {
|
|
1019
|
+
computePreparation = await prepareEndpointPayloadsForCompute(
|
|
1020
|
+
targetProjectId,
|
|
1021
|
+
rootDir,
|
|
1022
|
+
endpointNamesToWrite,
|
|
1023
|
+
local,
|
|
1024
|
+
remote.mapsCtx,
|
|
1025
|
+
)
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
return {
|
|
1028
|
+
success: false,
|
|
1029
|
+
applied: false,
|
|
1030
|
+
phase: "prepare_compute",
|
|
1031
|
+
error: error?.body?.detail?.message
|
|
1032
|
+
|| error?.detail?.message
|
|
1033
|
+
|| error?.detail
|
|
1034
|
+
|| error?.message
|
|
1035
|
+
|| String(error),
|
|
1036
|
+
hint: "No backend drafts were written. Update/retry after the Compute preparation path is healthy.",
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
const preparedByName = computePreparation.payloadsByName
|
|
950
1040
|
const CONCURRENCY = PUSH_CONCURRENCY
|
|
951
1041
|
// `applied.*_drafts` tracks the per-op subset that the API staged as
|
|
952
1042
|
// drafts (default behavior). The lists in `created/updated/deleted`
|
|
@@ -974,7 +1064,7 @@ export const dypaiPushTool = {
|
|
|
974
1064
|
|
|
975
1065
|
await runWithConcurrency(plan.create, CONCURRENCY, async (item) => {
|
|
976
1066
|
try {
|
|
977
|
-
const canonical =
|
|
1067
|
+
const canonical = canonicalPreparedEndpoint(item.name, local, preparedByName, remote.mapsCtx)
|
|
978
1068
|
const res = await applyCreate(canonical, targetProjectId)
|
|
979
1069
|
applied.created.push(item.name)
|
|
980
1070
|
if (isDraftResponse(res)) applied.created_drafts.push(item.name)
|
|
@@ -989,7 +1079,7 @@ export const dypaiPushTool = {
|
|
|
989
1079
|
|
|
990
1080
|
await runWithConcurrency(plan.update, CONCURRENCY, async (item) => {
|
|
991
1081
|
try {
|
|
992
|
-
const canonical =
|
|
1082
|
+
const canonical = canonicalPreparedEndpoint(item.name, local, preparedByName, remote.mapsCtx)
|
|
993
1083
|
const endpointId = remote.byName[item.name]?.id
|
|
994
1084
|
const res = endpointId
|
|
995
1085
|
? await applyUpdate(canonical, endpointId, targetProjectId, remote.byName[item.name]?.revision)
|
|
@@ -1139,6 +1229,7 @@ export const dypaiPushTool = {
|
|
|
1139
1229
|
non_blocking_conflicts: conflictSplit.non_blocking.length ? conflictSplit.non_blocking : undefined,
|
|
1140
1230
|
errors: errors.length ? errors : undefined,
|
|
1141
1231
|
types: typesGenerated,
|
|
1232
|
+
compute: computePreparation.compute,
|
|
1142
1233
|
automation_sync: automationSync,
|
|
1143
1234
|
source_checkpoint: sourceCheckpoint,
|
|
1144
1235
|
source_baseline: sourceBaseline,
|
|
@@ -1199,5 +1290,8 @@ export const __testing = {
|
|
|
1199
1290
|
normalizeEndpointSelection,
|
|
1200
1291
|
scopePlanToEndpoints,
|
|
1201
1292
|
backendSourcePathsForEndpointSelection,
|
|
1293
|
+
readComputePolicy,
|
|
1294
|
+
prepareEndpointPayloadsForCompute,
|
|
1295
|
+
canonicalPreparedEndpoint,
|
|
1202
1296
|
PUSH_CONCURRENCY,
|
|
1203
1297
|
}
|