hive-intelligence 1.3.0 → 1.4.0
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 +1 -1
- package/agent-skills/SKILL.md +1 -1
- package/agent-skills/packs/hive-build/SKILL.md +1 -1
- package/agent-skills/packs/hive-build-onboarding/SKILL.md +7 -7
- package/agent-skills/packs/hive-cli/SKILL.md +2 -2
- package/agent-skills/packs/hive-defi-research/SKILL.md +1 -1
- package/agent-skills/packs/hive-dex-pool-analysis/SKILL.md +1 -1
- package/agent-skills/packs/hive-market-research/SKILL.md +1 -1
- package/agent-skills/packs/hive-mcp/SKILL.md +1 -1
- package/agent-skills/packs/hive-network-infrastructure/SKILL.md +1 -1
- package/agent-skills/packs/hive-nft-research/SKILL.md +1 -1
- package/agent-skills/packs/hive-prediction-markets/SKILL.md +1 -1
- package/agent-skills/packs/hive-query/SKILL.md +1 -1
- package/agent-skills/packs/hive-security-risk/SKILL.md +1 -1
- package/agent-skills/packs/hive-solana-analysis/SKILL.md +1 -1
- package/agent-skills/packs/hive-stateful-monitoring/SKILL.md +1 -1
- package/agent-skills/packs/hive-token-diligence/SKILL.md +1 -1
- package/agent-skills/packs/hive-tool-discovery/SKILL.md +1 -1
- package/agent-skills/packs/hive-wallet-investigation/SKILL.md +1 -1
- package/build/{api-client-FTVDFW5V.js → api-client-WMWFWXC7.js} +3 -15
- package/build/{auth-EZFJ5RYB.js → auth-HRFQWQMZ.js} +1 -9
- package/build/browser-auth-KXIO5BHM.js +20 -0
- package/build/{chunk-I5Z6FZRL.js → chunk-3OZFBWFK.js} +14 -12
- package/build/{chunk-N32UIHBU.js → chunk-7DYHWY4Y.js} +1 -1
- package/build/{chunk-7GD2MFYI.js → chunk-AQQAPAER.js} +35 -15
- package/build/{upgrade-J4HIGCKE.js → chunk-GHCYCBA3.js} +1 -2
- package/build/{chunk-Q7VBYLX7.js → chunk-P7NLFSZQ.js} +0 -58
- package/build/{chunk-RUHO24SX.js → chunk-TVQJY47C.js} +2 -157
- package/build/{chunk-W2QL7LYI.js → chunk-UFTNBCSA.js} +885 -101
- package/build/cli.js +28 -39
- package/build/{doctor-ORDOCGLM.js → doctor-GHUAUFS2.js} +3 -3
- package/build/{init-all-BVWW3JVK.js → init-all-RG542NWG.js} +32 -13
- package/build/{mcpServer-RUEOTTF7.js → mcpServer-D7OXUDU5.js} +1 -1
- package/build/monitor-worker.js +2 -2
- package/build/release.json +4 -4
- package/build/{serve-DKLR2WQH.js → serve-K3IT7DS4.js} +1 -1
- package/build/server.js +1429 -415
- package/build/stdio.js +1 -1
- package/build/{tools-I7GLWTV6.js → tools-D72ZH6P5.js} +1 -1
- package/build/upgrade-4S5XT5JG.js +14 -0
- package/build/{watch-WVR4CGCD.js → watch-IOSRSCFE.js} +2 -2
- package/package.json +5 -3
- package/build/browser-auth-OJSUNFBI.js +0 -8
|
@@ -17608,11 +17608,11 @@ var require_client = __commonJS({
|
|
|
17608
17608
|
};
|
|
17609
17609
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17610
17610
|
exports.TerminatedCloseEvent = void 0;
|
|
17611
|
-
exports.createClient =
|
|
17611
|
+
exports.createClient = createClient3;
|
|
17612
17612
|
var common_1 = require_common();
|
|
17613
17613
|
var utils_1 = require_utils();
|
|
17614
17614
|
__exportStar(require_common(), exports);
|
|
17615
|
-
function
|
|
17615
|
+
function createClient3(options) {
|
|
17616
17616
|
const {
|
|
17617
17617
|
url,
|
|
17618
17618
|
connectionParams,
|
|
@@ -20827,7 +20827,7 @@ var require_websocket = __commonJS({
|
|
|
20827
20827
|
var http = __require("http");
|
|
20828
20828
|
var net2 = __require("net");
|
|
20829
20829
|
var tls = __require("tls");
|
|
20830
|
-
var { randomBytes: randomBytes4, createHash:
|
|
20830
|
+
var { randomBytes: randomBytes4, createHash: createHash8 } = __require("crypto");
|
|
20831
20831
|
var { Duplex, Readable } = __require("stream");
|
|
20832
20832
|
var { URL: URL2 } = __require("url");
|
|
20833
20833
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -21495,7 +21495,7 @@ var require_websocket = __commonJS({
|
|
|
21495
21495
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
21496
21496
|
return;
|
|
21497
21497
|
}
|
|
21498
|
-
const digest2 =
|
|
21498
|
+
const digest2 = createHash8("sha1").update(key + GUID).digest("base64");
|
|
21499
21499
|
if (res.headers["sec-websocket-accept"] !== digest2) {
|
|
21500
21500
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
21501
21501
|
return;
|
|
@@ -21864,7 +21864,7 @@ var require_websocket_server = __commonJS({
|
|
|
21864
21864
|
var EventEmitter = __require("events");
|
|
21865
21865
|
var http = __require("http");
|
|
21866
21866
|
var { Duplex } = __require("stream");
|
|
21867
|
-
var { createHash:
|
|
21867
|
+
var { createHash: createHash8 } = __require("crypto");
|
|
21868
21868
|
var extension = require_extension();
|
|
21869
21869
|
var PerMessageDeflate = require_permessage_deflate();
|
|
21870
21870
|
var subprotocol = require_subprotocol();
|
|
@@ -22171,7 +22171,7 @@ var require_websocket_server = __commonJS({
|
|
|
22171
22171
|
);
|
|
22172
22172
|
}
|
|
22173
22173
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
22174
|
-
const digest2 =
|
|
22174
|
+
const digest2 = createHash8("sha1").update(key + GUID).digest("base64");
|
|
22175
22175
|
const headers = [
|
|
22176
22176
|
"HTTP/1.1 101 Switching Protocols",
|
|
22177
22177
|
"Upgrade: websocket",
|
|
@@ -25358,6 +25358,11 @@ function getPort() {
|
|
|
25358
25358
|
function getRateLimitMax() {
|
|
25359
25359
|
return rateLimitMaxSchema.parse(process.env.RATE_LIMIT_MAX ?? 30);
|
|
25360
25360
|
}
|
|
25361
|
+
function getCredentialedRateLimitMax() {
|
|
25362
|
+
return rateLimitMaxSchema.parse(
|
|
25363
|
+
process.env.CREDENTIALED_RATE_LIMIT_MAX ?? 1500
|
|
25364
|
+
);
|
|
25365
|
+
}
|
|
25361
25366
|
function getCorsAllowedOrigins() {
|
|
25362
25367
|
return getCsvEnv("CORS_ALLOWED_ORIGINS");
|
|
25363
25368
|
}
|
|
@@ -32582,7 +32587,7 @@ Don't use when:
|
|
|
32582
32587
|
|
|
32583
32588
|
Error Handling:
|
|
32584
32589
|
- Returns 404 if NFT collection ID not found
|
|
32585
|
-
- Requires Analyst plan or higher
|
|
32590
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
32586
32591
|
- Rate limited to 10-50 calls/minute depending on API plan`,
|
|
32587
32592
|
inputSchema: {
|
|
32588
32593
|
type: "object",
|
|
@@ -32700,7 +32705,7 @@ Don't use when:
|
|
|
32700
32705
|
- You need historical NFT data (use coingecko_get_nft_market_chart)
|
|
32701
32706
|
|
|
32702
32707
|
Error Handling:
|
|
32703
|
-
- Requires Analyst plan or higher
|
|
32708
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
32704
32709
|
- Rate limited to 10-50 calls/minute depending on API plan`,
|
|
32705
32710
|
inputSchema: {
|
|
32706
32711
|
type: "object",
|
|
@@ -33112,7 +33117,7 @@ Don't use when:
|
|
|
33112
33117
|
|
|
33113
33118
|
Error Handling:
|
|
33114
33119
|
- Returns 404 if network or token address not found
|
|
33115
|
-
- Requires Analyst plan or higher
|
|
33120
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
33116
33121
|
- Not all networks support trader tracking
|
|
33117
33122
|
- Rate limited based on API plan`,
|
|
33118
33123
|
inputSchema: {
|
|
@@ -33187,7 +33192,7 @@ Don't use when:
|
|
|
33187
33192
|
|
|
33188
33193
|
Error Handling:
|
|
33189
33194
|
- Returns 404 if contract not found on the specified platform
|
|
33190
|
-
- Requires Analyst plan or higher
|
|
33195
|
+
- Requires CoinGecko's Analyst plan or higher (a provider tier, not a Hive plan)
|
|
33191
33196
|
- Rate limited based on API plan`,
|
|
33192
33197
|
inputSchema: {
|
|
33193
33198
|
type: "object",
|
|
@@ -33824,7 +33829,6 @@ var get_insights_default = { metadata: metadata88, tool: tool88, handler: handle
|
|
|
33824
33829
|
// src/db/supabase.ts
|
|
33825
33830
|
import { createClient } from "@supabase/supabase-js";
|
|
33826
33831
|
var _supabaseAdmin = null;
|
|
33827
|
-
var _supabaseAuth = null;
|
|
33828
33832
|
function isSupabaseConfigured() {
|
|
33829
33833
|
return hasEnvVars("SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY");
|
|
33830
33834
|
}
|
|
@@ -33843,21 +33847,6 @@ function getSupabaseAdmin() {
|
|
|
33843
33847
|
}
|
|
33844
33848
|
return _supabaseAdmin;
|
|
33845
33849
|
}
|
|
33846
|
-
function getSupabaseAuthClient() {
|
|
33847
|
-
if (!_supabaseAuth) {
|
|
33848
|
-
if (!hasEnvVars("SUPABASE_URL", "SUPABASE_ANON_KEY")) {
|
|
33849
|
-
throw new Error(
|
|
33850
|
-
"Supabase auth client not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY."
|
|
33851
|
-
);
|
|
33852
|
-
}
|
|
33853
|
-
_supabaseAuth = createClient(
|
|
33854
|
-
getRequiredEnvVar("SUPABASE_URL"),
|
|
33855
|
-
getRequiredEnvVar("SUPABASE_ANON_KEY"),
|
|
33856
|
-
{ auth: { persistSession: false, autoRefreshToken: false } }
|
|
33857
|
-
);
|
|
33858
|
-
}
|
|
33859
|
-
return _supabaseAuth;
|
|
33860
|
-
}
|
|
33861
33850
|
|
|
33862
33851
|
// src/utils/secureLogger.ts
|
|
33863
33852
|
var SENSITIVE_PATTERNS = [
|
|
@@ -33894,7 +33883,7 @@ var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
33894
33883
|
"GOPLUS_API_KEY"
|
|
33895
33884
|
]);
|
|
33896
33885
|
function isSensitiveEnvironmentKey(key) {
|
|
33897
|
-
return /(?:api[_-]?(?:key|id)|key|secret|password|token|auth|credential|private|bearer|redis.*url)/i.test(
|
|
33886
|
+
return /(?:api[_-]?(?:key|id)|key|secret|password|token|auth|credential|private|bearer|redis.*url|sharpe_archive_supabase)/i.test(
|
|
33898
33887
|
key
|
|
33899
33888
|
);
|
|
33900
33889
|
}
|
|
@@ -34497,6 +34486,23 @@ async function listSubjectAuditEvents(args) {
|
|
|
34497
34486
|
}
|
|
34498
34487
|
return { ok: true, data: data ?? [] };
|
|
34499
34488
|
}
|
|
34489
|
+
function buildStatefulWriteAuditEvent(input) {
|
|
34490
|
+
if (!input.isStatefulWrite) return null;
|
|
34491
|
+
const userId = input.subjectContext?.userId ?? input.userId;
|
|
34492
|
+
if (!userId) return null;
|
|
34493
|
+
return {
|
|
34494
|
+
userId,
|
|
34495
|
+
subjectId: input.subjectContext?.subjectId,
|
|
34496
|
+
keyId: input.keyId,
|
|
34497
|
+
transport: input.transport,
|
|
34498
|
+
toolName: input.toolName,
|
|
34499
|
+
requestId: input.requestId,
|
|
34500
|
+
metadata: {
|
|
34501
|
+
...input.authMethod ? { auth_method: input.authMethod } : {},
|
|
34502
|
+
...input.authMethod === "oauth" ? { oauth_client_id: input.oauthClientId ?? null } : {}
|
|
34503
|
+
}
|
|
34504
|
+
};
|
|
34505
|
+
}
|
|
34500
34506
|
function recordSubjectAuditEvent(args) {
|
|
34501
34507
|
if (!isSupabaseConfigured()) return;
|
|
34502
34508
|
getSupabaseAdmin().from("state_subject_audit_events").insert({
|
|
@@ -46384,8 +46390,8 @@ Don't use when:
|
|
|
46384
46390
|
}
|
|
46385
46391
|
if (tokens) filterParams.tokens = tokens;
|
|
46386
46392
|
const MAX_SAFE_LIMIT = RESPONSE_LIMITS.MAX_LIMITS.tokens;
|
|
46387
|
-
const
|
|
46388
|
-
const requestedLimit = limit ||
|
|
46393
|
+
const DEFAULT_LIMIT3 = RESPONSE_LIMITS.DEFAULT_LIMITS.tokens;
|
|
46394
|
+
const requestedLimit = limit || DEFAULT_LIMIT3;
|
|
46389
46395
|
const finalLimit = Math.min(requestedLimit, MAX_SAFE_LIMIT);
|
|
46390
46396
|
filterParams.limit = finalLimit;
|
|
46391
46397
|
filterParams.offset = offset || 0;
|
|
@@ -51866,8 +51872,8 @@ Error Handling:
|
|
|
51866
51872
|
},
|
|
51867
51873
|
async execute(args) {
|
|
51868
51874
|
const MAX_SAFE_LIMIT = 50;
|
|
51869
|
-
const
|
|
51870
|
-
const limit = Math.min(args?.limit ||
|
|
51875
|
+
const DEFAULT_LIMIT3 = 30;
|
|
51876
|
+
const limit = Math.min(args?.limit || DEFAULT_LIMIT3, MAX_SAFE_LIMIT);
|
|
51871
51877
|
const offset = args?.offset || 0;
|
|
51872
51878
|
try {
|
|
51873
51879
|
const data = await apiClient.getProtocols();
|
|
@@ -52975,8 +52981,8 @@ Error Handling:
|
|
|
52975
52981
|
},
|
|
52976
52982
|
async execute(args) {
|
|
52977
52983
|
const MAX_SAFE_LIMIT = 100;
|
|
52978
|
-
const
|
|
52979
|
-
const limit = Math.min(args?.limit ||
|
|
52984
|
+
const DEFAULT_LIMIT3 = 50;
|
|
52985
|
+
const limit = Math.min(args?.limit || DEFAULT_LIMIT3, MAX_SAFE_LIMIT);
|
|
52980
52986
|
const offset = args?.offset || 0;
|
|
52981
52987
|
const minTvl = args?.minTvl || 0;
|
|
52982
52988
|
try {
|
|
@@ -55092,8 +55098,8 @@ Error Handling:
|
|
|
55092
55098
|
},
|
|
55093
55099
|
async execute(args) {
|
|
55094
55100
|
const MAX_SAFE_LIMIT = 100;
|
|
55095
|
-
const
|
|
55096
|
-
const limit = Math.min(args.limit ||
|
|
55101
|
+
const DEFAULT_LIMIT3 = 50;
|
|
55102
|
+
const limit = Math.min(args.limit || DEFAULT_LIMIT3, MAX_SAFE_LIMIT);
|
|
55097
55103
|
const offset = args.offset || 0;
|
|
55098
55104
|
const minFees = args.minFees || 0;
|
|
55099
55105
|
try {
|
|
@@ -80540,6 +80546,594 @@ Don't use when:
|
|
|
80540
80546
|
];
|
|
80541
80547
|
}
|
|
80542
80548
|
|
|
80549
|
+
// src/tools/archive/client.ts
|
|
80550
|
+
import { createClient as createClient2 } from "@supabase/supabase-js";
|
|
80551
|
+
var ARCHIVE_URL_ENV = "SHARPE_ARCHIVE_SUPABASE_URL";
|
|
80552
|
+
var ARCHIVE_KEY_ENV = "SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY";
|
|
80553
|
+
var ARCHIVE_MISSING_CREDENTIALS_MESSAGE = `Hive Archive credentials not configured \u2014 set ${ARCHIVE_URL_ENV} and ${ARCHIVE_KEY_ENV} to enable archive_ history tools. Until then use the live derivatives tools (ccxt_fetch_funding_rates_multi, hyperliquid_get_funding_history) for current data.`;
|
|
80554
|
+
var _archiveClient = null;
|
|
80555
|
+
function isArchiveConfigured() {
|
|
80556
|
+
return hasEnvVars(ARCHIVE_URL_ENV, ARCHIVE_KEY_ENV);
|
|
80557
|
+
}
|
|
80558
|
+
function getArchiveClient() {
|
|
80559
|
+
if (!isArchiveConfigured()) return null;
|
|
80560
|
+
if (!_archiveClient) {
|
|
80561
|
+
_archiveClient = createClient2(
|
|
80562
|
+
getEnvVar(ARCHIVE_URL_ENV),
|
|
80563
|
+
getEnvVar(ARCHIVE_KEY_ENV),
|
|
80564
|
+
{ auth: { persistSession: false, autoRefreshToken: false } }
|
|
80565
|
+
);
|
|
80566
|
+
}
|
|
80567
|
+
return _archiveClient;
|
|
80568
|
+
}
|
|
80569
|
+
|
|
80570
|
+
// src/tools/archive/api.ts
|
|
80571
|
+
var DEFAULT_LIMIT2 = 100;
|
|
80572
|
+
var MAX_LIMIT2 = 500;
|
|
80573
|
+
var ARCHIVE_SINCE_NOTE = "archive since 2023-11";
|
|
80574
|
+
var QUERY_TIMEOUT_MS = 1e4;
|
|
80575
|
+
var VENUE_SCAN_MAX_STEPS = 64;
|
|
80576
|
+
var COVERAGE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
80577
|
+
var COVERAGE_CACHE_MAX_ENTRIES = 16;
|
|
80578
|
+
var HOURS_PER_YEAR2 = 8760;
|
|
80579
|
+
function annualizeRate(rate, intervalHours) {
|
|
80580
|
+
if (!Number.isFinite(rate)) return null;
|
|
80581
|
+
if (!Number.isFinite(intervalHours) || intervalHours <= 0) return null;
|
|
80582
|
+
return rate * (HOURS_PER_YEAR2 / intervalHours);
|
|
80583
|
+
}
|
|
80584
|
+
function clampLimit3(value) {
|
|
80585
|
+
const parsed = typeof value === "number" ? Math.floor(value) : NaN;
|
|
80586
|
+
if (!Number.isFinite(parsed)) return DEFAULT_LIMIT2;
|
|
80587
|
+
return Math.min(Math.max(parsed, 1), MAX_LIMIT2);
|
|
80588
|
+
}
|
|
80589
|
+
function escapeLikeLiteral(value) {
|
|
80590
|
+
return value.replace(/[\\%_]/g, (match) => `\\${match}`);
|
|
80591
|
+
}
|
|
80592
|
+
var ArchiveInputError = class extends Error {
|
|
80593
|
+
};
|
|
80594
|
+
function normalizeBaseCoin(value) {
|
|
80595
|
+
const coin = typeof value === "string" ? value.trim().toUpperCase() : "";
|
|
80596
|
+
if (!coin) {
|
|
80597
|
+
throw new ArchiveInputError(
|
|
80598
|
+
'base_coin is required \u2014 pass the base asset ticker, e.g. "BTC" or "ETH". Call archive_get_coverage to see which venues and datasets the archive holds.'
|
|
80599
|
+
);
|
|
80600
|
+
}
|
|
80601
|
+
return coin;
|
|
80602
|
+
}
|
|
80603
|
+
function normalizeExchange(value) {
|
|
80604
|
+
if (typeof value !== "string") return null;
|
|
80605
|
+
const exchange = value.trim();
|
|
80606
|
+
return exchange === "" ? null : exchange;
|
|
80607
|
+
}
|
|
80608
|
+
function normalizeIsoDate(value, field) {
|
|
80609
|
+
if (value === void 0 || value === null) return null;
|
|
80610
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
|
|
80611
|
+
throw new ArchiveInputError(
|
|
80612
|
+
`${field} must be an ISO 8601 datetime (e.g. "2024-01-01T00:00:00Z"); got ${JSON.stringify(value)}.`
|
|
80613
|
+
);
|
|
80614
|
+
}
|
|
80615
|
+
return value;
|
|
80616
|
+
}
|
|
80617
|
+
function parseHistoryArgs(args) {
|
|
80618
|
+
const from = normalizeIsoDate(args?.from, "from");
|
|
80619
|
+
const to = normalizeIsoDate(args?.to, "to");
|
|
80620
|
+
if (from && to && Date.parse(from) > Date.parse(to)) {
|
|
80621
|
+
throw new ArchiveInputError(
|
|
80622
|
+
`from (${from}) is after to (${to}) \u2014 swap the bounds.`
|
|
80623
|
+
);
|
|
80624
|
+
}
|
|
80625
|
+
return {
|
|
80626
|
+
base_coin: normalizeBaseCoin(args?.base_coin),
|
|
80627
|
+
exchange: normalizeExchange(args?.exchange),
|
|
80628
|
+
from,
|
|
80629
|
+
to,
|
|
80630
|
+
limit: clampLimit3(args?.limit)
|
|
80631
|
+
};
|
|
80632
|
+
}
|
|
80633
|
+
function archiveQueryError(context, error) {
|
|
80634
|
+
const detail = error.message ?? "unknown error";
|
|
80635
|
+
const code = error.code ? ` (code ${error.code})` : "";
|
|
80636
|
+
if (error.code === "57014" || /timeout/i.test(detail)) {
|
|
80637
|
+
return new Error(
|
|
80638
|
+
`Hive Archive query timed out while ${context}${code}. Narrow the time range with from/to or lower limit, then retry.`
|
|
80639
|
+
);
|
|
80640
|
+
}
|
|
80641
|
+
if (/jwt|apikey|api key|authorization/i.test(detail)) {
|
|
80642
|
+
return new Error(
|
|
80643
|
+
`Hive Archive rejected the configured credentials while ${context}${code}: ${detail}. The archive key needs rotation \u2014 this is a server configuration issue, not a query problem.`
|
|
80644
|
+
);
|
|
80645
|
+
}
|
|
80646
|
+
return new Error(
|
|
80647
|
+
`Hive Archive query failed while ${context}${code}: ${detail}. Retry once; if it persists the archive is degraded \u2014 use live derivatives tools instead.`
|
|
80648
|
+
);
|
|
80649
|
+
}
|
|
80650
|
+
var HISTORY_TABLES = {
|
|
80651
|
+
funding: {
|
|
80652
|
+
table: "funding_rates_history",
|
|
80653
|
+
columns: "exchange,symbol,base_coin,rate,interval_hours,settled_at",
|
|
80654
|
+
tsColumn: "settled_at"
|
|
80655
|
+
},
|
|
80656
|
+
open_interest: {
|
|
80657
|
+
table: "futures_open_interest",
|
|
80658
|
+
columns: "exchange,base_coin,symbol,open_interest_value,timestamp",
|
|
80659
|
+
tsColumn: "timestamp"
|
|
80660
|
+
},
|
|
80661
|
+
basis: {
|
|
80662
|
+
table: "futures_basis",
|
|
80663
|
+
columns: "exchange,base_coin,spot_price,futures_price,basis_rate,annualized_basis,timestamp",
|
|
80664
|
+
tsColumn: "timestamp"
|
|
80665
|
+
},
|
|
80666
|
+
long_short: {
|
|
80667
|
+
table: "futures_long_short_ratio",
|
|
80668
|
+
columns: "exchange,base_coin,symbol,long_ratio,short_ratio,timestamp",
|
|
80669
|
+
tsColumn: "timestamp"
|
|
80670
|
+
},
|
|
80671
|
+
liquidations: {
|
|
80672
|
+
table: "futures_liquidations",
|
|
80673
|
+
columns: "exchange,base_coin,symbol,side,quantity,price,timestamp",
|
|
80674
|
+
tsColumn: "timestamp"
|
|
80675
|
+
}
|
|
80676
|
+
};
|
|
80677
|
+
async function fetchHistoryRows(client2, dataset, filters) {
|
|
80678
|
+
const spec = HISTORY_TABLES[dataset];
|
|
80679
|
+
let query = client2.from(spec.table).select(spec.columns).eq("base_coin", filters.base_coin);
|
|
80680
|
+
if (filters.exchange) {
|
|
80681
|
+
query = query.ilike("exchange", escapeLikeLiteral(filters.exchange));
|
|
80682
|
+
}
|
|
80683
|
+
if (filters.from) query = query.gte(spec.tsColumn, filters.from);
|
|
80684
|
+
if (filters.to) query = query.lte(spec.tsColumn, filters.to);
|
|
80685
|
+
const { data, error } = await query.order(spec.tsColumn, { ascending: false }).limit(filters.limit).abortSignal(AbortSignal.timeout(QUERY_TIMEOUT_MS));
|
|
80686
|
+
if (error) {
|
|
80687
|
+
throw archiveQueryError(
|
|
80688
|
+
`reading ${spec.table} for ${filters.base_coin}`,
|
|
80689
|
+
error
|
|
80690
|
+
);
|
|
80691
|
+
}
|
|
80692
|
+
return data ?? [];
|
|
80693
|
+
}
|
|
80694
|
+
var coverageCache = /* @__PURE__ */ new Map();
|
|
80695
|
+
async function probeTimestamp(client2, spec, direction, baseCoin) {
|
|
80696
|
+
let query = client2.from(spec.table).select(spec.tsColumn);
|
|
80697
|
+
if (baseCoin) query = query.eq("base_coin", baseCoin);
|
|
80698
|
+
const { data, error } = await query.order(spec.tsColumn, { ascending: direction === "asc" }).limit(1).abortSignal(AbortSignal.timeout(QUERY_TIMEOUT_MS));
|
|
80699
|
+
if (error) {
|
|
80700
|
+
throw archiveQueryError(`probing ${spec.table} coverage`, error);
|
|
80701
|
+
}
|
|
80702
|
+
const row = (data ?? [])[0];
|
|
80703
|
+
const value = row?.[spec.tsColumn];
|
|
80704
|
+
return typeof value === "string" ? value : null;
|
|
80705
|
+
}
|
|
80706
|
+
async function listFundingVenues(client2) {
|
|
80707
|
+
const venues = [];
|
|
80708
|
+
let cursor = null;
|
|
80709
|
+
for (let step2 = 0; step2 < VENUE_SCAN_MAX_STEPS; step2++) {
|
|
80710
|
+
let query = client2.from("funding_rates_history").select("exchange");
|
|
80711
|
+
if (cursor !== null) query = query.gt("exchange", cursor);
|
|
80712
|
+
const { data, error } = await query.order("exchange", { ascending: true }).limit(1).abortSignal(AbortSignal.timeout(QUERY_TIMEOUT_MS));
|
|
80713
|
+
if (error) throw archiveQueryError("enumerating archive venues", error);
|
|
80714
|
+
const row = (data ?? [])[0];
|
|
80715
|
+
if (!row || typeof row.exchange !== "string") {
|
|
80716
|
+
return { venues, truncated: false };
|
|
80717
|
+
}
|
|
80718
|
+
venues.push(row.exchange);
|
|
80719
|
+
cursor = row.exchange;
|
|
80720
|
+
}
|
|
80721
|
+
return { venues, truncated: true };
|
|
80722
|
+
}
|
|
80723
|
+
async function fetchCoverage(client2, probeBaseCoin) {
|
|
80724
|
+
const cached = coverageCache.get(probeBaseCoin);
|
|
80725
|
+
if (cached && Date.now() - cached.at < COVERAGE_CACHE_TTL_MS) {
|
|
80726
|
+
return { coverage: cached.payload, cache: "hit" };
|
|
80727
|
+
}
|
|
80728
|
+
const futuresDatasets = [
|
|
80729
|
+
"open_interest",
|
|
80730
|
+
"basis",
|
|
80731
|
+
"long_short",
|
|
80732
|
+
"liquidations"
|
|
80733
|
+
];
|
|
80734
|
+
const [venueScan, fundingEarliest, fundingLatest, ...futuresExtremes] = await Promise.all([
|
|
80735
|
+
listFundingVenues(client2),
|
|
80736
|
+
probeTimestamp(client2, HISTORY_TABLES.funding, "asc", null),
|
|
80737
|
+
probeTimestamp(client2, HISTORY_TABLES.funding, "desc", null),
|
|
80738
|
+
...futuresDatasets.flatMap((dataset) => [
|
|
80739
|
+
probeTimestamp(client2, HISTORY_TABLES[dataset], "asc", probeBaseCoin),
|
|
80740
|
+
probeTimestamp(client2, HISTORY_TABLES[dataset], "desc", probeBaseCoin)
|
|
80741
|
+
])
|
|
80742
|
+
]);
|
|
80743
|
+
const datasets = [
|
|
80744
|
+
{
|
|
80745
|
+
dataset: "funding",
|
|
80746
|
+
table: HISTORY_TABLES.funding.table,
|
|
80747
|
+
scope: "all_coins",
|
|
80748
|
+
earliest_at: fundingEarliest,
|
|
80749
|
+
latest_at: fundingLatest
|
|
80750
|
+
},
|
|
80751
|
+
...futuresDatasets.map((dataset, index) => {
|
|
80752
|
+
return {
|
|
80753
|
+
dataset,
|
|
80754
|
+
table: HISTORY_TABLES[dataset].table,
|
|
80755
|
+
scope: "probe_coin",
|
|
80756
|
+
earliest_at: futuresExtremes[index * 2],
|
|
80757
|
+
latest_at: futuresExtremes[index * 2 + 1]
|
|
80758
|
+
};
|
|
80759
|
+
})
|
|
80760
|
+
];
|
|
80761
|
+
const coverage = {
|
|
80762
|
+
venues: venueScan.venues,
|
|
80763
|
+
venue_count: venueScan.venues.length,
|
|
80764
|
+
venues_truncated: venueScan.truncated,
|
|
80765
|
+
probe_base_coin: probeBaseCoin,
|
|
80766
|
+
datasets,
|
|
80767
|
+
note: `${ARCHIVE_SINCE_NOTE} (funding settlements). Futures datasets retain less history and their extremes are probed for probe_base_coin via the (base_coin, timestamp) indexes \u2014 pass base_coin to probe another asset. Venues are the all-time distinct funding exchanges.`
|
|
80768
|
+
};
|
|
80769
|
+
if (coverageCache.size >= COVERAGE_CACHE_MAX_ENTRIES) {
|
|
80770
|
+
const oldestKey = coverageCache.keys().next().value;
|
|
80771
|
+
if (oldestKey !== void 0) coverageCache.delete(oldestKey);
|
|
80772
|
+
}
|
|
80773
|
+
coverageCache.set(probeBaseCoin, { at: Date.now(), payload: coverage });
|
|
80774
|
+
return { coverage, cache: "miss" };
|
|
80775
|
+
}
|
|
80776
|
+
|
|
80777
|
+
// src/tools/archive/tools.ts
|
|
80778
|
+
var ARCHIVE_READ_ANNOTATIONS = {
|
|
80779
|
+
readOnlyHint: true,
|
|
80780
|
+
destructiveHint: false,
|
|
80781
|
+
idempotentHint: true,
|
|
80782
|
+
openWorldHint: false
|
|
80783
|
+
};
|
|
80784
|
+
var PROVIDER_NAME = "Hive Archive";
|
|
80785
|
+
var SHARED_ARGS_DOC = `Args:
|
|
80786
|
+
- base_coin (string, required): base asset ticker, e.g. "BTC", "ETH", "SOL" (case-insensitive; uppercased server-side).
|
|
80787
|
+
- exchange (string, optional): one venue, matched case-insensitively (e.g. "Binance", "okx"). Omit for all venues.
|
|
80788
|
+
- from (string, optional): ISO 8601 inclusive lower bound.
|
|
80789
|
+
- to (string, optional): ISO 8601 inclusive upper bound.
|
|
80790
|
+
- limit (integer, optional): max rows (1-${MAX_LIMIT2}, default ${DEFAULT_LIMIT2}), newest first.`;
|
|
80791
|
+
var SHARED_RETURNS_DOC = `Bounded JSON envelope: { "provider", "dataset", "runtime_status", "fetched_at", "note", "filters", "order", "returnedCount", "may_have_more", "rows" }. Rows are newest-first; may_have_more true means the limit was hit \u2014 page older data by setting "to" below the oldest returned timestamp.`;
|
|
80792
|
+
function historyInputSchema(baseCoinDescription) {
|
|
80793
|
+
return {
|
|
80794
|
+
type: "object",
|
|
80795
|
+
properties: {
|
|
80796
|
+
base_coin: {
|
|
80797
|
+
type: "string",
|
|
80798
|
+
description: baseCoinDescription
|
|
80799
|
+
},
|
|
80800
|
+
exchange: {
|
|
80801
|
+
type: "string",
|
|
80802
|
+
description: 'Venue filter, case-insensitive exact match (e.g. "Binance", "Hyperliquid"). Call archive_get_coverage for the venue list.'
|
|
80803
|
+
},
|
|
80804
|
+
from: {
|
|
80805
|
+
type: "string",
|
|
80806
|
+
description: 'Inclusive ISO 8601 lower time bound, e.g. "2024-01-01T00:00:00Z".'
|
|
80807
|
+
},
|
|
80808
|
+
to: {
|
|
80809
|
+
type: "string",
|
|
80810
|
+
description: 'Inclusive ISO 8601 upper time bound, e.g. "2024-06-30T23:59:59Z".'
|
|
80811
|
+
},
|
|
80812
|
+
limit: {
|
|
80813
|
+
type: "integer",
|
|
80814
|
+
minimum: 1,
|
|
80815
|
+
maximum: MAX_LIMIT2,
|
|
80816
|
+
description: `Maximum rows to return (1-${MAX_LIMIT2}, default ${DEFAULT_LIMIT2}). Rows are ordered newest first.`
|
|
80817
|
+
}
|
|
80818
|
+
},
|
|
80819
|
+
required: ["base_coin"]
|
|
80820
|
+
};
|
|
80821
|
+
}
|
|
80822
|
+
function errorResult2(tool89, error) {
|
|
80823
|
+
if (error instanceof ArchiveInputError) {
|
|
80824
|
+
return asTextContentResult(
|
|
80825
|
+
{
|
|
80826
|
+
error: error.message,
|
|
80827
|
+
runtime_status: "invalid_input",
|
|
80828
|
+
tool: tool89
|
|
80829
|
+
},
|
|
80830
|
+
true
|
|
80831
|
+
);
|
|
80832
|
+
}
|
|
80833
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80834
|
+
return asTextContentResult(
|
|
80835
|
+
{
|
|
80836
|
+
error: message,
|
|
80837
|
+
runtime_status: classifyRuntimeStatusFromError(message),
|
|
80838
|
+
tool: tool89
|
|
80839
|
+
},
|
|
80840
|
+
true
|
|
80841
|
+
);
|
|
80842
|
+
}
|
|
80843
|
+
function missingCredentialsResult(tool89) {
|
|
80844
|
+
return asTextContentResult(
|
|
80845
|
+
{
|
|
80846
|
+
error: ARCHIVE_MISSING_CREDENTIALS_MESSAGE,
|
|
80847
|
+
runtime_status: "missing_key",
|
|
80848
|
+
tool: tool89
|
|
80849
|
+
},
|
|
80850
|
+
true
|
|
80851
|
+
);
|
|
80852
|
+
}
|
|
80853
|
+
function historyHandler(toolName, dataset, options) {
|
|
80854
|
+
return async (_client, args) => {
|
|
80855
|
+
const supabase = getArchiveClient();
|
|
80856
|
+
if (!supabase) return missingCredentialsResult(toolName);
|
|
80857
|
+
try {
|
|
80858
|
+
const filters = parseHistoryArgs(args);
|
|
80859
|
+
const rows = await fetchHistoryRows(supabase, dataset, filters);
|
|
80860
|
+
const mapped = options?.mapRow ? rows.map(options.mapRow) : rows;
|
|
80861
|
+
const spec = HISTORY_TABLES[dataset];
|
|
80862
|
+
return asTextContentResult({
|
|
80863
|
+
provider: PROVIDER_NAME,
|
|
80864
|
+
dataset: spec.table,
|
|
80865
|
+
runtime_status: "ok",
|
|
80866
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
80867
|
+
note: options?.extraNote ? `${ARCHIVE_SINCE_NOTE}. ${options.extraNote}` : ARCHIVE_SINCE_NOTE,
|
|
80868
|
+
filters,
|
|
80869
|
+
order: `${spec.tsColumn} desc`,
|
|
80870
|
+
returnedCount: mapped.length,
|
|
80871
|
+
may_have_more: mapped.length === filters.limit,
|
|
80872
|
+
rows: mapped
|
|
80873
|
+
});
|
|
80874
|
+
} catch (error) {
|
|
80875
|
+
return errorResult2(toolName, error);
|
|
80876
|
+
}
|
|
80877
|
+
};
|
|
80878
|
+
}
|
|
80879
|
+
function getAllArchiveEndpoints() {
|
|
80880
|
+
return [
|
|
80881
|
+
{
|
|
80882
|
+
metadata: {
|
|
80883
|
+
resource: "archive-funding-history",
|
|
80884
|
+
operation: "read",
|
|
80885
|
+
tags: ["archive", "derivatives", "funding", "history", "cross-venue"]
|
|
80886
|
+
},
|
|
80887
|
+
tool: {
|
|
80888
|
+
name: "archive_get_funding_history",
|
|
80889
|
+
description: `Get historical perp funding-rate SETTLEMENTS for one base coin across up to 33 CEX+DEX venues from the Hive Archive (${ARCHIVE_SINCE_NOTE}; 9.8M settlement rows). Each row is one settled funding event: exchange, symbol, base_coin, rate (raw per-interval fraction), interval_hours, settled_at \u2014 plus a derived funding_annualized computed honestly from that row's own interval (rate * 8760 / interval_hours; null when the interval is unknown, never a guessed 8h default).
|
|
80890
|
+
|
|
80891
|
+
${SHARED_ARGS_DOC}
|
|
80892
|
+
|
|
80893
|
+
Returns:
|
|
80894
|
+
${SHARED_RETURNS_DOC}
|
|
80895
|
+
|
|
80896
|
+
Examples:
|
|
80897
|
+
- { "base_coin": "BTC", "from": "2024-01-01T00:00:00Z", "limit": 200 } \u2014 BTC settlements across venues since 2024.
|
|
80898
|
+
- { "base_coin": "ETH", "exchange": "Hyperliquid", "limit": 100 } \u2014 recent ETH settlements on one venue.
|
|
80899
|
+
|
|
80900
|
+
Use when:
|
|
80901
|
+
- "BTC funding history across exchanges since 2024" \u2014 long-window, cross-venue funding studies
|
|
80902
|
+
- You are computing realized carry, funding regime shifts, or venue funding divergence over time
|
|
80903
|
+
- You need settlements older than what live venue APIs retain
|
|
80904
|
+
|
|
80905
|
+
Don't use when:
|
|
80906
|
+
- You need CURRENT funding (use ccxt_fetch_funding_rates_multi or hyperliquid_get_predicted_fundings)
|
|
80907
|
+
- You need one venue's own raw history endpoint (use get_funding_rate_history or hyperliquid_get_funding_history)
|
|
80908
|
+
- You need tokenized-stock/RWA perp funding (use rwa_get_funding_rates)`,
|
|
80909
|
+
inputSchema: historyInputSchema(
|
|
80910
|
+
'Base asset ticker to fetch settlements for, e.g. "BTC". Required \u2014 the archive is indexed by base coin.'
|
|
80911
|
+
),
|
|
80912
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
80913
|
+
},
|
|
80914
|
+
handler: historyHandler("archive_get_funding_history", "funding", {
|
|
80915
|
+
mapRow: (row) => ({
|
|
80916
|
+
...row,
|
|
80917
|
+
funding_annualized: annualizeRate(
|
|
80918
|
+
Number(row.rate),
|
|
80919
|
+
Number(row.interval_hours)
|
|
80920
|
+
)
|
|
80921
|
+
})
|
|
80922
|
+
})
|
|
80923
|
+
},
|
|
80924
|
+
{
|
|
80925
|
+
metadata: {
|
|
80926
|
+
resource: "archive-open-interest-history",
|
|
80927
|
+
operation: "read",
|
|
80928
|
+
tags: [
|
|
80929
|
+
"archive",
|
|
80930
|
+
"derivatives",
|
|
80931
|
+
"open-interest",
|
|
80932
|
+
"history",
|
|
80933
|
+
"cross-venue"
|
|
80934
|
+
]
|
|
80935
|
+
},
|
|
80936
|
+
tool: {
|
|
80937
|
+
name: "archive_get_open_interest_history",
|
|
80938
|
+
description: `Get historical perp open-interest snapshots for one base coin across venues from the Hive Archive (2.1M rows). Each row: exchange, base_coin, symbol (venue-native contract), open_interest_value (venue-reported OI as collected), timestamp. Retention is shorter than funding \u2014 call archive_get_coverage for exact per-dataset depth.
|
|
80939
|
+
|
|
80940
|
+
${SHARED_ARGS_DOC}
|
|
80941
|
+
|
|
80942
|
+
Returns:
|
|
80943
|
+
${SHARED_RETURNS_DOC}
|
|
80944
|
+
|
|
80945
|
+
Examples:
|
|
80946
|
+
- { "base_coin": "ETH", "from": "2026-07-18T00:00:00Z", "to": "2026-08-18T00:00:00Z" } \u2014 ETH OI over the last month.
|
|
80947
|
+
- { "base_coin": "BTC", "exchange": "OKX", "limit": 500 } \u2014 recent BTC OI snapshots on OKX.
|
|
80948
|
+
|
|
80949
|
+
Use when:
|
|
80950
|
+
- "ETH open interest history last month" \u2014 OI trend and buildup/unwind analysis over time
|
|
80951
|
+
- You are correlating OI changes with funding or liquidation events across venues
|
|
80952
|
+
|
|
80953
|
+
Don't use when:
|
|
80954
|
+
- You need CURRENT open interest (use ccxt_fetch_open_interest_multi or get_open_interest)
|
|
80955
|
+
- You need one venue's own OI history endpoint shape (use get_open_interest_history via CCXT)`,
|
|
80956
|
+
inputSchema: historyInputSchema(
|
|
80957
|
+
'Base asset ticker to fetch open-interest snapshots for, e.g. "ETH". Required.'
|
|
80958
|
+
),
|
|
80959
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
80960
|
+
},
|
|
80961
|
+
handler: historyHandler(
|
|
80962
|
+
"archive_get_open_interest_history",
|
|
80963
|
+
"open_interest",
|
|
80964
|
+
{
|
|
80965
|
+
extraNote: "open_interest_value is the venue-reported figure as collected (units are venue-native)."
|
|
80966
|
+
}
|
|
80967
|
+
)
|
|
80968
|
+
},
|
|
80969
|
+
{
|
|
80970
|
+
metadata: {
|
|
80971
|
+
resource: "archive-basis-history",
|
|
80972
|
+
operation: "read",
|
|
80973
|
+
tags: ["archive", "derivatives", "basis", "history", "cross-venue"]
|
|
80974
|
+
},
|
|
80975
|
+
tool: {
|
|
80976
|
+
name: "archive_get_basis_history",
|
|
80977
|
+
description: `Get historical spot-vs-futures BASIS snapshots for one base coin across venues from the Hive Archive. Each row: exchange, base_coin, spot_price, futures_price, basis_rate (fraction), annualized_basis, timestamp \u2014 the basis fields were computed at collection time from the venue's own spot and futures quotes.
|
|
80978
|
+
|
|
80979
|
+
${SHARED_ARGS_DOC}
|
|
80980
|
+
|
|
80981
|
+
Returns:
|
|
80982
|
+
${SHARED_RETURNS_DOC}
|
|
80983
|
+
|
|
80984
|
+
Examples:
|
|
80985
|
+
- { "base_coin": "BTC", "from": "2026-05-01T00:00:00Z", "limit": 300 } \u2014 BTC basis regime since May.
|
|
80986
|
+
- { "base_coin": "SOL", "exchange": "Binance" } \u2014 recent SOL basis snapshots on Binance.
|
|
80987
|
+
|
|
80988
|
+
Use when:
|
|
80989
|
+
- You are studying cash-and-carry yield history or basis blowouts/compressions over time
|
|
80990
|
+
- You need basis context around a historical funding or liquidation event
|
|
80991
|
+
|
|
80992
|
+
Don't use when:
|
|
80993
|
+
- You need LIVE basis or dated-futures term structure right now (use ccxt_ mark/index tools or hyperliquid tools)
|
|
80994
|
+
- You need current cross-venue funding instead of basis (use archive_get_funding_history for history, ccxt_fetch_funding_rates_multi for now)`,
|
|
80995
|
+
inputSchema: historyInputSchema(
|
|
80996
|
+
'Base asset ticker to fetch basis snapshots for, e.g. "BTC". Required.'
|
|
80997
|
+
),
|
|
80998
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
80999
|
+
},
|
|
81000
|
+
handler: historyHandler("archive_get_basis_history", "basis")
|
|
81001
|
+
},
|
|
81002
|
+
{
|
|
81003
|
+
metadata: {
|
|
81004
|
+
resource: "archive-long-short-history",
|
|
81005
|
+
operation: "read",
|
|
81006
|
+
tags: [
|
|
81007
|
+
"archive",
|
|
81008
|
+
"derivatives",
|
|
81009
|
+
"long-short-ratio",
|
|
81010
|
+
"positioning",
|
|
81011
|
+
"history"
|
|
81012
|
+
]
|
|
81013
|
+
},
|
|
81014
|
+
tool: {
|
|
81015
|
+
name: "archive_get_long_short_history",
|
|
81016
|
+
description: `Get historical long/short-ratio snapshots for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, long_ratio, short_ratio (fractions summing to ~1), timestamp. The exchange column carries BOTH the all-account series (e.g. "Binance") and top-trader series (e.g. "Binance_top_trader") as separate venue values \u2014 filter with the exchange arg to pick one.
|
|
81017
|
+
|
|
81018
|
+
${SHARED_ARGS_DOC}
|
|
81019
|
+
|
|
81020
|
+
Returns:
|
|
81021
|
+
${SHARED_RETURNS_DOC}
|
|
81022
|
+
|
|
81023
|
+
Examples:
|
|
81024
|
+
- { "base_coin": "BTC", "exchange": "Binance_top_trader", "limit": 200 } \u2014 top-trader positioning history.
|
|
81025
|
+
- { "base_coin": "ETH", "from": "2026-06-01T00:00:00Z" } \u2014 all ETH positioning series since June.
|
|
81026
|
+
|
|
81027
|
+
Use when:
|
|
81028
|
+
- You are studying positioning shifts (crowded longs/shorts) around historical price moves
|
|
81029
|
+
- You need retail-vs-top-trader positioning divergence over time
|
|
81030
|
+
|
|
81031
|
+
Don't use when:
|
|
81032
|
+
- You need the CURRENT ratio (use get_long_short_ratio or ccxt_fetch_long_short_ratio_history for venue-native reads)
|
|
81033
|
+
- You need funding or OI history (use the dedicated archive_ tools)`,
|
|
81034
|
+
inputSchema: historyInputSchema(
|
|
81035
|
+
'Base asset ticker to fetch long/short snapshots for, e.g. "BTC". Required.'
|
|
81036
|
+
),
|
|
81037
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
81038
|
+
},
|
|
81039
|
+
handler: historyHandler("archive_get_long_short_history", "long_short")
|
|
81040
|
+
},
|
|
81041
|
+
{
|
|
81042
|
+
metadata: {
|
|
81043
|
+
resource: "archive-liquidations-history",
|
|
81044
|
+
operation: "read",
|
|
81045
|
+
tags: ["archive", "derivatives", "liquidations", "history"]
|
|
81046
|
+
},
|
|
81047
|
+
tool: {
|
|
81048
|
+
name: "archive_get_liquidations_history",
|
|
81049
|
+
description: `Get historical liquidation events for one base coin from the Hive Archive. Each row: exchange, base_coin, symbol, side ("long" = longs liquidated, "short" = shorts liquidated), quantity, price, timestamp. quantity and price are the venue-reported values as collected \u2014 units are venue-native, so compare quantities only within one exchange.
|
|
81050
|
+
|
|
81051
|
+
${SHARED_ARGS_DOC}
|
|
81052
|
+
|
|
81053
|
+
Returns:
|
|
81054
|
+
${SHARED_RETURNS_DOC}
|
|
81055
|
+
|
|
81056
|
+
Examples:
|
|
81057
|
+
- { "base_coin": "BTC", "from": "2026-08-01T00:00:00Z", "limit": 500 } \u2014 August BTC liquidation prints.
|
|
81058
|
+
- { "base_coin": "ETH", "exchange": "Binance", "limit": 100 } \u2014 recent ETH liquidations on Binance.
|
|
81059
|
+
|
|
81060
|
+
Use when:
|
|
81061
|
+
- You are reconstructing a liquidation cascade or mapping historical liquidation clusters by price
|
|
81062
|
+
- You need liquidation context around a historical funding/OI/basis move
|
|
81063
|
+
|
|
81064
|
+
Don't use when:
|
|
81065
|
+
- You need LIVE liquidations (use get_liquidations via CCXT)
|
|
81066
|
+
- You need aggregate liquidation totals per venue right now (live venue tools)`,
|
|
81067
|
+
inputSchema: historyInputSchema(
|
|
81068
|
+
'Base asset ticker to fetch liquidation events for, e.g. "BTC". Required.'
|
|
81069
|
+
),
|
|
81070
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
81071
|
+
},
|
|
81072
|
+
handler: historyHandler(
|
|
81073
|
+
"archive_get_liquidations_history",
|
|
81074
|
+
"liquidations"
|
|
81075
|
+
)
|
|
81076
|
+
},
|
|
81077
|
+
{
|
|
81078
|
+
metadata: {
|
|
81079
|
+
resource: "archive-coverage",
|
|
81080
|
+
operation: "read",
|
|
81081
|
+
tags: ["archive", "derivatives", "coverage", "discovery"]
|
|
81082
|
+
},
|
|
81083
|
+
tool: {
|
|
81084
|
+
name: "archive_get_coverage",
|
|
81085
|
+
description: `Discover what the Hive Archive holds: the all-time venue list (33 CEX+DEX funding venues) and earliest/latest timestamps per dataset (funding settlements, open interest, basis, long/short ratio, liquidations). Funding extremes are global; futures-dataset extremes are probed for one base coin (default "BTC" \u2014 verified to match the global extremes) because those tables are indexed by (base_coin, timestamp). Results are cached in-process for 10 minutes.
|
|
81086
|
+
|
|
81087
|
+
Args:
|
|
81088
|
+
- base_coin (string, optional): probe coin for the futures datasets (default "BTC"). Pass another ticker to check archive depth for that asset.
|
|
81089
|
+
|
|
81090
|
+
Returns:
|
|
81091
|
+
JSON object: { "provider", "runtime_status", "fetched_at", "cache", "venues": [...], "venue_count", "venues_truncated", "probe_base_coin", "datasets": [{ "dataset", "table", "scope", "earliest_at", "latest_at" }], "note" }.
|
|
81092
|
+
|
|
81093
|
+
Examples:
|
|
81094
|
+
- {} \u2014 full coverage picture with BTC probes.
|
|
81095
|
+
- { "base_coin": "DOGE" } \u2014 how far back DOGE futures datasets go.
|
|
81096
|
+
|
|
81097
|
+
Use when:
|
|
81098
|
+
- Before a history query, to learn venue names and how far back each dataset reaches
|
|
81099
|
+
- "Which exchanges does the funding archive cover?" / "since when is there OI history?"
|
|
81100
|
+
|
|
81101
|
+
Don't use when:
|
|
81102
|
+
- You already know the venue and range (call the history tool directly)
|
|
81103
|
+
- You need live venue universes (use ccxt_fetch_markets or venue tools)`,
|
|
81104
|
+
inputSchema: {
|
|
81105
|
+
type: "object",
|
|
81106
|
+
properties: {
|
|
81107
|
+
base_coin: {
|
|
81108
|
+
type: "string",
|
|
81109
|
+
description: 'Probe coin for futures-dataset extremes (default "BTC").'
|
|
81110
|
+
}
|
|
81111
|
+
}
|
|
81112
|
+
},
|
|
81113
|
+
annotations: ARCHIVE_READ_ANNOTATIONS
|
|
81114
|
+
},
|
|
81115
|
+
handler: async (_client, args) => {
|
|
81116
|
+
const toolName = "archive_get_coverage";
|
|
81117
|
+
const supabase = getArchiveClient();
|
|
81118
|
+
if (!supabase) return missingCredentialsResult(toolName);
|
|
81119
|
+
try {
|
|
81120
|
+
const probe = typeof args?.base_coin === "string" && args.base_coin.trim() !== "" ? args.base_coin.trim().toUpperCase() : "BTC";
|
|
81121
|
+
const { coverage, cache } = await fetchCoverage(supabase, probe);
|
|
81122
|
+
return asTextContentResult({
|
|
81123
|
+
provider: PROVIDER_NAME,
|
|
81124
|
+
runtime_status: "ok",
|
|
81125
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
81126
|
+
cache,
|
|
81127
|
+
...coverage
|
|
81128
|
+
});
|
|
81129
|
+
} catch (error) {
|
|
81130
|
+
return errorResult2(toolName, error);
|
|
81131
|
+
}
|
|
81132
|
+
}
|
|
81133
|
+
}
|
|
81134
|
+
];
|
|
81135
|
+
}
|
|
81136
|
+
|
|
80543
81137
|
// src/tools/fetch/allowlist.ts
|
|
80544
81138
|
var KB = 1024;
|
|
80545
81139
|
var FETCH_ALLOWLIST = [
|
|
@@ -81843,9 +82437,90 @@ function getOutputSchemaForTool(toolName) {
|
|
|
81843
82437
|
}
|
|
81844
82438
|
return GENERIC_TOOL_OUTPUT_SCHEMA;
|
|
81845
82439
|
}
|
|
82440
|
+
var SOURCE_RECEIPT_SCHEMA = {
|
|
82441
|
+
type: "object",
|
|
82442
|
+
additionalProperties: true,
|
|
82443
|
+
required: ["provider", "runtime_status", "fetched_at"],
|
|
82444
|
+
properties: {
|
|
82445
|
+
provider: { type: "string" },
|
|
82446
|
+
tool: { type: "string" },
|
|
82447
|
+
runtime_status: { type: "string", enum: [...RUNTIME_STATUSES] },
|
|
82448
|
+
fetched_at: { type: "string", format: "date-time" },
|
|
82449
|
+
cache_status: { type: "string" },
|
|
82450
|
+
source: { type: "string" },
|
|
82451
|
+
receipt_id: { type: "string" }
|
|
82452
|
+
},
|
|
82453
|
+
description: "Provenance for this observation: which upstream served it, its runtime status, and when it was fetched."
|
|
82454
|
+
};
|
|
82455
|
+
var RECEIPT_REF = {
|
|
82456
|
+
type: "object",
|
|
82457
|
+
additionalProperties: true,
|
|
82458
|
+
required: ["provider", "runtime_status", "fetched_at"],
|
|
82459
|
+
properties: {
|
|
82460
|
+
provider: { type: "string" },
|
|
82461
|
+
runtime_status: { type: "string", enum: [...RUNTIME_STATUSES] },
|
|
82462
|
+
fetched_at: { type: "string", format: "date-time" }
|
|
82463
|
+
}
|
|
82464
|
+
};
|
|
82465
|
+
var GET_TOKEN_PRICE_OUTPUT_SCHEMA = {
|
|
82466
|
+
type: "object",
|
|
82467
|
+
additionalProperties: true,
|
|
82468
|
+
required: ["source_receipt"],
|
|
82469
|
+
properties: {
|
|
82470
|
+
source_receipt: RECEIPT_REF,
|
|
82471
|
+
token: { type: "string" },
|
|
82472
|
+
vs_currency: { type: "string" },
|
|
82473
|
+
price: { type: "number" },
|
|
82474
|
+
market_cap: { type: "number" },
|
|
82475
|
+
change_24h_percent: { type: "number" }
|
|
82476
|
+
}
|
|
82477
|
+
};
|
|
82478
|
+
var CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA = {
|
|
82479
|
+
type: "object",
|
|
82480
|
+
additionalProperties: true,
|
|
82481
|
+
required: ["source_receipt"],
|
|
82482
|
+
properties: {
|
|
82483
|
+
source_receipt: RECEIPT_REF,
|
|
82484
|
+
address: { type: "string" },
|
|
82485
|
+
chain: { type: "string" },
|
|
82486
|
+
risk_flags: {
|
|
82487
|
+
type: "object",
|
|
82488
|
+
additionalProperties: true,
|
|
82489
|
+
description: "Raw provider risk datapoints; '1' means flagged. A missing key means the provider did not report that signal, not that it passed."
|
|
82490
|
+
},
|
|
82491
|
+
holder_count: {},
|
|
82492
|
+
token_name: { type: "string" },
|
|
82493
|
+
note: { type: "string" }
|
|
82494
|
+
}
|
|
82495
|
+
};
|
|
82496
|
+
var GET_WALLET_PORTFOLIO_OUTPUT_SCHEMA = {
|
|
82497
|
+
type: "object",
|
|
82498
|
+
additionalProperties: true,
|
|
82499
|
+
required: ["source_receipt"],
|
|
82500
|
+
properties: {
|
|
82501
|
+
source_receipt: RECEIPT_REF,
|
|
82502
|
+
address: { type: "string" },
|
|
82503
|
+
chain: { type: "string" },
|
|
82504
|
+
token_count: {
|
|
82505
|
+
type: "number",
|
|
82506
|
+
description: "Total holdings found, which may exceed tokens.length."
|
|
82507
|
+
},
|
|
82508
|
+
tokens: {
|
|
82509
|
+
type: "array",
|
|
82510
|
+
items: { type: "object", additionalProperties: true },
|
|
82511
|
+
description: "Holdings; truncated in concise mode when note is present."
|
|
82512
|
+
},
|
|
82513
|
+
note: { type: "string" }
|
|
82514
|
+
}
|
|
82515
|
+
};
|
|
82516
|
+
var HERO_TOOL_OUTPUT_SCHEMAS = {
|
|
82517
|
+
get_token_price: GET_TOKEN_PRICE_OUTPUT_SCHEMA,
|
|
82518
|
+
check_token_safety: CHECK_TOKEN_SAFETY_OUTPUT_SCHEMA,
|
|
82519
|
+
get_wallet_portfolio: GET_WALLET_PORTFOLIO_OUTPUT_SCHEMA
|
|
82520
|
+
};
|
|
81846
82521
|
|
|
81847
82522
|
// src/utils/analytics.ts
|
|
81848
|
-
import { randomUUID } from "crypto";
|
|
82523
|
+
import { createHash as createHash3, randomUUID } from "crypto";
|
|
81849
82524
|
var GA_ENDPOINT = "https://www.google-analytics.com/mp/collect";
|
|
81850
82525
|
function getMeasurementId() {
|
|
81851
82526
|
return getEnvVar("GA_MEASUREMENT_ID");
|
|
@@ -81876,7 +82551,9 @@ async function sendEvents(clientId, events) {
|
|
|
81876
82551
|
}
|
|
81877
82552
|
}
|
|
81878
82553
|
function deriveClientId(ip, userAgent, userId) {
|
|
81879
|
-
if (userId)
|
|
82554
|
+
if (userId) {
|
|
82555
|
+
return `u_${createHash3("sha256").update(userId).digest("hex").slice(0, 32)}`;
|
|
82556
|
+
}
|
|
81880
82557
|
const raw = `${ip || "unknown"}_${userAgent || "unknown"}`;
|
|
81881
82558
|
let hash = 0;
|
|
81882
82559
|
for (let i = 0; i < raw.length; i++) {
|
|
@@ -82020,24 +82697,32 @@ async function closeRedisClient() {
|
|
|
82020
82697
|
current.disconnect();
|
|
82021
82698
|
}
|
|
82022
82699
|
}
|
|
82700
|
+
var DEGRADED_AFTER_ATTEMPTS = 3;
|
|
82701
|
+
var MAX_RECONNECT_DELAY_MS = 3e3;
|
|
82702
|
+
function redisRetryStrategy(times) {
|
|
82703
|
+
if (times === DEGRADED_AFTER_ATTEMPTS) {
|
|
82704
|
+
secureLogger.warn(
|
|
82705
|
+
`[redis] Connection failed after ${DEGRADED_AFTER_ATTEMPTS} retries, falling back to in-memory while reconnection continues`,
|
|
82706
|
+
{}
|
|
82707
|
+
);
|
|
82708
|
+
available = false;
|
|
82709
|
+
}
|
|
82710
|
+
return Math.min(times * 100, MAX_RECONNECT_DELAY_MS);
|
|
82711
|
+
}
|
|
82712
|
+
function redisClientOptions() {
|
|
82713
|
+
return {
|
|
82714
|
+
maxRetriesPerRequest: 3,
|
|
82715
|
+
retryStrategy: redisRetryStrategy,
|
|
82716
|
+
lazyConnect: true
|
|
82717
|
+
};
|
|
82718
|
+
}
|
|
82023
82719
|
var redisUrl = getEnvVar("REDIS_URL");
|
|
82720
|
+
function isRedisConfigured() {
|
|
82721
|
+
return Boolean(redisUrl);
|
|
82722
|
+
}
|
|
82024
82723
|
if (redisUrl) {
|
|
82025
82724
|
try {
|
|
82026
|
-
client = new Redis(redisUrl,
|
|
82027
|
-
maxRetriesPerRequest: 3,
|
|
82028
|
-
retryStrategy: (times) => {
|
|
82029
|
-
if (times > 3) {
|
|
82030
|
-
secureLogger.warn(
|
|
82031
|
-
"[redis] Connection failed after 3 retries, falling back to in-memory",
|
|
82032
|
-
{}
|
|
82033
|
-
);
|
|
82034
|
-
available = false;
|
|
82035
|
-
return null;
|
|
82036
|
-
}
|
|
82037
|
-
return Math.min(times * 100, 1e3);
|
|
82038
|
-
},
|
|
82039
|
-
lazyConnect: true
|
|
82040
|
-
});
|
|
82725
|
+
client = new Redis(redisUrl, redisClientOptions());
|
|
82041
82726
|
client.connect().then(() => {
|
|
82042
82727
|
available = true;
|
|
82043
82728
|
logRedisInfo("[redis] Connected successfully");
|
|
@@ -82313,6 +82998,24 @@ var PROVIDER_CATALOG = [
|
|
|
82313
82998
|
alwaysAvailable: true,
|
|
82314
82999
|
matches: (toolName, metadata93) => matchesProvider("hyperliquid", toolName, metadata93)
|
|
82315
83000
|
},
|
|
83001
|
+
{
|
|
83002
|
+
// The Hive-native hive_archive_monitor tool contains the "_archive_"
|
|
83003
|
+
// infix this matcher would accept; the "hive" entry is listed first and
|
|
83004
|
+
// catalog order decides classification, so hive_ tools never land here.
|
|
83005
|
+
id: "archive",
|
|
83006
|
+
name: "Hive Archive",
|
|
83007
|
+
prefix: "archive_",
|
|
83008
|
+
type: "Cross-venue crypto derivatives history since 2023-11 (33 CEX+DEX venues): funding-rate settlements, open interest, basis, long/short ratio, and liquidations from Hive's own archive store",
|
|
83009
|
+
clientStrategy: "internal",
|
|
83010
|
+
credentialRequirement: {
|
|
83011
|
+
label: "SHARPE_ARCHIVE_SUPABASE_URL and SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY",
|
|
83012
|
+
isConfigured: () => hasAllEnvVars(
|
|
83013
|
+
"SHARPE_ARCHIVE_SUPABASE_URL",
|
|
83014
|
+
"SHARPE_ARCHIVE_SUPABASE_SERVICE_ROLE_KEY"
|
|
83015
|
+
)
|
|
83016
|
+
},
|
|
83017
|
+
matches: (toolName, metadata93) => matchesProvider("archive", toolName, metadata93)
|
|
83018
|
+
},
|
|
82316
83019
|
{
|
|
82317
83020
|
id: "fetch",
|
|
82318
83021
|
name: "Open Data Fetch",
|
|
@@ -83089,6 +83792,7 @@ function getAllWrappedEndpoints() {
|
|
|
83089
83792
|
...getAllAlchemyEndpoints(),
|
|
83090
83793
|
...getAllHyperliquidEndpoints(),
|
|
83091
83794
|
...getAllRwaEndpoints(),
|
|
83795
|
+
...getAllArchiveEndpoints(),
|
|
83092
83796
|
...getAllFetchEndpoints()
|
|
83093
83797
|
];
|
|
83094
83798
|
const allEndpointsJSON = wrapper;
|
|
@@ -83163,6 +83867,7 @@ function getAllWrappedEndpoints() {
|
|
|
83163
83867
|
"hive_",
|
|
83164
83868
|
"hyperliquid_",
|
|
83165
83869
|
"rwa_",
|
|
83870
|
+
"archive_",
|
|
83166
83871
|
"fetch_"
|
|
83167
83872
|
];
|
|
83168
83873
|
if (selfDescribedPrefixes.some((p) => tool89.name.startsWith(p))) {
|
|
@@ -83222,7 +83927,7 @@ var ToolRegistry = [
|
|
|
83222
83927
|
{
|
|
83223
83928
|
category: "Market Data and Price",
|
|
83224
83929
|
name: "get_market_and_price_endpoints",
|
|
83225
|
-
description: "Endpoints to retrieve real-time and historical cryptocurrency prices, market caps, trading volumes, OHLC data, global market statistics, supported currencies, basic market performance metrics across different timeframes and asset platforms, stablecoin market analytics, stablecoin price tracking, comprehensive stablecoin market cap data across multiple chains, centralized exchange (CEX) trading data including real-time tickers, order books, recent trades, candlestick charts, best bid/ask prices, CEX exchange metadata and volume charts, derivatives pricing (index, mark, and premium), perpetual futures funding rates, leverage tiers, options trading analytics, and available trading markets/currencies across major exchanges. Provider guide: Use coingecko tools for market-wide price data. Use ccxt_ tools for CEX trading data. Use alchemy_ tools for supported token price reads and EVM gas data. Use codex tools for on-chain DEX token price, chart, and volume analytics.",
|
|
83930
|
+
description: "Endpoints to retrieve real-time and historical cryptocurrency prices, market caps, trading volumes, OHLC data, global market statistics, supported currencies, basic market performance metrics across different timeframes and asset platforms, stablecoin market analytics, stablecoin price tracking, comprehensive stablecoin market cap data across multiple chains, centralized exchange (CEX) trading data including real-time tickers, order books, recent trades, candlestick charts, best bid/ask prices, CEX exchange metadata and volume charts, derivatives pricing (index, mark, and premium), perpetual futures funding rates, leverage tiers, options trading analytics, and available trading markets/currencies across major exchanges. Provider guide: Use coingecko tools for market-wide price data. Use ccxt_ tools for CEX trading data. Use alchemy_ tools for supported token price reads and EVM gas data. Use codex tools for on-chain DEX token price, chart, and volume analytics. Use archive_ tools for cross-venue derivatives HISTORY (funding settlements, open interest, basis, long/short ratio, liquidations) since 2023-11.",
|
|
83226
83931
|
tools: [
|
|
83227
83932
|
"get_coins_index",
|
|
83228
83933
|
"get_coins_market_data",
|
|
@@ -83289,6 +83994,15 @@ var ToolRegistry = [
|
|
|
83289
83994
|
"rwa_get_avantis_markets",
|
|
83290
83995
|
"rwa_get_lighter_markets",
|
|
83291
83996
|
"rwa_get_extended_markets",
|
|
83997
|
+
// Hive Archive cross-venue derivatives history (since 2023-11).
|
|
83998
|
+
// Market data by fit, same reasoning as the RWA slice — the registry
|
|
83999
|
+
// stays at 10 categories (no 11th category surface).
|
|
84000
|
+
"archive_get_funding_history",
|
|
84001
|
+
"archive_get_open_interest_history",
|
|
84002
|
+
"archive_get_basis_history",
|
|
84003
|
+
"archive_get_long_short_history",
|
|
84004
|
+
"archive_get_liquidations_history",
|
|
84005
|
+
"archive_get_coverage",
|
|
83292
84006
|
"get_stablecoins",
|
|
83293
84007
|
"get_stablecoin_chains",
|
|
83294
84008
|
"get_stablecoin_charts_global",
|
|
@@ -83948,7 +84662,7 @@ import { z as z15 } from "zod";
|
|
|
83948
84662
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
83949
84663
|
|
|
83950
84664
|
// packages/mcp-client/src/receipt-integrity.ts
|
|
83951
|
-
import { createHash as
|
|
84665
|
+
import { createHash as createHash4 } from "crypto";
|
|
83952
84666
|
function compareJsonKeys(left, right) {
|
|
83953
84667
|
if (left === right) return 0;
|
|
83954
84668
|
return left < right ? -1 : 1;
|
|
@@ -83973,7 +84687,7 @@ function canonicalizeHiveJson(value) {
|
|
|
83973
84687
|
}
|
|
83974
84688
|
}
|
|
83975
84689
|
function digestHiveCanonicalJson(value) {
|
|
83976
|
-
return
|
|
84690
|
+
return createHash4("sha256").update(canonicalizeHiveJson(value)).digest("hex");
|
|
83977
84691
|
}
|
|
83978
84692
|
|
|
83979
84693
|
// src/utils/jsonSchemaValidator.ts
|
|
@@ -84785,6 +85499,7 @@ function getMcpCreditCost(toolName) {
|
|
|
84785
85499
|
// src/services/analytics-writer.ts
|
|
84786
85500
|
import { createHmac as createHmac2 } from "crypto";
|
|
84787
85501
|
var PRINCIPAL_HASH_DOMAIN = "hive-analytics-principal-v1";
|
|
85502
|
+
var DROP_LOG_INTERVAL_MS = 6e4;
|
|
84788
85503
|
var MAX_QUEUE_SIZE = 500;
|
|
84789
85504
|
var FLUSH_INTERVAL_MS = 5e3;
|
|
84790
85505
|
var FLUSH_BATCH_SIZE = 100;
|
|
@@ -84817,6 +85532,7 @@ function createAnalyticsWriter(overrides = {}) {
|
|
|
84817
85532
|
};
|
|
84818
85533
|
const queue = [];
|
|
84819
85534
|
let dropped = 0;
|
|
85535
|
+
let lastDropLoggedAt = 0;
|
|
84820
85536
|
let timer = null;
|
|
84821
85537
|
let flushing = null;
|
|
84822
85538
|
function hashPrincipal(raw) {
|
|
@@ -84876,6 +85592,14 @@ function createAnalyticsWriter(overrides = {}) {
|
|
|
84876
85592
|
if (!deps.isConfigured()) return false;
|
|
84877
85593
|
if (queue.length >= deps.maxQueueSize) {
|
|
84878
85594
|
dropped += 1;
|
|
85595
|
+
const now = Date.now();
|
|
85596
|
+
if (now - lastDropLoggedAt >= DROP_LOG_INTERVAL_MS) {
|
|
85597
|
+
lastDropLoggedAt = now;
|
|
85598
|
+
secureLogger.warn(
|
|
85599
|
+
"Analytics queue is full \u2014 product-analytics rows are being dropped",
|
|
85600
|
+
{ table, queueSize: queue.length, droppedTotal: dropped }
|
|
85601
|
+
);
|
|
85602
|
+
}
|
|
84879
85603
|
return false;
|
|
84880
85604
|
}
|
|
84881
85605
|
queue.push({ table, row });
|
|
@@ -85771,7 +86495,7 @@ function isSolanaChain(chain, address) {
|
|
|
85771
86495
|
if (chain) return SOLANA_CHAIN_NAMES.has(normalizeChain(chain));
|
|
85772
86496
|
return !address.trim().toLowerCase().startsWith("0x");
|
|
85773
86497
|
}
|
|
85774
|
-
function
|
|
86498
|
+
function errorResult3(message) {
|
|
85775
86499
|
return {
|
|
85776
86500
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
85777
86501
|
isError: true
|
|
@@ -85805,7 +86529,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
85805
86529
|
const executeUnderlying = async (toolName, args) => {
|
|
85806
86530
|
const lookup = resolveEndpoint(endpoints, toolName);
|
|
85807
86531
|
if (!lookup.ok) {
|
|
85808
|
-
return
|
|
86532
|
+
return errorResult3(
|
|
85809
86533
|
`Underlying endpoint '${toolName}' is not registered. Call search_tools to locate the current tool for this intent.`
|
|
85810
86534
|
);
|
|
85811
86535
|
}
|
|
@@ -85890,12 +86614,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
85890
86614
|
title: "Get Token Price",
|
|
85891
86615
|
description: 'Current price for one token. Pass token as a CoinGecko id or symbol ("bitcoin", "BTC"), or pass chain + address for a specific contract. Returns price, market cap, and 24h change with provider provenance.',
|
|
85892
86616
|
inputSchema: zodToInputSchema2(tokenPriceSchema),
|
|
86617
|
+
outputSchema: heroOutputSchema("get_token_price"),
|
|
85893
86618
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
85894
86619
|
},
|
|
85895
86620
|
handler: async (_client, args) => {
|
|
85896
86621
|
const parsed = tokenPriceSchema.safeParse(args ?? {});
|
|
85897
86622
|
if (!parsed.success) {
|
|
85898
|
-
return
|
|
86623
|
+
return errorResult3(
|
|
85899
86624
|
`Invalid arguments: ${JSON.stringify(parsed.error.format())}`
|
|
85900
86625
|
);
|
|
85901
86626
|
}
|
|
@@ -85911,7 +86636,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
85911
86636
|
return respond(result3, format, trimOnchainPrice(address));
|
|
85912
86637
|
}
|
|
85913
86638
|
if (!token) {
|
|
85914
|
-
return
|
|
86639
|
+
return errorResult3(
|
|
85915
86640
|
'Pass token (CoinGecko id or symbol, for example "bitcoin" or "BTC"), or chain + address for a contract.'
|
|
85916
86641
|
);
|
|
85917
86642
|
}
|
|
@@ -85990,12 +86715,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
85990
86715
|
title: "Check Token Safety",
|
|
85991
86716
|
description: "Token security screen for a contract address: honeypot, taxes, ownership, and related GoPlus risk datapoints. EVM chains use chain + address; Solana addresses are detected automatically.",
|
|
85992
86717
|
inputSchema: zodToInputSchema2(tokenSafetySchema),
|
|
86718
|
+
outputSchema: heroOutputSchema("check_token_safety"),
|
|
85993
86719
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
85994
86720
|
},
|
|
85995
86721
|
handler: async (_client, args) => {
|
|
85996
86722
|
const parsed = tokenSafetySchema.safeParse(args ?? {});
|
|
85997
86723
|
if (!parsed.success) {
|
|
85998
|
-
return
|
|
86724
|
+
return errorResult3(
|
|
85999
86725
|
`Invalid arguments: ${JSON.stringify(parsed.error.format())}`
|
|
86000
86726
|
);
|
|
86001
86727
|
}
|
|
@@ -86010,7 +86736,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
86010
86736
|
const normalized = normalizeChain(chain);
|
|
86011
86737
|
const chainId = /^\d+$/.test(normalized) ? normalized : GOPLUS_CHAIN_IDS[normalized];
|
|
86012
86738
|
if (!chainId) {
|
|
86013
|
-
return
|
|
86739
|
+
return errorResult3(
|
|
86014
86740
|
`Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(GOPLUS_CHAIN_IDS).slice(0, 6).join(", ")}, \u2026), a numeric chain id, or "solana".`
|
|
86015
86741
|
);
|
|
86016
86742
|
}
|
|
@@ -86074,12 +86800,13 @@ function heroTools(endpoints, options = {}) {
|
|
|
86074
86800
|
title: "Get Wallet Portfolio",
|
|
86075
86801
|
description: "Token holdings for a wallet with balances and USD values where available. EVM chains route through Alchemy; Solana wallets are detected automatically and route through Helius.",
|
|
86076
86802
|
inputSchema: zodToInputSchema2(walletPortfolioSchema),
|
|
86803
|
+
outputSchema: heroOutputSchema("get_wallet_portfolio"),
|
|
86077
86804
|
annotations: VOLATILE_READ_ANNOTATIONS
|
|
86078
86805
|
},
|
|
86079
86806
|
handler: async (_client, args) => {
|
|
86080
86807
|
const parsed = walletPortfolioSchema.safeParse(args ?? {});
|
|
86081
86808
|
if (!parsed.success) {
|
|
86082
|
-
return
|
|
86809
|
+
return errorResult3(
|
|
86083
86810
|
`Invalid arguments: ${JSON.stringify(parsed.error.format())}`
|
|
86084
86811
|
);
|
|
86085
86812
|
}
|
|
@@ -86094,7 +86821,7 @@ function heroTools(endpoints, options = {}) {
|
|
|
86094
86821
|
const normalized = normalizeChain(chain);
|
|
86095
86822
|
const network = ALCHEMY_NETWORKS[normalized];
|
|
86096
86823
|
if (!network) {
|
|
86097
|
-
return
|
|
86824
|
+
return errorResult3(
|
|
86098
86825
|
`Unknown chain '${chain}'. Pass an EVM chain name (${Object.keys(ALCHEMY_NETWORKS).slice(0, 6).join(", ")}, \u2026) or "solana".`
|
|
86099
86826
|
);
|
|
86100
86827
|
}
|
|
@@ -86109,6 +86836,9 @@ function heroTools(endpoints, options = {}) {
|
|
|
86109
86836
|
};
|
|
86110
86837
|
return [getTokenPrice, checkTokenSafety, getWalletPortfolio];
|
|
86111
86838
|
}
|
|
86839
|
+
function heroOutputSchema(name) {
|
|
86840
|
+
return HERO_TOOL_OUTPUT_SCHEMAS[name];
|
|
86841
|
+
}
|
|
86112
86842
|
function zodToInputSchema2(schema) {
|
|
86113
86843
|
const jsonSchema = schema.toJSONSchema();
|
|
86114
86844
|
const { $schema, ...rest } = jsonSchema;
|
|
@@ -87156,6 +87886,10 @@ var TASK_TOOLSET_CATALOG = [
|
|
|
87156
87886
|
"get_derivatives_tickers",
|
|
87157
87887
|
"get_open_interest_history",
|
|
87158
87888
|
"get_funding_rate_history",
|
|
87889
|
+
// Hive Archive: cross-venue derivatives history since 2023-11 for
|
|
87890
|
+
// windows older than live venue APIs retain.
|
|
87891
|
+
"archive_get_funding_history",
|
|
87892
|
+
"archive_get_open_interest_history",
|
|
87159
87893
|
"hyperliquid_get_perp_dexs",
|
|
87160
87894
|
"hyperliquid_get_perps_at_oi_cap",
|
|
87161
87895
|
"hyperliquid_get_meta",
|
|
@@ -88852,7 +89586,9 @@ function redisAdapter() {
|
|
|
88852
89586
|
if (!redis || !isRedisAvailable()) {
|
|
88853
89587
|
throw new Error("Redis is not available");
|
|
88854
89588
|
}
|
|
88855
|
-
await redis.
|
|
89589
|
+
const results = await redis.pipeline().lpush(key, value).ltrim(key, 0, limit - 1).exec();
|
|
89590
|
+
const failure = results?.find(([error]) => error)?.[0];
|
|
89591
|
+
if (failure) throw failure;
|
|
88856
89592
|
}
|
|
88857
89593
|
};
|
|
88858
89594
|
}
|
|
@@ -88929,13 +89665,8 @@ var CanaryHistoryStore = class {
|
|
|
88929
89665
|
0,
|
|
88930
89666
|
this.limit
|
|
88931
89667
|
);
|
|
88932
|
-
const
|
|
88933
|
-
|
|
88934
|
-
);
|
|
88935
|
-
const memoryOnly = this.memory.some(
|
|
88936
|
-
(entry) => !persistedValues.has(JSON.stringify(entry))
|
|
88937
|
-
);
|
|
88938
|
-
const hasUnpersisted = this.unpersisted.size > 0 || memoryOnly;
|
|
89668
|
+
const storeLostEverything = this.memory.length > 0 && entries.length === 0;
|
|
89669
|
+
const hasUnpersisted = this.unpersisted.size > 0 || storeLostEverything;
|
|
88939
89670
|
const requiredUnavailable = this.backend === "redis" && hasUnpersisted;
|
|
88940
89671
|
if (!hasUnpersisted) this.lastPersistenceError = null;
|
|
88941
89672
|
return {
|
|
@@ -90337,7 +91068,7 @@ async function runMcpProtocolHealth(options = {}) {
|
|
|
90337
91068
|
}
|
|
90338
91069
|
|
|
90339
91070
|
// src/health/oauthLifecycle.ts
|
|
90340
|
-
import { createHash as
|
|
91071
|
+
import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
|
|
90341
91072
|
|
|
90342
91073
|
// src/auth/oauth/provider.ts
|
|
90343
91074
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -90713,10 +91444,10 @@ function isHiveOAuthAccessToken(token) {
|
|
|
90713
91444
|
}
|
|
90714
91445
|
|
|
90715
91446
|
// src/auth/oauth/store.ts
|
|
90716
|
-
import { createHash as
|
|
91447
|
+
import { createHash as createHash5 } from "crypto";
|
|
90717
91448
|
var REVOKED_FAMILY_TTL_SECONDS = 7776e3;
|
|
90718
91449
|
function digest(value) {
|
|
90719
|
-
return
|
|
91450
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
90720
91451
|
}
|
|
90721
91452
|
function parseJson(raw) {
|
|
90722
91453
|
if (!raw) return void 0;
|
|
@@ -91168,7 +91899,7 @@ async function operationRejects(operation) {
|
|
|
91168
91899
|
}
|
|
91169
91900
|
async function issueCanaryFamily(args) {
|
|
91170
91901
|
const verifier = randomBytes3(48).toString("base64url");
|
|
91171
|
-
const challenge =
|
|
91902
|
+
const challenge = createHash6("sha256").update(verifier).digest("base64url");
|
|
91172
91903
|
let consentLocation;
|
|
91173
91904
|
const response = {
|
|
91174
91905
|
redirect(status, location) {
|
|
@@ -91480,7 +92211,7 @@ async function runOAuthLifecycleHealth(overrides = {}) {
|
|
|
91480
92211
|
import Coingecko2 from "@coingecko/coingecko-typescript";
|
|
91481
92212
|
|
|
91482
92213
|
// src/tools/goplus/goplusClient.ts
|
|
91483
|
-
import { createHash as
|
|
92214
|
+
import { createHash as createHash7 } from "crypto";
|
|
91484
92215
|
function parsePositiveInteger(value, fallback) {
|
|
91485
92216
|
const parsed = Number.parseInt(value || "", 10);
|
|
91486
92217
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -91574,7 +92305,7 @@ var GoPlusClient = class {
|
|
|
91574
92305
|
const time = Math.floor(Date.now() / 1e3);
|
|
91575
92306
|
const timeStr = time.toString();
|
|
91576
92307
|
const signInput = this.appKey + timeStr + this.appSecret;
|
|
91577
|
-
const sign =
|
|
92308
|
+
const sign = createHash7("sha1").update(signInput).digest("hex");
|
|
91578
92309
|
secureLogger.info("GoPlus: requesting access token", {
|
|
91579
92310
|
appKeyLength: this.appKey.length,
|
|
91580
92311
|
appSecretLength: this.appSecret.length,
|
|
@@ -92884,6 +93615,7 @@ var PROVIDER_RESOURCE_PATTERNS = [
|
|
|
92884
93615
|
["defillama", /^(defillama|get-)/],
|
|
92885
93616
|
["hyperliquid", /^hyperliquid(?:[.-]|$)/],
|
|
92886
93617
|
["rwa", /^rwa(?:[.-]|$)/],
|
|
93618
|
+
["archive", /^archive(?:[.-]|$)/],
|
|
92887
93619
|
["fetch", /^fetch(?:[.-]|$)/],
|
|
92888
93620
|
["helius", /^helius\./],
|
|
92889
93621
|
["moralis", /^moralis\./],
|
|
@@ -92907,6 +93639,7 @@ function classifyProvider(endpoint) {
|
|
|
92907
93639
|
if (toolName.startsWith("defillama_")) return "defillama";
|
|
92908
93640
|
if (toolName.startsWith("hyperliquid_")) return "hyperliquid";
|
|
92909
93641
|
if (toolName.startsWith("rwa_")) return "rwa";
|
|
93642
|
+
if (toolName.startsWith("archive_")) return "archive";
|
|
92910
93643
|
if (toolName.startsWith("fetch_")) return "fetch";
|
|
92911
93644
|
if (toolName.startsWith("hive_")) return "hive";
|
|
92912
93645
|
return "coingecko";
|
|
@@ -92939,6 +93672,9 @@ var QUICK_PROBE_ARGS = {
|
|
|
92939
93672
|
hyperliquid: { toolName: "hyperliquid_get_perp_dexs", args: {} },
|
|
92940
93673
|
// Single-request keyless venue read (official Ostium subgraph).
|
|
92941
93674
|
rwa: { toolName: "rwa_get_ostium_markets", args: { limit: 5 } },
|
|
93675
|
+
// Cheap coverage probe (index-only extremes, 10-min in-process cache).
|
|
93676
|
+
// Unset archive credentials classify as missing-key skip, not failure.
|
|
93677
|
+
archive: { toolName: "archive_get_coverage", args: {} },
|
|
92942
93678
|
fetch: {
|
|
92943
93679
|
toolName: "fetch_public_api",
|
|
92944
93680
|
args: { host: "api.coingecko.com", path: "/api/v3/ping" }
|
|
@@ -93042,6 +93778,8 @@ var FULL_MODE_DEFAULT_ARGS = {
|
|
|
93042
93778
|
},
|
|
93043
93779
|
// Keyless RWA venue reads: bound every full-mode probe.
|
|
93044
93780
|
rwa: { limit: 10 },
|
|
93781
|
+
// Archive history reads: BTC exists in every dataset; keep probes bounded.
|
|
93782
|
+
archive: { base_coin: "BTC", limit: 5 },
|
|
93045
93783
|
fetch: {
|
|
93046
93784
|
host: "api.coingecko.com",
|
|
93047
93785
|
path: "/api/v3/ping"
|
|
@@ -94389,6 +95127,7 @@ function getClientForProvider(provider, clients) {
|
|
|
94389
95127
|
case "hyperliquid":
|
|
94390
95128
|
case "fetch":
|
|
94391
95129
|
case "defillama":
|
|
95130
|
+
case "archive":
|
|
94392
95131
|
return null;
|
|
94393
95132
|
case "codex":
|
|
94394
95133
|
return null;
|
|
@@ -95149,14 +95888,17 @@ var TASK_CANARY_PROBES = [
|
|
|
95149
95888
|
args: { query: "bitcoin" }
|
|
95150
95889
|
}
|
|
95151
95890
|
];
|
|
95891
|
+
var PROBE_SKIPPED = /* @__PURE__ */ Symbol("probe_skipped");
|
|
95152
95892
|
function classifyRuntimeStatus(probeStatus, error) {
|
|
95153
95893
|
if (probeStatus === "passed") return "ok";
|
|
95154
|
-
if (probeStatus === "skipped") return
|
|
95894
|
+
if (probeStatus === "skipped") return PROBE_SKIPPED;
|
|
95155
95895
|
return classifyRuntimeStatusFromError(error);
|
|
95156
95896
|
}
|
|
95157
95897
|
function aggregateTaskStatus(results) {
|
|
95158
95898
|
if (results.length === 0) return "unknown";
|
|
95159
|
-
|
|
95899
|
+
const observed = results.filter((result2) => !result2.skipped).map((result2) => result2.status);
|
|
95900
|
+
if (observed.length === 0) return "unknown";
|
|
95901
|
+
return aggregateRuntimeStatus(observed);
|
|
95160
95902
|
}
|
|
95161
95903
|
async function getTaskCanaryHistory(limit = 20) {
|
|
95162
95904
|
const safeLimit = Math.max(1, Math.min(limit, TASK_CANARY_HISTORY_LIMIT));
|
|
@@ -95219,11 +95961,14 @@ async function runTaskCanary(toolsetId) {
|
|
|
95219
95961
|
const toolset = TASK_TOOLSETS.find((item) => item.id === toolsetId);
|
|
95220
95962
|
if (!probe || !toolset) return null;
|
|
95221
95963
|
const result2 = await runSelfTestTool(probe.toolName, probe.args);
|
|
95222
|
-
const
|
|
95964
|
+
const classified = classifyRuntimeStatus(result2.status, result2.error);
|
|
95965
|
+
const skipped = classified === PROBE_SKIPPED;
|
|
95966
|
+
const status = skipped ? "missing_key" : classified;
|
|
95223
95967
|
return {
|
|
95224
95968
|
toolsetId,
|
|
95225
95969
|
label: toolset.label,
|
|
95226
95970
|
status,
|
|
95971
|
+
...skipped && { skipped: true },
|
|
95227
95972
|
probeToolName: probe.toolName,
|
|
95228
95973
|
provider: result2.provider,
|
|
95229
95974
|
description: probe.description,
|
|
@@ -95234,20 +95979,57 @@ async function runTaskCanary(toolsetId) {
|
|
|
95234
95979
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
95235
95980
|
};
|
|
95236
95981
|
}
|
|
95982
|
+
var TASK_CANARY_PROBE_TIMEOUT_MS = Number.parseInt(
|
|
95983
|
+
process.env.HIVE_TASK_CANARY_TIMEOUT_MS || "10000",
|
|
95984
|
+
10
|
|
95985
|
+
);
|
|
95986
|
+
var TASK_CANARY_CONCURRENCY = Number.parseInt(
|
|
95987
|
+
process.env.HIVE_TASK_CANARY_CONCURRENCY || "4",
|
|
95988
|
+
10
|
|
95989
|
+
);
|
|
95990
|
+
async function runTaskCanaryBounded(probe) {
|
|
95991
|
+
if (TASK_CANARY_PROBE_TIMEOUT_MS <= 0) return runTaskCanary(probe.toolsetId);
|
|
95992
|
+
const startedAt = Date.now();
|
|
95993
|
+
let timer;
|
|
95994
|
+
const timeout = new Promise((resolve) => {
|
|
95995
|
+
timer = setTimeout(() => resolve("timeout"), TASK_CANARY_PROBE_TIMEOUT_MS);
|
|
95996
|
+
});
|
|
95997
|
+
try {
|
|
95998
|
+
const outcome = await Promise.race([runTaskCanary(probe.toolsetId), timeout]);
|
|
95999
|
+
if (outcome !== "timeout") return outcome;
|
|
96000
|
+
const toolset = TASK_TOOLSETS.find((item) => item.id === probe.toolsetId);
|
|
96001
|
+
return {
|
|
96002
|
+
toolsetId: probe.toolsetId,
|
|
96003
|
+
label: toolset?.label ?? probe.toolsetId,
|
|
96004
|
+
status: "degraded",
|
|
96005
|
+
probeToolName: probe.toolName,
|
|
96006
|
+
provider: "unknown",
|
|
96007
|
+
description: probe.description,
|
|
96008
|
+
duration: Date.now() - startedAt,
|
|
96009
|
+
error: `Canary exceeded ${TASK_CANARY_PROBE_TIMEOUT_MS}ms and was abandoned; the provider is slow or backing off rather than returning.`,
|
|
96010
|
+
requiredIdentifiers: toolset?.requiredIdentifiers ?? [],
|
|
96011
|
+
stableFixtures: toolset?.stableFixtures ?? [],
|
|
96012
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
96013
|
+
};
|
|
96014
|
+
} finally {
|
|
96015
|
+
if (timer) clearTimeout(timer);
|
|
96016
|
+
}
|
|
96017
|
+
}
|
|
95237
96018
|
async function runTaskCanariesQuick() {
|
|
95238
96019
|
const start = Date.now();
|
|
95239
|
-
const results = [];
|
|
95240
96020
|
const interProbeDelayMs = Number.parseInt(
|
|
95241
96021
|
process.env.HIVE_TASK_CANARY_DELAY_MS || "350",
|
|
95242
96022
|
10
|
|
95243
96023
|
);
|
|
95244
|
-
|
|
95245
|
-
|
|
95246
|
-
|
|
95247
|
-
|
|
95248
|
-
|
|
95249
|
-
|
|
95250
|
-
|
|
96024
|
+
const settled = await mapWithConcurrency2(
|
|
96025
|
+
TASK_CANARY_PROBES,
|
|
96026
|
+
TASK_CANARY_CONCURRENCY,
|
|
96027
|
+
(probe) => runTaskCanaryBounded(probe),
|
|
96028
|
+
interProbeDelayMs
|
|
96029
|
+
);
|
|
96030
|
+
const results = settled.flatMap(
|
|
96031
|
+
(entry) => entry.status === "fulfilled" && entry.value ? [entry.value] : []
|
|
96032
|
+
);
|
|
95251
96033
|
const observedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
95252
96034
|
const summary = {
|
|
95253
96035
|
...getCurrentCanaryIdentity(observedAt),
|
|
@@ -97213,7 +97995,7 @@ var TASK_RESULT_VALIDATION_OUTPUT_SCHEMA = {
|
|
|
97213
97995
|
warnings: { type: "array", items: { type: "string" } }
|
|
97214
97996
|
}
|
|
97215
97997
|
};
|
|
97216
|
-
function
|
|
97998
|
+
function errorResult4(message) {
|
|
97217
97999
|
return {
|
|
97218
98000
|
content: [
|
|
97219
98001
|
{
|
|
@@ -97237,13 +98019,13 @@ function parseCursor(cursor) {
|
|
|
97237
98019
|
const parsed = Number.parseInt(cursor, 10);
|
|
97238
98020
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
97239
98021
|
}
|
|
97240
|
-
function
|
|
98022
|
+
function clampLimit4(limit, defaultLimit, maxLimit) {
|
|
97241
98023
|
if (!limit || !Number.isFinite(limit)) return defaultLimit;
|
|
97242
98024
|
return Math.min(Math.max(Math.floor(limit), 1), maxLimit);
|
|
97243
98025
|
}
|
|
97244
98026
|
function paginate(items, cursor, limit, defaultLimit, maxLimit) {
|
|
97245
98027
|
const offset = parseCursor(cursor);
|
|
97246
|
-
const pageSize =
|
|
98028
|
+
const pageSize = clampLimit4(limit, defaultLimit, maxLimit);
|
|
97247
98029
|
const page = items.slice(offset, offset + pageSize);
|
|
97248
98030
|
const nextOffset = offset + page.length;
|
|
97249
98031
|
const hasMore = nextOffset < items.length;
|
|
@@ -97775,14 +98557,14 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97775
98557
|
},
|
|
97776
98558
|
handler: async (_client, args) => {
|
|
97777
98559
|
if (!args) {
|
|
97778
|
-
return
|
|
98560
|
+
return errorResult4(
|
|
97779
98561
|
"No endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with the tool name."
|
|
97780
98562
|
);
|
|
97781
98563
|
}
|
|
97782
98564
|
const parsedEndpointArgs = getEndpointSchema.parse(args);
|
|
97783
98565
|
const requestedEndpoint = parsedEndpointArgs.endpoint ?? parsedEndpointArgs.endpoint_name;
|
|
97784
98566
|
if (!requestedEndpoint) {
|
|
97785
|
-
return
|
|
98567
|
+
return errorResult4(
|
|
97786
98568
|
"No endpoint provided. Pass an 'endpoint' (or 'endpoint_name') parameter with the tool name."
|
|
97787
98569
|
);
|
|
97788
98570
|
}
|
|
@@ -97796,7 +98578,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97796
98578
|
...categoryTools
|
|
97797
98579
|
].find((endpoint2) => endpoint2.tool.name === requestedEndpoint);
|
|
97798
98580
|
if (!lookup.ok && !metaEndpoint) {
|
|
97799
|
-
return
|
|
98581
|
+
return errorResult4(
|
|
97800
98582
|
`Endpoint '${lookup.resolvedToolName || lookup.requestedToolName}' not found. Use a category listing tool to discover available endpoints.`
|
|
97801
98583
|
);
|
|
97802
98584
|
}
|
|
@@ -97831,12 +98613,12 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97831
98613
|
const invokeWithPolicy = async (invoker, client2, args) => {
|
|
97832
98614
|
const { success, data, error } = invokeEndpointSchema.safeParse(args);
|
|
97833
98615
|
if (!success) {
|
|
97834
|
-
return
|
|
98616
|
+
return errorResult4(
|
|
97835
98617
|
`Invalid arguments: ${JSON.stringify(error?.format())}`
|
|
97836
98618
|
);
|
|
97837
98619
|
}
|
|
97838
98620
|
const invokeArgs = normalizeInvokeArgs(data);
|
|
97839
|
-
if (!invokeArgs.ok) return
|
|
98621
|
+
if (!invokeArgs.ok) return errorResult4(invokeArgs.message);
|
|
97840
98622
|
const lookup = resolveEndpoint(endpoints, invokeArgs.resolvedToolName);
|
|
97841
98623
|
if (!lookup.ok) {
|
|
97842
98624
|
return createToolNotFoundResult(
|
|
@@ -97844,7 +98626,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97844
98626
|
).mcpResult;
|
|
97845
98627
|
}
|
|
97846
98628
|
if (!endpointAllowedForInvoker(invoker, lookup.endpoint)) {
|
|
97847
|
-
return
|
|
98629
|
+
return errorResult4(invokerPolicyMessage(invoker, lookup.endpoint));
|
|
97848
98630
|
}
|
|
97849
98631
|
const executionContext = {
|
|
97850
98632
|
transport: "dynamic",
|
|
@@ -97918,7 +98700,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97918
98700
|
handler: async (_client, args) => {
|
|
97919
98701
|
const parsed = validateTaskResultSchema.safeParse(args);
|
|
97920
98702
|
if (!parsed.success) {
|
|
97921
|
-
return
|
|
98703
|
+
return errorResult4(
|
|
97922
98704
|
`Invalid task result validation request: ${JSON.stringify(parsed.error.format())}`
|
|
97923
98705
|
);
|
|
97924
98706
|
}
|
|
@@ -97926,7 +98708,7 @@ function dynamicTools(endpoints, options = {}) {
|
|
|
97926
98708
|
(entry) => entry.id === parsed.data.task_toolset_id
|
|
97927
98709
|
);
|
|
97928
98710
|
if (!toolset) {
|
|
97929
|
-
return
|
|
98711
|
+
return errorResult4(
|
|
97930
98712
|
`Unknown task toolset '${parsed.data.task_toolset_id}'. Use search_tools to select a canonical id.`
|
|
97931
98713
|
);
|
|
97932
98714
|
}
|
|
@@ -99355,22 +100137,22 @@ function createHiveMcpServer(scope, options) {
|
|
|
99355
100137
|
|
|
99356
100138
|
export {
|
|
99357
100139
|
getEnvVar,
|
|
99358
|
-
hasEnvVars,
|
|
99359
100140
|
getCsvEnv,
|
|
99360
100141
|
getNodeEnv,
|
|
99361
100142
|
isProduction,
|
|
99362
100143
|
getPort,
|
|
99363
100144
|
getRateLimitMax,
|
|
100145
|
+
getCredentialedRateLimitMax,
|
|
99364
100146
|
getCorsAllowedOrigins,
|
|
99365
100147
|
isSupabaseConfigured,
|
|
99366
100148
|
getSupabaseAdmin,
|
|
99367
|
-
getSupabaseAuthClient,
|
|
99368
100149
|
secureLogger,
|
|
99369
100150
|
isB2BSubjectsEnabled,
|
|
99370
100151
|
hasSubjectSigningSecret,
|
|
99371
100152
|
buildSubjectBodyDigest,
|
|
99372
100153
|
resolveSubjectContext,
|
|
99373
100154
|
materializeSubjectContext,
|
|
100155
|
+
buildStatefulWriteAuditEvent,
|
|
99374
100156
|
recordSubjectAuditEvent,
|
|
99375
100157
|
runWithSubjectContext,
|
|
99376
100158
|
HIVE_AUTH_USER_ARG,
|
|
@@ -99385,7 +100167,9 @@ export {
|
|
|
99385
100167
|
trackOAuthFirstMaterialReceipt,
|
|
99386
100168
|
getRedisClient,
|
|
99387
100169
|
isRedisAvailable,
|
|
100170
|
+
waitForRedisAvailability,
|
|
99388
100171
|
closeRedisClient,
|
|
100172
|
+
isRedisConfigured,
|
|
99389
100173
|
PROVIDER_CATALOG,
|
|
99390
100174
|
getProviderRuntimeStatus,
|
|
99391
100175
|
READ_ENDPOINT_INVOKER,
|