@vizzor/cli 0.9.0 → 0.10.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/dist/index.js +1254 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -139,6 +139,30 @@ function loadConfig() {
|
|
|
139
139
|
}
|
|
140
140
|
raw["ai"]["provider"] = process.env["VIZZOR_AI_PROVIDER"];
|
|
141
141
|
}
|
|
142
|
+
if (process.env["DATABASE_TYPE"] || process.env["DATABASE_URL"]) {
|
|
143
|
+
if (!raw["database"] || typeof raw["database"] !== "object") {
|
|
144
|
+
raw["database"] = {};
|
|
145
|
+
}
|
|
146
|
+
const db2 = raw["database"];
|
|
147
|
+
if (process.env["DATABASE_TYPE"]) db2["type"] = process.env["DATABASE_TYPE"];
|
|
148
|
+
if (process.env["DATABASE_URL"]) db2["url"] = process.env["DATABASE_URL"];
|
|
149
|
+
}
|
|
150
|
+
if (process.env["ML_ENABLED"] || process.env["ML_SIDECAR_URL"]) {
|
|
151
|
+
if (!raw["ml"] || typeof raw["ml"] !== "object") {
|
|
152
|
+
raw["ml"] = {};
|
|
153
|
+
}
|
|
154
|
+
const ml = raw["ml"];
|
|
155
|
+
if (process.env["ML_ENABLED"]) ml["enabled"] = process.env["ML_ENABLED"] === "true";
|
|
156
|
+
if (process.env["ML_SIDECAR_URL"]) ml["sidecarUrl"] = process.env["ML_SIDECAR_URL"];
|
|
157
|
+
}
|
|
158
|
+
if (process.env["API_PORT"] || process.env["API_HOST"]) {
|
|
159
|
+
if (!raw["api"] || typeof raw["api"] !== "object") {
|
|
160
|
+
raw["api"] = {};
|
|
161
|
+
}
|
|
162
|
+
const api = raw["api"];
|
|
163
|
+
if (process.env["API_PORT"]) api["port"] = Number(process.env["API_PORT"]);
|
|
164
|
+
if (process.env["API_HOST"]) api["host"] = process.env["API_HOST"];
|
|
165
|
+
}
|
|
142
166
|
const config2 = vizzorConfigSchema.parse(raw);
|
|
143
167
|
cachedConfig = config2;
|
|
144
168
|
return config2;
|
|
@@ -176,7 +200,10 @@ function saveConfigValue(key, value) {
|
|
|
176
200
|
if (!raw[section] || typeof raw[section] !== "object") {
|
|
177
201
|
raw[section] = {};
|
|
178
202
|
}
|
|
179
|
-
|
|
203
|
+
let parsed = value;
|
|
204
|
+
if (field === "maxTokens" || field === "port") parsed = Number(value);
|
|
205
|
+
else if (value === "true") parsed = true;
|
|
206
|
+
else if (value === "false") parsed = false;
|
|
180
207
|
raw[section][field] = parsed;
|
|
181
208
|
} else {
|
|
182
209
|
raw[key] = value;
|
|
@@ -207,7 +234,18 @@ var init_loader = __esm({
|
|
|
207
234
|
"ai.provider": { env: "VIZZOR_AI_PROVIDER", nested: "ai" },
|
|
208
235
|
"ai.model": { env: "VIZZOR_AI_MODEL", nested: "ai" },
|
|
209
236
|
"ai.maxTokens": { env: "VIZZOR_AI_MAX_TOKENS", nested: "ai" },
|
|
210
|
-
"ai.ollamaHost": { env: "OLLAMA_HOST", nested: "ai" }
|
|
237
|
+
"ai.ollamaHost": { env: "OLLAMA_HOST", nested: "ai" },
|
|
238
|
+
"database.type": { env: "DATABASE_TYPE", nested: "database" },
|
|
239
|
+
"database.url": { env: "DATABASE_URL", nested: "database" },
|
|
240
|
+
"ml.enabled": { env: "ML_ENABLED", nested: "ml" },
|
|
241
|
+
"ml.sidecarUrl": { env: "ML_SIDECAR_URL", nested: "ml" },
|
|
242
|
+
"ml.fallbackToRules": { env: "ML_FALLBACK_TO_RULES", nested: "ml" },
|
|
243
|
+
"api.port": { env: "API_PORT", nested: "api" },
|
|
244
|
+
"api.host": { env: "API_HOST", nested: "api" },
|
|
245
|
+
"api.enableAuth": { env: "API_ENABLE_AUTH", nested: "api" },
|
|
246
|
+
"api.corsOrigin": { env: "API_CORS_ORIGIN", nested: "api" },
|
|
247
|
+
"n8n.enabled": { env: "N8N_ENABLED", nested: "n8n" },
|
|
248
|
+
"n8n.webhookUrl": { env: "N8N_WEBHOOK_URL", nested: "n8n" }
|
|
211
249
|
};
|
|
212
250
|
}
|
|
213
251
|
});
|
|
@@ -830,6 +868,329 @@ var init_registry = __esm({
|
|
|
830
868
|
}
|
|
831
869
|
});
|
|
832
870
|
|
|
871
|
+
// src/utils/logger.ts
|
|
872
|
+
import pino from "pino";
|
|
873
|
+
function createLogger(name) {
|
|
874
|
+
const isDev = process.env["NODE_ENV"] !== "production";
|
|
875
|
+
if (isDev) {
|
|
876
|
+
return pino({
|
|
877
|
+
name,
|
|
878
|
+
level,
|
|
879
|
+
transport: {
|
|
880
|
+
target: "pino-pretty",
|
|
881
|
+
options: {
|
|
882
|
+
colorize: true
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return pino({ name, level });
|
|
888
|
+
}
|
|
889
|
+
var level;
|
|
890
|
+
var init_logger = __esm({
|
|
891
|
+
"src/utils/logger.ts"() {
|
|
892
|
+
"use strict";
|
|
893
|
+
level = process.env["VIZZOR_LOG_LEVEL"] ?? "info";
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// src/ml/client.ts
|
|
898
|
+
function initMLClient(url) {
|
|
899
|
+
mlClient = new MLClient(url);
|
|
900
|
+
return mlClient;
|
|
901
|
+
}
|
|
902
|
+
function getMLClient() {
|
|
903
|
+
return mlClient;
|
|
904
|
+
}
|
|
905
|
+
var log, MLClient, mlClient;
|
|
906
|
+
var init_client = __esm({
|
|
907
|
+
"src/ml/client.ts"() {
|
|
908
|
+
"use strict";
|
|
909
|
+
init_logger();
|
|
910
|
+
log = createLogger("ml-client");
|
|
911
|
+
MLClient = class {
|
|
912
|
+
baseUrl;
|
|
913
|
+
healthy = false;
|
|
914
|
+
constructor(baseUrl) {
|
|
915
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
916
|
+
}
|
|
917
|
+
async predict(features) {
|
|
918
|
+
try {
|
|
919
|
+
const res = await fetch(`${this.baseUrl}/predict`, {
|
|
920
|
+
method: "POST",
|
|
921
|
+
headers: { "Content-Type": "application/json" },
|
|
922
|
+
body: JSON.stringify(features),
|
|
923
|
+
signal: AbortSignal.timeout(5e3)
|
|
924
|
+
});
|
|
925
|
+
if (!res.ok) return null;
|
|
926
|
+
return await res.json();
|
|
927
|
+
} catch (err) {
|
|
928
|
+
log.debug(`ML predict failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
async batchPredict(features) {
|
|
933
|
+
try {
|
|
934
|
+
const res = await fetch(`${this.baseUrl}/predict/batch`, {
|
|
935
|
+
method: "POST",
|
|
936
|
+
headers: { "Content-Type": "application/json" },
|
|
937
|
+
body: JSON.stringify({ features }),
|
|
938
|
+
signal: AbortSignal.timeout(15e3)
|
|
939
|
+
});
|
|
940
|
+
if (!res.ok) return [];
|
|
941
|
+
const data = await res.json();
|
|
942
|
+
return data.predictions;
|
|
943
|
+
} catch (err) {
|
|
944
|
+
log.debug(`ML batch predict failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
945
|
+
return [];
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async detectAnomalies(flows) {
|
|
949
|
+
try {
|
|
950
|
+
const res = await fetch(`${this.baseUrl}/anomalies`, {
|
|
951
|
+
method: "POST",
|
|
952
|
+
headers: { "Content-Type": "application/json" },
|
|
953
|
+
body: JSON.stringify({ flows }),
|
|
954
|
+
signal: AbortSignal.timeout(1e4)
|
|
955
|
+
});
|
|
956
|
+
if (!res.ok) return [];
|
|
957
|
+
const data = await res.json();
|
|
958
|
+
return data.anomalies;
|
|
959
|
+
} catch (err) {
|
|
960
|
+
log.debug(`ML anomaly detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
961
|
+
return [];
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
async healthCheck() {
|
|
965
|
+
try {
|
|
966
|
+
const res = await fetch(`${this.baseUrl}/health`, {
|
|
967
|
+
signal: AbortSignal.timeout(3e3)
|
|
968
|
+
});
|
|
969
|
+
this.healthy = res.ok;
|
|
970
|
+
return this.healthy;
|
|
971
|
+
} catch {
|
|
972
|
+
this.healthy = false;
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
async predictRug(features) {
|
|
977
|
+
try {
|
|
978
|
+
const res = await fetch(`${this.baseUrl}/predict/rug`, {
|
|
979
|
+
method: "POST",
|
|
980
|
+
headers: { "Content-Type": "application/json" },
|
|
981
|
+
body: JSON.stringify(features),
|
|
982
|
+
signal: AbortSignal.timeout(5e3)
|
|
983
|
+
});
|
|
984
|
+
if (!res.ok) return null;
|
|
985
|
+
return await res.json();
|
|
986
|
+
} catch (err) {
|
|
987
|
+
log.debug(`ML rug predict failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
async classifyWallet(features) {
|
|
992
|
+
try {
|
|
993
|
+
const res = await fetch(`${this.baseUrl}/predict/wallet`, {
|
|
994
|
+
method: "POST",
|
|
995
|
+
headers: { "Content-Type": "application/json" },
|
|
996
|
+
body: JSON.stringify(features),
|
|
997
|
+
signal: AbortSignal.timeout(5e3)
|
|
998
|
+
});
|
|
999
|
+
if (!res.ok) return null;
|
|
1000
|
+
return await res.json();
|
|
1001
|
+
} catch (err) {
|
|
1002
|
+
log.debug(`ML wallet classify failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
async analyzeSentiment(text) {
|
|
1007
|
+
try {
|
|
1008
|
+
const res = await fetch(`${this.baseUrl}/predict/sentiment`, {
|
|
1009
|
+
method: "POST",
|
|
1010
|
+
headers: { "Content-Type": "application/json" },
|
|
1011
|
+
body: JSON.stringify({ text }),
|
|
1012
|
+
signal: AbortSignal.timeout(5e3)
|
|
1013
|
+
});
|
|
1014
|
+
if (!res.ok) return null;
|
|
1015
|
+
return await res.json();
|
|
1016
|
+
} catch (err) {
|
|
1017
|
+
log.debug(`ML sentiment failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
async analyzeSentimentBatch(texts) {
|
|
1022
|
+
try {
|
|
1023
|
+
const res = await fetch(`${this.baseUrl}/predict/sentiment/batch`, {
|
|
1024
|
+
method: "POST",
|
|
1025
|
+
headers: { "Content-Type": "application/json" },
|
|
1026
|
+
body: JSON.stringify({ texts }),
|
|
1027
|
+
signal: AbortSignal.timeout(15e3)
|
|
1028
|
+
});
|
|
1029
|
+
if (!res.ok) return [];
|
|
1030
|
+
const data = await res.json();
|
|
1031
|
+
return data.results;
|
|
1032
|
+
} catch (err) {
|
|
1033
|
+
log.debug(`ML sentiment batch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1034
|
+
return [];
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
// -----------------------------------------------------------------------
|
|
1038
|
+
// v0.11.0 — New ML endpoints
|
|
1039
|
+
// -----------------------------------------------------------------------
|
|
1040
|
+
async scoreTrend(features) {
|
|
1041
|
+
try {
|
|
1042
|
+
const res = await fetch(`${this.baseUrl}/predict/trend`, {
|
|
1043
|
+
method: "POST",
|
|
1044
|
+
headers: { "Content-Type": "application/json" },
|
|
1045
|
+
body: JSON.stringify(features),
|
|
1046
|
+
signal: AbortSignal.timeout(5e3)
|
|
1047
|
+
});
|
|
1048
|
+
if (!res.ok) return null;
|
|
1049
|
+
return await res.json();
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
log.debug(`ML trend score failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
async interpretTA(features) {
|
|
1056
|
+
try {
|
|
1057
|
+
const res = await fetch(`${this.baseUrl}/predict/ta`, {
|
|
1058
|
+
method: "POST",
|
|
1059
|
+
headers: { "Content-Type": "application/json" },
|
|
1060
|
+
body: JSON.stringify(features),
|
|
1061
|
+
signal: AbortSignal.timeout(5e3)
|
|
1062
|
+
});
|
|
1063
|
+
if (!res.ok) return null;
|
|
1064
|
+
return await res.json();
|
|
1065
|
+
} catch (err) {
|
|
1066
|
+
log.debug(`ML TA interpret failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
async evaluateStrategy(features) {
|
|
1071
|
+
try {
|
|
1072
|
+
const res = await fetch(`${this.baseUrl}/predict/strategy`, {
|
|
1073
|
+
method: "POST",
|
|
1074
|
+
headers: { "Content-Type": "application/json" },
|
|
1075
|
+
body: JSON.stringify(features),
|
|
1076
|
+
signal: AbortSignal.timeout(5e3)
|
|
1077
|
+
});
|
|
1078
|
+
if (!res.ok) return null;
|
|
1079
|
+
return await res.json();
|
|
1080
|
+
} catch (err) {
|
|
1081
|
+
log.debug(`ML strategy eval failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
async detectRegime(features) {
|
|
1086
|
+
try {
|
|
1087
|
+
const res = await fetch(`${this.baseUrl}/predict/regime`, {
|
|
1088
|
+
method: "POST",
|
|
1089
|
+
headers: { "Content-Type": "application/json" },
|
|
1090
|
+
body: JSON.stringify(features),
|
|
1091
|
+
signal: AbortSignal.timeout(5e3)
|
|
1092
|
+
});
|
|
1093
|
+
if (!res.ok) return null;
|
|
1094
|
+
return await res.json();
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
log.debug(`ML regime detect failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
async scoreProjectRisk(features) {
|
|
1101
|
+
try {
|
|
1102
|
+
const res = await fetch(`${this.baseUrl}/predict/project-risk`, {
|
|
1103
|
+
method: "POST",
|
|
1104
|
+
headers: { "Content-Type": "application/json" },
|
|
1105
|
+
body: JSON.stringify(features),
|
|
1106
|
+
signal: AbortSignal.timeout(5e3)
|
|
1107
|
+
});
|
|
1108
|
+
if (!res.ok) return null;
|
|
1109
|
+
return await res.json();
|
|
1110
|
+
} catch (err) {
|
|
1111
|
+
log.debug(`ML project risk failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async optimizePortfolio(features) {
|
|
1116
|
+
try {
|
|
1117
|
+
const res = await fetch(`${this.baseUrl}/predict/portfolio-opt`, {
|
|
1118
|
+
method: "POST",
|
|
1119
|
+
headers: { "Content-Type": "application/json" },
|
|
1120
|
+
body: JSON.stringify(features),
|
|
1121
|
+
signal: AbortSignal.timeout(5e3)
|
|
1122
|
+
});
|
|
1123
|
+
if (!res.ok) return null;
|
|
1124
|
+
return await res.json();
|
|
1125
|
+
} catch (err) {
|
|
1126
|
+
log.debug(`ML portfolio opt failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
async classifyIntent(text) {
|
|
1131
|
+
try {
|
|
1132
|
+
const res = await fetch(`${this.baseUrl}/predict/intent`, {
|
|
1133
|
+
method: "POST",
|
|
1134
|
+
headers: { "Content-Type": "application/json" },
|
|
1135
|
+
body: JSON.stringify({ text }),
|
|
1136
|
+
signal: AbortSignal.timeout(5e3)
|
|
1137
|
+
});
|
|
1138
|
+
if (!res.ok) return null;
|
|
1139
|
+
return await res.json();
|
|
1140
|
+
} catch (err) {
|
|
1141
|
+
log.debug(`ML intent classify failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
async scoreBytecodeRisk(features) {
|
|
1146
|
+
try {
|
|
1147
|
+
const res = await fetch(`${this.baseUrl}/predict/bytecode-risk`, {
|
|
1148
|
+
method: "POST",
|
|
1149
|
+
headers: { "Content-Type": "application/json" },
|
|
1150
|
+
body: JSON.stringify(features),
|
|
1151
|
+
signal: AbortSignal.timeout(5e3)
|
|
1152
|
+
});
|
|
1153
|
+
if (!res.ok) return null;
|
|
1154
|
+
return await res.json();
|
|
1155
|
+
} catch (err) {
|
|
1156
|
+
log.debug(`ML bytecode risk failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1157
|
+
return null;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
async predictPortfolioForward(features) {
|
|
1161
|
+
try {
|
|
1162
|
+
const res = await fetch(`${this.baseUrl}/predict/portfolio-forward`, {
|
|
1163
|
+
method: "POST",
|
|
1164
|
+
headers: { "Content-Type": "application/json" },
|
|
1165
|
+
body: JSON.stringify(features),
|
|
1166
|
+
signal: AbortSignal.timeout(5e3)
|
|
1167
|
+
});
|
|
1168
|
+
if (!res.ok) return null;
|
|
1169
|
+
return await res.json();
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
log.debug(`ML portfolio forward failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
async getModelHealth() {
|
|
1176
|
+
try {
|
|
1177
|
+
const res = await fetch(`${this.baseUrl}/health`, {
|
|
1178
|
+
signal: AbortSignal.timeout(3e3)
|
|
1179
|
+
});
|
|
1180
|
+
if (!res.ok) return null;
|
|
1181
|
+
return await res.json();
|
|
1182
|
+
} catch {
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
isHealthy() {
|
|
1187
|
+
return this.healthy;
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
mlClient = null;
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
|
|
833
1194
|
// src/core/scanner/project-analyzer.ts
|
|
834
1195
|
async function analyzeProject(address, adapter) {
|
|
835
1196
|
const [token, code, holders] = await Promise.allSettled([
|
|
@@ -848,17 +1209,48 @@ async function analyzeProject(address, adapter) {
|
|
|
848
1209
|
);
|
|
849
1210
|
const totalSupply = tokenInfo?.totalSupply ?? 0n;
|
|
850
1211
|
const topHolderPercentage = topHolders.length > 0 && totalSupply > 0n ? Number((topHolders[0]?.balance ?? 0n) * 10000n / totalSupply) / 100 : 0;
|
|
1212
|
+
const topHoldersMapped = topHolders.map((h) => ({
|
|
1213
|
+
address: h.address,
|
|
1214
|
+
percentage: totalSupply > 0n ? Number(h.balance * 10000n / totalSupply) / 100 : 0
|
|
1215
|
+
}));
|
|
1216
|
+
const top10Pct = topHoldersMapped.reduce((sum, h) => sum + h.percentage, 0);
|
|
1217
|
+
let mlRisk;
|
|
1218
|
+
const mlClient2 = getMLClient();
|
|
1219
|
+
if (mlClient2) {
|
|
1220
|
+
try {
|
|
1221
|
+
const result = await mlClient2.scoreProjectRisk({
|
|
1222
|
+
bytecode_size: contractCode.length,
|
|
1223
|
+
is_verified: hasSourceCode ? 1 : 0,
|
|
1224
|
+
holder_concentration: topHolderPercentage,
|
|
1225
|
+
has_proxy: 0,
|
|
1226
|
+
has_mint: 0,
|
|
1227
|
+
has_pause: 0,
|
|
1228
|
+
has_blacklist: 0,
|
|
1229
|
+
liquidity_locked: 0,
|
|
1230
|
+
buy_tax: 0,
|
|
1231
|
+
sell_tax: 0,
|
|
1232
|
+
contract_age_days: 0,
|
|
1233
|
+
total_transfers: 0,
|
|
1234
|
+
owner_balance_pct: topHolderPercentage,
|
|
1235
|
+
is_open_source: hasSourceCode ? 1 : 0,
|
|
1236
|
+
top10_holder_pct: top10Pct,
|
|
1237
|
+
has_token_info: tokenInfo ? 1 : 0
|
|
1238
|
+
});
|
|
1239
|
+
if (result) {
|
|
1240
|
+
mlRisk = result;
|
|
1241
|
+
}
|
|
1242
|
+
} catch {
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
851
1245
|
return {
|
|
852
1246
|
token: tokenInfo,
|
|
853
1247
|
contractVerified: hasSourceCode,
|
|
854
1248
|
hasSourceCode,
|
|
855
1249
|
holderConcentration: topHolderPercentage,
|
|
856
|
-
topHolders:
|
|
857
|
-
address: h.address,
|
|
858
|
-
percentage: totalSupply > 0n ? Number(h.balance * 10000n / totalSupply) / 100 : 0
|
|
859
|
-
})),
|
|
1250
|
+
topHolders: topHoldersMapped,
|
|
860
1251
|
riskIndicators,
|
|
861
|
-
riskScore: Math.min(100, riskScore)
|
|
1252
|
+
riskScore: Math.min(100, riskScore),
|
|
1253
|
+
mlRisk
|
|
862
1254
|
};
|
|
863
1255
|
}
|
|
864
1256
|
function evaluateRiskIndicators(token, hasSource, topHolders) {
|
|
@@ -893,16 +1285,30 @@ function evaluateRiskIndicators(token, hasSource, topHolders) {
|
|
|
893
1285
|
var init_project_analyzer = __esm({
|
|
894
1286
|
"src/core/scanner/project-analyzer.ts"() {
|
|
895
1287
|
"use strict";
|
|
1288
|
+
init_client();
|
|
896
1289
|
}
|
|
897
1290
|
});
|
|
898
1291
|
|
|
899
1292
|
// src/core/scanner/risk-scorer.ts
|
|
900
1293
|
function assessRisk(analysis) {
|
|
901
|
-
const { riskScore, riskIndicators } = analysis;
|
|
902
|
-
const
|
|
1294
|
+
const { riskScore, riskIndicators, mlRisk } = analysis;
|
|
1295
|
+
const effectiveScore = mlRisk ? Math.round(mlRisk.risk_probability * 100) : riskScore;
|
|
1296
|
+
const level2 = getRiskLevel(effectiveScore);
|
|
903
1297
|
const factors = riskIndicators.filter((i) => i.detected).map((i) => `${i.name}: ${i.description} (+${i.points})`);
|
|
1298
|
+
if (mlRisk) {
|
|
1299
|
+
for (const f of mlRisk.risk_factors) {
|
|
1300
|
+
factors.push(`ML: ${f.factor} (importance: ${(f.importance * 100).toFixed(1)}%)`);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
904
1303
|
const summary = buildSummary(level2, factors.length);
|
|
905
|
-
return {
|
|
1304
|
+
return {
|
|
1305
|
+
score: effectiveScore,
|
|
1306
|
+
level: level2,
|
|
1307
|
+
summary,
|
|
1308
|
+
factors,
|
|
1309
|
+
mlScore: mlRisk ? Math.round(mlRisk.risk_probability * 100) : void 0,
|
|
1310
|
+
mlLevel: mlRisk?.risk_level
|
|
1311
|
+
};
|
|
906
1312
|
}
|
|
907
1313
|
function getRiskLevel(score) {
|
|
908
1314
|
if (score <= 20) return "low";
|
|
@@ -1138,6 +1544,7 @@ var init_market = __esm({
|
|
|
1138
1544
|
"src/core/trends/market.ts"() {
|
|
1139
1545
|
"use strict";
|
|
1140
1546
|
init_dexscreener();
|
|
1547
|
+
init_client();
|
|
1141
1548
|
}
|
|
1142
1549
|
});
|
|
1143
1550
|
|
|
@@ -1193,20 +1600,47 @@ async function analyzeSentiment(query) {
|
|
|
1193
1600
|
}
|
|
1194
1601
|
const news = await fetchCryptoNews(query, apiToken);
|
|
1195
1602
|
if (news.length > 0) {
|
|
1196
|
-
let
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1603
|
+
let mlClient2 = getMLClient();
|
|
1604
|
+
if (!mlClient2) {
|
|
1605
|
+
try {
|
|
1606
|
+
const cfg = getConfig();
|
|
1607
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
1608
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
1609
|
+
}
|
|
1610
|
+
} catch {
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
let newsScore;
|
|
1614
|
+
let mlSentiment;
|
|
1615
|
+
let mlConfidence;
|
|
1616
|
+
let mlTopics;
|
|
1617
|
+
if (mlClient2) {
|
|
1618
|
+
const headlines = news.slice(0, 10).map((n) => n.title);
|
|
1619
|
+
const mlResults = await mlClient2.analyzeSentimentBatch(headlines);
|
|
1620
|
+
if (mlResults.length > 0) {
|
|
1621
|
+
const avgScore = mlResults.reduce((s, r) => s + r.score, 0) / mlResults.length;
|
|
1622
|
+
const avgConf = mlResults.reduce((s, r) => s + r.confidence, 0) / mlResults.length;
|
|
1623
|
+
const allTopics = [...new Set(mlResults.flatMap((r) => r.key_topics))];
|
|
1624
|
+
newsScore = avgScore;
|
|
1625
|
+
mlSentiment = avgScore > 0.2 ? "bullish" : avgScore < -0.2 ? "bearish" : "neutral";
|
|
1626
|
+
mlConfidence = avgConf;
|
|
1627
|
+
mlTopics = allTopics.slice(0, 5);
|
|
1628
|
+
} else {
|
|
1629
|
+
newsScore = countBasedScore(news);
|
|
1630
|
+
}
|
|
1631
|
+
} else {
|
|
1632
|
+
newsScore = countBasedScore(news);
|
|
1201
1633
|
}
|
|
1202
1634
|
const total = news.length;
|
|
1203
|
-
const newsScore = total > 0 ? (positiveCount - negativeCount) / total : 0;
|
|
1204
1635
|
sources.push({
|
|
1205
|
-
source: "CryptoPanic News",
|
|
1636
|
+
source: mlSentiment ? "ML NLP Sentiment" : "CryptoPanic News",
|
|
1206
1637
|
score: newsScore,
|
|
1207
1638
|
volume: total,
|
|
1208
1639
|
trending: total > 5,
|
|
1209
|
-
topMentions: news.slice(0, 3).map((n) => n.title)
|
|
1640
|
+
topMentions: news.slice(0, 3).map((n) => n.title),
|
|
1641
|
+
mlSentiment,
|
|
1642
|
+
mlConfidence,
|
|
1643
|
+
mlTopics
|
|
1210
1644
|
});
|
|
1211
1645
|
}
|
|
1212
1646
|
} catch {
|
|
@@ -1250,12 +1684,23 @@ async function analyzeSentiment(query) {
|
|
|
1250
1684
|
}
|
|
1251
1685
|
return { overall, sources, consensus };
|
|
1252
1686
|
}
|
|
1687
|
+
function countBasedScore(news) {
|
|
1688
|
+
let pos = 0;
|
|
1689
|
+
let neg = 0;
|
|
1690
|
+
for (const article of news) {
|
|
1691
|
+
if (article.sentiment === "positive") pos++;
|
|
1692
|
+
else if (article.sentiment === "negative") neg++;
|
|
1693
|
+
}
|
|
1694
|
+
const total = news.length;
|
|
1695
|
+
return total > 0 ? (pos - neg) / total : 0;
|
|
1696
|
+
}
|
|
1253
1697
|
var init_sentiment = __esm({
|
|
1254
1698
|
"src/core/trends/sentiment.ts"() {
|
|
1255
1699
|
"use strict";
|
|
1256
1700
|
init_cryptopanic();
|
|
1257
1701
|
init_dexscreener();
|
|
1258
1702
|
init_loader();
|
|
1703
|
+
init_client();
|
|
1259
1704
|
}
|
|
1260
1705
|
});
|
|
1261
1706
|
|
|
@@ -1540,15 +1985,74 @@ async function analyzeTechnicals(symbol, timeframe = "4h") {
|
|
|
1540
1985
|
}
|
|
1541
1986
|
const atr = calculateATR(highs, lows, closes);
|
|
1542
1987
|
if (atr !== null) {
|
|
1543
|
-
const
|
|
1544
|
-
signals.push(interpretATR(atr,
|
|
1988
|
+
const currentPrice2 = closes[closes.length - 1];
|
|
1989
|
+
signals.push(interpretATR(atr, currentPrice2));
|
|
1545
1990
|
}
|
|
1546
1991
|
const obv = calculateOBV(closes, volumes);
|
|
1547
1992
|
if (obv !== null) {
|
|
1548
|
-
const
|
|
1549
|
-
signals.push(interpretOBV(obv,
|
|
1993
|
+
const priceChange2 = closes.length >= 2 ? closes[closes.length - 1] - closes[closes.length - 2] : 0;
|
|
1994
|
+
signals.push(interpretOBV(obv, priceChange2));
|
|
1550
1995
|
}
|
|
1551
1996
|
const sma20 = calculateSMA(closes, 20);
|
|
1997
|
+
const currentPrice = closes[closes.length - 1];
|
|
1998
|
+
const prevPrice = closes.length >= 2 ? closes[closes.length - 2] : currentPrice;
|
|
1999
|
+
const priceChange = currentPrice - prevPrice;
|
|
2000
|
+
const ema12Val = ema12.length > 0 ? ema12[ema12.length - 1] : 0;
|
|
2001
|
+
const ema26Val = ema26.length > 0 ? ema26[ema26.length - 1] : 0;
|
|
2002
|
+
const emaCrossPct = ema26Val !== 0 ? (ema12Val - ema26Val) / ema26Val * 100 : 0;
|
|
2003
|
+
const atrPct = atr !== null && currentPrice > 0 ? atr / currentPrice * 100 : 0;
|
|
2004
|
+
const bbBandwidth = bb !== null && bb.middle > 0 ? (bb.upper - bb.lower) / bb.middle * 100 : 0;
|
|
2005
|
+
const mlClient2 = getMLClient();
|
|
2006
|
+
if (mlClient2) {
|
|
2007
|
+
try {
|
|
2008
|
+
const mlResult = await mlClient2.interpretTA({
|
|
2009
|
+
rsi: rsi ?? 50,
|
|
2010
|
+
macd_histogram: macd?.histogram ?? 0,
|
|
2011
|
+
macd_line: macd?.macd ?? 0,
|
|
2012
|
+
macd_signal: macd?.signal ?? 0,
|
|
2013
|
+
bb_percent_b: bb?.percentB ?? 0.5,
|
|
2014
|
+
bb_bandwidth: bbBandwidth,
|
|
2015
|
+
ema12: ema12Val,
|
|
2016
|
+
ema26: ema26Val,
|
|
2017
|
+
ema_cross_pct: emaCrossPct,
|
|
2018
|
+
atr: atr ?? 0,
|
|
2019
|
+
atr_pct: atrPct,
|
|
2020
|
+
obv: obv ?? 0,
|
|
2021
|
+
price_change: priceChange
|
|
2022
|
+
});
|
|
2023
|
+
if (mlResult) {
|
|
2024
|
+
const mlSignals = mlResult.signals.map((s) => ({
|
|
2025
|
+
name: s.name,
|
|
2026
|
+
value: 0,
|
|
2027
|
+
signal: s.direction,
|
|
2028
|
+
strength: s.strength,
|
|
2029
|
+
description: s.description
|
|
2030
|
+
}));
|
|
2031
|
+
return {
|
|
2032
|
+
symbol: symbol.toUpperCase(),
|
|
2033
|
+
timeframe,
|
|
2034
|
+
signals: mlSignals,
|
|
2035
|
+
composite: {
|
|
2036
|
+
direction: mlResult.composite.direction,
|
|
2037
|
+
score: mlResult.composite.score,
|
|
2038
|
+
confidence: mlResult.composite.confidence
|
|
2039
|
+
},
|
|
2040
|
+
indicators: {
|
|
2041
|
+
rsi,
|
|
2042
|
+
macd,
|
|
2043
|
+
bollingerBands: bb,
|
|
2044
|
+
ema12: ema12Val || null,
|
|
2045
|
+
ema26: ema26Val || null,
|
|
2046
|
+
sma20: sma20.length > 0 ? sma20[sma20.length - 1] : null,
|
|
2047
|
+
atr,
|
|
2048
|
+
obv
|
|
2049
|
+
},
|
|
2050
|
+
timestamp: Date.now()
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
} catch {
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
1552
2056
|
const composite = calculateComposite(signals);
|
|
1553
2057
|
return {
|
|
1554
2058
|
symbol: symbol.toUpperCase(),
|
|
@@ -1559,8 +2063,8 @@ async function analyzeTechnicals(symbol, timeframe = "4h") {
|
|
|
1559
2063
|
rsi,
|
|
1560
2064
|
macd,
|
|
1561
2065
|
bollingerBands: bb,
|
|
1562
|
-
ema12:
|
|
1563
|
-
ema26:
|
|
2066
|
+
ema12: ema12Val || null,
|
|
2067
|
+
ema26: ema26Val || null,
|
|
1564
2068
|
sma20: sma20.length > 0 ? sma20[sma20.length - 1] : null,
|
|
1565
2069
|
atr,
|
|
1566
2070
|
obv
|
|
@@ -1741,6 +2245,7 @@ var init_analyzer = __esm({
|
|
|
1741
2245
|
"use strict";
|
|
1742
2246
|
init_binance();
|
|
1743
2247
|
init_indicators();
|
|
2248
|
+
init_client();
|
|
1744
2249
|
WEIGHTS = {
|
|
1745
2250
|
RSI: 20,
|
|
1746
2251
|
MACD: 20,
|
|
@@ -1800,46 +2305,6 @@ var init_fear_greed = __esm({
|
|
|
1800
2305
|
}
|
|
1801
2306
|
});
|
|
1802
2307
|
|
|
1803
|
-
// src/utils/logger.ts
|
|
1804
|
-
import pino from "pino";
|
|
1805
|
-
function createLogger(name) {
|
|
1806
|
-
const isDev = process.env["NODE_ENV"] !== "production";
|
|
1807
|
-
if (isDev) {
|
|
1808
|
-
return pino({
|
|
1809
|
-
name,
|
|
1810
|
-
level,
|
|
1811
|
-
transport: {
|
|
1812
|
-
target: "pino-pretty",
|
|
1813
|
-
options: {
|
|
1814
|
-
colorize: true
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
});
|
|
1818
|
-
}
|
|
1819
|
-
return pino({ name, level });
|
|
1820
|
-
}
|
|
1821
|
-
var level;
|
|
1822
|
-
var init_logger = __esm({
|
|
1823
|
-
"src/utils/logger.ts"() {
|
|
1824
|
-
"use strict";
|
|
1825
|
-
level = process.env["VIZZOR_LOG_LEVEL"] ?? "info";
|
|
1826
|
-
}
|
|
1827
|
-
});
|
|
1828
|
-
|
|
1829
|
-
// src/ml/client.ts
|
|
1830
|
-
function getMLClient() {
|
|
1831
|
-
return mlClient;
|
|
1832
|
-
}
|
|
1833
|
-
var log, mlClient;
|
|
1834
|
-
var init_client = __esm({
|
|
1835
|
-
"src/ml/client.ts"() {
|
|
1836
|
-
"use strict";
|
|
1837
|
-
init_logger();
|
|
1838
|
-
log = createLogger("ml-client");
|
|
1839
|
-
mlClient = null;
|
|
1840
|
-
}
|
|
1841
|
-
});
|
|
1842
|
-
|
|
1843
2308
|
// src/ml/feature-engineer.ts
|
|
1844
2309
|
async function buildFeatureVector(symbol) {
|
|
1845
2310
|
const [ta, fundingResult, tickerResult, fgResult, klines] = await Promise.allSettled([
|
|
@@ -2035,7 +2500,16 @@ async function generatePrediction(symbol) {
|
|
|
2035
2500
|
composite: Math.round(composite),
|
|
2036
2501
|
disclaimer: "This is not financial advice. Predictions are based on historical data and AI analysis. Always do your own research."
|
|
2037
2502
|
};
|
|
2038
|
-
|
|
2503
|
+
let mlClient2 = getMLClient();
|
|
2504
|
+
if (!mlClient2) {
|
|
2505
|
+
try {
|
|
2506
|
+
const cfg = getConfig();
|
|
2507
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
2508
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
2509
|
+
}
|
|
2510
|
+
} catch {
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2039
2513
|
if (mlClient2) {
|
|
2040
2514
|
try {
|
|
2041
2515
|
const features = await buildFeatureVector(symbol);
|
|
@@ -2074,6 +2548,7 @@ var init_predictor = __esm({
|
|
|
2074
2548
|
init_binance();
|
|
2075
2549
|
init_client();
|
|
2076
2550
|
init_feature_engineer();
|
|
2551
|
+
init_loader();
|
|
2077
2552
|
WEIGHTS2 = {
|
|
2078
2553
|
technical: 40,
|
|
2079
2554
|
sentiment: 20,
|
|
@@ -2162,7 +2637,39 @@ async function analyzeWallet(address, adapter) {
|
|
|
2162
2637
|
]);
|
|
2163
2638
|
const walletBalance = balance.status === "fulfilled" ? balance.value : 0n;
|
|
2164
2639
|
const txHistory = transactions.status === "fulfilled" ? transactions.value : [];
|
|
2165
|
-
const patterns = detectPatterns(txHistory
|
|
2640
|
+
const patterns = detectPatterns(txHistory);
|
|
2641
|
+
let mlBehavior;
|
|
2642
|
+
try {
|
|
2643
|
+
let mlClient2 = getMLClient();
|
|
2644
|
+
if (!mlClient2) {
|
|
2645
|
+
try {
|
|
2646
|
+
const cfg = getConfig();
|
|
2647
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
2648
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
2649
|
+
}
|
|
2650
|
+
} catch {
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
if (mlClient2 && txHistory.length > 0) {
|
|
2654
|
+
const features = buildWalletFeatures(txHistory);
|
|
2655
|
+
const result = await mlClient2.classifyWallet(features);
|
|
2656
|
+
if (result) {
|
|
2657
|
+
mlBehavior = result;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
} catch {
|
|
2661
|
+
}
|
|
2662
|
+
let riskLevel = "clean";
|
|
2663
|
+
if (patterns.some((p) => p.severity === "danger")) {
|
|
2664
|
+
riskLevel = "flagged";
|
|
2665
|
+
} else if (patterns.some((p) => p.severity === "warning")) {
|
|
2666
|
+
riskLevel = "suspicious";
|
|
2667
|
+
}
|
|
2668
|
+
if (mlBehavior && mlBehavior.risk_score > 0.6) {
|
|
2669
|
+
riskLevel = "flagged";
|
|
2670
|
+
} else if (mlBehavior && mlBehavior.risk_score > 0.3 && riskLevel === "clean") {
|
|
2671
|
+
riskLevel = "suspicious";
|
|
2672
|
+
}
|
|
2166
2673
|
return {
|
|
2167
2674
|
address,
|
|
2168
2675
|
chain: adapter.chainId,
|
|
@@ -2170,23 +2677,129 @@ async function analyzeWallet(address, adapter) {
|
|
|
2170
2677
|
transactionCount: txHistory.length,
|
|
2171
2678
|
tokenBalances: [],
|
|
2172
2679
|
patterns,
|
|
2173
|
-
riskLevel
|
|
2680
|
+
riskLevel,
|
|
2681
|
+
mlBehavior
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
function buildWalletFeatures(txHistory) {
|
|
2685
|
+
const txCount = txHistory.length;
|
|
2686
|
+
if (txCount === 0) {
|
|
2687
|
+
return {
|
|
2688
|
+
tx_count: 0,
|
|
2689
|
+
avg_value_eth: 0,
|
|
2690
|
+
max_value_eth: 0,
|
|
2691
|
+
avg_gas_used: 0,
|
|
2692
|
+
unique_recipients: 0,
|
|
2693
|
+
unique_methods: 0,
|
|
2694
|
+
time_span_hours: 0,
|
|
2695
|
+
avg_interval_seconds: 3600,
|
|
2696
|
+
min_interval_seconds: 60,
|
|
2697
|
+
contract_interaction_pct: 0,
|
|
2698
|
+
self_transfer_pct: 0,
|
|
2699
|
+
high_value_tx_pct: 0,
|
|
2700
|
+
failed_tx_pct: 0,
|
|
2701
|
+
token_diversity: 0
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
const values = txHistory.map((tx) => Number(tx.value) / 1e18);
|
|
2705
|
+
const gasValues = txHistory.map((tx) => Number(tx.gasUsed ?? 21e3));
|
|
2706
|
+
const recipients = new Set(txHistory.map((tx) => tx.to));
|
|
2707
|
+
const methods = new Set(
|
|
2708
|
+
txHistory.filter((tx) => tx.input && tx.input.length > 10).map((tx) => tx.input.slice(0, 10))
|
|
2709
|
+
);
|
|
2710
|
+
const timestamps = txHistory.map((tx) => tx.timestamp).sort((a, b) => a - b);
|
|
2711
|
+
const timeSpan = timestamps.length > 1 ? (timestamps.at(-1) - timestamps[0]) / 3600 : 0;
|
|
2712
|
+
const intervals = [];
|
|
2713
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
2714
|
+
intervals.push(timestamps[i] - timestamps[i - 1]);
|
|
2715
|
+
}
|
|
2716
|
+
const selfTxs = txHistory.filter((tx) => tx.from === tx.to).length;
|
|
2717
|
+
const contractTxs = txHistory.filter((tx) => tx.input && tx.input.length > 10).length;
|
|
2718
|
+
const revertedTxs = txHistory.filter((tx) => tx.status === "reverted").length;
|
|
2719
|
+
const highValueTxs = values.filter((v) => v > 10).length;
|
|
2720
|
+
return {
|
|
2721
|
+
tx_count: txCount,
|
|
2722
|
+
avg_value_eth: values.reduce((a, b) => a + b, 0) / txCount,
|
|
2723
|
+
max_value_eth: Math.max(...values),
|
|
2724
|
+
avg_gas_used: gasValues.reduce((a, b) => a + b, 0) / txCount,
|
|
2725
|
+
unique_recipients: recipients.size,
|
|
2726
|
+
unique_methods: methods.size,
|
|
2727
|
+
time_span_hours: timeSpan,
|
|
2728
|
+
avg_interval_seconds: intervals.length > 0 ? intervals.reduce((a, b) => a + b, 0) / intervals.length : 3600,
|
|
2729
|
+
min_interval_seconds: intervals.length > 0 ? Math.min(...intervals) : 60,
|
|
2730
|
+
contract_interaction_pct: contractTxs / txCount,
|
|
2731
|
+
self_transfer_pct: selfTxs / txCount,
|
|
2732
|
+
high_value_tx_pct: highValueTxs / txCount,
|
|
2733
|
+
failed_tx_pct: revertedTxs / txCount,
|
|
2734
|
+
token_diversity: methods.size
|
|
2174
2735
|
};
|
|
2175
2736
|
}
|
|
2176
|
-
function detectPatterns(
|
|
2737
|
+
function detectPatterns(txHistory) {
|
|
2177
2738
|
const patterns = [];
|
|
2739
|
+
const txCount = txHistory.length;
|
|
2178
2740
|
if (txCount === 0) {
|
|
2179
2741
|
patterns.push({
|
|
2180
2742
|
type: "new_wallet",
|
|
2181
2743
|
description: "Wallet has no transaction history",
|
|
2182
2744
|
severity: "info"
|
|
2183
2745
|
});
|
|
2746
|
+
return patterns;
|
|
2747
|
+
}
|
|
2748
|
+
if (txCount > 50) {
|
|
2749
|
+
const timestamps = txHistory.map((tx) => tx.timestamp).sort((a, b) => a - b);
|
|
2750
|
+
const intervals = [];
|
|
2751
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
2752
|
+
intervals.push(timestamps[i] - timestamps[i - 1]);
|
|
2753
|
+
}
|
|
2754
|
+
const minInterval = Math.min(...intervals);
|
|
2755
|
+
if (minInterval < 5) {
|
|
2756
|
+
patterns.push({
|
|
2757
|
+
type: "rapid_transactions",
|
|
2758
|
+
description: `Extremely rapid transactions detected (${minInterval}s apart) \u2014 possible bot activity`,
|
|
2759
|
+
severity: "warning"
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
const revertedTxs = txHistory.filter((tx) => tx.status === "reverted").length;
|
|
2764
|
+
if (txCount > 10 && revertedTxs / txCount > 0.3) {
|
|
2765
|
+
patterns.push({
|
|
2766
|
+
type: "high_failure_rate",
|
|
2767
|
+
description: `${Math.round(revertedTxs / txCount * 100)}% of transactions failed \u2014 possible sniper or MEV bot`,
|
|
2768
|
+
severity: "warning"
|
|
2769
|
+
});
|
|
2770
|
+
}
|
|
2771
|
+
const values = txHistory.map((tx) => Number(tx.value) / 1e18);
|
|
2772
|
+
const maxValue = Math.max(...values);
|
|
2773
|
+
if (maxValue > 100) {
|
|
2774
|
+
patterns.push({
|
|
2775
|
+
type: "whale_activity",
|
|
2776
|
+
description: `Large transfers detected (max: ${maxValue.toFixed(2)} ETH)`,
|
|
2777
|
+
severity: "info"
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
const selfTxs = txHistory.filter((tx) => tx.from === tx.to).length;
|
|
2781
|
+
if (selfTxs / txCount > 0.2) {
|
|
2782
|
+
patterns.push({
|
|
2783
|
+
type: "self_transfers",
|
|
2784
|
+
description: `${Math.round(selfTxs / txCount * 100)}% self-transfers \u2014 possible mixing activity`,
|
|
2785
|
+
severity: "danger"
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
const contractTxs = txHistory.filter((tx) => tx.input && tx.input.length > 10).length;
|
|
2789
|
+
if (contractTxs / txCount > 0.9 && txCount > 20) {
|
|
2790
|
+
patterns.push({
|
|
2791
|
+
type: "contract_heavy",
|
|
2792
|
+
description: "Almost exclusively interacts with smart contracts \u2014 automated behavior",
|
|
2793
|
+
severity: "warning"
|
|
2794
|
+
});
|
|
2184
2795
|
}
|
|
2185
2796
|
return patterns;
|
|
2186
2797
|
}
|
|
2187
2798
|
var init_wallet_analyzer = __esm({
|
|
2188
2799
|
"src/core/forensics/wallet-analyzer.ts"() {
|
|
2189
2800
|
"use strict";
|
|
2801
|
+
init_client();
|
|
2802
|
+
init_loader();
|
|
2190
2803
|
}
|
|
2191
2804
|
});
|
|
2192
2805
|
|
|
@@ -2771,8 +3384,6 @@ async function handleConfigInit() {
|
|
|
2771
3384
|
console.log(` 3. Try scanning a project: ${chalk6.cyan("vizzor scan ethereum")}`);
|
|
2772
3385
|
}
|
|
2773
3386
|
async function handleConfigSet(key, value) {
|
|
2774
|
-
const configDir = getConfigDir();
|
|
2775
|
-
const configPath = resolve(configDir, "config.yaml");
|
|
2776
3387
|
const isSensitive = key.toLowerCase().includes("key") || key.toLowerCase().includes("token");
|
|
2777
3388
|
if (isSensitive) {
|
|
2778
3389
|
const error = validateKey(key, value);
|
|
@@ -2784,20 +3395,12 @@ async function handleConfigSet(key, value) {
|
|
|
2784
3395
|
console.log(chalk6.yellow(error));
|
|
2785
3396
|
}
|
|
2786
3397
|
}
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
if (!updatedConfig[section] || typeof updatedConfig[section] !== "object") {
|
|
2793
|
-
updatedConfig[section] = {};
|
|
2794
|
-
}
|
|
2795
|
-
const parsed = field === "maxTokens" ? Number(value) : value;
|
|
2796
|
-
updatedConfig[section][field] = parsed;
|
|
2797
|
-
} else {
|
|
2798
|
-
updatedConfig[key] = value;
|
|
3398
|
+
try {
|
|
3399
|
+
saveConfigValue(key, value);
|
|
3400
|
+
} catch (err) {
|
|
3401
|
+
console.log(chalk6.red(err instanceof Error ? err.message : String(err)));
|
|
3402
|
+
return;
|
|
2799
3403
|
}
|
|
2800
|
-
writeFileSync2(configPath, yamlStringify(updatedConfig), "utf-8");
|
|
2801
3404
|
const displayValue = isSensitive ? maskKey(value) : value;
|
|
2802
3405
|
console.log(chalk6.green(`Set ${key} = ${displayValue}`));
|
|
2803
3406
|
}
|
|
@@ -3780,10 +4383,25 @@ var init_goplus = __esm({
|
|
|
3780
4383
|
async function buildContextBlock(userMessage) {
|
|
3781
4384
|
const lower = userMessage.toLowerCase();
|
|
3782
4385
|
const sections = [];
|
|
3783
|
-
|
|
4386
|
+
let mlIntent = null;
|
|
4387
|
+
const mlClientForIntent = getMLClient();
|
|
4388
|
+
if (mlClientForIntent) {
|
|
4389
|
+
try {
|
|
4390
|
+
const intentResult = await mlClientForIntent.classifyIntent(userMessage);
|
|
4391
|
+
if (intentResult && intentResult.confidence > 0.7) {
|
|
4392
|
+
mlIntent = intentResult.intent;
|
|
4393
|
+
}
|
|
4394
|
+
} catch {
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
const evmMatch = userMessage.match(/0x[a-fA-F0-9]{40}/);
|
|
4398
|
+
const solanaMatch = userMessage.match(/\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/);
|
|
4399
|
+
const isSolanaAddr = solanaMatch && !evmMatch && /\d/.test(solanaMatch[1]) && solanaMatch[1].length >= 32;
|
|
4400
|
+
const addressMatch = evmMatch ?? (isSolanaAddr ? solanaMatch : null);
|
|
4401
|
+
const detectedChain = evmMatch ? null : isSolanaAddr ? "solana" : null;
|
|
3784
4402
|
const mentionedTokens = detectTokens(lower);
|
|
3785
4403
|
const unknownTokens = detectUnknownTokens(userMessage, mentionedTokens);
|
|
3786
|
-
const isAnalysisQuery = matchesAny(lower, ANALYSIS_KEYWORDS);
|
|
4404
|
+
const isAnalysisQuery = matchesAny(lower, ANALYSIS_KEYWORDS) || mlIntent === "analysis";
|
|
3787
4405
|
const tasks = [];
|
|
3788
4406
|
if (mentionedTokens.length > 0 || addressMatch || matchesAny(lower, PRICE_KEYWORDS)) {
|
|
3789
4407
|
tasks.push(
|
|
@@ -3809,8 +4427,7 @@ async function buildContextBlock(userMessage) {
|
|
|
3809
4427
|
}
|
|
3810
4428
|
}
|
|
3811
4429
|
if (addressMatch && isAnalysisQuery) {
|
|
3812
|
-
const
|
|
3813
|
-
const chain = cfg.defaultChain || "ethereum";
|
|
4430
|
+
const chain = detectedChain || getConfig().defaultChain || "ethereum";
|
|
3814
4431
|
tasks.push(
|
|
3815
4432
|
fetchSecurityData(addressMatch[0], chain).then((data) => {
|
|
3816
4433
|
if (data) sections.push(data);
|
|
@@ -3851,8 +4468,9 @@ async function buildContextBlock(userMessage) {
|
|
|
3851
4468
|
})
|
|
3852
4469
|
);
|
|
3853
4470
|
}
|
|
3854
|
-
const isBroadQuery = matchesAny(lower, BROAD_KEYWORDS);
|
|
3855
|
-
|
|
4471
|
+
const isBroadQuery = matchesAny(lower, BROAD_KEYWORDS) || mlIntent === "broad_overview";
|
|
4472
|
+
const hasSpecificIntent = mentionedTokens.length > 0 || unknownTokens.length > 0 || !!addressMatch;
|
|
4473
|
+
if (isBroadQuery && !hasSpecificIntent) {
|
|
3856
4474
|
if (!matchesAny(lower, TRENDING_KEYWORDS)) {
|
|
3857
4475
|
tasks.push(
|
|
3858
4476
|
fetchTrendingData().then((data) => {
|
|
@@ -3874,9 +4492,18 @@ async function buildContextBlock(userMessage) {
|
|
|
3874
4492
|
})
|
|
3875
4493
|
);
|
|
3876
4494
|
}
|
|
3877
|
-
if (!matchesAny(lower, PRICE_KEYWORDS)
|
|
4495
|
+
if (!matchesAny(lower, PRICE_KEYWORDS)) {
|
|
4496
|
+
tasks.push(
|
|
4497
|
+
fetchTokenData(["bitcoin", "ethereum", "solana"]).then((data) => {
|
|
4498
|
+
if (data) sections.push(data);
|
|
4499
|
+
})
|
|
4500
|
+
);
|
|
4501
|
+
}
|
|
4502
|
+
} else if (isBroadQuery && hasSpecificIntent) {
|
|
4503
|
+
if (!matchesAny(lower, NEWS_KEYWORDS)) {
|
|
4504
|
+
const symbol = mentionedTokens[0]?.toUpperCase() || unknownTokens[0]?.toUpperCase();
|
|
3878
4505
|
tasks.push(
|
|
3879
|
-
|
|
4506
|
+
fetchNewsData(symbol).then((data) => {
|
|
3880
4507
|
if (data) sections.push(data);
|
|
3881
4508
|
})
|
|
3882
4509
|
);
|
|
@@ -4158,8 +4785,30 @@ async function fetchNewsData(symbol) {
|
|
|
4158
4785
|
try {
|
|
4159
4786
|
const news = await fetchCryptoNews(symbol, getConfig().cryptopanicApiKey);
|
|
4160
4787
|
if (news.length > 0) {
|
|
4788
|
+
const headlines = news.slice(0, 8);
|
|
4161
4789
|
const lines = [`## Latest Crypto News${symbol ? ` (${symbol})` : ""}`];
|
|
4162
|
-
|
|
4790
|
+
const mlClient2 = getMLClient();
|
|
4791
|
+
if (mlClient2) {
|
|
4792
|
+
try {
|
|
4793
|
+
const texts = headlines.map((n) => n.title);
|
|
4794
|
+
const mlResults = await mlClient2.analyzeSentimentBatch(texts);
|
|
4795
|
+
if (mlResults.length > 0) {
|
|
4796
|
+
for (let i = 0; i < headlines.length; i++) {
|
|
4797
|
+
const n = headlines[i];
|
|
4798
|
+
const ml = mlResults[i];
|
|
4799
|
+
const label = ml ? `${ml.sentiment.toUpperCase()} (${(ml.confidence * 100).toFixed(0)}%)` : n.sentiment.toUpperCase();
|
|
4800
|
+
lines.push(`- [${label}] ${n.title} (${n.source.title}, ${n.publishedAt})`);
|
|
4801
|
+
}
|
|
4802
|
+
const avgScore = mlResults.reduce((s, r) => s + r.score, 0) / mlResults.length;
|
|
4803
|
+
const avgSentiment = avgScore > 0.2 ? "BULLISH" : avgScore < -0.2 ? "BEARISH" : "NEUTRAL";
|
|
4804
|
+
lines.push(`
|
|
4805
|
+
ML Aggregate Sentiment: ${avgSentiment} (score: ${avgScore.toFixed(3)})`);
|
|
4806
|
+
return lines.join("\n");
|
|
4807
|
+
}
|
|
4808
|
+
} catch {
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
for (const n of headlines) {
|
|
4163
4812
|
lines.push(
|
|
4164
4813
|
`- [${n.sentiment.toUpperCase()}] ${n.title} (${n.source.title}, ${n.publishedAt})`
|
|
4165
4814
|
);
|
|
@@ -4198,7 +4847,7 @@ async function fetchRaisesData() {
|
|
|
4198
4847
|
if (raises.length === 0) return null;
|
|
4199
4848
|
const lines = ["## Recent Crypto Fundraising Rounds (last 30 days)"];
|
|
4200
4849
|
for (const r of raises.slice(0, 10)) {
|
|
4201
|
-
const amount = r.amount ? `$${(r.amount / 1e6).toFixed(1)}M` : "undisclosed";
|
|
4850
|
+
const amount = r.amount ? r.amount >= 1e9 ? `$${(r.amount / 1e9).toFixed(1)}B` : r.amount >= 1e6 ? `$${(r.amount / 1e6).toFixed(1)}M` : r.amount >= 1e3 ? `$${(r.amount / 1e3).toFixed(0)}K` : `$${r.amount.toLocaleString()}` : "undisclosed";
|
|
4202
4851
|
const date = new Date(r.date * 1e3).toISOString().split("T")[0];
|
|
4203
4852
|
lines.push(
|
|
4204
4853
|
`- ${r.name} \u2014 ${r.round} (${amount}) on ${r.chains.join(", ") || "multi-chain"} [${date}]${r.leadInvestors.length > 0 ? ` Led by: ${r.leadInvestors.join(", ")}` : ""}`
|
|
@@ -5135,6 +5784,7 @@ var init_context_injector = __esm({
|
|
|
5135
5784
|
init_goplus();
|
|
5136
5785
|
init_loader();
|
|
5137
5786
|
init_constants();
|
|
5787
|
+
init_client();
|
|
5138
5788
|
PRICE_KEYWORDS = ["price", "worth", "cost", "value", "how much"];
|
|
5139
5789
|
TRENDING_KEYWORDS = ["trending", "hot", "popular", "top", "best", "hype"];
|
|
5140
5790
|
NEWS_KEYWORDS = ["news", "latest", "update", "happening", "announcement"];
|
|
@@ -5514,6 +6164,7 @@ var DANGEROUS_FUNCTIONS, DANGEROUS_OPCODES;
|
|
|
5514
6164
|
var init_bytecode_scanner = __esm({
|
|
5515
6165
|
"src/core/forensics/bytecode-scanner.ts"() {
|
|
5516
6166
|
"use strict";
|
|
6167
|
+
init_client();
|
|
5517
6168
|
DANGEROUS_FUNCTIONS = [
|
|
5518
6169
|
// Mint functions — inflation risk
|
|
5519
6170
|
{
|
|
@@ -5671,6 +6322,42 @@ async function detectRugIndicators(tokenAddress, adapter) {
|
|
|
5671
6322
|
}
|
|
5672
6323
|
}
|
|
5673
6324
|
const riskScore = calculateRugRisk(details);
|
|
6325
|
+
let mlAnalysis;
|
|
6326
|
+
try {
|
|
6327
|
+
let mlClient2 = getMLClient();
|
|
6328
|
+
if (!mlClient2) {
|
|
6329
|
+
try {
|
|
6330
|
+
const cfg = getConfig();
|
|
6331
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
6332
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
6333
|
+
}
|
|
6334
|
+
} catch {
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
if (mlClient2) {
|
|
6338
|
+
const mlResult = await mlClient2.predictRug({
|
|
6339
|
+
bytecode_size: hasCode ? code.length : 0,
|
|
6340
|
+
is_verified: hasCode ? 1 : 0,
|
|
6341
|
+
holder_concentration: 0,
|
|
6342
|
+
has_proxy: 0,
|
|
6343
|
+
has_mint: canMint ? 1 : 0,
|
|
6344
|
+
has_pause: canPause ? 1 : 0,
|
|
6345
|
+
has_blacklist: hasBlacklist_ ? 1 : 0,
|
|
6346
|
+
liquidity_locked: 0,
|
|
6347
|
+
buy_tax: 0,
|
|
6348
|
+
sell_tax: 0,
|
|
6349
|
+
contract_age_days: 0,
|
|
6350
|
+
total_transfers: 0,
|
|
6351
|
+
owner_balance_pct: 0,
|
|
6352
|
+
is_open_source: hasCode ? 1 : 0,
|
|
6353
|
+
top10_holder_pct: 0
|
|
6354
|
+
});
|
|
6355
|
+
if (mlResult) {
|
|
6356
|
+
mlAnalysis = mlResult;
|
|
6357
|
+
}
|
|
6358
|
+
}
|
|
6359
|
+
} catch {
|
|
6360
|
+
}
|
|
5674
6361
|
return {
|
|
5675
6362
|
isHoneypot: canPause || hasBlacklist_,
|
|
5676
6363
|
hasLiquidityLock: false,
|
|
@@ -5678,8 +6365,9 @@ async function detectRugIndicators(tokenAddress, adapter) {
|
|
|
5678
6365
|
ownerCanPause: canPause,
|
|
5679
6366
|
hasBlacklist: hasBlacklist_,
|
|
5680
6367
|
highSellTax: false,
|
|
5681
|
-
riskScore,
|
|
5682
|
-
details
|
|
6368
|
+
riskScore: mlAnalysis ? Math.round(riskScore * 0.4 + mlAnalysis.rug_probability * 100 * 0.6) : riskScore,
|
|
6369
|
+
details,
|
|
6370
|
+
mlAnalysis
|
|
5683
6371
|
};
|
|
5684
6372
|
}
|
|
5685
6373
|
function calculateRugRisk(details) {
|
|
@@ -5705,6 +6393,83 @@ var init_rug_detector = __esm({
|
|
|
5705
6393
|
"src/core/forensics/rug-detector.ts"() {
|
|
5706
6394
|
"use strict";
|
|
5707
6395
|
init_bytecode_scanner();
|
|
6396
|
+
init_client();
|
|
6397
|
+
init_loader();
|
|
6398
|
+
}
|
|
6399
|
+
});
|
|
6400
|
+
|
|
6401
|
+
// src/core/trends/regime.ts
|
|
6402
|
+
async function detectMarketRegime(_symbol, features) {
|
|
6403
|
+
const mlClient2 = getMLClient();
|
|
6404
|
+
if (mlClient2) {
|
|
6405
|
+
try {
|
|
6406
|
+
const result = await mlClient2.detectRegime(features);
|
|
6407
|
+
if (result) {
|
|
6408
|
+
return {
|
|
6409
|
+
regime: result.regime,
|
|
6410
|
+
confidence: result.confidence,
|
|
6411
|
+
probabilities: result.probabilities,
|
|
6412
|
+
model: result.model
|
|
6413
|
+
};
|
|
6414
|
+
}
|
|
6415
|
+
} catch {
|
|
6416
|
+
}
|
|
6417
|
+
}
|
|
6418
|
+
return detectRegimeHeuristic(features);
|
|
6419
|
+
}
|
|
6420
|
+
function detectRegimeHeuristic(features) {
|
|
6421
|
+
const vol = features.volatility_14d;
|
|
6422
|
+
const ret7d = features.returns_7d;
|
|
6423
|
+
const fg = features.fear_greed;
|
|
6424
|
+
const rsi = features.rsi;
|
|
6425
|
+
let regime;
|
|
6426
|
+
let confidence;
|
|
6427
|
+
if (fg < 15 && ret7d < -20) {
|
|
6428
|
+
regime = "capitulation";
|
|
6429
|
+
confidence = 80;
|
|
6430
|
+
} else if (vol > 8) {
|
|
6431
|
+
regime = "volatile";
|
|
6432
|
+
confidence = 70;
|
|
6433
|
+
} else if (vol > 5 && ret7d > 10) {
|
|
6434
|
+
regime = "trending_bull";
|
|
6435
|
+
confidence = 65;
|
|
6436
|
+
} else if (vol > 5 && ret7d < -10) {
|
|
6437
|
+
regime = "trending_bear";
|
|
6438
|
+
confidence = 65;
|
|
6439
|
+
} else if (ret7d > 5 && rsi > 55) {
|
|
6440
|
+
regime = "trending_bull";
|
|
6441
|
+
confidence = 55;
|
|
6442
|
+
} else if (ret7d < -5 && rsi < 45) {
|
|
6443
|
+
regime = "trending_bear";
|
|
6444
|
+
confidence = 55;
|
|
6445
|
+
} else {
|
|
6446
|
+
regime = "ranging";
|
|
6447
|
+
confidence = 60;
|
|
6448
|
+
}
|
|
6449
|
+
const probabilities = {
|
|
6450
|
+
trending_bull: 0.05,
|
|
6451
|
+
trending_bear: 0.05,
|
|
6452
|
+
ranging: 0.05,
|
|
6453
|
+
volatile: 0.05,
|
|
6454
|
+
capitulation: 0.05
|
|
6455
|
+
};
|
|
6456
|
+
probabilities[regime] = confidence / 100;
|
|
6457
|
+
const remaining = 1 - probabilities[regime];
|
|
6458
|
+
const others = Object.keys(probabilities).filter((k) => k !== regime);
|
|
6459
|
+
for (const k of others) {
|
|
6460
|
+
probabilities[k] = remaining / others.length;
|
|
6461
|
+
}
|
|
6462
|
+
return {
|
|
6463
|
+
regime,
|
|
6464
|
+
confidence,
|
|
6465
|
+
probabilities,
|
|
6466
|
+
model: "heuristic-regime-detector"
|
|
6467
|
+
};
|
|
6468
|
+
}
|
|
6469
|
+
var init_regime = __esm({
|
|
6470
|
+
"src/core/trends/regime.ts"() {
|
|
6471
|
+
"use strict";
|
|
6472
|
+
init_client();
|
|
5708
6473
|
}
|
|
5709
6474
|
});
|
|
5710
6475
|
|
|
@@ -5895,6 +6660,7 @@ var momentumStrategy;
|
|
|
5895
6660
|
var init_momentum = __esm({
|
|
5896
6661
|
"src/core/agent/strategies/momentum.ts"() {
|
|
5897
6662
|
"use strict";
|
|
6663
|
+
init_client();
|
|
5898
6664
|
momentumStrategy = {
|
|
5899
6665
|
name: "momentum",
|
|
5900
6666
|
description: "RSI reversal + MACD confirmation. Buy oversold bounces, sell overbought peaks.",
|
|
@@ -5970,6 +6736,7 @@ var trendFollowingStrategy;
|
|
|
5970
6736
|
var init_trend_following = __esm({
|
|
5971
6737
|
"src/core/agent/strategies/trend-following.ts"() {
|
|
5972
6738
|
"use strict";
|
|
6739
|
+
init_client();
|
|
5973
6740
|
trendFollowingStrategy = {
|
|
5974
6741
|
name: "trend-following",
|
|
5975
6742
|
description: "EMA crossover trend detection. Buy golden cross, sell death cross.",
|
|
@@ -6128,7 +6895,7 @@ var init_ml_adaptive = __esm({
|
|
|
6128
6895
|
"src/core/agent/strategies/ml-adaptive.ts"() {
|
|
6129
6896
|
"use strict";
|
|
6130
6897
|
init_client();
|
|
6131
|
-
|
|
6898
|
+
init_regime();
|
|
6132
6899
|
mlAdaptiveStrategy = {
|
|
6133
6900
|
name: "ml-adaptive",
|
|
6134
6901
|
description: "ML-powered strategy using contextual bandit approach. Uses LSTM/RF predictions when available, falls back to rule-based when < 100 training samples.",
|
|
@@ -7078,7 +7845,16 @@ async function handleTool(name, input) {
|
|
|
7078
7845
|
}
|
|
7079
7846
|
case "get_ml_prediction": {
|
|
7080
7847
|
const symbol = String(params["symbol"] ?? "BTC");
|
|
7081
|
-
|
|
7848
|
+
let mlClient2 = getMLClient();
|
|
7849
|
+
if (!mlClient2) {
|
|
7850
|
+
try {
|
|
7851
|
+
const cfg = getConfig();
|
|
7852
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
7853
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
7854
|
+
}
|
|
7855
|
+
} catch {
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7082
7858
|
if (!mlClient2) {
|
|
7083
7859
|
const prediction = await generatePrediction(symbol);
|
|
7084
7860
|
return {
|
|
@@ -7139,6 +7915,256 @@ async function handleTool(name, input) {
|
|
|
7139
7915
|
}
|
|
7140
7916
|
};
|
|
7141
7917
|
}
|
|
7918
|
+
case "get_rug_ml_analysis": {
|
|
7919
|
+
const address = String(params["address"] ?? "");
|
|
7920
|
+
const chain = String(params["chain"] ?? DEFAULT_CHAIN);
|
|
7921
|
+
const adapter = getAdapter(chain);
|
|
7922
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
7923
|
+
const indicators = await detectRugIndicators(address, adapter);
|
|
7924
|
+
let goplus = null;
|
|
7925
|
+
try {
|
|
7926
|
+
goplus = await checkTokenSecurity(address, chain);
|
|
7927
|
+
} catch {
|
|
7928
|
+
}
|
|
7929
|
+
let mlClient2 = getMLClient();
|
|
7930
|
+
if (!mlClient2) {
|
|
7931
|
+
try {
|
|
7932
|
+
const cfg = getConfig();
|
|
7933
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
7934
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
7935
|
+
}
|
|
7936
|
+
} catch {
|
|
7937
|
+
}
|
|
7938
|
+
}
|
|
7939
|
+
let mlResult = indicators.mlAnalysis ?? null;
|
|
7940
|
+
if (!mlResult && mlClient2 && goplus) {
|
|
7941
|
+
mlResult = await mlClient2.predictRug({
|
|
7942
|
+
bytecode_size: 0,
|
|
7943
|
+
is_verified: goplus.isOpenSource ? 1 : 0,
|
|
7944
|
+
holder_concentration: (goplus.creatorPercent ?? 0) + (goplus.ownerPercent ?? 0),
|
|
7945
|
+
has_proxy: goplus.isProxy ? 1 : 0,
|
|
7946
|
+
has_mint: goplus.isMintable ? 1 : 0,
|
|
7947
|
+
has_pause: indicators.ownerCanPause ? 1 : 0,
|
|
7948
|
+
has_blacklist: indicators.hasBlacklist ? 1 : 0,
|
|
7949
|
+
liquidity_locked: 0,
|
|
7950
|
+
buy_tax: goplus.buyTax ?? 0,
|
|
7951
|
+
sell_tax: goplus.sellTax ?? 0,
|
|
7952
|
+
contract_age_days: 0,
|
|
7953
|
+
total_transfers: 0,
|
|
7954
|
+
owner_balance_pct: goplus.ownerPercent ?? 0,
|
|
7955
|
+
is_open_source: goplus.isOpenSource ? 1 : 0,
|
|
7956
|
+
top10_holder_pct: 0
|
|
7957
|
+
}) ?? null;
|
|
7958
|
+
}
|
|
7959
|
+
return {
|
|
7960
|
+
address,
|
|
7961
|
+
chain,
|
|
7962
|
+
ruleBasedRiskScore: indicators.riskScore,
|
|
7963
|
+
mlAnalysis: mlResult ?? { note: "ML sidecar not available" },
|
|
7964
|
+
indicators: {
|
|
7965
|
+
isHoneypot: indicators.isHoneypot,
|
|
7966
|
+
ownerCanMint: indicators.ownerCanMint,
|
|
7967
|
+
ownerCanPause: indicators.ownerCanPause,
|
|
7968
|
+
hasBlacklist: indicators.hasBlacklist,
|
|
7969
|
+
highSellTax: indicators.highSellTax
|
|
7970
|
+
},
|
|
7971
|
+
details: indicators.details,
|
|
7972
|
+
goplus: goplus ? {
|
|
7973
|
+
riskLevel: goplus.riskLevel,
|
|
7974
|
+
buyTax: goplus.buyTax,
|
|
7975
|
+
sellTax: goplus.sellTax,
|
|
7976
|
+
isHoneypot: goplus.isHoneypot,
|
|
7977
|
+
isMintable: goplus.isMintable,
|
|
7978
|
+
holderCount: goplus.holderCount
|
|
7979
|
+
} : null
|
|
7980
|
+
};
|
|
7981
|
+
}
|
|
7982
|
+
case "get_wallet_behavior": {
|
|
7983
|
+
const address = String(params["address"] ?? "");
|
|
7984
|
+
const chain = String(params["chain"] ?? DEFAULT_CHAIN);
|
|
7985
|
+
const adapter = getAdapter(chain);
|
|
7986
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
7987
|
+
const analysis = await analyzeWallet(address, adapter);
|
|
7988
|
+
return {
|
|
7989
|
+
address: analysis.address,
|
|
7990
|
+
chain: analysis.chain,
|
|
7991
|
+
balance: analysis.balance.toString(),
|
|
7992
|
+
transactionCount: analysis.transactionCount,
|
|
7993
|
+
riskLevel: analysis.riskLevel,
|
|
7994
|
+
patterns: analysis.patterns,
|
|
7995
|
+
mlBehavior: analysis.mlBehavior ?? { note: "ML sidecar not available" }
|
|
7996
|
+
};
|
|
7997
|
+
}
|
|
7998
|
+
case "analyze_news_sentiment": {
|
|
7999
|
+
const symbol = String(params["symbol"] ?? "");
|
|
8000
|
+
let mlClient2 = getMLClient();
|
|
8001
|
+
if (!mlClient2) {
|
|
8002
|
+
try {
|
|
8003
|
+
const cfg = getConfig();
|
|
8004
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
8005
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
8006
|
+
}
|
|
8007
|
+
} catch {
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
const news = await fetchCryptoNews(symbol || void 0, getConfig().cryptopanicApiKey);
|
|
8011
|
+
if (news.length === 0) {
|
|
8012
|
+
return { symbol, sentiment: "neutral", note: "No news found" };
|
|
8013
|
+
}
|
|
8014
|
+
const headlines = news.slice(0, 10).map((n) => n.title);
|
|
8015
|
+
if (mlClient2) {
|
|
8016
|
+
const results = await mlClient2.analyzeSentimentBatch(headlines);
|
|
8017
|
+
if (results.length > 0) {
|
|
8018
|
+
const avgScore = results.reduce((s, r) => s + r.score, 0) / results.length;
|
|
8019
|
+
const avgConf = results.reduce((s, r) => s + r.confidence, 0) / results.length;
|
|
8020
|
+
const allTopics = [...new Set(results.flatMap((r) => r.key_topics))];
|
|
8021
|
+
return {
|
|
8022
|
+
symbol,
|
|
8023
|
+
sentiment: avgScore > 0.2 ? "bullish" : avgScore < -0.2 ? "bearish" : "neutral",
|
|
8024
|
+
score: Math.round(avgScore * 1e3) / 1e3,
|
|
8025
|
+
confidence: Math.round(avgConf * 100),
|
|
8026
|
+
topics: allTopics.slice(0, 5),
|
|
8027
|
+
headlines: results.map((r, i) => ({
|
|
8028
|
+
title: headlines[i],
|
|
8029
|
+
sentiment: r.sentiment,
|
|
8030
|
+
score: r.score,
|
|
8031
|
+
confidence: r.confidence
|
|
8032
|
+
})),
|
|
8033
|
+
model: results[0]?.model ?? "unknown",
|
|
8034
|
+
articleCount: news.length
|
|
8035
|
+
};
|
|
8036
|
+
}
|
|
8037
|
+
}
|
|
8038
|
+
let pos = 0;
|
|
8039
|
+
let neg = 0;
|
|
8040
|
+
for (const n of news) {
|
|
8041
|
+
if (n.sentiment === "positive") pos++;
|
|
8042
|
+
else if (n.sentiment === "negative") neg++;
|
|
8043
|
+
}
|
|
8044
|
+
const score = news.length > 0 ? (pos - neg) / news.length : 0;
|
|
8045
|
+
return {
|
|
8046
|
+
symbol,
|
|
8047
|
+
sentiment: score > 0.2 ? "bullish" : score < -0.2 ? "bearish" : "neutral",
|
|
8048
|
+
score,
|
|
8049
|
+
confidence: 50,
|
|
8050
|
+
topics: [],
|
|
8051
|
+
headlines: headlines.map((h, i) => ({
|
|
8052
|
+
title: h,
|
|
8053
|
+
sentiment: news[i]?.sentiment ?? "neutral"
|
|
8054
|
+
})),
|
|
8055
|
+
model: "vote-count-fallback",
|
|
8056
|
+
articleCount: news.length
|
|
8057
|
+
};
|
|
8058
|
+
}
|
|
8059
|
+
case "get_market_regime": {
|
|
8060
|
+
const symbol = String(params["symbol"] ?? "BTC");
|
|
8061
|
+
const ta = await analyzeTechnicals(symbol, "4h");
|
|
8062
|
+
const [fgResult, fundingResult] = await Promise.allSettled([
|
|
8063
|
+
fetchFearGreedIndex(1),
|
|
8064
|
+
fetchFundingRate(symbol)
|
|
8065
|
+
]);
|
|
8066
|
+
const fg = fgResult.status === "fulfilled" ? fgResult.value.current.value : 50;
|
|
8067
|
+
const funding = fundingResult.status === "fulfilled" ? fundingResult.value.fundingRate : 0;
|
|
8068
|
+
const price = ta.indicators.ema12 ?? 0;
|
|
8069
|
+
const atrVal = ta.indicators.atr ?? 0;
|
|
8070
|
+
const atrPct = price > 0 ? atrVal / price * 100 : 3;
|
|
8071
|
+
const regime = await detectMarketRegime(symbol, {
|
|
8072
|
+
returns_1d: 0,
|
|
8073
|
+
returns_7d: 0,
|
|
8074
|
+
volatility_14d: atrPct,
|
|
8075
|
+
volume_ratio: 1,
|
|
8076
|
+
rsi: ta.indicators.rsi ?? 50,
|
|
8077
|
+
bb_width: ta.indicators.bollingerBands ? (ta.indicators.bollingerBands.upper - ta.indicators.bollingerBands.lower) / ta.indicators.bollingerBands.middle * 100 : 0,
|
|
8078
|
+
fear_greed: fg,
|
|
8079
|
+
funding_rate: funding,
|
|
8080
|
+
price_vs_sma200: 0
|
|
8081
|
+
});
|
|
8082
|
+
return {
|
|
8083
|
+
symbol: symbol.toUpperCase(),
|
|
8084
|
+
regime: regime.regime,
|
|
8085
|
+
confidence: regime.confidence,
|
|
8086
|
+
probabilities: regime.probabilities,
|
|
8087
|
+
model: regime.model
|
|
8088
|
+
};
|
|
8089
|
+
}
|
|
8090
|
+
case "get_ta_ml_analysis": {
|
|
8091
|
+
const symbol = String(params["symbol"] ?? "BTC");
|
|
8092
|
+
const timeframe = String(params["timeframe"] ?? "4h");
|
|
8093
|
+
const ta = await analyzeTechnicals(symbol, timeframe);
|
|
8094
|
+
return {
|
|
8095
|
+
symbol: ta.symbol,
|
|
8096
|
+
timeframe: ta.timeframe,
|
|
8097
|
+
composite: ta.composite,
|
|
8098
|
+
signals: ta.signals.map((s) => ({
|
|
8099
|
+
name: s.name,
|
|
8100
|
+
signal: s.signal,
|
|
8101
|
+
strength: s.strength,
|
|
8102
|
+
description: s.description
|
|
8103
|
+
})),
|
|
8104
|
+
indicators: {
|
|
8105
|
+
rsi: ta.indicators.rsi ? Math.round(ta.indicators.rsi * 100) / 100 : null,
|
|
8106
|
+
macd: ta.indicators.macd,
|
|
8107
|
+
bollingerBands: ta.indicators.bollingerBands,
|
|
8108
|
+
ema12: ta.indicators.ema12,
|
|
8109
|
+
ema26: ta.indicators.ema26,
|
|
8110
|
+
atr: ta.indicators.atr
|
|
8111
|
+
},
|
|
8112
|
+
note: "ML-enhanced: signals and composite use learned weights when ML sidecar is available"
|
|
8113
|
+
};
|
|
8114
|
+
}
|
|
8115
|
+
case "get_project_risk_ml": {
|
|
8116
|
+
const address = String(params["address"] ?? "");
|
|
8117
|
+
const chain = String(params["chain"] ?? DEFAULT_CHAIN);
|
|
8118
|
+
const adapter = getAdapter(chain);
|
|
8119
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
8120
|
+
const analysis = await analyzeProject(address, adapter);
|
|
8121
|
+
const risk = assessRisk(analysis);
|
|
8122
|
+
return {
|
|
8123
|
+
address,
|
|
8124
|
+
chain,
|
|
8125
|
+
riskScore: risk.score,
|
|
8126
|
+
riskLevel: risk.level,
|
|
8127
|
+
summary: risk.summary,
|
|
8128
|
+
factors: risk.factors,
|
|
8129
|
+
mlScore: risk.mlScore ?? null,
|
|
8130
|
+
mlLevel: risk.mlLevel ?? null,
|
|
8131
|
+
token: analysis.token ? {
|
|
8132
|
+
name: analysis.token.name,
|
|
8133
|
+
symbol: analysis.token.symbol,
|
|
8134
|
+
decimals: analysis.token.decimals
|
|
8135
|
+
} : null,
|
|
8136
|
+
holderConcentration: analysis.holderConcentration,
|
|
8137
|
+
contractVerified: analysis.contractVerified
|
|
8138
|
+
};
|
|
8139
|
+
}
|
|
8140
|
+
case "get_portfolio_forecast": {
|
|
8141
|
+
const agentName = String(params["agentName"] ?? "");
|
|
8142
|
+
const agent = getAgentByName(agentName);
|
|
8143
|
+
if (!agent) return { error: `Agent "${agentName}" not found` };
|
|
8144
|
+
let mlClient2 = getMLClient();
|
|
8145
|
+
if (!mlClient2) {
|
|
8146
|
+
try {
|
|
8147
|
+
const cfg = getConfig();
|
|
8148
|
+
if (cfg.ml?.enabled && cfg.ml.sidecarUrl) {
|
|
8149
|
+
mlClient2 = initMLClient(cfg.ml.sidecarUrl);
|
|
8150
|
+
}
|
|
8151
|
+
} catch {
|
|
8152
|
+
}
|
|
8153
|
+
}
|
|
8154
|
+
if (!mlClient2) {
|
|
8155
|
+
return {
|
|
8156
|
+
agentName,
|
|
8157
|
+
error: "ML sidecar not available for portfolio forecast"
|
|
8158
|
+
};
|
|
8159
|
+
}
|
|
8160
|
+
const state = getAgentStatus(agent.id);
|
|
8161
|
+
return {
|
|
8162
|
+
agentName,
|
|
8163
|
+
status: state?.status ?? "idle",
|
|
8164
|
+
cycleCount: state?.cycleCount ?? 0,
|
|
8165
|
+
note: "Portfolio forecast requires trade history. Use calculateMetricsWithForecast() in agent engine for full predictions."
|
|
8166
|
+
};
|
|
8167
|
+
}
|
|
7142
8168
|
case "create_agent": {
|
|
7143
8169
|
const agentName = String(params["name"] ?? "");
|
|
7144
8170
|
const strategy = String(params["strategy"] ?? "momentum");
|
|
@@ -7215,6 +8241,9 @@ var init_tool_handler = __esm({
|
|
|
7215
8241
|
init_fear_greed();
|
|
7216
8242
|
init_technical_analysis();
|
|
7217
8243
|
init_predictor();
|
|
8244
|
+
init_regime();
|
|
8245
|
+
init_project_analyzer();
|
|
8246
|
+
init_risk_scorer();
|
|
7218
8247
|
init_agent();
|
|
7219
8248
|
init_client();
|
|
7220
8249
|
init_feature_engineer();
|
|
@@ -7508,6 +8537,120 @@ var init_tools = __esm({
|
|
|
7508
8537
|
required: []
|
|
7509
8538
|
}
|
|
7510
8539
|
},
|
|
8540
|
+
{
|
|
8541
|
+
name: "get_rug_ml_analysis",
|
|
8542
|
+
description: "Run ML-powered rug pull analysis on a token. Uses Gradient Boosted classifier trained on historical rug patterns to predict rug probability, risk level, and key risk factors. Enhanced version of check_rug_indicators with ML scoring.",
|
|
8543
|
+
input_schema: {
|
|
8544
|
+
type: "object",
|
|
8545
|
+
properties: {
|
|
8546
|
+
address: {
|
|
8547
|
+
type: "string",
|
|
8548
|
+
description: "The token contract address to analyze."
|
|
8549
|
+
},
|
|
8550
|
+
chain: {
|
|
8551
|
+
type: "string",
|
|
8552
|
+
description: "The blockchain the token is deployed on."
|
|
8553
|
+
}
|
|
8554
|
+
},
|
|
8555
|
+
required: ["address", "chain"]
|
|
8556
|
+
}
|
|
8557
|
+
},
|
|
8558
|
+
{
|
|
8559
|
+
name: "get_wallet_behavior",
|
|
8560
|
+
description: "ML-powered wallet behavior classification. Uses LSTM model to classify a wallet as: normal_trader, bot, whale, sniper, mev_bot, mixer_user, or rug_deployer. Returns behavior type, confidence, risk score, and behavioral indicators.",
|
|
8561
|
+
input_schema: {
|
|
8562
|
+
type: "object",
|
|
8563
|
+
properties: {
|
|
8564
|
+
address: {
|
|
8565
|
+
type: "string",
|
|
8566
|
+
description: "The wallet address to classify."
|
|
8567
|
+
},
|
|
8568
|
+
chain: {
|
|
8569
|
+
type: "string",
|
|
8570
|
+
description: "The blockchain the wallet is on."
|
|
8571
|
+
}
|
|
8572
|
+
},
|
|
8573
|
+
required: ["address", "chain"]
|
|
8574
|
+
}
|
|
8575
|
+
},
|
|
8576
|
+
{
|
|
8577
|
+
name: "analyze_news_sentiment",
|
|
8578
|
+
description: "ML-powered NLP sentiment analysis on crypto news. Uses DistilBERT model to analyze news headlines for a token, returning bullish/bearish/neutral sentiment, confidence score, and detected topics (regulation, defi, security, etc.).",
|
|
8579
|
+
input_schema: {
|
|
8580
|
+
type: "object",
|
|
8581
|
+
properties: {
|
|
8582
|
+
symbol: {
|
|
8583
|
+
type: "string",
|
|
8584
|
+
description: 'Token symbol to analyze news sentiment for (e.g. "BTC", "ETH").'
|
|
8585
|
+
}
|
|
8586
|
+
},
|
|
8587
|
+
required: ["symbol"]
|
|
8588
|
+
}
|
|
8589
|
+
},
|
|
8590
|
+
{
|
|
8591
|
+
name: "get_market_regime",
|
|
8592
|
+
description: "Detect the current market regime using ML Hidden Markov Model. Returns regime type (trending_bull, trending_bear, ranging, volatile, capitulation), confidence, and probability distribution across all regimes. Uses HMM when available, falls back to heuristic analysis.",
|
|
8593
|
+
input_schema: {
|
|
8594
|
+
type: "object",
|
|
8595
|
+
properties: {
|
|
8596
|
+
symbol: {
|
|
8597
|
+
type: "string",
|
|
8598
|
+
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
8599
|
+
}
|
|
8600
|
+
},
|
|
8601
|
+
required: ["symbol"]
|
|
8602
|
+
}
|
|
8603
|
+
},
|
|
8604
|
+
{
|
|
8605
|
+
name: "get_ta_ml_analysis",
|
|
8606
|
+
description: "Run ML-enhanced technical analysis with learned signal weights. Uses Random Forest to interpret RSI, MACD, Bollinger Bands, EMA crossover, ATR, OBV simultaneously. Returns signals with ML-derived importance weights and composite direction. More accurate than static weight TA.",
|
|
8607
|
+
input_schema: {
|
|
8608
|
+
type: "object",
|
|
8609
|
+
properties: {
|
|
8610
|
+
symbol: {
|
|
8611
|
+
type: "string",
|
|
8612
|
+
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
8613
|
+
},
|
|
8614
|
+
timeframe: {
|
|
8615
|
+
type: "string",
|
|
8616
|
+
description: 'Kline interval: "1h", "4h", "1d". Defaults to "4h".'
|
|
8617
|
+
}
|
|
8618
|
+
},
|
|
8619
|
+
required: ["symbol"]
|
|
8620
|
+
}
|
|
8621
|
+
},
|
|
8622
|
+
{
|
|
8623
|
+
name: "get_project_risk_ml",
|
|
8624
|
+
description: "Run ML-powered project risk scoring on a token. Uses GBM classifier trained on contract features (verification, holder concentration, taxes, mint/pause/blacklist capabilities) to predict overall project risk probability and identify top risk factors.",
|
|
8625
|
+
input_schema: {
|
|
8626
|
+
type: "object",
|
|
8627
|
+
properties: {
|
|
8628
|
+
address: {
|
|
8629
|
+
type: "string",
|
|
8630
|
+
description: "The token contract address to analyze."
|
|
8631
|
+
},
|
|
8632
|
+
chain: {
|
|
8633
|
+
type: "string",
|
|
8634
|
+
description: "The blockchain the token is deployed on."
|
|
8635
|
+
}
|
|
8636
|
+
},
|
|
8637
|
+
required: ["address", "chain"]
|
|
8638
|
+
}
|
|
8639
|
+
},
|
|
8640
|
+
{
|
|
8641
|
+
name: "get_portfolio_forecast",
|
|
8642
|
+
description: "Generate forward-looking portfolio performance predictions using ML. Analyzes trade history to predict next-period return, Sharpe ratio, and max drawdown. Requires at least 10 completed trades.",
|
|
8643
|
+
input_schema: {
|
|
8644
|
+
type: "object",
|
|
8645
|
+
properties: {
|
|
8646
|
+
agentName: {
|
|
8647
|
+
type: "string",
|
|
8648
|
+
description: "The trading agent name to forecast for."
|
|
8649
|
+
}
|
|
8650
|
+
},
|
|
8651
|
+
required: ["agentName"]
|
|
8652
|
+
}
|
|
8653
|
+
},
|
|
7511
8654
|
{
|
|
7512
8655
|
name: "create_agent",
|
|
7513
8656
|
description: "Create an autonomous trading agent that monitors crypto pairs using a strategy (momentum or trend-following). Returns the created agent config.",
|
|
@@ -9726,9 +10869,19 @@ var init_price_ticker = __esm({
|
|
|
9726
10869
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
9727
10870
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
9728
10871
|
function WelcomeBanner() {
|
|
9729
|
-
return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
|
|
9730
|
-
/* @__PURE__ */
|
|
9731
|
-
|
|
10872
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
|
|
10873
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
10874
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: " " }),
|
|
10875
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "blue", children: "vizzor" }),
|
|
10876
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: " v0.10.0" }),
|
|
10877
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " \u2014 AI-powered crypto chronovisor" })
|
|
10878
|
+
] }),
|
|
10879
|
+
/* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " ML models: LSTM + Random Forest + Isolation Forest + GBM Rug + Wallet LSTM + DistilBERT NLP" }) }),
|
|
10880
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
10881
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " Type " }),
|
|
10882
|
+
/* @__PURE__ */ jsx3(Text3, { color: "yellow", children: "/help" }),
|
|
10883
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " for commands or ask anything about crypto." })
|
|
10884
|
+
] })
|
|
9732
10885
|
] });
|
|
9733
10886
|
}
|
|
9734
10887
|
var init_welcome_banner = __esm({
|
|
@@ -11412,7 +12565,7 @@ collectCmd.command("status").description("Show data collection status").action(a
|
|
|
11412
12565
|
const { handleCollectStatus: handleCollectStatus2 } = await Promise.resolve().then(() => (init_collect(), collect_exports));
|
|
11413
12566
|
handleCollectStatus2();
|
|
11414
12567
|
});
|
|
11415
|
-
program.command("serve").description("Start the REST API server").option("--port <port>", "Server port", parseInt, 3e3).option("--host <host>", "Server host", "0.0.0.0").option("--auth", "Enable API key authentication", false).action(async (options) => {
|
|
12568
|
+
program.command("serve").description("Start the REST API server").option("--port <port>", "Server port", (v) => parseInt(v, 10), 3e3).option("--host <host>", "Server host", "0.0.0.0").option("--auth", "Enable API key authentication", false).action(async (options) => {
|
|
11416
12569
|
const { handleServe: handleServe2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
|
|
11417
12570
|
await handleServe2(options);
|
|
11418
12571
|
});
|