@vizzor/cli 0.8.5 → 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 +1258 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -70,6 +70,10 @@ var init_schema = __esm({
|
|
|
70
70
|
enableAuth: false,
|
|
71
71
|
corsOrigin: "*"
|
|
72
72
|
})),
|
|
73
|
+
n8n: z.object({
|
|
74
|
+
enabled: z.boolean().default(false),
|
|
75
|
+
webhookUrl: z.string().optional()
|
|
76
|
+
}).default(() => ({ enabled: false })),
|
|
73
77
|
discordToken: z.string().optional(),
|
|
74
78
|
discordGuildId: z.string().optional(),
|
|
75
79
|
telegramToken: z.string().optional()
|
|
@@ -135,6 +139,30 @@ function loadConfig() {
|
|
|
135
139
|
}
|
|
136
140
|
raw["ai"]["provider"] = process.env["VIZZOR_AI_PROVIDER"];
|
|
137
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
|
+
}
|
|
138
166
|
const config2 = vizzorConfigSchema.parse(raw);
|
|
139
167
|
cachedConfig = config2;
|
|
140
168
|
return config2;
|
|
@@ -172,7 +200,10 @@ function saveConfigValue(key, value) {
|
|
|
172
200
|
if (!raw[section] || typeof raw[section] !== "object") {
|
|
173
201
|
raw[section] = {};
|
|
174
202
|
}
|
|
175
|
-
|
|
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;
|
|
176
207
|
raw[section][field] = parsed;
|
|
177
208
|
} else {
|
|
178
209
|
raw[key] = value;
|
|
@@ -203,7 +234,18 @@ var init_loader = __esm({
|
|
|
203
234
|
"ai.provider": { env: "VIZZOR_AI_PROVIDER", nested: "ai" },
|
|
204
235
|
"ai.model": { env: "VIZZOR_AI_MODEL", nested: "ai" },
|
|
205
236
|
"ai.maxTokens": { env: "VIZZOR_AI_MAX_TOKENS", nested: "ai" },
|
|
206
|
-
"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" }
|
|
207
249
|
};
|
|
208
250
|
}
|
|
209
251
|
});
|
|
@@ -826,6 +868,329 @@ var init_registry = __esm({
|
|
|
826
868
|
}
|
|
827
869
|
});
|
|
828
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
|
+
|
|
829
1194
|
// src/core/scanner/project-analyzer.ts
|
|
830
1195
|
async function analyzeProject(address, adapter) {
|
|
831
1196
|
const [token, code, holders] = await Promise.allSettled([
|
|
@@ -844,17 +1209,48 @@ async function analyzeProject(address, adapter) {
|
|
|
844
1209
|
);
|
|
845
1210
|
const totalSupply = tokenInfo?.totalSupply ?? 0n;
|
|
846
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
|
+
}
|
|
847
1245
|
return {
|
|
848
1246
|
token: tokenInfo,
|
|
849
1247
|
contractVerified: hasSourceCode,
|
|
850
1248
|
hasSourceCode,
|
|
851
1249
|
holderConcentration: topHolderPercentage,
|
|
852
|
-
topHolders:
|
|
853
|
-
address: h.address,
|
|
854
|
-
percentage: totalSupply > 0n ? Number(h.balance * 10000n / totalSupply) / 100 : 0
|
|
855
|
-
})),
|
|
1250
|
+
topHolders: topHoldersMapped,
|
|
856
1251
|
riskIndicators,
|
|
857
|
-
riskScore: Math.min(100, riskScore)
|
|
1252
|
+
riskScore: Math.min(100, riskScore),
|
|
1253
|
+
mlRisk
|
|
858
1254
|
};
|
|
859
1255
|
}
|
|
860
1256
|
function evaluateRiskIndicators(token, hasSource, topHolders) {
|
|
@@ -889,16 +1285,30 @@ function evaluateRiskIndicators(token, hasSource, topHolders) {
|
|
|
889
1285
|
var init_project_analyzer = __esm({
|
|
890
1286
|
"src/core/scanner/project-analyzer.ts"() {
|
|
891
1287
|
"use strict";
|
|
1288
|
+
init_client();
|
|
892
1289
|
}
|
|
893
1290
|
});
|
|
894
1291
|
|
|
895
1292
|
// src/core/scanner/risk-scorer.ts
|
|
896
1293
|
function assessRisk(analysis) {
|
|
897
|
-
const { riskScore, riskIndicators } = analysis;
|
|
898
|
-
const
|
|
1294
|
+
const { riskScore, riskIndicators, mlRisk } = analysis;
|
|
1295
|
+
const effectiveScore = mlRisk ? Math.round(mlRisk.risk_probability * 100) : riskScore;
|
|
1296
|
+
const level2 = getRiskLevel(effectiveScore);
|
|
899
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
|
+
}
|
|
900
1303
|
const summary = buildSummary(level2, factors.length);
|
|
901
|
-
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
|
+
};
|
|
902
1312
|
}
|
|
903
1313
|
function getRiskLevel(score) {
|
|
904
1314
|
if (score <= 20) return "low";
|
|
@@ -1134,6 +1544,7 @@ var init_market = __esm({
|
|
|
1134
1544
|
"src/core/trends/market.ts"() {
|
|
1135
1545
|
"use strict";
|
|
1136
1546
|
init_dexscreener();
|
|
1547
|
+
init_client();
|
|
1137
1548
|
}
|
|
1138
1549
|
});
|
|
1139
1550
|
|
|
@@ -1189,20 +1600,47 @@ async function analyzeSentiment(query) {
|
|
|
1189
1600
|
}
|
|
1190
1601
|
const news = await fetchCryptoNews(query, apiToken);
|
|
1191
1602
|
if (news.length > 0) {
|
|
1192
|
-
let
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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);
|
|
1197
1633
|
}
|
|
1198
1634
|
const total = news.length;
|
|
1199
|
-
const newsScore = total > 0 ? (positiveCount - negativeCount) / total : 0;
|
|
1200
1635
|
sources.push({
|
|
1201
|
-
source: "CryptoPanic News",
|
|
1636
|
+
source: mlSentiment ? "ML NLP Sentiment" : "CryptoPanic News",
|
|
1202
1637
|
score: newsScore,
|
|
1203
1638
|
volume: total,
|
|
1204
1639
|
trending: total > 5,
|
|
1205
|
-
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
|
|
1206
1644
|
});
|
|
1207
1645
|
}
|
|
1208
1646
|
} catch {
|
|
@@ -1246,12 +1684,23 @@ async function analyzeSentiment(query) {
|
|
|
1246
1684
|
}
|
|
1247
1685
|
return { overall, sources, consensus };
|
|
1248
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
|
+
}
|
|
1249
1697
|
var init_sentiment = __esm({
|
|
1250
1698
|
"src/core/trends/sentiment.ts"() {
|
|
1251
1699
|
"use strict";
|
|
1252
1700
|
init_cryptopanic();
|
|
1253
1701
|
init_dexscreener();
|
|
1254
1702
|
init_loader();
|
|
1703
|
+
init_client();
|
|
1255
1704
|
}
|
|
1256
1705
|
});
|
|
1257
1706
|
|
|
@@ -1536,15 +1985,74 @@ async function analyzeTechnicals(symbol, timeframe = "4h") {
|
|
|
1536
1985
|
}
|
|
1537
1986
|
const atr = calculateATR(highs, lows, closes);
|
|
1538
1987
|
if (atr !== null) {
|
|
1539
|
-
const
|
|
1540
|
-
signals.push(interpretATR(atr,
|
|
1988
|
+
const currentPrice2 = closes[closes.length - 1];
|
|
1989
|
+
signals.push(interpretATR(atr, currentPrice2));
|
|
1541
1990
|
}
|
|
1542
1991
|
const obv = calculateOBV(closes, volumes);
|
|
1543
1992
|
if (obv !== null) {
|
|
1544
|
-
const
|
|
1545
|
-
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));
|
|
1546
1995
|
}
|
|
1547
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
|
+
}
|
|
1548
2056
|
const composite = calculateComposite(signals);
|
|
1549
2057
|
return {
|
|
1550
2058
|
symbol: symbol.toUpperCase(),
|
|
@@ -1555,8 +2063,8 @@ async function analyzeTechnicals(symbol, timeframe = "4h") {
|
|
|
1555
2063
|
rsi,
|
|
1556
2064
|
macd,
|
|
1557
2065
|
bollingerBands: bb,
|
|
1558
|
-
ema12:
|
|
1559
|
-
ema26:
|
|
2066
|
+
ema12: ema12Val || null,
|
|
2067
|
+
ema26: ema26Val || null,
|
|
1560
2068
|
sma20: sma20.length > 0 ? sma20[sma20.length - 1] : null,
|
|
1561
2069
|
atr,
|
|
1562
2070
|
obv
|
|
@@ -1737,6 +2245,7 @@ var init_analyzer = __esm({
|
|
|
1737
2245
|
"use strict";
|
|
1738
2246
|
init_binance();
|
|
1739
2247
|
init_indicators();
|
|
2248
|
+
init_client();
|
|
1740
2249
|
WEIGHTS = {
|
|
1741
2250
|
RSI: 20,
|
|
1742
2251
|
MACD: 20,
|
|
@@ -1796,46 +2305,6 @@ var init_fear_greed = __esm({
|
|
|
1796
2305
|
}
|
|
1797
2306
|
});
|
|
1798
2307
|
|
|
1799
|
-
// src/utils/logger.ts
|
|
1800
|
-
import pino from "pino";
|
|
1801
|
-
function createLogger(name) {
|
|
1802
|
-
const isDev = process.env["NODE_ENV"] !== "production";
|
|
1803
|
-
if (isDev) {
|
|
1804
|
-
return pino({
|
|
1805
|
-
name,
|
|
1806
|
-
level,
|
|
1807
|
-
transport: {
|
|
1808
|
-
target: "pino-pretty",
|
|
1809
|
-
options: {
|
|
1810
|
-
colorize: true
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
});
|
|
1814
|
-
}
|
|
1815
|
-
return pino({ name, level });
|
|
1816
|
-
}
|
|
1817
|
-
var level;
|
|
1818
|
-
var init_logger = __esm({
|
|
1819
|
-
"src/utils/logger.ts"() {
|
|
1820
|
-
"use strict";
|
|
1821
|
-
level = process.env["VIZZOR_LOG_LEVEL"] ?? "info";
|
|
1822
|
-
}
|
|
1823
|
-
});
|
|
1824
|
-
|
|
1825
|
-
// src/ml/client.ts
|
|
1826
|
-
function getMLClient() {
|
|
1827
|
-
return mlClient;
|
|
1828
|
-
}
|
|
1829
|
-
var log, mlClient;
|
|
1830
|
-
var init_client = __esm({
|
|
1831
|
-
"src/ml/client.ts"() {
|
|
1832
|
-
"use strict";
|
|
1833
|
-
init_logger();
|
|
1834
|
-
log = createLogger("ml-client");
|
|
1835
|
-
mlClient = null;
|
|
1836
|
-
}
|
|
1837
|
-
});
|
|
1838
|
-
|
|
1839
2308
|
// src/ml/feature-engineer.ts
|
|
1840
2309
|
async function buildFeatureVector(symbol) {
|
|
1841
2310
|
const [ta, fundingResult, tickerResult, fgResult, klines] = await Promise.allSettled([
|
|
@@ -2031,7 +2500,16 @@ async function generatePrediction(symbol) {
|
|
|
2031
2500
|
composite: Math.round(composite),
|
|
2032
2501
|
disclaimer: "This is not financial advice. Predictions are based on historical data and AI analysis. Always do your own research."
|
|
2033
2502
|
};
|
|
2034
|
-
|
|
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
|
+
}
|
|
2035
2513
|
if (mlClient2) {
|
|
2036
2514
|
try {
|
|
2037
2515
|
const features = await buildFeatureVector(symbol);
|
|
@@ -2070,6 +2548,7 @@ var init_predictor = __esm({
|
|
|
2070
2548
|
init_binance();
|
|
2071
2549
|
init_client();
|
|
2072
2550
|
init_feature_engineer();
|
|
2551
|
+
init_loader();
|
|
2073
2552
|
WEIGHTS2 = {
|
|
2074
2553
|
technical: 40,
|
|
2075
2554
|
sentiment: 20,
|
|
@@ -2158,7 +2637,39 @@ async function analyzeWallet(address, adapter) {
|
|
|
2158
2637
|
]);
|
|
2159
2638
|
const walletBalance = balance.status === "fulfilled" ? balance.value : 0n;
|
|
2160
2639
|
const txHistory = transactions.status === "fulfilled" ? transactions.value : [];
|
|
2161
|
-
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
|
+
}
|
|
2162
2673
|
return {
|
|
2163
2674
|
address,
|
|
2164
2675
|
chain: adapter.chainId,
|
|
@@ -2166,23 +2677,129 @@ async function analyzeWallet(address, adapter) {
|
|
|
2166
2677
|
transactionCount: txHistory.length,
|
|
2167
2678
|
tokenBalances: [],
|
|
2168
2679
|
patterns,
|
|
2169
|
-
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
|
|
2170
2735
|
};
|
|
2171
2736
|
}
|
|
2172
|
-
function detectPatterns(
|
|
2737
|
+
function detectPatterns(txHistory) {
|
|
2173
2738
|
const patterns = [];
|
|
2739
|
+
const txCount = txHistory.length;
|
|
2174
2740
|
if (txCount === 0) {
|
|
2175
2741
|
patterns.push({
|
|
2176
2742
|
type: "new_wallet",
|
|
2177
2743
|
description: "Wallet has no transaction history",
|
|
2178
2744
|
severity: "info"
|
|
2179
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
|
+
});
|
|
2180
2795
|
}
|
|
2181
2796
|
return patterns;
|
|
2182
2797
|
}
|
|
2183
2798
|
var init_wallet_analyzer = __esm({
|
|
2184
2799
|
"src/core/forensics/wallet-analyzer.ts"() {
|
|
2185
2800
|
"use strict";
|
|
2801
|
+
init_client();
|
|
2802
|
+
init_loader();
|
|
2186
2803
|
}
|
|
2187
2804
|
});
|
|
2188
2805
|
|
|
@@ -2767,8 +3384,6 @@ async function handleConfigInit() {
|
|
|
2767
3384
|
console.log(` 3. Try scanning a project: ${chalk6.cyan("vizzor scan ethereum")}`);
|
|
2768
3385
|
}
|
|
2769
3386
|
async function handleConfigSet(key, value) {
|
|
2770
|
-
const configDir = getConfigDir();
|
|
2771
|
-
const configPath = resolve(configDir, "config.yaml");
|
|
2772
3387
|
const isSensitive = key.toLowerCase().includes("key") || key.toLowerCase().includes("token");
|
|
2773
3388
|
if (isSensitive) {
|
|
2774
3389
|
const error = validateKey(key, value);
|
|
@@ -2780,20 +3395,12 @@ async function handleConfigSet(key, value) {
|
|
|
2780
3395
|
console.log(chalk6.yellow(error));
|
|
2781
3396
|
}
|
|
2782
3397
|
}
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
if (!updatedConfig[section] || typeof updatedConfig[section] !== "object") {
|
|
2789
|
-
updatedConfig[section] = {};
|
|
2790
|
-
}
|
|
2791
|
-
const parsed = field === "maxTokens" ? Number(value) : value;
|
|
2792
|
-
updatedConfig[section][field] = parsed;
|
|
2793
|
-
} else {
|
|
2794
|
-
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;
|
|
2795
3403
|
}
|
|
2796
|
-
writeFileSync2(configPath, yamlStringify(updatedConfig), "utf-8");
|
|
2797
3404
|
const displayValue = isSensitive ? maskKey(value) : value;
|
|
2798
3405
|
console.log(chalk6.green(`Set ${key} = ${displayValue}`));
|
|
2799
3406
|
}
|
|
@@ -3776,10 +4383,25 @@ var init_goplus = __esm({
|
|
|
3776
4383
|
async function buildContextBlock(userMessage) {
|
|
3777
4384
|
const lower = userMessage.toLowerCase();
|
|
3778
4385
|
const sections = [];
|
|
3779
|
-
|
|
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;
|
|
3780
4402
|
const mentionedTokens = detectTokens(lower);
|
|
3781
4403
|
const unknownTokens = detectUnknownTokens(userMessage, mentionedTokens);
|
|
3782
|
-
const isAnalysisQuery = matchesAny(lower, ANALYSIS_KEYWORDS);
|
|
4404
|
+
const isAnalysisQuery = matchesAny(lower, ANALYSIS_KEYWORDS) || mlIntent === "analysis";
|
|
3783
4405
|
const tasks = [];
|
|
3784
4406
|
if (mentionedTokens.length > 0 || addressMatch || matchesAny(lower, PRICE_KEYWORDS)) {
|
|
3785
4407
|
tasks.push(
|
|
@@ -3805,8 +4427,7 @@ async function buildContextBlock(userMessage) {
|
|
|
3805
4427
|
}
|
|
3806
4428
|
}
|
|
3807
4429
|
if (addressMatch && isAnalysisQuery) {
|
|
3808
|
-
const
|
|
3809
|
-
const chain = cfg.defaultChain || "ethereum";
|
|
4430
|
+
const chain = detectedChain || getConfig().defaultChain || "ethereum";
|
|
3810
4431
|
tasks.push(
|
|
3811
4432
|
fetchSecurityData(addressMatch[0], chain).then((data) => {
|
|
3812
4433
|
if (data) sections.push(data);
|
|
@@ -3847,8 +4468,9 @@ async function buildContextBlock(userMessage) {
|
|
|
3847
4468
|
})
|
|
3848
4469
|
);
|
|
3849
4470
|
}
|
|
3850
|
-
const isBroadQuery = matchesAny(lower, BROAD_KEYWORDS);
|
|
3851
|
-
|
|
4471
|
+
const isBroadQuery = matchesAny(lower, BROAD_KEYWORDS) || mlIntent === "broad_overview";
|
|
4472
|
+
const hasSpecificIntent = mentionedTokens.length > 0 || unknownTokens.length > 0 || !!addressMatch;
|
|
4473
|
+
if (isBroadQuery && !hasSpecificIntent) {
|
|
3852
4474
|
if (!matchesAny(lower, TRENDING_KEYWORDS)) {
|
|
3853
4475
|
tasks.push(
|
|
3854
4476
|
fetchTrendingData().then((data) => {
|
|
@@ -3870,9 +4492,18 @@ async function buildContextBlock(userMessage) {
|
|
|
3870
4492
|
})
|
|
3871
4493
|
);
|
|
3872
4494
|
}
|
|
3873
|
-
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();
|
|
3874
4505
|
tasks.push(
|
|
3875
|
-
|
|
4506
|
+
fetchNewsData(symbol).then((data) => {
|
|
3876
4507
|
if (data) sections.push(data);
|
|
3877
4508
|
})
|
|
3878
4509
|
);
|
|
@@ -4154,8 +4785,30 @@ async function fetchNewsData(symbol) {
|
|
|
4154
4785
|
try {
|
|
4155
4786
|
const news = await fetchCryptoNews(symbol, getConfig().cryptopanicApiKey);
|
|
4156
4787
|
if (news.length > 0) {
|
|
4788
|
+
const headlines = news.slice(0, 8);
|
|
4157
4789
|
const lines = [`## Latest Crypto News${symbol ? ` (${symbol})` : ""}`];
|
|
4158
|
-
|
|
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) {
|
|
4159
4812
|
lines.push(
|
|
4160
4813
|
`- [${n.sentiment.toUpperCase()}] ${n.title} (${n.source.title}, ${n.publishedAt})`
|
|
4161
4814
|
);
|
|
@@ -4194,7 +4847,7 @@ async function fetchRaisesData() {
|
|
|
4194
4847
|
if (raises.length === 0) return null;
|
|
4195
4848
|
const lines = ["## Recent Crypto Fundraising Rounds (last 30 days)"];
|
|
4196
4849
|
for (const r of raises.slice(0, 10)) {
|
|
4197
|
-
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";
|
|
4198
4851
|
const date = new Date(r.date * 1e3).toISOString().split("T")[0];
|
|
4199
4852
|
lines.push(
|
|
4200
4853
|
`- ${r.name} \u2014 ${r.round} (${amount}) on ${r.chains.join(", ") || "multi-chain"} [${date}]${r.leadInvestors.length > 0 ? ` Led by: ${r.leadInvestors.join(", ")}` : ""}`
|
|
@@ -5131,6 +5784,7 @@ var init_context_injector = __esm({
|
|
|
5131
5784
|
init_goplus();
|
|
5132
5785
|
init_loader();
|
|
5133
5786
|
init_constants();
|
|
5787
|
+
init_client();
|
|
5134
5788
|
PRICE_KEYWORDS = ["price", "worth", "cost", "value", "how much"];
|
|
5135
5789
|
TRENDING_KEYWORDS = ["trending", "hot", "popular", "top", "best", "hype"];
|
|
5136
5790
|
NEWS_KEYWORDS = ["news", "latest", "update", "happening", "announcement"];
|
|
@@ -5510,6 +6164,7 @@ var DANGEROUS_FUNCTIONS, DANGEROUS_OPCODES;
|
|
|
5510
6164
|
var init_bytecode_scanner = __esm({
|
|
5511
6165
|
"src/core/forensics/bytecode-scanner.ts"() {
|
|
5512
6166
|
"use strict";
|
|
6167
|
+
init_client();
|
|
5513
6168
|
DANGEROUS_FUNCTIONS = [
|
|
5514
6169
|
// Mint functions — inflation risk
|
|
5515
6170
|
{
|
|
@@ -5667,6 +6322,42 @@ async function detectRugIndicators(tokenAddress, adapter) {
|
|
|
5667
6322
|
}
|
|
5668
6323
|
}
|
|
5669
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
|
+
}
|
|
5670
6361
|
return {
|
|
5671
6362
|
isHoneypot: canPause || hasBlacklist_,
|
|
5672
6363
|
hasLiquidityLock: false,
|
|
@@ -5674,8 +6365,9 @@ async function detectRugIndicators(tokenAddress, adapter) {
|
|
|
5674
6365
|
ownerCanPause: canPause,
|
|
5675
6366
|
hasBlacklist: hasBlacklist_,
|
|
5676
6367
|
highSellTax: false,
|
|
5677
|
-
riskScore,
|
|
5678
|
-
details
|
|
6368
|
+
riskScore: mlAnalysis ? Math.round(riskScore * 0.4 + mlAnalysis.rug_probability * 100 * 0.6) : riskScore,
|
|
6369
|
+
details,
|
|
6370
|
+
mlAnalysis
|
|
5679
6371
|
};
|
|
5680
6372
|
}
|
|
5681
6373
|
function calculateRugRisk(details) {
|
|
@@ -5701,6 +6393,83 @@ var init_rug_detector = __esm({
|
|
|
5701
6393
|
"src/core/forensics/rug-detector.ts"() {
|
|
5702
6394
|
"use strict";
|
|
5703
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();
|
|
5704
6473
|
}
|
|
5705
6474
|
});
|
|
5706
6475
|
|
|
@@ -5891,6 +6660,7 @@ var momentumStrategy;
|
|
|
5891
6660
|
var init_momentum = __esm({
|
|
5892
6661
|
"src/core/agent/strategies/momentum.ts"() {
|
|
5893
6662
|
"use strict";
|
|
6663
|
+
init_client();
|
|
5894
6664
|
momentumStrategy = {
|
|
5895
6665
|
name: "momentum",
|
|
5896
6666
|
description: "RSI reversal + MACD confirmation. Buy oversold bounces, sell overbought peaks.",
|
|
@@ -5966,6 +6736,7 @@ var trendFollowingStrategy;
|
|
|
5966
6736
|
var init_trend_following = __esm({
|
|
5967
6737
|
"src/core/agent/strategies/trend-following.ts"() {
|
|
5968
6738
|
"use strict";
|
|
6739
|
+
init_client();
|
|
5969
6740
|
trendFollowingStrategy = {
|
|
5970
6741
|
name: "trend-following",
|
|
5971
6742
|
description: "EMA crossover trend detection. Buy golden cross, sell death cross.",
|
|
@@ -6124,7 +6895,7 @@ var init_ml_adaptive = __esm({
|
|
|
6124
6895
|
"src/core/agent/strategies/ml-adaptive.ts"() {
|
|
6125
6896
|
"use strict";
|
|
6126
6897
|
init_client();
|
|
6127
|
-
|
|
6898
|
+
init_regime();
|
|
6128
6899
|
mlAdaptiveStrategy = {
|
|
6129
6900
|
name: "ml-adaptive",
|
|
6130
6901
|
description: "ML-powered strategy using contextual bandit approach. Uses LSTM/RF predictions when available, falls back to rule-based when < 100 training samples.",
|
|
@@ -7074,7 +7845,16 @@ async function handleTool(name, input) {
|
|
|
7074
7845
|
}
|
|
7075
7846
|
case "get_ml_prediction": {
|
|
7076
7847
|
const symbol = String(params["symbol"] ?? "BTC");
|
|
7077
|
-
|
|
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
|
+
}
|
|
7078
7858
|
if (!mlClient2) {
|
|
7079
7859
|
const prediction = await generatePrediction(symbol);
|
|
7080
7860
|
return {
|
|
@@ -7135,6 +7915,256 @@ async function handleTool(name, input) {
|
|
|
7135
7915
|
}
|
|
7136
7916
|
};
|
|
7137
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
|
+
}
|
|
7138
8168
|
case "create_agent": {
|
|
7139
8169
|
const agentName = String(params["name"] ?? "");
|
|
7140
8170
|
const strategy = String(params["strategy"] ?? "momentum");
|
|
@@ -7211,6 +8241,9 @@ var init_tool_handler = __esm({
|
|
|
7211
8241
|
init_fear_greed();
|
|
7212
8242
|
init_technical_analysis();
|
|
7213
8243
|
init_predictor();
|
|
8244
|
+
init_regime();
|
|
8245
|
+
init_project_analyzer();
|
|
8246
|
+
init_risk_scorer();
|
|
7214
8247
|
init_agent();
|
|
7215
8248
|
init_client();
|
|
7216
8249
|
init_feature_engineer();
|
|
@@ -7504,6 +8537,120 @@ var init_tools = __esm({
|
|
|
7504
8537
|
required: []
|
|
7505
8538
|
}
|
|
7506
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
|
+
},
|
|
7507
8654
|
{
|
|
7508
8655
|
name: "create_agent",
|
|
7509
8656
|
description: "Create an autonomous trading agent that monitors crypto pairs using a strategy (momentum or trend-following). Returns the created agent config.",
|
|
@@ -9722,9 +10869,19 @@ var init_price_ticker = __esm({
|
|
|
9722
10869
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
9723
10870
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
9724
10871
|
function WelcomeBanner() {
|
|
9725
|
-
return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
|
|
9726
|
-
/* @__PURE__ */
|
|
9727
|
-
|
|
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
|
+
] })
|
|
9728
10885
|
] });
|
|
9729
10886
|
}
|
|
9730
10887
|
var init_welcome_banner = __esm({
|
|
@@ -11408,7 +12565,7 @@ collectCmd.command("status").description("Show data collection status").action(a
|
|
|
11408
12565
|
const { handleCollectStatus: handleCollectStatus2 } = await Promise.resolve().then(() => (init_collect(), collect_exports));
|
|
11409
12566
|
handleCollectStatus2();
|
|
11410
12567
|
});
|
|
11411
|
-
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) => {
|
|
11412
12569
|
const { handleServe: handleServe2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
|
|
11413
12570
|
await handleServe2(options);
|
|
11414
12571
|
});
|