@wowok/agent-mcp 2.5.3 → 2.5.5
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/README.md +33 -26
- package/dist/customer/index.d.ts +2 -2
- package/dist/customer/index.js +1 -1
- package/dist/customer/info-puzzle.d.ts +39 -2
- package/dist/customer/info-puzzle.js +370 -29
- package/dist/customer/risk-assessment.js +51 -14
- package/dist/customer/types.d.ts +85 -0
- package/dist/knowledge/machine-ledger.js +32 -29
- package/dist/knowledge/machine-risk.js +23 -0
- package/dist/knowledge/machine-templates.js +118 -86
- package/dist/knowledge/reward-confirm.d.ts +3 -0
- package/dist/knowledge/reward-confirm.js +30 -0
- package/dist/knowledge/reward-puzzle.d.ts +1 -0
- package/dist/knowledge/reward-puzzle.js +12 -0
- package/dist/knowledge/reward-risk.d.ts +4 -1
- package/dist/knowledge/reward-risk.js +85 -3
- package/dist/knowledge/reward-templates.d.ts +1 -1
- package/dist/knowledge/reward-templates.js +77 -5
- package/dist/knowledge/tools-reference.js +5 -4
- package/dist/knowledge/trust-metrics.d.ts +4 -0
- package/dist/knowledge/trust-metrics.js +67 -7
- package/dist/project/evaluation.d.ts +53 -0
- package/dist/project/evaluation.js +853 -0
- package/dist/project/graph-builder.d.ts +43 -0
- package/dist/project/graph-builder.js +572 -0
- package/dist/project/handlers.d.ts +152 -0
- package/dist/project/handlers.js +458 -0
- package/dist/project/index.d.ts +1 -0
- package/dist/project/index.js +24 -1
- package/dist/project/query.d.ts +1 -0
- package/dist/project/query.js +2 -0
- package/dist/project/semantic-assembler.d.ts +93 -0
- package/dist/project/semantic-assembler.js +345 -0
- package/dist/schema/call/arbitration.d.ts +54 -54
- package/dist/schema/call/base.js +1 -1
- package/dist/schema/call/demand.d.ts +48 -48
- package/dist/schema/operations.d.ts +76 -76
- package/dist/schema/project/index.d.ts +1558 -3885
- package/dist/schema/project/index.js +357 -961
- package/dist/schema/trust/index.d.ts +24 -24
- package/dist/schemas/bridge_operation.schema.json +1 -1
- package/dist/schemas/guard2file.schema.json +1 -1
- package/dist/schemas/index.json +2 -2
- package/dist/schemas/machineNode2file.schema.json +1 -1
- package/dist/schemas/onchain_operations.schema.json +5 -5
- package/dist/schemas/onchain_operations_allocation.schema.json +1 -1
- package/dist/schemas/onchain_operations_arbitration.schema.json +1 -1
- package/dist/schemas/onchain_operations_contact.schema.json +1 -1
- package/dist/schemas/onchain_operations_demand.schema.json +1 -1
- package/dist/schemas/onchain_operations_gen_passport.schema.json +2 -2
- package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
- package/dist/schemas/onchain_operations_guard.schema.json +1 -1
- package/dist/schemas/onchain_operations_machine.schema.json +1 -1
- package/dist/schemas/onchain_operations_order.schema.json +1 -1
- package/dist/schemas/onchain_operations_payment.schema.json +1 -1
- package/dist/schemas/onchain_operations_permission.schema.json +1 -1
- package/dist/schemas/onchain_operations_personal.schema.json +1 -1
- package/dist/schemas/onchain_operations_progress.schema.json +1 -1
- package/dist/schemas/onchain_operations_proof.schema.json +1 -1
- package/dist/schemas/onchain_operations_repository.schema.json +1 -1
- package/dist/schemas/onchain_operations_reward.schema.json +1 -1
- package/dist/schemas/onchain_operations_service.schema.json +1 -1
- package/dist/schemas/onchain_operations_treasury.schema.json +1 -1
- package/dist/schemas/project_operation.output.json +975 -2471
- package/dist/schemas/project_operation.schema.json +207 -534
- package/dist/tools/handlers/onchain.js +97 -19
- package/dist/tools/handlers/project.js +34 -1046
- package/dist/tools/index.js +84 -55
- package/package.json +2 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type GraphData } from "@wowok/wowok";
|
|
2
|
+
export interface GraphBuildOptions {
|
|
3
|
+
maxDepth?: number;
|
|
4
|
+
maxConcurrency?: number;
|
|
5
|
+
rateLimitQPS?: number;
|
|
6
|
+
cacheTTL?: number;
|
|
7
|
+
enableRegistrar?: boolean;
|
|
8
|
+
enablePotential?: boolean;
|
|
9
|
+
projectId?: string | null;
|
|
10
|
+
network?: string;
|
|
11
|
+
batchSize?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ReputationData {
|
|
14
|
+
likes: number;
|
|
15
|
+
dislikes: number;
|
|
16
|
+
affiliations: number;
|
|
17
|
+
voters: Array<{
|
|
18
|
+
who: string;
|
|
19
|
+
like: boolean;
|
|
20
|
+
dislike: boolean;
|
|
21
|
+
affiliation: boolean;
|
|
22
|
+
time: number;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface GraphBuildResult {
|
|
26
|
+
graph: GraphData;
|
|
27
|
+
reputation: Map<string, ReputationData>;
|
|
28
|
+
}
|
|
29
|
+
export declare class GraphBuilder {
|
|
30
|
+
private localProject;
|
|
31
|
+
private rateLimiter;
|
|
32
|
+
private options;
|
|
33
|
+
constructor(options?: GraphBuildOptions);
|
|
34
|
+
buildRelationshipGraph(rootAddress: string): Promise<GraphBuildResult>;
|
|
35
|
+
private buildInternalRefsBatched;
|
|
36
|
+
private fetchObjectsBatch;
|
|
37
|
+
private mapRefTypeToRelationship;
|
|
38
|
+
private buildRegistrarRefs;
|
|
39
|
+
private buildPotentialRefs;
|
|
40
|
+
private resolveConflicts;
|
|
41
|
+
private buildNodes;
|
|
42
|
+
}
|
|
43
|
+
export declare function buildGraph(rootAddress: string, options?: GraphBuildOptions): Promise<GraphBuildResult>;
|
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import { query_objects, queryTableItem_EntityLinker, LocalProject, } from "@wowok/wowok";
|
|
2
|
+
const MAX_OBJECTS_PER_QUERY = 50;
|
|
3
|
+
function extractAddressFields(obj) {
|
|
4
|
+
const refs = [];
|
|
5
|
+
const objType = obj.type;
|
|
6
|
+
const isAddress = (v) => typeof v === "string" && v.startsWith("0x") && v.length >= 10;
|
|
7
|
+
const addRef = (addr, field, refType) => {
|
|
8
|
+
if (isAddress(addr) && addr !== "0x0" && addr !== obj.object) {
|
|
9
|
+
refs.push({ address: addr, field, type: refType });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const addRefArray = (arr, field, refType) => {
|
|
13
|
+
if (Array.isArray(arr)) {
|
|
14
|
+
for (const item of arr) {
|
|
15
|
+
addRef(item, field, refType);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const addRefOption = (val, field, refType) => {
|
|
20
|
+
if (val !== null && val !== undefined) {
|
|
21
|
+
addRef(val, field, refType);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const data = obj;
|
|
25
|
+
switch (objType) {
|
|
26
|
+
case "Service": {
|
|
27
|
+
addRefOption(data.machine, "machine", "bind");
|
|
28
|
+
addRefOption(data.buy_guard, "buy_guard", "validate");
|
|
29
|
+
addRefOption(data.um, "um", "bind");
|
|
30
|
+
addRef(data.permission, "permission", "validate");
|
|
31
|
+
addRefArray(data.repositories, "repositories", "consume");
|
|
32
|
+
addRefArray(data.arbitrations, "arbitrations", "trigger");
|
|
33
|
+
addRefArray(data.rewards, "rewards", "trigger");
|
|
34
|
+
if (data.order_allocators?.allocators) {
|
|
35
|
+
for (const alloc of data.order_allocators.allocators) {
|
|
36
|
+
addRef(alloc.guard, "allocator_guard", "validate");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
addRefOption(data.compensation_fund, "compensation_fund", "bind");
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "Machine": {
|
|
43
|
+
addRef(data.permission, "permission", "validate");
|
|
44
|
+
addRefArray(data.repositories, "repositories", "consume");
|
|
45
|
+
if (data.nodes) {
|
|
46
|
+
for (const node of data.nodes) {
|
|
47
|
+
if (node.forward?.guard) {
|
|
48
|
+
addRef(node.forward.guard, "node_guard", "validate");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "Order": {
|
|
55
|
+
addRef(data.service, "service", "consume");
|
|
56
|
+
addRefOption(data.machine, "machine", "consume");
|
|
57
|
+
addRefOption(data.progress, "progress", "trigger");
|
|
58
|
+
addRefOption(data.arbitration, "arbitration", "trigger");
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "Progress": {
|
|
62
|
+
addRef(data.machine, "machine", "bind");
|
|
63
|
+
addRefOption(data.parent, "parent", "bind");
|
|
64
|
+
addRefOption(data.order, "order", "bind");
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case "Treasury": {
|
|
68
|
+
addRef(data.permission, "permission", "validate");
|
|
69
|
+
addRefOption(data.guard, "guard", "validate");
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "Allocation":
|
|
73
|
+
case "Arbitration":
|
|
74
|
+
case "Reward":
|
|
75
|
+
case "Repository":
|
|
76
|
+
case "Demand": {
|
|
77
|
+
addRef(data.permission, "permission", "validate");
|
|
78
|
+
addRefOption(data.guard, "guard", "validate");
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "Contact": {
|
|
82
|
+
addRef(data.permission, "permission", "validate");
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case "Permission": {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case "Payment": {
|
|
89
|
+
addRefOption(data.for_object, "for_object", "bind");
|
|
90
|
+
addRefOption(data.for_guard, "for_guard", "validate");
|
|
91
|
+
addRefArray(data.recipients, "recipients", "consume");
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "Guard": {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
return refs;
|
|
101
|
+
}
|
|
102
|
+
class RateLimiter {
|
|
103
|
+
maxConcurrency;
|
|
104
|
+
qps;
|
|
105
|
+
queue = [];
|
|
106
|
+
activeCount = 0;
|
|
107
|
+
lastRequestTime = 0;
|
|
108
|
+
constructor(maxConcurrency, qps) {
|
|
109
|
+
this.maxConcurrency = maxConcurrency;
|
|
110
|
+
this.qps = qps;
|
|
111
|
+
}
|
|
112
|
+
async acquire() {
|
|
113
|
+
if (this.activeCount < this.maxConcurrency) {
|
|
114
|
+
this.activeCount++;
|
|
115
|
+
await this.enforceRate();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
this.queue.push(() => {
|
|
120
|
+
this.activeCount++;
|
|
121
|
+
this.enforceRate().then(resolve);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
release() {
|
|
126
|
+
this.activeCount--;
|
|
127
|
+
if (this.queue.length > 0) {
|
|
128
|
+
const next = this.queue.shift();
|
|
129
|
+
next();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async enforceRate() {
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const minInterval = 1000 / this.qps;
|
|
135
|
+
const elapsed = now - this.lastRequestTime;
|
|
136
|
+
if (elapsed < minInterval) {
|
|
137
|
+
await sleep(minInterval - elapsed);
|
|
138
|
+
}
|
|
139
|
+
this.lastRequestTime = Date.now();
|
|
140
|
+
}
|
|
141
|
+
async run(fn) {
|
|
142
|
+
await this.acquire();
|
|
143
|
+
try {
|
|
144
|
+
return await fn();
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
this.release();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function sleep(ms) {
|
|
152
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
153
|
+
}
|
|
154
|
+
const EXPECTED_RELATIONSHIPS = new Map([
|
|
155
|
+
["Service", [
|
|
156
|
+
{ targetType: "Machine", fieldName: "machine", relationshipType: "bind", single: true },
|
|
157
|
+
{ targetType: "Guard", fieldName: "buy_guard", relationshipType: "validate", single: true },
|
|
158
|
+
{ targetType: "Permission", fieldName: "permission", relationshipType: "validate", single: true },
|
|
159
|
+
{ targetType: "Allocation", fieldName: "order_allocators", relationshipType: "create" },
|
|
160
|
+
{ targetType: "Treasury", fieldName: "compensation_fund", relationshipType: "bind", single: true },
|
|
161
|
+
{ targetType: "Arbitration", fieldName: "arbitrations", relationshipType: "trigger" },
|
|
162
|
+
{ targetType: "Reward", fieldName: "rewards", relationshipType: "trigger" },
|
|
163
|
+
{ targetType: "Repository", fieldName: "repositories", relationshipType: "consume" },
|
|
164
|
+
]],
|
|
165
|
+
["Machine", [
|
|
166
|
+
{ targetType: "Guard", fieldName: "nodes.guard", relationshipType: "validate" },
|
|
167
|
+
{ targetType: "Permission", fieldName: "permission", relationshipType: "validate", single: true },
|
|
168
|
+
{ targetType: "Repository", fieldName: "repositories", relationshipType: "consume" },
|
|
169
|
+
]],
|
|
170
|
+
["Order", [
|
|
171
|
+
{ targetType: "Service", fieldName: "service", relationshipType: "consume", single: true },
|
|
172
|
+
{ targetType: "Progress", fieldName: "progress", relationshipType: "trigger", single: true },
|
|
173
|
+
]],
|
|
174
|
+
["Progress", [
|
|
175
|
+
{ targetType: "Machine", fieldName: "parent", relationshipType: "bind", single: true },
|
|
176
|
+
{ targetType: "Machine", fieldName: "machine", relationshipType: "bind", single: true },
|
|
177
|
+
]],
|
|
178
|
+
["Allocation", [
|
|
179
|
+
{ targetType: "Treasury", fieldName: "treasury", relationshipType: "consume", single: true },
|
|
180
|
+
{ targetType: "Guard", fieldName: "guard", relationshipType: "validate", single: true },
|
|
181
|
+
]],
|
|
182
|
+
["Reward", [
|
|
183
|
+
{ targetType: "Guard", fieldName: "guard", relationshipType: "validate", single: true },
|
|
184
|
+
{ targetType: "Permission", fieldName: "permission", relationshipType: "validate", single: true },
|
|
185
|
+
]],
|
|
186
|
+
["Repository", [
|
|
187
|
+
{ targetType: "Guard", fieldName: "guard", relationshipType: "validate", single: true },
|
|
188
|
+
{ targetType: "Permission", fieldName: "permission", relationshipType: "validate", single: true },
|
|
189
|
+
]],
|
|
190
|
+
["Demand", [
|
|
191
|
+
{ targetType: "Service", fieldName: "service", relationshipType: "consume", single: true },
|
|
192
|
+
{ targetType: "Guard", fieldName: "guards", relationshipType: "validate" },
|
|
193
|
+
{ targetType: "Reward", fieldName: "rewards", relationshipType: "trigger" },
|
|
194
|
+
]],
|
|
195
|
+
["Treasury", [
|
|
196
|
+
{ targetType: "Permission", fieldName: "permission", relationshipType: "validate", single: true },
|
|
197
|
+
{ targetType: "Guard", fieldName: "guard", relationshipType: "validate", single: true },
|
|
198
|
+
]],
|
|
199
|
+
["Arbitration", [
|
|
200
|
+
{ targetType: "Permission", fieldName: "permission", relationshipType: "validate", single: true },
|
|
201
|
+
{ targetType: "Guard", fieldName: "guard", relationshipType: "validate", single: true },
|
|
202
|
+
]],
|
|
203
|
+
["Payment", [
|
|
204
|
+
{ targetType: "Guard", fieldName: "for_guard", relationshipType: "validate", single: true },
|
|
205
|
+
]],
|
|
206
|
+
]);
|
|
207
|
+
export class GraphBuilder {
|
|
208
|
+
localProject;
|
|
209
|
+
rateLimiter;
|
|
210
|
+
options;
|
|
211
|
+
constructor(options) {
|
|
212
|
+
const batchSize = Math.min(Math.max(options?.batchSize ?? MAX_OBJECTS_PER_QUERY, 1), MAX_OBJECTS_PER_QUERY);
|
|
213
|
+
this.options = {
|
|
214
|
+
maxDepth: options?.maxDepth ?? 3,
|
|
215
|
+
maxConcurrency: options?.maxConcurrency ?? 5,
|
|
216
|
+
rateLimitQPS: options?.rateLimitQPS ?? 10,
|
|
217
|
+
cacheTTL: options?.cacheTTL ?? 3600000,
|
|
218
|
+
enableRegistrar: options?.enableRegistrar ?? true,
|
|
219
|
+
enablePotential: options?.enablePotential ?? false,
|
|
220
|
+
projectId: options?.projectId ?? null,
|
|
221
|
+
network: options?.network ?? "testnet",
|
|
222
|
+
batchSize,
|
|
223
|
+
};
|
|
224
|
+
this.rateLimiter = new RateLimiter(this.options.maxConcurrency, this.options.rateLimitQPS);
|
|
225
|
+
this.localProject = LocalProject.Instance();
|
|
226
|
+
}
|
|
227
|
+
async buildRelationshipGraph(rootAddress) {
|
|
228
|
+
const allEdges = [];
|
|
229
|
+
const reputationMap = new Map();
|
|
230
|
+
const visited = new Set();
|
|
231
|
+
const objectCache = new Map();
|
|
232
|
+
const pendingAddresses = new Set([rootAddress]);
|
|
233
|
+
await this.buildInternalRefsBatched(pendingAddresses, visited, allEdges, objectCache);
|
|
234
|
+
if (this.options.enableRegistrar) {
|
|
235
|
+
const allAddresses = new Set([rootAddress]);
|
|
236
|
+
for (const edge of allEdges) {
|
|
237
|
+
allAddresses.add(edge.sourceObject);
|
|
238
|
+
allAddresses.add(edge.targetObject);
|
|
239
|
+
}
|
|
240
|
+
const registrarPromises = Array.from(allAddresses).map((addr) => this.buildRegistrarRefs(addr).then((result) => {
|
|
241
|
+
allEdges.push(...result.edges);
|
|
242
|
+
if (result.reputation) {
|
|
243
|
+
reputationMap.set(addr, result.reputation);
|
|
244
|
+
}
|
|
245
|
+
}));
|
|
246
|
+
await Promise.all(registrarPromises);
|
|
247
|
+
}
|
|
248
|
+
if (this.options.enablePotential && this.options.projectId) {
|
|
249
|
+
const potentialEdges = await this.buildPotentialRefs(this.options.projectId, allEdges);
|
|
250
|
+
allEdges.push(...potentialEdges);
|
|
251
|
+
}
|
|
252
|
+
const resolvedEdges = this.resolveConflicts(allEdges);
|
|
253
|
+
const nodes = this.buildNodes(resolvedEdges, objectCache, rootAddress);
|
|
254
|
+
const savedEdges = await this.localProject.saveGraphEdges(this.options.projectId, resolvedEdges);
|
|
255
|
+
return {
|
|
256
|
+
graph: {
|
|
257
|
+
nodes,
|
|
258
|
+
edges: savedEdges,
|
|
259
|
+
builtAt: Date.now(),
|
|
260
|
+
},
|
|
261
|
+
reputation: reputationMap,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async buildInternalRefsBatched(pendingAddresses, visited, edges, objectCache) {
|
|
265
|
+
const batchSize = this.options.batchSize;
|
|
266
|
+
for (let depth = 0; depth < this.options.maxDepth; depth++) {
|
|
267
|
+
const currentLevel = [];
|
|
268
|
+
for (const addr of pendingAddresses) {
|
|
269
|
+
if (!visited.has(addr)) {
|
|
270
|
+
visited.add(addr);
|
|
271
|
+
currentLevel.push(addr);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
pendingAddresses.clear();
|
|
275
|
+
if (currentLevel.length === 0)
|
|
276
|
+
break;
|
|
277
|
+
const nextLevelPending = new Set();
|
|
278
|
+
for (let i = 0; i < currentLevel.length; i += batchSize) {
|
|
279
|
+
const batch = currentLevel.slice(i, i + batchSize);
|
|
280
|
+
const objects = await this.fetchObjectsBatch(batch, objectCache);
|
|
281
|
+
for (const addr of batch) {
|
|
282
|
+
const obj = objects.get(addr);
|
|
283
|
+
if (!obj)
|
|
284
|
+
continue;
|
|
285
|
+
const refs = extractAddressFields(obj);
|
|
286
|
+
for (const ref of refs) {
|
|
287
|
+
edges.push({
|
|
288
|
+
sourceObject: addr,
|
|
289
|
+
targetObject: ref.address,
|
|
290
|
+
edgeSource: "internal",
|
|
291
|
+
relationshipType: this.mapRefTypeToRelationship(ref.type),
|
|
292
|
+
fieldName: ref.field,
|
|
293
|
+
depth,
|
|
294
|
+
verified: true,
|
|
295
|
+
weight: 1.0,
|
|
296
|
+
direction: "outbound",
|
|
297
|
+
metadata: { ref_type: ref.type },
|
|
298
|
+
});
|
|
299
|
+
if (!visited.has(ref.address)) {
|
|
300
|
+
nextLevelPending.add(ref.address);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
for (const addr of nextLevelPending) {
|
|
306
|
+
pendingAddresses.add(addr);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async fetchObjectsBatch(addresses, objectCache) {
|
|
311
|
+
const result = new Map();
|
|
312
|
+
const toFetch = [];
|
|
313
|
+
for (const addr of addresses) {
|
|
314
|
+
const cached = objectCache.get(addr);
|
|
315
|
+
if (cached) {
|
|
316
|
+
result.set(addr, cached);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
toFetch.push(addr);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (toFetch.length === 0)
|
|
323
|
+
return result;
|
|
324
|
+
const response = await this.rateLimiter.run(async () => {
|
|
325
|
+
return await query_objects({
|
|
326
|
+
objects: toFetch,
|
|
327
|
+
network: this.options.network,
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
for (const obj of response.objects ?? []) {
|
|
331
|
+
if (!obj || !obj.object)
|
|
332
|
+
continue;
|
|
333
|
+
objectCache.set(obj.object, obj);
|
|
334
|
+
result.set(obj.object, obj);
|
|
335
|
+
void this.localProject.cacheObject(obj.object, obj.type ?? null, JSON.stringify(obj), this.options.network).catch(() => {
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
mapRefTypeToRelationship(refType) {
|
|
341
|
+
switch (refType) {
|
|
342
|
+
case "bind":
|
|
343
|
+
return "bind";
|
|
344
|
+
case "validate":
|
|
345
|
+
return "validate";
|
|
346
|
+
case "consume":
|
|
347
|
+
return "consume";
|
|
348
|
+
case "trigger":
|
|
349
|
+
return "trigger";
|
|
350
|
+
case "reference":
|
|
351
|
+
return "affiliation";
|
|
352
|
+
default:
|
|
353
|
+
return "bind";
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async buildRegistrarRefs(address) {
|
|
357
|
+
try {
|
|
358
|
+
const result = await this.rateLimiter.run(async () => {
|
|
359
|
+
return await queryTableItem_EntityLinker({
|
|
360
|
+
address,
|
|
361
|
+
network: this.options.network,
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
if (!result || !result.votes || result.votes.length === 0) {
|
|
365
|
+
return { edges: [] };
|
|
366
|
+
}
|
|
367
|
+
const edges = [];
|
|
368
|
+
const reputation = {
|
|
369
|
+
likes: 0,
|
|
370
|
+
dislikes: 0,
|
|
371
|
+
affiliations: 0,
|
|
372
|
+
voters: [],
|
|
373
|
+
};
|
|
374
|
+
for (const vote of result.votes) {
|
|
375
|
+
if (vote.like)
|
|
376
|
+
reputation.likes++;
|
|
377
|
+
if (vote.dislike)
|
|
378
|
+
reputation.dislikes++;
|
|
379
|
+
if (vote.affiliation)
|
|
380
|
+
reputation.affiliations++;
|
|
381
|
+
reputation.voters.push({
|
|
382
|
+
who: vote.address,
|
|
383
|
+
like: vote.like ?? false,
|
|
384
|
+
dislike: vote.dislike ?? false,
|
|
385
|
+
affiliation: vote.affiliation ?? false,
|
|
386
|
+
time: vote.time,
|
|
387
|
+
});
|
|
388
|
+
if (vote.affiliation) {
|
|
389
|
+
edges.push({
|
|
390
|
+
sourceObject: vote.address,
|
|
391
|
+
targetObject: address,
|
|
392
|
+
edgeSource: "registrar",
|
|
393
|
+
relationshipType: "affiliation",
|
|
394
|
+
fieldName: null,
|
|
395
|
+
depth: 0,
|
|
396
|
+
verified: true,
|
|
397
|
+
weight: 0.9,
|
|
398
|
+
direction: "inbound",
|
|
399
|
+
metadata: {
|
|
400
|
+
like: vote.like ?? false,
|
|
401
|
+
dislike: vote.dislike ?? false,
|
|
402
|
+
affiliation: true,
|
|
403
|
+
time: vote.time,
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
else if (vote.like || vote.dislike) {
|
|
408
|
+
edges.push({
|
|
409
|
+
sourceObject: vote.address,
|
|
410
|
+
targetObject: address,
|
|
411
|
+
edgeSource: "registrar",
|
|
412
|
+
relationshipType: "affiliation",
|
|
413
|
+
fieldName: null,
|
|
414
|
+
depth: 0,
|
|
415
|
+
verified: true,
|
|
416
|
+
weight: 0.3,
|
|
417
|
+
direction: "inbound",
|
|
418
|
+
metadata: {
|
|
419
|
+
like: vote.like ?? false,
|
|
420
|
+
dislike: vote.dislike ?? false,
|
|
421
|
+
affiliation: false,
|
|
422
|
+
disaffiliated: true,
|
|
423
|
+
time: vote.time,
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return { edges, reputation };
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return { edges: [] };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async buildPotentialRefs(projectId, existingEdges) {
|
|
435
|
+
const potentialEdges = [];
|
|
436
|
+
const projectObjects = await this.localProject.listObjects(projectId);
|
|
437
|
+
if (projectObjects.length === 0)
|
|
438
|
+
return potentialEdges;
|
|
439
|
+
const existingPairs = new Set(existingEdges.map((e) => `${e.sourceObject}→${e.targetObject}`));
|
|
440
|
+
const sourceFieldBindings = new Map();
|
|
441
|
+
for (const e of existingEdges) {
|
|
442
|
+
if (e.fieldName) {
|
|
443
|
+
const key = `${e.sourceObject}#${e.fieldName}`;
|
|
444
|
+
if (!sourceFieldBindings.has(key)) {
|
|
445
|
+
sourceFieldBindings.set(key, new Set());
|
|
446
|
+
}
|
|
447
|
+
sourceFieldBindings.get(key).add(e.targetObject);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
const byType = new Map();
|
|
451
|
+
for (const obj of projectObjects) {
|
|
452
|
+
const type = obj.objectType ?? "unknown";
|
|
453
|
+
if (!byType.has(type))
|
|
454
|
+
byType.set(type, []);
|
|
455
|
+
byType.get(type).push({
|
|
456
|
+
address: obj.objectAddress,
|
|
457
|
+
name: obj.objectName,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
for (const [sourceType, expectedRels] of EXPECTED_RELATIONSHIPS) {
|
|
461
|
+
const sources = byType.get(sourceType);
|
|
462
|
+
if (!sources || sources.length === 0)
|
|
463
|
+
continue;
|
|
464
|
+
for (const source of sources) {
|
|
465
|
+
for (const expected of expectedRels) {
|
|
466
|
+
const targets = byType.get(expected.targetType);
|
|
467
|
+
if (!targets || targets.length === 0)
|
|
468
|
+
continue;
|
|
469
|
+
const fieldKey = `${source.address}#${expected.fieldName}`;
|
|
470
|
+
const existingBindings = sourceFieldBindings.get(fieldKey);
|
|
471
|
+
const hasOtherBinding = existingBindings !== undefined && existingBindings.size > 0;
|
|
472
|
+
for (const target of targets) {
|
|
473
|
+
if (source.address === target.address)
|
|
474
|
+
continue;
|
|
475
|
+
const pairKey = `${source.address}→${target.address}`;
|
|
476
|
+
if (existingPairs.has(pairKey))
|
|
477
|
+
continue;
|
|
478
|
+
if (existingBindings?.has(target.address))
|
|
479
|
+
continue;
|
|
480
|
+
potentialEdges.push({
|
|
481
|
+
sourceObject: source.address,
|
|
482
|
+
targetObject: target.address,
|
|
483
|
+
edgeSource: "potential",
|
|
484
|
+
relationshipType: expected.relationshipType,
|
|
485
|
+
fieldName: expected.fieldName,
|
|
486
|
+
depth: 0,
|
|
487
|
+
verified: false,
|
|
488
|
+
weight: hasOtherBinding ? 0.3 : 0.6,
|
|
489
|
+
direction: "outbound",
|
|
490
|
+
metadata: {
|
|
491
|
+
reason: hasOtherBinding
|
|
492
|
+
? "field_already_bound_to_other"
|
|
493
|
+
: "expected_relationship_missing",
|
|
494
|
+
expected_target_type: expected.targetType,
|
|
495
|
+
source_object_name: source.name,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return potentialEdges;
|
|
503
|
+
}
|
|
504
|
+
resolveConflicts(edges) {
|
|
505
|
+
const edgeMap = new Map();
|
|
506
|
+
for (const edge of edges) {
|
|
507
|
+
const key = `${edge.sourceObject}→${edge.targetObject}:${edge.relationshipType}`;
|
|
508
|
+
const list = edgeMap.get(key) ?? [];
|
|
509
|
+
list.push(edge);
|
|
510
|
+
edgeMap.set(key, list);
|
|
511
|
+
}
|
|
512
|
+
const result = [];
|
|
513
|
+
for (const list of edgeMap.values()) {
|
|
514
|
+
if (list.length === 1) {
|
|
515
|
+
result.push(list[0]);
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const priority = (e) => {
|
|
519
|
+
let p = 0;
|
|
520
|
+
if (e.edgeSource === "internal")
|
|
521
|
+
p += 100;
|
|
522
|
+
if (e.edgeSource === "registrar")
|
|
523
|
+
p += 50;
|
|
524
|
+
if (e.verified)
|
|
525
|
+
p += 10;
|
|
526
|
+
p += (e.weight ?? 0.5) * 10;
|
|
527
|
+
return p;
|
|
528
|
+
};
|
|
529
|
+
list.sort((a, b) => priority(b) - priority(a));
|
|
530
|
+
const winner = list[0];
|
|
531
|
+
const mergedMetadata = { ...(winner.metadata ?? {}) };
|
|
532
|
+
for (let i = 1; i < list.length; i++) {
|
|
533
|
+
mergedMetadata[`alt_${list[i].edgeSource}`] = list[i].metadata ?? {};
|
|
534
|
+
}
|
|
535
|
+
result.push({ ...winner, metadata: mergedMetadata });
|
|
536
|
+
}
|
|
537
|
+
return result;
|
|
538
|
+
}
|
|
539
|
+
buildNodes(edges, objectCache, rootAddress) {
|
|
540
|
+
const nodeMap = new Map();
|
|
541
|
+
const ensureNode = (addr) => {
|
|
542
|
+
if (!nodeMap.has(addr)) {
|
|
543
|
+
const obj = objectCache.get(addr);
|
|
544
|
+
nodeMap.set(addr, {
|
|
545
|
+
address: addr,
|
|
546
|
+
objectType: obj?.type ?? null,
|
|
547
|
+
objectName: null,
|
|
548
|
+
role: null,
|
|
549
|
+
isCore: addr === rootAddress,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
return nodeMap.get(addr);
|
|
553
|
+
};
|
|
554
|
+
for (const edge of edges) {
|
|
555
|
+
const source = ensureNode(edge.sourceObject);
|
|
556
|
+
const target = ensureNode(edge.targetObject);
|
|
557
|
+
if (edge.edgeSource === "internal" && edge.relationshipType === "bind") {
|
|
558
|
+
if (source.isCore) {
|
|
559
|
+
target.role = "dependency";
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (edge.edgeSource === "potential") {
|
|
563
|
+
target.role = "reference";
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return Array.from(nodeMap.values());
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
export async function buildGraph(rootAddress, options) {
|
|
570
|
+
const builder = new GraphBuilder(options);
|
|
571
|
+
return builder.buildRelationshipGraph(rootAddress);
|
|
572
|
+
}
|