@undesirables/plugin-tcg-oracle 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +581 -399
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.js +0 -184
- package/dist/__tests__/index.test.js.map +0 -1
- package/dist/environment.d.ts +0 -10
- package/dist/environment.d.ts.map +0 -1
- package/dist/environment.js +0 -27
- package/dist/environment.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,434 +1,616 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
// src/environment.ts
|
|
2
|
+
function validateTcgOracleConfig(runtime) {
|
|
3
|
+
const url = runtime.getSetting?.("TCG_ORACLE_URL") || process.env.TCG_ORACLE_URL || "";
|
|
4
|
+
if (!url) {
|
|
5
|
+
return {
|
|
6
|
+
valid: false,
|
|
7
|
+
error: "TCG_ORACLE_URL is required. Set it in your character.json settings or as an environment variable. Example: https://oracle.the-undesirables.com"
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
new URL(url);
|
|
12
|
+
} catch {
|
|
13
|
+
return {
|
|
14
|
+
valid: false,
|
|
15
|
+
error: `TCG_ORACLE_URL is not a valid URL: "${url}"`
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return { valid: true };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/services.ts
|
|
22
|
+
import { Service } from "@elizaos/core";
|
|
23
|
+
var MarketMonitorService = class _MarketMonitorService extends Service {
|
|
24
|
+
// Use a string getter to satisfy ElizaOS ServiceType constraints if needed,
|
|
25
|
+
// or define static serviceType depending on the core version.
|
|
26
|
+
static serviceType = "TCG_MARKET_MONITOR";
|
|
27
|
+
get capabilityDescription() {
|
|
28
|
+
return "Monitors TCG market data for price spikes and trending cards";
|
|
29
|
+
}
|
|
30
|
+
static async start(runtime) {
|
|
31
|
+
const service = new _MarketMonitorService();
|
|
32
|
+
await service.initialize(runtime);
|
|
33
|
+
return service;
|
|
34
|
+
}
|
|
35
|
+
monitorInterval = null;
|
|
36
|
+
async initialize(_runtime) {
|
|
37
|
+
console.log("[TCG Oracle Service] Market Monitor Service initialized.");
|
|
38
|
+
}
|
|
39
|
+
async stop() {
|
|
40
|
+
if (this.monitorInterval) {
|
|
41
|
+
clearInterval(this.monitorInterval);
|
|
42
|
+
}
|
|
43
|
+
console.log("[TCG Oracle Service] Market Monitor Service stopped.");
|
|
44
|
+
}
|
|
45
|
+
pollMarket() {
|
|
46
|
+
console.log("[TCG Oracle Service] Background task: Polling TCG market for price spikes...");
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/index.ts
|
|
51
|
+
var SUPPORTED_GAMES = [
|
|
52
|
+
"Pokemon",
|
|
53
|
+
"Magic: The Gathering",
|
|
54
|
+
"Yu-Gi-Oh!",
|
|
55
|
+
"Lorcana",
|
|
56
|
+
"One Piece",
|
|
57
|
+
"Flesh and Blood",
|
|
58
|
+
"Star Wars: Unlimited",
|
|
59
|
+
"Dragon Ball Super",
|
|
60
|
+
"Digimon",
|
|
61
|
+
"MetaZoo",
|
|
62
|
+
"Union Arena",
|
|
63
|
+
"Gundam Card Game",
|
|
64
|
+
"LoL Riftbound"
|
|
35
65
|
];
|
|
36
|
-
|
|
37
|
-
// ============================================================
|
|
38
|
-
// API Client
|
|
39
|
-
// ============================================================
|
|
66
|
+
var SIMULATION_MODELS = ["heston", "merton", "kou"];
|
|
40
67
|
function getBaseUrl(runtime) {
|
|
41
|
-
|
|
42
|
-
process.env.TCG_ORACLE_URL ||
|
|
43
|
-
"");
|
|
68
|
+
return runtime.getSetting?.("TCG_ORACLE_URL") || process.env.TCG_ORACLE_URL || "";
|
|
44
69
|
}
|
|
45
70
|
async function callApi(baseUrl, endpoint, params) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
71
|
+
if (!baseUrl) {
|
|
72
|
+
return {
|
|
73
|
+
error: "TCG_ORACLE_URL not set. Set it in your character.json settings or as an environment variable. Install the server: pip install undesirables-mcp-server"
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const url = new URL(`${baseUrl}${endpoint}`);
|
|
77
|
+
for (const [key, value] of Object.entries(params)) {
|
|
78
|
+
if (value !== void 0 && value !== null && value !== "") {
|
|
79
|
+
url.searchParams.set(key, String(value));
|
|
56
80
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const json = (await response.json());
|
|
69
|
-
return { data: json.data || json };
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
85
|
+
const response = await fetch(url.toString(), {
|
|
86
|
+
signal: controller.signal,
|
|
87
|
+
headers: { "User-Agent": "elizaos-tcg-oracle-plugin/1.0.0" }
|
|
88
|
+
});
|
|
89
|
+
clearTimeout(timeout);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
return { error: `API returned HTTP ${response.status}` };
|
|
70
92
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
error: "Failed to connect to TCG Oracle API. Is the server running?",
|
|
77
|
-
};
|
|
93
|
+
const json = await response.json();
|
|
94
|
+
return { data: json.data || json };
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err.name === "AbortError") {
|
|
97
|
+
return { error: "TCG Oracle API timed out (30s)" };
|
|
78
98
|
}
|
|
99
|
+
return {
|
|
100
|
+
error: "Failed to connect to TCG Oracle API. Is the server running?"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
79
103
|
}
|
|
80
|
-
// ============================================================
|
|
81
|
-
// Helper: Truncate data safely before JSON.stringify
|
|
82
|
-
// ============================================================
|
|
83
104
|
function safeStringify(data, maxItems = 10) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
if (Array.isArray(data)) {
|
|
106
|
+
return JSON.stringify(data.slice(0, maxItems), null, 2);
|
|
107
|
+
}
|
|
108
|
+
return JSON.stringify(data, null, 2).slice(0, 2e3);
|
|
88
109
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
var searchAction = {
|
|
111
|
+
name: "TCG_ORACLE_SEARCH",
|
|
112
|
+
description: "Search for trading card products across 25 games including Pokemon, Magic: The Gathering, Yu-Gi-Oh, Lorcana, One Piece, and more. Returns product names, set info, and current market prices from 370K+ real products sourced from TCGCSV daily.",
|
|
113
|
+
similes: [
|
|
114
|
+
"SEARCH_CARDS",
|
|
115
|
+
"FIND_CARD",
|
|
116
|
+
"CARD_LOOKUP",
|
|
117
|
+
"CARD_SEARCH",
|
|
118
|
+
"TCG_SEARCH",
|
|
119
|
+
"FIND_POKEMON",
|
|
120
|
+
"SEARCH_MTG"
|
|
121
|
+
],
|
|
122
|
+
parameters: [
|
|
123
|
+
{
|
|
124
|
+
name: "query",
|
|
125
|
+
description: "The card name, set name, or keyword to search for (e.g., 'Charizard', 'Base Set', 'Pikachu VMAX')",
|
|
126
|
+
required: true,
|
|
127
|
+
schema: { type: "string" }
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "limit",
|
|
131
|
+
description: "Maximum number of results to return (default: 10, max: 50)",
|
|
132
|
+
required: false,
|
|
133
|
+
schema: { type: "number" }
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
examples: [
|
|
137
|
+
[
|
|
138
|
+
{ name: "{{user1}}", content: { text: "Search for Charizard cards" } },
|
|
139
|
+
{ name: "{{agentName}}", content: { text: "Searching TCG Oracle for Charizard...", action: "TCG_ORACLE_SEARCH" } }
|
|
103
140
|
],
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
description: "The card name, set name, or keyword to search for (e.g., 'Charizard', 'Base Set', 'Pikachu VMAX')",
|
|
108
|
-
required: true,
|
|
109
|
-
schema: { type: "string" },
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
name: "limit",
|
|
113
|
-
description: "Maximum number of results to return (default: 10, max: 50)",
|
|
114
|
-
required: false,
|
|
115
|
-
schema: { type: "number" },
|
|
116
|
-
},
|
|
141
|
+
[
|
|
142
|
+
{ name: "{{user1}}", content: { text: "What Pikachu VMAX cards are available?" } },
|
|
143
|
+
{ name: "{{agentName}}", content: { text: "Looking up Pikachu VMAX products...", action: "TCG_ORACLE_SEARCH" } }
|
|
117
144
|
],
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
145
|
+
[
|
|
146
|
+
{ name: "{{user1}}", content: { text: "Find me Base Set holographic cards" } },
|
|
147
|
+
{ name: "{{agentName}}", content: { text: "Searching for Base Set holographic cards...", action: "TCG_ORACLE_SEARCH" } }
|
|
148
|
+
]
|
|
149
|
+
],
|
|
150
|
+
validate: async (runtime) => {
|
|
151
|
+
return !!getBaseUrl(runtime);
|
|
152
|
+
},
|
|
153
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
154
|
+
const text = message.content.text || "";
|
|
155
|
+
const query = text.replace(/^(search|find|look up|lookup|what|show me)\s+(for\s+)?/i, "").replace(/\s+cards?\s*$/i, "").trim();
|
|
156
|
+
if (!query) {
|
|
157
|
+
if (callback) await callback({ text: "What card would you like me to search for?" });
|
|
158
|
+
return { success: false, error: "No search query provided" };
|
|
159
|
+
}
|
|
160
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/search", {
|
|
161
|
+
query,
|
|
162
|
+
limit: 10
|
|
163
|
+
});
|
|
164
|
+
if (result.error) {
|
|
165
|
+
if (callback) await callback({ text: `Search failed: ${result.error}` });
|
|
166
|
+
return { success: false, error: result.error };
|
|
167
|
+
}
|
|
168
|
+
const summary = safeStringify(result.data);
|
|
169
|
+
const responseText = `Found products matching "${query}":
|
|
170
|
+
|
|
171
|
+
${summary}`;
|
|
172
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_SEARCH");
|
|
173
|
+
return { success: true, text: responseText, data: { search_results: result.data } };
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var gradeAction = {
|
|
177
|
+
name: "TCG_ORACLE_GRADE",
|
|
178
|
+
description: "Grade a TCG card image using AI vision (Qwen VL model running locally). Predicts PSA and Beckett grading scores by analyzing centering, corners, edges, surface condition, and print quality. Works with Pokemon, MTG, Yu-Gi-Oh, and other TCG cards.",
|
|
179
|
+
similes: [
|
|
180
|
+
"GRADE_CARD",
|
|
181
|
+
"CARD_GRADING",
|
|
182
|
+
"PSA_GRADE",
|
|
183
|
+
"BECKETT_GRADE",
|
|
184
|
+
"RATE_CARD",
|
|
185
|
+
"EVALUATE_CARD"
|
|
186
|
+
],
|
|
187
|
+
parameters: [
|
|
188
|
+
{
|
|
189
|
+
name: "image_url",
|
|
190
|
+
description: "Direct URL to the card image (PNG, JPG, or WebP)",
|
|
191
|
+
required: true,
|
|
192
|
+
schema: { type: "string" }
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: "game",
|
|
196
|
+
description: "The TCG game this card belongs to (default: Pokemon)",
|
|
197
|
+
required: false,
|
|
198
|
+
schema: { type: "string" }
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
examples: [
|
|
202
|
+
[
|
|
203
|
+
{ name: "{{user1}}", content: { text: "Grade this card: https://example.com/charizard.jpg" } },
|
|
204
|
+
{ name: "{{agentName}}", content: { text: "Analyzing card image for grading...", action: "TCG_ORACLE_GRADE" } }
|
|
131
205
|
],
|
|
132
|
-
|
|
133
|
-
|
|
206
|
+
[
|
|
207
|
+
{ name: "{{user1}}", content: { text: "What PSA grade would this get? https://i.imgur.com/card.png" } },
|
|
208
|
+
{ name: "{{agentName}}", content: { text: "Running AI vision grading analysis...", action: "TCG_ORACLE_GRADE" } }
|
|
209
|
+
],
|
|
210
|
+
[
|
|
211
|
+
{ name: "{{user1}}", content: { text: "Rate the condition of this Magic card https://example.com/mtg.jpg" } },
|
|
212
|
+
{ name: "{{agentName}}", content: { text: "Evaluating card condition for Beckett grading...", action: "TCG_ORACLE_GRADE" } }
|
|
213
|
+
]
|
|
214
|
+
],
|
|
215
|
+
validate: async (runtime) => !!getBaseUrl(runtime),
|
|
216
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
217
|
+
const text = message.content.text || "";
|
|
218
|
+
const urlMatch = text.match(/https?:\/\/[^\s]+/);
|
|
219
|
+
if (!urlMatch) {
|
|
220
|
+
if (callback) await callback({ text: "Please provide a card image URL to grade." });
|
|
221
|
+
return { success: false, error: "No image URL found in message" };
|
|
222
|
+
}
|
|
223
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/grade", {
|
|
224
|
+
image_url: urlMatch[0],
|
|
225
|
+
game: "Pokemon"
|
|
226
|
+
});
|
|
227
|
+
if (result.error) {
|
|
228
|
+
if (callback) await callback({ text: `Grading failed: ${result.error}` });
|
|
229
|
+
return { success: false, error: result.error };
|
|
230
|
+
}
|
|
231
|
+
const summary = safeStringify(result.data);
|
|
232
|
+
const responseText = `Card grading complete:
|
|
233
|
+
|
|
234
|
+
${summary}`;
|
|
235
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_GRADE");
|
|
236
|
+
return { success: true, text: responseText, data: { grade_result: result.data } };
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
var simulateAction = {
|
|
240
|
+
name: "TCG_ORACLE_SIMULATE",
|
|
241
|
+
description: "Run Monte Carlo simulation on a TCG card's price to predict future value. Uses real drift and volatility computed from TCGCSV historical data. Supports three stochastic models: Heston (stochastic volatility), Merton (jump-diffusion), and Kou (double-exponential jumps). Returns percentile bands (5th, 25th, 50th, 75th, 95th) for the forecast period.",
|
|
242
|
+
similes: [
|
|
243
|
+
"SIMULATE_PRICE",
|
|
244
|
+
"PRICE_PREDICTION",
|
|
245
|
+
"FORECAST_PRICE",
|
|
246
|
+
"MONTE_CARLO",
|
|
247
|
+
"PRICE_FORECAST",
|
|
248
|
+
"WHAT_WILL_THIS_BE_WORTH"
|
|
249
|
+
],
|
|
250
|
+
parameters: [
|
|
251
|
+
{
|
|
252
|
+
name: "card_name",
|
|
253
|
+
description: "Name of the card to simulate",
|
|
254
|
+
required: true,
|
|
255
|
+
schema: { type: "string" }
|
|
134
256
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.replace(/\s+cards?\s*$/i, "")
|
|
141
|
-
.trim();
|
|
142
|
-
if (!query) {
|
|
143
|
-
if (callback)
|
|
144
|
-
await callback({ text: "What card would you like me to search for?" });
|
|
145
|
-
return { success: false, error: "No search query provided" };
|
|
146
|
-
}
|
|
147
|
-
const result = await callApi(getBaseUrl(runtime), "/api/v1/search", {
|
|
148
|
-
query,
|
|
149
|
-
limit: 10,
|
|
150
|
-
});
|
|
151
|
-
if (result.error) {
|
|
152
|
-
if (callback)
|
|
153
|
-
await callback({ text: `Search failed: ${result.error}` });
|
|
154
|
-
return { success: false, error: result.error };
|
|
155
|
-
}
|
|
156
|
-
const summary = safeStringify(result.data);
|
|
157
|
-
const responseText = `Found products matching "${query}":\n\n${summary}`;
|
|
158
|
-
if (callback)
|
|
159
|
-
await callback({ text: responseText }, "TCG_ORACLE_SEARCH");
|
|
160
|
-
return { success: true, text: responseText, data: { search_results: result.data } };
|
|
257
|
+
{
|
|
258
|
+
name: "current_price",
|
|
259
|
+
description: "Current price in USD",
|
|
260
|
+
required: true,
|
|
261
|
+
schema: { type: "number" }
|
|
161
262
|
},
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
name: "image_url",
|
|
180
|
-
description: "Direct URL to the card image (PNG, JPG, or WebP)",
|
|
181
|
-
required: true,
|
|
182
|
-
schema: { type: "string" },
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
name: "game",
|
|
186
|
-
description: "The TCG game this card belongs to (default: Pokemon)",
|
|
187
|
-
required: false,
|
|
188
|
-
schema: { type: "string" },
|
|
189
|
-
},
|
|
263
|
+
{
|
|
264
|
+
name: "model",
|
|
265
|
+
description: "Stochastic model: 'heston', 'merton', or 'kou'",
|
|
266
|
+
required: false,
|
|
267
|
+
schema: { type: "string", enumValues: ["heston", "merton", "kou"] }
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "days",
|
|
271
|
+
description: "Forecast horizon in days (default: 30)",
|
|
272
|
+
required: false,
|
|
273
|
+
schema: { type: "number" }
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
examples: [
|
|
277
|
+
[
|
|
278
|
+
{ name: "{{user1}}", content: { text: "Simulate the price of Charizard VMAX at $350 for 60 days" } },
|
|
279
|
+
{ name: "{{agentName}}", content: { text: "Running Monte Carlo simulation on Charizard VMAX...", action: "TCG_ORACLE_SIMULATE" } }
|
|
190
280
|
],
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{ name: "{{agentName}}", content: { text: "Analyzing card image for grading...", action: "TCG_ORACLE_GRADE" } },
|
|
195
|
-
],
|
|
196
|
-
[
|
|
197
|
-
{ name: "{{user1}}", content: { text: "What PSA grade would this get? https://i.imgur.com/card.png" } },
|
|
198
|
-
{ name: "{{agentName}}", content: { text: "Running AI vision grading analysis...", action: "TCG_ORACLE_GRADE" } },
|
|
199
|
-
],
|
|
200
|
-
[
|
|
201
|
-
{ name: "{{user1}}", content: { text: "Rate the condition of this Magic card https://example.com/mtg.jpg" } },
|
|
202
|
-
{ name: "{{agentName}}", content: { text: "Evaluating card condition for Beckett grading...", action: "TCG_ORACLE_GRADE" } },
|
|
203
|
-
],
|
|
281
|
+
[
|
|
282
|
+
{ name: "{{user1}}", content: { text: "What will a PSA 10 Base Set Blastoise worth $800 be in 90 days using merton model?" } },
|
|
283
|
+
{ name: "{{agentName}}", content: { text: "Running Merton jump-diffusion simulation for Blastoise...", action: "TCG_ORACLE_SIMULATE" } }
|
|
204
284
|
],
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
285
|
+
[
|
|
286
|
+
{ name: "{{user1}}", content: { text: "Forecast Lorcana Elsa at $25 for 30 days" } },
|
|
287
|
+
{ name: "{{agentName}}", content: { text: "Simulating Lorcana Elsa price trajectory...", action: "TCG_ORACLE_SIMULATE" } }
|
|
288
|
+
]
|
|
289
|
+
],
|
|
290
|
+
validate: async (runtime) => !!getBaseUrl(runtime),
|
|
291
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
292
|
+
const text = message.content.text || "";
|
|
293
|
+
const priceMatch = text.match(/\$?([\d,]+(?:\.\d{2})?)/);
|
|
294
|
+
const daysMatch = text.match(/(\d+)\s*days?/i);
|
|
295
|
+
const modelMatch = text.match(/\b(heston|merton|kou)\b/i);
|
|
296
|
+
const cardName = text.replace(/simulate|price|forecast|monte carlo|at|\$[\d,.]+|\d+\s*days?|heston|merton|kou/gi, "").replace(/\s+/g, " ").trim() || "Unknown Card";
|
|
297
|
+
const currentPrice = priceMatch ? parseFloat(priceMatch[1].replace(",", "")) : 0;
|
|
298
|
+
const days = daysMatch ? parseInt(daysMatch[1]) : 30;
|
|
299
|
+
const model = modelMatch ? modelMatch[1].toLowerCase() : "heston";
|
|
300
|
+
if (currentPrice <= 0) {
|
|
301
|
+
if (callback) await callback({ text: "Please provide a current price for the simulation. Example: 'Simulate Charizard VMAX at $350 for 60 days'" });
|
|
302
|
+
return { success: false, error: "Invalid or missing current price" };
|
|
303
|
+
}
|
|
304
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/simulate", {
|
|
305
|
+
card_name: cardName,
|
|
306
|
+
current_price: currentPrice,
|
|
307
|
+
model,
|
|
308
|
+
days,
|
|
309
|
+
simulations: 1e4
|
|
310
|
+
});
|
|
311
|
+
if (result.error) {
|
|
312
|
+
if (callback) await callback({ text: `Simulation failed: ${result.error}` });
|
|
313
|
+
return { success: false, error: result.error };
|
|
314
|
+
}
|
|
315
|
+
const summary = safeStringify(result.data);
|
|
316
|
+
const responseText = `Monte Carlo simulation for ${cardName} ($${currentPrice}, ${model} model, ${days} days):
|
|
317
|
+
|
|
318
|
+
${summary}`;
|
|
319
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_SIMULATE");
|
|
320
|
+
return { success: true, text: responseText, data: { simulation_result: result.data } };
|
|
321
|
+
}
|
|
230
322
|
};
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
description: "Current price in USD",
|
|
255
|
-
required: true,
|
|
256
|
-
schema: { type: "number" },
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
name: "model",
|
|
260
|
-
description: "Stochastic model: 'heston', 'merton', or 'kou'",
|
|
261
|
-
required: false,
|
|
262
|
-
schema: { type: "string", enumValues: ["heston", "merton", "kou"] },
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
name: "days",
|
|
266
|
-
description: "Forecast horizon in days (default: 30)",
|
|
267
|
-
required: false,
|
|
268
|
-
schema: { type: "number" },
|
|
269
|
-
},
|
|
323
|
+
var marketAction = {
|
|
324
|
+
name: "TCG_ORACLE_MARKET",
|
|
325
|
+
description: "Retrieve the latest daily market data snapshot from TCGCSV for a specific game. Returns top movers, price changes, volume data, and market trends. Data is refreshed daily via automated pipeline.",
|
|
326
|
+
similes: [
|
|
327
|
+
"MARKET_SNAPSHOT",
|
|
328
|
+
"MARKET_DATA",
|
|
329
|
+
"TCG_MARKET",
|
|
330
|
+
"CARD_MARKET",
|
|
331
|
+
"TOP_MOVERS",
|
|
332
|
+
"PRICE_CHANGES"
|
|
333
|
+
],
|
|
334
|
+
parameters: [
|
|
335
|
+
{
|
|
336
|
+
name: "game",
|
|
337
|
+
description: "Game name: Pokemon, Magic: The Gathering, Yu-Gi-Oh, Lorcana, One Piece, etc",
|
|
338
|
+
required: false,
|
|
339
|
+
schema: { type: "string" }
|
|
340
|
+
}
|
|
341
|
+
],
|
|
342
|
+
examples: [
|
|
343
|
+
[
|
|
344
|
+
{ name: "{{user1}}", content: { text: "Show me the Pokemon market snapshot" } },
|
|
345
|
+
{ name: "{{agentName}}", content: { text: "Pulling the latest Pokemon market data...", action: "TCG_ORACLE_MARKET" } }
|
|
270
346
|
],
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
{ name: "{{agentName}}", content: { text: "Running Monte Carlo simulation on Charizard VMAX...", action: "TCG_ORACLE_SIMULATE" } },
|
|
275
|
-
],
|
|
276
|
-
[
|
|
277
|
-
{ name: "{{user1}}", content: { text: "What will a PSA 10 Base Set Blastoise worth $800 be in 90 days using merton model?" } },
|
|
278
|
-
{ name: "{{agentName}}", content: { text: "Running Merton jump-diffusion simulation for Blastoise...", action: "TCG_ORACLE_SIMULATE" } },
|
|
279
|
-
],
|
|
280
|
-
[
|
|
281
|
-
{ name: "{{user1}}", content: { text: "Forecast Lorcana Elsa at $25 for 30 days" } },
|
|
282
|
-
{ name: "{{agentName}}", content: { text: "Simulating Lorcana Elsa price trajectory...", action: "TCG_ORACLE_SIMULATE" } },
|
|
283
|
-
],
|
|
347
|
+
[
|
|
348
|
+
{ name: "{{user1}}", content: { text: "What's happening in the Yu-Gi-Oh market today?" } },
|
|
349
|
+
{ name: "{{agentName}}", content: { text: "Fetching Yu-Gi-Oh market snapshot...", action: "TCG_ORACLE_MARKET" } }
|
|
284
350
|
],
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (callback)
|
|
313
|
-
await callback({ text: `Simulation failed: ${result.error}` });
|
|
314
|
-
return { success: false, error: result.error };
|
|
315
|
-
}
|
|
316
|
-
const summary = safeStringify(result.data);
|
|
317
|
-
const responseText = `Monte Carlo simulation for ${cardName} ($${currentPrice}, ${model} model, ${days} days):\n\n${summary}`;
|
|
318
|
-
if (callback)
|
|
319
|
-
await callback({ text: responseText }, "TCG_ORACLE_SIMULATE");
|
|
320
|
-
return { success: true, text: responseText, data: { simulation_result: result.data } };
|
|
321
|
-
},
|
|
351
|
+
[
|
|
352
|
+
{ name: "{{user1}}", content: { text: "Top movers in Magic: The Gathering" } },
|
|
353
|
+
{ name: "{{agentName}}", content: { text: "Pulling Magic: The Gathering market data...", action: "TCG_ORACLE_MARKET" } }
|
|
354
|
+
]
|
|
355
|
+
],
|
|
356
|
+
validate: async (runtime) => !!getBaseUrl(runtime),
|
|
357
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
358
|
+
const text = message.content.text?.toLowerCase() || "";
|
|
359
|
+
let game = "Pokemon";
|
|
360
|
+
for (const g of SUPPORTED_GAMES) {
|
|
361
|
+
if (text.includes(g.toLowerCase())) {
|
|
362
|
+
game = g;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/market", { game });
|
|
367
|
+
if (result.error) {
|
|
368
|
+
if (callback) await callback({ text: `Market data failed: ${result.error}` });
|
|
369
|
+
return { success: false, error: result.error };
|
|
370
|
+
}
|
|
371
|
+
const summary = safeStringify(result.data, 25);
|
|
372
|
+
const responseText = `Market snapshot for ${game}:
|
|
373
|
+
|
|
374
|
+
${summary}`;
|
|
375
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_MARKET");
|
|
376
|
+
return { success: true, text: responseText, data: { market_data: result.data } };
|
|
377
|
+
}
|
|
322
378
|
};
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
379
|
+
var gradeOrNotAction = {
|
|
380
|
+
name: "TCG_ORACLE_GRADE_OR_NOT",
|
|
381
|
+
description: "Answers 'will grading this card make me money?' by combining AI grade prediction with PSA fee schedules, shipping costs, and graded market values. Returns a GO/NO-GO verdict with expected ROI and profit scenarios (best case, predicted, worst case).",
|
|
382
|
+
similes: [
|
|
383
|
+
"SHOULD_I_GRADE",
|
|
384
|
+
"GRADE_ROI",
|
|
385
|
+
"GRADING_PROFIT",
|
|
386
|
+
"IS_IT_WORTH_GRADING",
|
|
387
|
+
"GRADE_OR_NOT"
|
|
388
|
+
],
|
|
389
|
+
parameters: [
|
|
390
|
+
{
|
|
391
|
+
name: "card_name",
|
|
392
|
+
description: "Name of the card to evaluate (e.g., 'Base Set Charizard Holo')",
|
|
393
|
+
required: true,
|
|
394
|
+
schema: { type: "string" }
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: "predicted_grade",
|
|
398
|
+
description: "Your predicted PSA grade (0 = auto-estimate)",
|
|
399
|
+
required: false,
|
|
400
|
+
schema: { type: "number" }
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: "raw_price",
|
|
404
|
+
description: "Current raw card value in USD (0 = auto-lookup)",
|
|
405
|
+
required: false,
|
|
406
|
+
schema: { type: "number" }
|
|
407
|
+
}
|
|
408
|
+
],
|
|
409
|
+
examples: [
|
|
410
|
+
[
|
|
411
|
+
{ name: "{{user1}}", content: { text: "Should I grade my Base Set Charizard?" } },
|
|
412
|
+
{ name: "{{agentName}}", content: { text: "Running Grade-or-Not analysis...", action: "TCG_ORACLE_GRADE_OR_NOT" } }
|
|
344
413
|
],
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
414
|
+
[
|
|
415
|
+
{ name: "{{user1}}", content: { text: "Is it worth grading a PSA 8 Pikachu VMAX worth $45?" } },
|
|
416
|
+
{ name: "{{agentName}}", content: { text: "Calculating grading ROI for Pikachu VMAX...", action: "TCG_ORACLE_GRADE_OR_NOT" } }
|
|
417
|
+
]
|
|
418
|
+
],
|
|
419
|
+
validate: async (runtime) => !!getBaseUrl(runtime),
|
|
420
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
421
|
+
const text = message.content.text || "";
|
|
422
|
+
const cardName = text.replace(/^(should i|is it worth|grade or not|grading roi|will grading)\s*/i, "").replace(/\s*(make me money|be profitable|worth it)\s*\??$/i, "").replace(/^(grade|grading)\s*/i, "").replace(/\bmy\b/i, "").trim() || "Unknown Card";
|
|
423
|
+
const priceMatch = text.match(/\$?([\d,]+(?:\.\d{2})?)/);
|
|
424
|
+
const gradeMatch = text.match(/\bpsa\s*(\d+(?:\.\d)?)\b/i) || text.match(/\bgrade\s*(\d+(?:\.\d)?)\b/i);
|
|
425
|
+
const params = { card_name: cardName };
|
|
426
|
+
if (priceMatch) params.raw_price = parseFloat(priceMatch[1].replace(",", ""));
|
|
427
|
+
if (gradeMatch) params.predicted_grade = parseFloat(gradeMatch[1]);
|
|
428
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/grade-or-not", params);
|
|
429
|
+
if (result.error) {
|
|
430
|
+
if (callback) await callback({ text: `Grade-or-Not analysis failed: ${result.error}` });
|
|
431
|
+
return { success: false, error: result.error };
|
|
432
|
+
}
|
|
433
|
+
const data = result.data;
|
|
434
|
+
const verdict = data?.verdict || "Unknown";
|
|
435
|
+
const explanation = data?.explanation || "";
|
|
436
|
+
const responseText = `Grade-or-Not verdict for ${cardName}:
|
|
437
|
+
|
|
438
|
+
${verdict}
|
|
439
|
+
${explanation}
|
|
440
|
+
|
|
441
|
+
Full analysis:
|
|
442
|
+
${safeStringify(data)}`;
|
|
443
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_GRADE_OR_NOT");
|
|
444
|
+
return { success: true, text: responseText, data: { grade_or_not: data } };
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
var trendingAction = {
|
|
448
|
+
name: "TCG_ORACLE_TRENDING",
|
|
449
|
+
description: "Returns the top trending trading cards by market activity (30-day sales volume, views, and price velocity). Covers all 25 supported TCG games. Useful for spotting market momentum and emerging opportunities.",
|
|
450
|
+
similes: [
|
|
451
|
+
"TRENDING_CARDS",
|
|
452
|
+
"HOT_CARDS",
|
|
453
|
+
"TOP_MOVERS",
|
|
454
|
+
"WHATS_TRENDING",
|
|
455
|
+
"POPULAR_CARDS",
|
|
456
|
+
"MARKET_TRENDS"
|
|
457
|
+
],
|
|
458
|
+
parameters: [
|
|
459
|
+
{
|
|
460
|
+
name: "game",
|
|
461
|
+
description: "Filter by game name (empty = all games)",
|
|
462
|
+
required: false,
|
|
463
|
+
schema: { type: "string" }
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "limit",
|
|
467
|
+
description: "Number of results (default: 25, max: 100)",
|
|
468
|
+
required: false,
|
|
469
|
+
schema: { type: "number" }
|
|
470
|
+
}
|
|
471
|
+
],
|
|
472
|
+
examples: [
|
|
473
|
+
[
|
|
474
|
+
{ name: "{{user1}}", content: { text: "What Pokemon cards are trending right now?" } },
|
|
475
|
+
{ name: "{{agentName}}", content: { text: "Pulling trending Pokemon cards...", action: "TCG_ORACLE_TRENDING" } }
|
|
358
476
|
],
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
477
|
+
[
|
|
478
|
+
{ name: "{{user1}}", content: { text: "Show me the hottest cards across all games" } },
|
|
479
|
+
{ name: "{{agentName}}", content: { text: "Fetching trending cards across all TCG games...", action: "TCG_ORACLE_TRENDING" } }
|
|
480
|
+
]
|
|
481
|
+
],
|
|
482
|
+
validate: async (runtime) => !!getBaseUrl(runtime),
|
|
483
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
484
|
+
const text = message.content?.text?.toLowerCase() || "";
|
|
485
|
+
let game = "";
|
|
486
|
+
for (const g of SUPPORTED_GAMES) {
|
|
487
|
+
if (text.includes(g.toLowerCase())) {
|
|
488
|
+
game = g;
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const params = { limit: 25 };
|
|
493
|
+
if (game) params.game = game;
|
|
494
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/trending", params);
|
|
495
|
+
if (result.error) {
|
|
496
|
+
if (callback) await callback({ text: `Trending cards failed: ${result.error}` });
|
|
497
|
+
return { success: false, error: result.error };
|
|
498
|
+
}
|
|
499
|
+
const summary = safeStringify(result.data, 15);
|
|
500
|
+
const responseText = `Trending cards${game ? ` for ${game}` : " across all games"}:
|
|
501
|
+
|
|
502
|
+
${summary}`;
|
|
503
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_TRENDING");
|
|
504
|
+
return { success: true, text: responseText, data: { trending: result.data } };
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
var arbGradeAction = {
|
|
508
|
+
name: "TCG_ORACLE_ARB_GRADE",
|
|
509
|
+
description: "Scans the TCG database for undervalued raw cards where grading would produce ROI above a threshold. Estimates PSA grades based on price tier and rarity, calculates expected graded values, and returns ranked opportunities sorted by expected profit.",
|
|
510
|
+
similes: [
|
|
511
|
+
"GRADING_ARBITRAGE",
|
|
512
|
+
"FIND_UNDERVALUED",
|
|
513
|
+
"ARBITRAGE_SCAN",
|
|
514
|
+
"PROFITABLE_CARDS",
|
|
515
|
+
"WHAT_TO_GRADE"
|
|
516
|
+
],
|
|
517
|
+
parameters: [
|
|
518
|
+
{
|
|
519
|
+
name: "game",
|
|
520
|
+
description: "TCG game to scan (default: Pokemon)",
|
|
521
|
+
required: false,
|
|
522
|
+
schema: { type: "string" }
|
|
381
523
|
},
|
|
524
|
+
{
|
|
525
|
+
name: "min_roi",
|
|
526
|
+
description: "Minimum expected ROI % to flag as opportunity (default: 50)",
|
|
527
|
+
required: false,
|
|
528
|
+
schema: { type: "number" }
|
|
529
|
+
}
|
|
530
|
+
],
|
|
531
|
+
examples: [
|
|
532
|
+
[
|
|
533
|
+
{ name: "{{user1}}", content: { text: "Find me profitable Pokemon cards to grade" } },
|
|
534
|
+
{ name: "{{agentName}}", content: { text: "Scanning for grading arbitrage opportunities...", action: "TCG_ORACLE_ARB_GRADE" } }
|
|
535
|
+
],
|
|
536
|
+
[
|
|
537
|
+
{ name: "{{user1}}", content: { text: "What Magic cards have the best grading ROI?" } },
|
|
538
|
+
{ name: "{{agentName}}", content: { text: "Running arbitrage scanner on Magic: The Gathering...", action: "TCG_ORACLE_ARB_GRADE" } }
|
|
539
|
+
]
|
|
540
|
+
],
|
|
541
|
+
validate: async (runtime) => !!getBaseUrl(runtime),
|
|
542
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
543
|
+
const text = message.content?.text?.toLowerCase() || "";
|
|
544
|
+
let game = "Pokemon";
|
|
545
|
+
for (const g of SUPPORTED_GAMES) {
|
|
546
|
+
if (text.includes(g.toLowerCase())) {
|
|
547
|
+
game = g;
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const params = {
|
|
552
|
+
game,
|
|
553
|
+
min_roi: 50,
|
|
554
|
+
limit: 20
|
|
555
|
+
};
|
|
556
|
+
const result = await callApi(getBaseUrl(runtime), "/api/v1/arb-grade", params);
|
|
557
|
+
if (result.error) {
|
|
558
|
+
if (callback) await callback({ text: `Arbitrage scan failed: ${result.error}` });
|
|
559
|
+
return { success: false, error: result.error };
|
|
560
|
+
}
|
|
561
|
+
const summary = safeStringify(result.data, 15);
|
|
562
|
+
const responseText = `Grading arbitrage opportunities for ${game}:
|
|
563
|
+
|
|
564
|
+
${summary}`;
|
|
565
|
+
if (callback) await callback({ text: responseText }, "TCG_ORACLE_ARB_GRADE");
|
|
566
|
+
return { success: true, text: responseText, data: { arbitrage: result.data } };
|
|
567
|
+
}
|
|
382
568
|
};
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const status = baseUrl ? "connected" : "not configured";
|
|
392
|
-
return {
|
|
393
|
-
text: `[TCG Oracle]
|
|
569
|
+
var tcgOracleProvider = {
|
|
570
|
+
name: "tcg-oracle",
|
|
571
|
+
description: "Provides TCG Oracle market intelligence context to the agent",
|
|
572
|
+
get: async (runtime, _message, _state) => {
|
|
573
|
+
const baseUrl = getBaseUrl(runtime);
|
|
574
|
+
const status = baseUrl ? "connected" : "not configured";
|
|
575
|
+
return {
|
|
576
|
+
text: `[TCG Oracle v1.1.0]
|
|
394
577
|
Status: ${status}
|
|
395
578
|
Database: 370,158 products across 25 games
|
|
396
579
|
Data source: TCGCSV daily snapshots
|
|
397
580
|
Models: Heston (stochastic vol), Merton (jump-diffusion), Kou (double-exp)
|
|
581
|
+
Actions: search, grade, simulate, market, grade-or-not, trending, arb-grade
|
|
398
582
|
Supported games: ${SUPPORTED_GAMES.join(", ")}
|
|
399
583
|
API: ${baseUrl || "TCG_ORACLE_URL not set"}
|
|
400
584
|
|
|
401
|
-
The agent can search cards, grade card images, simulate prices via Monte Carlo,
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
585
|
+
The agent can search cards, grade card images, simulate prices via Monte Carlo, pull market snapshots, check grading ROI, view trending cards, and scan for grading arbitrage opportunities.`,
|
|
586
|
+
values: {
|
|
587
|
+
tcgOracleStatus: status,
|
|
588
|
+
tcgOracleUrl: baseUrl || "not set",
|
|
589
|
+
totalProducts: 370158
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
}
|
|
409
593
|
};
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
594
|
+
var tcgOraclePlugin = {
|
|
595
|
+
name: "@undesirables/plugin-tcg-oracle",
|
|
596
|
+
description: "TCG Oracle \u2014 AI-powered trading card market intelligence for ElizaOS agents. Covers 370K+ products across 25 games with Monte Carlo price simulation (Heston/Merton/Kou), AI vision card grading with ROI analysis, trending cards feed, grading arbitrage scanner, and real-time market snapshots. Powered by TCGCSV daily data pipeline.",
|
|
597
|
+
init: async (config, runtime) => {
|
|
598
|
+
const validation = validateTcgOracleConfig(runtime);
|
|
599
|
+
if (!validation.valid) {
|
|
600
|
+
console.warn(`\u26A0\uFE0F TCG Oracle: ${validation.error}`);
|
|
601
|
+
} else {
|
|
602
|
+
console.log(`\u2705 TCG Oracle v1.1.0 connected to ${runtime.getSetting?.("TCG_ORACLE_URL")}`);
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
actions: [searchAction, gradeAction, simulateAction, marketAction, gradeOrNotAction, trendingAction, arbGradeAction],
|
|
606
|
+
providers: [tcgOracleProvider],
|
|
607
|
+
evaluators: [],
|
|
608
|
+
services: [MarketMonitorService]
|
|
609
|
+
};
|
|
610
|
+
var index_default = tcgOraclePlugin;
|
|
611
|
+
export {
|
|
612
|
+
SIMULATION_MODELS,
|
|
613
|
+
SUPPORTED_GAMES,
|
|
614
|
+
index_default as default
|
|
431
615
|
};
|
|
432
|
-
export default tcgOraclePlugin;
|
|
433
|
-
export { SUPPORTED_GAMES, SIMULATION_MODELS };
|
|
434
616
|
//# sourceMappingURL=index.js.map
|