@helm-protocol/ttt-mcp 0.2.2 → 0.3.1
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 +327 -171
- package/dist/auth.js +1 -1
- package/dist/index.js +213 -166
- package/dist/server.js +136 -0
- package/dist/tools.js +376 -9
- package/package.json +26 -5
package/dist/tools.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,24 +17,67 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var tools_exports = {};
|
|
20
30
|
__export(tools_exports, {
|
|
31
|
+
potCheckpoint: () => potCheckpoint,
|
|
21
32
|
potGenerate: () => potGenerate,
|
|
33
|
+
potGraph: () => potGraph,
|
|
22
34
|
potHealth: () => potHealth,
|
|
23
35
|
potQuery: () => potQuery,
|
|
24
36
|
potStats: () => potStats,
|
|
25
|
-
potVerify: () => potVerify
|
|
37
|
+
potVerify: () => potVerify,
|
|
38
|
+
redis: () => redis,
|
|
39
|
+
restoreDagEntry: () => restoreDagEntry,
|
|
40
|
+
tttsFreshnessSeal: () => tttsFreshnessSeal,
|
|
41
|
+
updateLastPot: () => updateLastPot
|
|
26
42
|
});
|
|
27
43
|
module.exports = __toCommonJS(tools_exports);
|
|
28
44
|
var import_openttt = require("openttt");
|
|
29
45
|
var import_telemetry = require("./telemetry");
|
|
46
|
+
var import_server = require("./server");
|
|
47
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
48
|
+
const GrgPipeline = (
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
|
+
require("openttt").GrgPipeline ?? null
|
|
51
|
+
);
|
|
52
|
+
function applyAdvisory(result, advisory) {
|
|
53
|
+
if (!advisory) return result;
|
|
54
|
+
if (result === null || typeof result !== "object") return result;
|
|
55
|
+
const notices = [];
|
|
56
|
+
if (advisory.warning) notices.push(advisory.warning);
|
|
57
|
+
if (advisory.overageActive) notices.push("Overage billing is active \u2014 usage above your plan limit will be charged.");
|
|
58
|
+
if (notices.length === 0) return result;
|
|
59
|
+
return { ...result, _quotaNotice: notices.join(" | ") };
|
|
60
|
+
}
|
|
61
|
+
function resolvePaidApiKey() {
|
|
62
|
+
return process.env.TTT_API_KEY?.trim() || void 0;
|
|
63
|
+
}
|
|
64
|
+
const redis = new import_ioredis.default(process.env.REDIS_URL ?? "redis://127.0.0.1:6379", {
|
|
65
|
+
lazyConnect: true,
|
|
66
|
+
enableOfflineQueue: false,
|
|
67
|
+
maxRetriesPerRequest: 1
|
|
68
|
+
});
|
|
69
|
+
redis.on("error", () => {
|
|
70
|
+
});
|
|
30
71
|
const timeSynth = new import_openttt.TimeSynthesis();
|
|
31
72
|
const adaptiveSwitch = new import_openttt.AdaptiveSwitch();
|
|
32
73
|
const potSigner = new import_openttt.PotSigner();
|
|
33
74
|
const startedAt = Date.now();
|
|
34
75
|
const POT_LOG_MAX = 1e4;
|
|
35
76
|
const potLog = [];
|
|
77
|
+
const potByEventId = /* @__PURE__ */ new Map();
|
|
78
|
+
const potByPrevEventId = /* @__PURE__ */ new Map();
|
|
79
|
+
const evictedEventIds = /* @__PURE__ */ new Set();
|
|
80
|
+
const EVICTED_MAX = 1e3;
|
|
36
81
|
function bigintReplacer(_key, value) {
|
|
37
82
|
return typeof value === "bigint" ? value.toString() : value;
|
|
38
83
|
}
|
|
@@ -40,26 +85,213 @@ function serialize(obj) {
|
|
|
40
85
|
return JSON.parse(JSON.stringify(obj, bigintReplacer));
|
|
41
86
|
}
|
|
42
87
|
const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744392/openttt-base-sepolia/v0.1.0";
|
|
88
|
+
function compressEntry(entry, depth, depthThreshold) {
|
|
89
|
+
const t = depthThreshold ?? { compact: 5, minimal: 20, rollup: 50 };
|
|
90
|
+
if (depth <= t.compact) return entry;
|
|
91
|
+
if (depth <= t.minimal) return {
|
|
92
|
+
// compact
|
|
93
|
+
eventId: entry.eventId,
|
|
94
|
+
potHash: entry.potHash,
|
|
95
|
+
timestamp: entry.timestamp,
|
|
96
|
+
prevEventId: entry.prevEventId
|
|
97
|
+
};
|
|
98
|
+
if (depth <= t.rollup) return {
|
|
99
|
+
// minimal
|
|
100
|
+
eventId: entry.eventId,
|
|
101
|
+
timestamp: entry.timestamp
|
|
102
|
+
};
|
|
103
|
+
return `${entry.eventId ?? "?"}@${entry.timestamp}`;
|
|
104
|
+
}
|
|
105
|
+
function depthThresholdFromTokens(maxTokens, entryCount) {
|
|
106
|
+
const charBudget = maxTokens * 4;
|
|
107
|
+
const fullBudget = charBudget * 0.6;
|
|
108
|
+
const compactBudget = charBudget * 0.25;
|
|
109
|
+
const minimalBudget = charBudget * 0.1;
|
|
110
|
+
const fullDepth = Math.max(1, Math.floor(fullBudget / 300));
|
|
111
|
+
const compactDepth = fullDepth + Math.max(0, Math.floor(compactBudget / 120));
|
|
112
|
+
const minimalDepth = compactDepth + Math.max(0, Math.floor(minimalBudget / 60));
|
|
113
|
+
return {
|
|
114
|
+
compact: Math.min(fullDepth, entryCount),
|
|
115
|
+
minimal: Math.min(compactDepth, entryCount),
|
|
116
|
+
rollup: Math.min(minimalDepth, entryCount)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
let _lastPotTimestampNs = null;
|
|
120
|
+
let _lastPotStratum = 16;
|
|
121
|
+
let _lastPotSources = 0;
|
|
122
|
+
function updateLastPot(timestampNs, stratum, sources) {
|
|
123
|
+
_lastPotTimestampNs = timestampNs;
|
|
124
|
+
_lastPotStratum = stratum;
|
|
125
|
+
_lastPotSources = sources;
|
|
126
|
+
}
|
|
127
|
+
function tttsFreshnessSeal() {
|
|
128
|
+
if (_lastPotTimestampNs === null) return null;
|
|
129
|
+
const nowNs = BigInt(Date.now()) * 1000000n;
|
|
130
|
+
const age_ms = Number((nowNs - _lastPotTimestampNs) / 1000000n);
|
|
131
|
+
const ttlMs = _lastPotStratum <= 3 ? 100 : _lastPotStratum <= 8 ? 1e3 : _lastPotStratum <= 15 ? 5e3 : 0;
|
|
132
|
+
return { age_ms, stratum: _lastPotStratum, sources: _lastPotSources, ttlMs };
|
|
133
|
+
}
|
|
134
|
+
function restoreDagEntry(entry) {
|
|
135
|
+
if (potByEventId.has(entry.eventId)) return;
|
|
136
|
+
const e = {
|
|
137
|
+
potHash: entry.potHash,
|
|
138
|
+
timestamp: entry.timestamp,
|
|
139
|
+
stratum: entry.stratum,
|
|
140
|
+
mode: entry.mode,
|
|
141
|
+
chainId: entry.chainId ?? void 0,
|
|
142
|
+
poolAddress: entry.poolAddress ?? void 0,
|
|
143
|
+
eventId: entry.eventId,
|
|
144
|
+
prevEventId: entry.prevEventId ?? void 0,
|
|
145
|
+
createdAt: entry.createdAt
|
|
146
|
+
};
|
|
147
|
+
if (potLog.length >= POT_LOG_MAX) {
|
|
148
|
+
const evicted = potLog.shift();
|
|
149
|
+
if (evicted.eventId) {
|
|
150
|
+
potByEventId.delete(evicted.eventId);
|
|
151
|
+
evictedEventIds.add(evicted.eventId);
|
|
152
|
+
if (evictedEventIds.size > EVICTED_MAX) {
|
|
153
|
+
const first = evictedEventIds.values().next().value;
|
|
154
|
+
if (first !== void 0) evictedEventIds.delete(first);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (evicted.prevEventId) {
|
|
158
|
+
const siblings = potByPrevEventId.get(evicted.prevEventId);
|
|
159
|
+
if (siblings) {
|
|
160
|
+
const filtered = siblings.filter((s) => s !== evicted);
|
|
161
|
+
if (filtered.length === 0) potByPrevEventId.delete(evicted.prevEventId);
|
|
162
|
+
else potByPrevEventId.set(evicted.prevEventId, filtered);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
potLog.push(e);
|
|
167
|
+
potByEventId.set(entry.eventId, e);
|
|
168
|
+
if (entry.prevEventId) {
|
|
169
|
+
const bucket = potByPrevEventId.get(entry.prevEventId) ?? [];
|
|
170
|
+
bucket.push(e);
|
|
171
|
+
potByPrevEventId.set(entry.prevEventId, bucket);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
43
174
|
async function potGenerate(args) {
|
|
175
|
+
if (!args.eventId && !args.txHash) {
|
|
176
|
+
throw new Error("Either eventId (Claude Code) or txHash (DeFi) is required");
|
|
177
|
+
}
|
|
44
178
|
(0, import_telemetry.telemetryIncrement)("pot_generate");
|
|
45
|
-
const
|
|
179
|
+
const apiKey = resolvePaidApiKey();
|
|
180
|
+
if (apiKey && args.eventId) {
|
|
181
|
+
const { data, advisory } = await (0, import_server.delegateToServer)({
|
|
182
|
+
apiKey,
|
|
183
|
+
method: "POST",
|
|
184
|
+
path: "/pot/generate",
|
|
185
|
+
body: { eventId: args.eventId, prevEventId: args.prevEventId }
|
|
186
|
+
});
|
|
187
|
+
if (data && typeof data === "object") {
|
|
188
|
+
const d = data;
|
|
189
|
+
if (typeof d.stratum === "number" && typeof d.sources === "number" && typeof d.timestamp === "string") {
|
|
190
|
+
updateLastPot(BigInt(d.timestamp), d.stratum, d.sources);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return applyAdvisory(data, advisory);
|
|
194
|
+
}
|
|
195
|
+
let pot;
|
|
196
|
+
let isOfflineFallback = false;
|
|
197
|
+
try {
|
|
198
|
+
pot = await timeSynth.generateProofOfTime();
|
|
199
|
+
} catch {
|
|
200
|
+
isOfflineFallback = true;
|
|
201
|
+
const nowMs = BigInt(Date.now());
|
|
202
|
+
pot = {
|
|
203
|
+
timestamp: nowMs * 1000000n,
|
|
204
|
+
stratum: 16,
|
|
205
|
+
uncertainty: 999999999,
|
|
206
|
+
confidence: 0,
|
|
207
|
+
sources: 0,
|
|
208
|
+
nonce: Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString("hex"),
|
|
209
|
+
expiresAt: (nowMs + 300000n) * 1000000n,
|
|
210
|
+
sourceReadings: []
|
|
211
|
+
};
|
|
212
|
+
}
|
|
46
213
|
const potHash = import_openttt.TimeSynthesis.getOnChainHash(pot);
|
|
47
|
-
|
|
48
|
-
|
|
214
|
+
let grgShards = [];
|
|
215
|
+
let currentMode;
|
|
216
|
+
if (args.txHash && args.chainId != null && args.poolAddress) {
|
|
217
|
+
const txData = new TextEncoder().encode(args.txHash);
|
|
218
|
+
grgShards = GrgPipeline.processForward(txData, args.chainId, args.poolAddress).map((s) => Buffer.from(s).toString("hex"));
|
|
219
|
+
const syntheticBlock = {
|
|
220
|
+
timestamp: Number(pot.timestamp / 1000000n),
|
|
221
|
+
// ns → ms
|
|
222
|
+
txs: [args.txHash],
|
|
223
|
+
data: new TextEncoder().encode(args.txHash)
|
|
224
|
+
};
|
|
225
|
+
const syntheticTTTRecord = {
|
|
226
|
+
time: Number(pot.timestamp / 1000000n),
|
|
227
|
+
txOrder: [args.txHash],
|
|
228
|
+
grgPayload: []
|
|
229
|
+
};
|
|
230
|
+
currentMode = adaptiveSwitch.verifyBlock(syntheticBlock, syntheticTTTRecord, args.chainId, args.poolAddress);
|
|
231
|
+
} else {
|
|
232
|
+
currentMode = adaptiveSwitch.getCurrentMode();
|
|
233
|
+
}
|
|
49
234
|
const signature = potSigner.signPot(potHash);
|
|
50
235
|
const entry = {
|
|
51
236
|
potHash,
|
|
52
237
|
timestamp: pot.timestamp.toString(),
|
|
53
238
|
stratum: pot.stratum,
|
|
54
|
-
mode:
|
|
239
|
+
mode: currentMode,
|
|
55
240
|
chainId: args.chainId,
|
|
56
241
|
poolAddress: args.poolAddress,
|
|
242
|
+
eventId: args.eventId,
|
|
243
|
+
prevEventId: args.prevEventId,
|
|
57
244
|
createdAt: Date.now()
|
|
58
245
|
};
|
|
246
|
+
if (potLog.length >= POT_LOG_MAX) {
|
|
247
|
+
const evicted = potLog.shift();
|
|
248
|
+
if (evicted.eventId) {
|
|
249
|
+
potByEventId.delete(evicted.eventId);
|
|
250
|
+
evictedEventIds.add(evicted.eventId);
|
|
251
|
+
if (evictedEventIds.size > EVICTED_MAX) {
|
|
252
|
+
const first = evictedEventIds.values().next().value;
|
|
253
|
+
if (first !== void 0) evictedEventIds.delete(first);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (evicted.prevEventId) {
|
|
257
|
+
const siblings = potByPrevEventId.get(evicted.prevEventId);
|
|
258
|
+
if (siblings) {
|
|
259
|
+
const filtered = siblings.filter((e) => e !== evicted);
|
|
260
|
+
if (filtered.length === 0) potByPrevEventId.delete(evicted.prevEventId);
|
|
261
|
+
else potByPrevEventId.set(evicted.prevEventId, filtered);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
59
265
|
potLog.push(entry);
|
|
60
|
-
if (
|
|
266
|
+
if (args.eventId) potByEventId.set(args.eventId, entry);
|
|
267
|
+
if (args.prevEventId) {
|
|
268
|
+
const bucket = potByPrevEventId.get(args.prevEventId) ?? [];
|
|
269
|
+
bucket.push(entry);
|
|
270
|
+
potByPrevEventId.set(args.prevEventId, bucket);
|
|
271
|
+
}
|
|
272
|
+
if (args.eventId) {
|
|
273
|
+
redis.setex(
|
|
274
|
+
`dag:${args.eventId}`,
|
|
275
|
+
90 * 86400,
|
|
276
|
+
JSON.stringify({
|
|
277
|
+
eventId: args.eventId,
|
|
278
|
+
prevEventId: args.prevEventId ?? null,
|
|
279
|
+
potHash,
|
|
280
|
+
timestamp: pot.timestamp.toString(),
|
|
281
|
+
stratum: pot.stratum,
|
|
282
|
+
mode: currentMode,
|
|
283
|
+
createdAt: entry.createdAt,
|
|
284
|
+
chainId: args.chainId ?? null,
|
|
285
|
+
poolAddress: args.poolAddress ?? null
|
|
286
|
+
})
|
|
287
|
+
).catch(() => {
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
updateLastPot(pot.timestamp, pot.stratum, pot.sources);
|
|
61
291
|
return serialize({
|
|
62
292
|
potHash,
|
|
293
|
+
eventId: args.eventId ?? null,
|
|
294
|
+
prevEventId: args.prevEventId ?? null,
|
|
63
295
|
timestamp: pot.timestamp.toString(),
|
|
64
296
|
stratum: pot.stratum,
|
|
65
297
|
uncertainty: pot.uncertainty,
|
|
@@ -67,7 +299,8 @@ async function potGenerate(args) {
|
|
|
67
299
|
sources: pot.sources,
|
|
68
300
|
nonce: pot.nonce,
|
|
69
301
|
expiresAt: pot.expiresAt.toString(),
|
|
70
|
-
|
|
302
|
+
...isOfflineFallback && { mode: "local" },
|
|
303
|
+
...grgShards.length > 0 && { grgShards },
|
|
71
304
|
signature: {
|
|
72
305
|
issuerPubKey: signature.issuerPubKey,
|
|
73
306
|
signature: signature.signature,
|
|
@@ -81,7 +314,7 @@ async function potVerify(args) {
|
|
|
81
314
|
let valid = false;
|
|
82
315
|
let reconstructedSize = 0;
|
|
83
316
|
try {
|
|
84
|
-
const recovered =
|
|
317
|
+
const recovered = GrgPipeline.processInverse(shards, 0, args.chainId, args.poolAddress);
|
|
85
318
|
valid = recovered.length > 0;
|
|
86
319
|
reconstructedSize = recovered.length;
|
|
87
320
|
} catch {
|
|
@@ -98,6 +331,31 @@ async function potVerify(args) {
|
|
|
98
331
|
}
|
|
99
332
|
async function potQuery(args) {
|
|
100
333
|
(0, import_telemetry.telemetryIncrement)("pot_query");
|
|
334
|
+
const apiKey = resolvePaidApiKey();
|
|
335
|
+
if (apiKey) {
|
|
336
|
+
const { data, advisory } = await (0, import_server.delegateToServer)({
|
|
337
|
+
apiKey,
|
|
338
|
+
method: "GET",
|
|
339
|
+
path: "/pot/query",
|
|
340
|
+
query: {
|
|
341
|
+
eventId: args.eventId,
|
|
342
|
+
startTimeNs: args.startTime != null ? args.startTime * 1e6 : void 0,
|
|
343
|
+
endTimeNs: args.endTime != null ? args.endTime * 1e6 : void 0,
|
|
344
|
+
limit: args.limit
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
return applyAdvisory(data, advisory);
|
|
348
|
+
}
|
|
349
|
+
if (args.eventId) {
|
|
350
|
+
const entry = potByEventId.get(args.eventId);
|
|
351
|
+
return serialize({
|
|
352
|
+
local: entry ? [entry] : [],
|
|
353
|
+
subgraph: [],
|
|
354
|
+
found: !!entry,
|
|
355
|
+
totalLocal: potLog.length,
|
|
356
|
+
query: { eventId: args.eventId }
|
|
357
|
+
});
|
|
358
|
+
}
|
|
101
359
|
const limit = Math.min(args.limit ?? 100, 1e3);
|
|
102
360
|
const now = Date.now();
|
|
103
361
|
const startTime = args.startTime ?? now - 864e5;
|
|
@@ -140,8 +398,56 @@ async function potQuery(args) {
|
|
|
140
398
|
query: { startTime, endTime, limit }
|
|
141
399
|
});
|
|
142
400
|
}
|
|
401
|
+
async function potGraph(args) {
|
|
402
|
+
(0, import_telemetry.telemetryIncrement)("pot_graph");
|
|
403
|
+
const apiKey = resolvePaidApiKey();
|
|
404
|
+
if (apiKey) {
|
|
405
|
+
const { data, advisory } = await (0, import_server.delegateToServer)({
|
|
406
|
+
apiKey,
|
|
407
|
+
method: "GET",
|
|
408
|
+
path: "/pot/graph",
|
|
409
|
+
query: { eventId: args.eventId, depth: args.depth }
|
|
410
|
+
});
|
|
411
|
+
return applyAdvisory(data, advisory);
|
|
412
|
+
}
|
|
413
|
+
const maxDepth = Math.min(args.depth ?? 10, 100);
|
|
414
|
+
const backwardChain = [];
|
|
415
|
+
let cursor = potByEventId.get(args.eventId);
|
|
416
|
+
let d = 0;
|
|
417
|
+
while (cursor && d < maxDepth) {
|
|
418
|
+
backwardChain.unshift(cursor);
|
|
419
|
+
cursor = cursor.prevEventId ? potByEventId.get(cursor.prevEventId) : void 0;
|
|
420
|
+
d++;
|
|
421
|
+
}
|
|
422
|
+
const forwardChain = potByPrevEventId.get(args.eventId) ?? [];
|
|
423
|
+
const found = potByEventId.has(args.eventId);
|
|
424
|
+
const chainBroken = backwardChain.some((e) => e.eventId && evictedEventIds.has(e.eventId)) || cursor?.prevEventId != null && potByEventId.get(cursor.prevEventId) == null;
|
|
425
|
+
const chainRoot = backwardChain.length > 0 ? backwardChain[0] : null;
|
|
426
|
+
const isServerRestart = chainBroken && chainRoot !== null && chainRoot.prevEventId != null && !potByEventId.has(chainRoot.prevEventId) && !evictedEventIds.has(chainRoot.prevEventId);
|
|
427
|
+
const brokenAt = chainBroken ? isServerRestart ? "server_restart" : backwardChain[0]?.eventId ?? null : null;
|
|
428
|
+
const compressedBackward = backwardChain.map((e, i) => compressEntry(e, i + 1));
|
|
429
|
+
return serialize({
|
|
430
|
+
eventId: args.eventId,
|
|
431
|
+
found,
|
|
432
|
+
backwardChain: compressedBackward,
|
|
433
|
+
forwardChain,
|
|
434
|
+
chainLength: backwardChain.length + forwardChain.length,
|
|
435
|
+
reachableDepth: backwardChain.length,
|
|
436
|
+
chainBroken,
|
|
437
|
+
brokenAt
|
|
438
|
+
});
|
|
439
|
+
}
|
|
143
440
|
async function potStats(args) {
|
|
144
441
|
(0, import_telemetry.telemetryIncrement)("pot_stats");
|
|
442
|
+
const apiKey = resolvePaidApiKey();
|
|
443
|
+
if (apiKey) {
|
|
444
|
+
const { data, advisory } = await (0, import_server.delegateToServer)({
|
|
445
|
+
apiKey,
|
|
446
|
+
method: "GET",
|
|
447
|
+
path: "/pot/stats"
|
|
448
|
+
});
|
|
449
|
+
return applyAdvisory(data, advisory);
|
|
450
|
+
}
|
|
145
451
|
const now = Date.now();
|
|
146
452
|
const periodMs = {
|
|
147
453
|
day: 864e5,
|
|
@@ -220,11 +526,72 @@ async function potHealth() {
|
|
|
220
526
|
}
|
|
221
527
|
});
|
|
222
528
|
}
|
|
529
|
+
async function potCheckpoint(args) {
|
|
530
|
+
(0, import_telemetry.telemetryIncrement)("pot_checkpoint");
|
|
531
|
+
const apiKey = resolvePaidApiKey();
|
|
532
|
+
if (apiKey) {
|
|
533
|
+
const { data, advisory } = await (0, import_server.delegateToServer)({
|
|
534
|
+
apiKey,
|
|
535
|
+
method: "GET",
|
|
536
|
+
path: "/pot/checkpoint",
|
|
537
|
+
query: {
|
|
538
|
+
fromEventId: args.fromEventId,
|
|
539
|
+
toEventId: args.toEventId,
|
|
540
|
+
startTime: args.startTime,
|
|
541
|
+
endTime: args.endTime,
|
|
542
|
+
maxTokens: args.maxTokens
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
return applyAdvisory(data, advisory);
|
|
546
|
+
}
|
|
547
|
+
const now = Date.now();
|
|
548
|
+
const startTime = args.startTime ?? now - 36e5;
|
|
549
|
+
const endTime = args.endTime ?? now;
|
|
550
|
+
let entries;
|
|
551
|
+
if (args.fromEventId && args.toEventId) {
|
|
552
|
+
entries = [];
|
|
553
|
+
let cursor = potByEventId.get(args.fromEventId);
|
|
554
|
+
const maxDepth = 1e3;
|
|
555
|
+
let d = 0;
|
|
556
|
+
while (cursor && d < maxDepth) {
|
|
557
|
+
entries.push(cursor);
|
|
558
|
+
if (cursor.eventId === args.toEventId) break;
|
|
559
|
+
const nexts = potByPrevEventId.get(cursor.eventId ?? "") ?? [];
|
|
560
|
+
cursor = nexts[0];
|
|
561
|
+
d++;
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
entries = potLog.filter((e) => e.createdAt >= startTime && e.createdAt <= endTime);
|
|
565
|
+
}
|
|
566
|
+
const eventCount = entries.length;
|
|
567
|
+
const depthThreshold = args.maxTokens ? depthThresholdFromTokens(args.maxTokens, entries.length) : void 0;
|
|
568
|
+
const compressed = entries.map((e, i) => compressEntry(e, i + 1, depthThreshold));
|
|
569
|
+
const chainIntact = !entries.some((e) => e.eventId && evictedEventIds.has(e.eventId));
|
|
570
|
+
const nextCheckpointHint = Math.max(10, 240 - eventCount % 240);
|
|
571
|
+
const checkpointId = `ckpt_${now}_${eventCount}`;
|
|
572
|
+
const firstTs = entries[0]?.timestamp ?? null;
|
|
573
|
+
const lastTs = entries[entries.length - 1]?.timestamp ?? null;
|
|
574
|
+
return serialize({
|
|
575
|
+
checkpointId,
|
|
576
|
+
eventCount,
|
|
577
|
+
chainIntact,
|
|
578
|
+
nextCheckpointHint,
|
|
579
|
+
rollup: compressed,
|
|
580
|
+
summary: `${eventCount} events from ${firstTs ?? "?"} to ${lastTs ?? "?"}`,
|
|
581
|
+
generatedAt: now
|
|
582
|
+
});
|
|
583
|
+
}
|
|
223
584
|
// Annotate the CommonJS export names for ESM import in node:
|
|
224
585
|
0 && (module.exports = {
|
|
586
|
+
potCheckpoint,
|
|
225
587
|
potGenerate,
|
|
588
|
+
potGraph,
|
|
226
589
|
potHealth,
|
|
227
590
|
potQuery,
|
|
228
591
|
potStats,
|
|
229
|
-
potVerify
|
|
592
|
+
potVerify,
|
|
593
|
+
redis,
|
|
594
|
+
restoreDagEntry,
|
|
595
|
+
tttsFreshnessSeal,
|
|
596
|
+
updateLastPot
|
|
230
597
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helm-protocol/ttt-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "MCP Server for OpenTTT — Proof of Time tools for AI agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,9 +11,26 @@
|
|
|
11
11
|
"README.md"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "esbuild index.ts tools.ts telemetry.ts auth.ts --platform=node --target=node18 --format=cjs --outdir=dist",
|
|
14
|
+
"build": "esbuild index.ts tools.ts telemetry.ts auth.ts server.ts --platform=node --target=node18 --format=cjs --outdir=dist",
|
|
15
15
|
"start": "node dist/index.js",
|
|
16
|
-
"dev": "npx ts-node index.ts"
|
|
16
|
+
"dev": "npx ts-node index.ts",
|
|
17
|
+
"test": "jest --forceExit",
|
|
18
|
+
"typecheck": "NODE_OPTIONS='--max-old-space-size=4096' tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"jest": {
|
|
21
|
+
"preset": "ts-jest",
|
|
22
|
+
"testEnvironment": "node",
|
|
23
|
+
"testMatch": ["**/__tests__/**/*.test.ts"],
|
|
24
|
+
"moduleNameMapper": {
|
|
25
|
+
"^openttt$": "<rootDir>/node_modules/openttt/dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"transform": {
|
|
28
|
+
"^.+\\.tsx?$": ["ts-jest", {
|
|
29
|
+
"tsconfig": {
|
|
30
|
+
"module": "CommonJS"
|
|
31
|
+
}
|
|
32
|
+
}]
|
|
33
|
+
}
|
|
17
34
|
},
|
|
18
35
|
"keywords": [
|
|
19
36
|
"mcp",
|
|
@@ -35,13 +52,17 @@
|
|
|
35
52
|
"node": ">=18"
|
|
36
53
|
},
|
|
37
54
|
"dependencies": {
|
|
38
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
39
|
-
"
|
|
55
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
56
|
+
"ioredis": "^5.11.0",
|
|
57
|
+
"openttt": "^0.2.13",
|
|
40
58
|
"zod": "^3.25.0"
|
|
41
59
|
},
|
|
42
60
|
"devDependencies": {
|
|
61
|
+
"@types/jest": "^30.0.0",
|
|
43
62
|
"@types/node": "^20.11.19",
|
|
44
63
|
"esbuild": "^0.27.4",
|
|
64
|
+
"jest": "^30.4.2",
|
|
65
|
+
"ts-jest": "^29.4.11",
|
|
45
66
|
"typescript": "^5.3.3"
|
|
46
67
|
},
|
|
47
68
|
"mcpName": "io.github.Helm-Protocol/openttt-pot"
|