apexbot 1.0.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/LICENSE +21 -0
- package/README.md +307 -0
- package/dist/adapters/manifold.js +166 -0
- package/dist/adapters/telegram.js +133 -0
- package/dist/agent/agentManager.js +315 -0
- package/dist/backtest/backtester.js +24 -0
- package/dist/channels/channelManager.js +72 -0
- package/dist/channels/discord.js +136 -0
- package/dist/channels/telegram.js +186 -0
- package/dist/cli/index.js +838 -0
- package/dist/core/eventBus.js +33 -0
- package/dist/decisionEngine.js +69 -0
- package/dist/eventBus.js +21 -0
- package/dist/gateway/dashboard.js +1009 -0
- package/dist/gateway/index.js +303 -0
- package/dist/index.js +195 -0
- package/dist/math/ev.js +25 -0
- package/dist/math/kelly.js +24 -0
- package/dist/rateLimiter.js +42 -0
- package/dist/safety/failsafe.js +32 -0
- package/dist/safety/llmChecker.js +148 -0
- package/dist/sessions/sessionManager.js +120 -0
- package/dist/strategy/arbitrage.js +151 -0
- package/package.json +78 -0
- package/scripts/install.ps1 +114 -0
- package/scripts/install.sh +146 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SafetyChecker = void 0;
|
|
4
|
+
const generative_ai_1 = require("@google/generative-ai");
|
|
5
|
+
const eventBus_1 = require("../eventBus");
|
|
6
|
+
/**
|
|
7
|
+
* LLM-based safety checker for market descriptions.
|
|
8
|
+
* Uses Google Gemini API.
|
|
9
|
+
* Analyzes market text for:
|
|
10
|
+
* - Ambiguous resolution criteria
|
|
11
|
+
* - Potential for bad-faith resolution
|
|
12
|
+
* - Misleading question framing
|
|
13
|
+
* - Political/controversial topics with unclear outcomes
|
|
14
|
+
*/
|
|
15
|
+
class SafetyChecker {
|
|
16
|
+
client;
|
|
17
|
+
cfg;
|
|
18
|
+
cache = new Map();
|
|
19
|
+
constructor(cfg) {
|
|
20
|
+
this.cfg = {
|
|
21
|
+
model: 'gemini-2.0-flash',
|
|
22
|
+
cacheEnabled: true,
|
|
23
|
+
...cfg,
|
|
24
|
+
};
|
|
25
|
+
this.client = new generative_ai_1.GoogleGenerativeAI(cfg.googleApiKey);
|
|
26
|
+
}
|
|
27
|
+
async checkMarket(marketId, question, description, creatorId) {
|
|
28
|
+
const cacheKey = `${marketId}:${question.slice(0, 50)}`;
|
|
29
|
+
if (this.cfg.cacheEnabled && this.cache.has(cacheKey)) {
|
|
30
|
+
return this.cache.get(cacheKey);
|
|
31
|
+
}
|
|
32
|
+
const prompt = `You are a prediction market analyst evaluating market quality and safety for automated trading.
|
|
33
|
+
|
|
34
|
+
Analyze this market and identify potential issues:
|
|
35
|
+
|
|
36
|
+
**Question:** ${question}
|
|
37
|
+
|
|
38
|
+
**Description:** ${description || '(no description provided)'}
|
|
39
|
+
|
|
40
|
+
**Creator ID:** ${creatorId || 'unknown'}
|
|
41
|
+
|
|
42
|
+
Evaluate for these risks:
|
|
43
|
+
1. AMBIGUOUS RESOLUTION: Are the resolution criteria clear and objective?
|
|
44
|
+
2. BAD FAITH RISK: Could the creator resolve this unfairly?
|
|
45
|
+
3. MISLEADING FRAMING: Is the question misleading or loaded?
|
|
46
|
+
4. EXTERNAL DEPENDENCY: Does resolution depend on hard-to-verify information?
|
|
47
|
+
5. TIME RISK: Is there a clear end date? Could it drag on indefinitely?
|
|
48
|
+
|
|
49
|
+
Respond in this exact JSON format:
|
|
50
|
+
{
|
|
51
|
+
"safe": true/false,
|
|
52
|
+
"score": 0.0-1.0,
|
|
53
|
+
"warnings": ["warning1", "warning2"],
|
|
54
|
+
"reasoning": "brief explanation"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Only output the JSON, nothing else.`;
|
|
58
|
+
try {
|
|
59
|
+
const model = this.client.getGenerativeModel({ model: this.cfg.model });
|
|
60
|
+
const result = await model.generateContent(prompt);
|
|
61
|
+
const response = await result.response;
|
|
62
|
+
const text = response.text();
|
|
63
|
+
const parsed = this.parseResponse(text, marketId);
|
|
64
|
+
if (this.cfg.cacheEnabled) {
|
|
65
|
+
this.cache.set(cacheKey, parsed);
|
|
66
|
+
}
|
|
67
|
+
// Emit warning if unsafe
|
|
68
|
+
if (!parsed.safe || parsed.score < 0.5) {
|
|
69
|
+
(0, eventBus_1.emit)('safety:warning', {
|
|
70
|
+
marketId,
|
|
71
|
+
question,
|
|
72
|
+
reason: parsed.warnings.join('; ') || parsed.reasoning,
|
|
73
|
+
score: parsed.score,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.error('Safety check failed:', e);
|
|
80
|
+
// Return conservative result on error
|
|
81
|
+
return {
|
|
82
|
+
safe: false,
|
|
83
|
+
score: 0,
|
|
84
|
+
warnings: ['Safety check API error'],
|
|
85
|
+
reasoning: 'Could not complete safety analysis',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
parseResponse(text, marketId) {
|
|
90
|
+
try {
|
|
91
|
+
// Extract JSON from response (handle markdown code blocks)
|
|
92
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
93
|
+
if (!jsonMatch)
|
|
94
|
+
throw new Error('No JSON found');
|
|
95
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
96
|
+
return {
|
|
97
|
+
safe: Boolean(parsed.safe),
|
|
98
|
+
score: Math.max(0, Math.min(1, Number(parsed.score) || 0)),
|
|
99
|
+
warnings: Array.isArray(parsed.warnings) ? parsed.warnings : [],
|
|
100
|
+
reasoning: String(parsed.reasoning || ''),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
console.error(`Failed to parse safety response for ${marketId}:`, text);
|
|
105
|
+
return {
|
|
106
|
+
safe: false,
|
|
107
|
+
score: 0.3,
|
|
108
|
+
warnings: ['Could not parse safety analysis'],
|
|
109
|
+
reasoning: text.slice(0, 200),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Quick heuristic check (no API call).
|
|
115
|
+
* Returns true if market passes basic sanity checks.
|
|
116
|
+
*/
|
|
117
|
+
quickCheck(question, description) {
|
|
118
|
+
const issues = [];
|
|
119
|
+
// Check for common red flags
|
|
120
|
+
if (!description || description.length < 20) {
|
|
121
|
+
issues.push('No or very short description');
|
|
122
|
+
}
|
|
123
|
+
if (question.includes('?') === false) {
|
|
124
|
+
issues.push('Question mark missing - may not be a clear question');
|
|
125
|
+
}
|
|
126
|
+
const redFlags = [
|
|
127
|
+
/i will resolve/i,
|
|
128
|
+
/resolve.*my (discretion|judgment)/i,
|
|
129
|
+
/subjective/i,
|
|
130
|
+
/vibe|feeling|think/i,
|
|
131
|
+
/probably|maybe|might/i,
|
|
132
|
+
];
|
|
133
|
+
for (const flag of redFlags) {
|
|
134
|
+
if (flag.test(description)) {
|
|
135
|
+
issues.push(`Red flag pattern: ${flag.toString()}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
pass: issues.length === 0,
|
|
140
|
+
issues,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
clearCache() {
|
|
144
|
+
this.cache.clear();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.SafetyChecker = SafetyChecker;
|
|
148
|
+
exports.default = SafetyChecker;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Manager - handles conversation sessions
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SessionManager = void 0;
|
|
7
|
+
class SessionManager {
|
|
8
|
+
sessions = new Map();
|
|
9
|
+
maxHistoryLength = 50;
|
|
10
|
+
makeId(channel, peer, isGroup, groupId) {
|
|
11
|
+
if (isGroup && groupId) {
|
|
12
|
+
return `${channel}:group:${groupId}`;
|
|
13
|
+
}
|
|
14
|
+
return `${channel}:dm:${peer}`;
|
|
15
|
+
}
|
|
16
|
+
getOrCreate(channel, peer, isGroup, groupId) {
|
|
17
|
+
const id = this.makeId(channel, peer, isGroup, groupId);
|
|
18
|
+
let session = this.sessions.get(id);
|
|
19
|
+
if (!session) {
|
|
20
|
+
session = {
|
|
21
|
+
id,
|
|
22
|
+
channel,
|
|
23
|
+
peer,
|
|
24
|
+
isGroup,
|
|
25
|
+
groupId,
|
|
26
|
+
messages: [],
|
|
27
|
+
messageCount: 0,
|
|
28
|
+
createdAt: Date.now(),
|
|
29
|
+
lastActivity: Date.now(),
|
|
30
|
+
};
|
|
31
|
+
this.sessions.set(id, session);
|
|
32
|
+
console.log(`[Sessions] Created session: ${id}`);
|
|
33
|
+
}
|
|
34
|
+
return session;
|
|
35
|
+
}
|
|
36
|
+
get(id) {
|
|
37
|
+
return this.sessions.get(id);
|
|
38
|
+
}
|
|
39
|
+
addMessage(sessionId, role, content) {
|
|
40
|
+
const session = this.sessions.get(sessionId);
|
|
41
|
+
if (!session)
|
|
42
|
+
return;
|
|
43
|
+
session.messages.push({
|
|
44
|
+
role,
|
|
45
|
+
content,
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
});
|
|
48
|
+
session.messageCount++;
|
|
49
|
+
session.lastActivity = Date.now();
|
|
50
|
+
// Trim history if too long
|
|
51
|
+
if (session.messages.length > this.maxHistoryLength) {
|
|
52
|
+
// Keep system message if present, trim oldest user/assistant messages
|
|
53
|
+
const systemMsgs = session.messages.filter(m => m.role === 'system');
|
|
54
|
+
const otherMsgs = session.messages.filter(m => m.role !== 'system');
|
|
55
|
+
session.messages = [
|
|
56
|
+
...systemMsgs,
|
|
57
|
+
...otherMsgs.slice(-this.maxHistoryLength + systemMsgs.length),
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
getHistory(sessionId) {
|
|
62
|
+
const session = this.sessions.get(sessionId);
|
|
63
|
+
return session?.messages || [];
|
|
64
|
+
}
|
|
65
|
+
reset(sessionId) {
|
|
66
|
+
const session = this.sessions.get(sessionId);
|
|
67
|
+
if (session) {
|
|
68
|
+
const systemMsgs = session.messages.filter(m => m.role === 'system');
|
|
69
|
+
session.messages = systemMsgs; // Keep system prompt
|
|
70
|
+
session.messageCount = 0;
|
|
71
|
+
console.log(`[Sessions] Reset session: ${sessionId}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
delete(sessionId) {
|
|
75
|
+
this.sessions.delete(sessionId);
|
|
76
|
+
}
|
|
77
|
+
getAll() {
|
|
78
|
+
return Array.from(this.sessions.values());
|
|
79
|
+
}
|
|
80
|
+
count() {
|
|
81
|
+
return this.sessions.size;
|
|
82
|
+
}
|
|
83
|
+
setSystemPrompt(sessionId, prompt) {
|
|
84
|
+
const session = this.sessions.get(sessionId);
|
|
85
|
+
if (session) {
|
|
86
|
+
session.systemPrompt = prompt;
|
|
87
|
+
// Add or update system message
|
|
88
|
+
const systemIdx = session.messages.findIndex(m => m.role === 'system');
|
|
89
|
+
if (systemIdx >= 0) {
|
|
90
|
+
session.messages[systemIdx].content = prompt;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
session.messages.unshift({ role: 'system', content: prompt, timestamp: Date.now() });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
setModel(sessionId, model) {
|
|
98
|
+
const session = this.sessions.get(sessionId);
|
|
99
|
+
if (session) {
|
|
100
|
+
session.model = model;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Cleanup old sessions
|
|
104
|
+
cleanup(maxAgeMs = 24 * 60 * 60 * 1000) {
|
|
105
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
106
|
+
let cleaned = 0;
|
|
107
|
+
for (const [id, session] of this.sessions) {
|
|
108
|
+
if (session.lastActivity < cutoff) {
|
|
109
|
+
this.sessions.delete(id);
|
|
110
|
+
cleaned++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (cleaned > 0) {
|
|
114
|
+
console.log(`[Sessions] Cleaned up ${cleaned} old sessions`);
|
|
115
|
+
}
|
|
116
|
+
return cleaned;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.SessionManager = SessionManager;
|
|
120
|
+
exports.default = SessionManager;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ArbitrageDetector = void 0;
|
|
4
|
+
const eventBus_1 = require("../eventBus");
|
|
5
|
+
/**
|
|
6
|
+
* Arbitrage detector for Manifold Markets.
|
|
7
|
+
* Detects:
|
|
8
|
+
* 1. Binary complement arb: market A (YES) + market B (NO on same event) should sum to ~1
|
|
9
|
+
* 2. Multi-outcome arb: multiple choice markets where probabilities don't sum to 1
|
|
10
|
+
* 3. Correlated market arb: related markets with known relationships
|
|
11
|
+
*/
|
|
12
|
+
class ArbitrageDetector {
|
|
13
|
+
cfg;
|
|
14
|
+
manifold;
|
|
15
|
+
marketCache = new Map();
|
|
16
|
+
constructor(manifold, cfg) {
|
|
17
|
+
this.manifold = manifold;
|
|
18
|
+
this.cfg = cfg;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check for binary complement arbitrage.
|
|
22
|
+
* If buying YES on market A at price pA and YES on market B (which is "NOT A") at pB,
|
|
23
|
+
* and pA + pB < 1, there's an arbitrage opportunity.
|
|
24
|
+
*/
|
|
25
|
+
checkBinaryComplement(marketA, marketB) {
|
|
26
|
+
const pA = marketA.probability;
|
|
27
|
+
const pB = marketB.probability;
|
|
28
|
+
// If these are complementary events (A and NOT A), probabilities should sum to 1
|
|
29
|
+
// If pA + pB < 1, buy both YES; if pA + pB > 1, buy both NO
|
|
30
|
+
const sum = pA + pB;
|
|
31
|
+
if (sum < 1 - 0.02) {
|
|
32
|
+
// Buy YES on both; guaranteed profit = 1 - sum per $1 total invested
|
|
33
|
+
const profitPerDollar = 1 - sum;
|
|
34
|
+
const expectedProfit = profitPerDollar * 100; // assuming $100 total stake
|
|
35
|
+
if (expectedProfit >= this.cfg.minProfitThreshold) {
|
|
36
|
+
return {
|
|
37
|
+
type: 'binary_complement',
|
|
38
|
+
markets: [marketA.id, marketB.id],
|
|
39
|
+
expectedProfit,
|
|
40
|
+
details: `Buy YES on both: ${marketA.question} (${(pA * 100).toFixed(1)}%) + ${marketB.question} (${(pB * 100).toFixed(1)}%) = ${(sum * 100).toFixed(1)}% < 100%`,
|
|
41
|
+
timestamp: Date.now(),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (sum > 1 + 0.02) {
|
|
46
|
+
// Buy NO on both; profit = sum - 1
|
|
47
|
+
const profitPerDollar = sum - 1;
|
|
48
|
+
const expectedProfit = profitPerDollar * 100;
|
|
49
|
+
if (expectedProfit >= this.cfg.minProfitThreshold) {
|
|
50
|
+
return {
|
|
51
|
+
type: 'binary_complement',
|
|
52
|
+
markets: [marketA.id, marketB.id],
|
|
53
|
+
expectedProfit,
|
|
54
|
+
details: `Buy NO on both: ${marketA.question} (${(pA * 100).toFixed(1)}%) + ${marketB.question} (${(pB * 100).toFixed(1)}%) = ${(sum * 100).toFixed(1)}% > 100%`,
|
|
55
|
+
timestamp: Date.now(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check correlated pairs from config.
|
|
63
|
+
*/
|
|
64
|
+
async checkCorrelatedPairs() {
|
|
65
|
+
const opportunities = [];
|
|
66
|
+
for (const pair of this.cfg.correlatedPairs) {
|
|
67
|
+
try {
|
|
68
|
+
const [marketA, marketB] = await Promise.all([
|
|
69
|
+
this.getMarketCached(pair.marketA),
|
|
70
|
+
this.getMarketCached(pair.marketB),
|
|
71
|
+
]);
|
|
72
|
+
if (!marketA || !marketB)
|
|
73
|
+
continue;
|
|
74
|
+
if (pair.relation === 'sum_to_one') {
|
|
75
|
+
const arb = this.checkBinaryComplement(marketA, marketB);
|
|
76
|
+
if (arb) {
|
|
77
|
+
arb.type = 'correlated';
|
|
78
|
+
opportunities.push(arb);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (pair.relation === 'positive') {
|
|
82
|
+
// Markets should move together; large divergence = opportunity
|
|
83
|
+
const diff = Math.abs(marketA.probability - marketB.probability);
|
|
84
|
+
if (diff > 0.15) { // >15% divergence
|
|
85
|
+
opportunities.push({
|
|
86
|
+
type: 'correlated',
|
|
87
|
+
markets: [marketA.id, marketB.id],
|
|
88
|
+
expectedProfit: diff * 50, // rough estimate
|
|
89
|
+
details: `Correlated markets diverged: ${marketA.question} (${(marketA.probability * 100).toFixed(1)}%) vs ${marketB.question} (${(marketB.probability * 100).toFixed(1)}%)`,
|
|
90
|
+
timestamp: Date.now(),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (pair.relation === 'negative') {
|
|
95
|
+
// Markets should move opposite; if both high or both low = opportunity
|
|
96
|
+
const sum = marketA.probability + marketB.probability;
|
|
97
|
+
if (Math.abs(sum - 1) > 0.15) {
|
|
98
|
+
opportunities.push({
|
|
99
|
+
type: 'correlated',
|
|
100
|
+
markets: [marketA.id, marketB.id],
|
|
101
|
+
expectedProfit: Math.abs(sum - 1) * 50,
|
|
102
|
+
details: `Negative correlation broken: ${marketA.question} (${(marketA.probability * 100).toFixed(1)}%) + ${marketB.question} (${(marketB.probability * 100).toFixed(1)}%) = ${(sum * 100).toFixed(1)}%`,
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
console.error(`Error checking pair ${pair.marketA}/${pair.marketB}:`, e);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return opportunities;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Scan for arbitrage across all markets (expensive; use sparingly).
|
|
116
|
+
*/
|
|
117
|
+
async scanAllMarkets(limit = 100) {
|
|
118
|
+
const markets = await this.manifold.getMarkets(limit);
|
|
119
|
+
const opportunities = [];
|
|
120
|
+
// Update cache
|
|
121
|
+
for (const m of markets) {
|
|
122
|
+
this.marketCache.set(m.id, m);
|
|
123
|
+
}
|
|
124
|
+
// Check configured correlated pairs
|
|
125
|
+
const corrArbs = await this.checkCorrelatedPairs();
|
|
126
|
+
opportunities.push(...corrArbs);
|
|
127
|
+
// Emit opportunities
|
|
128
|
+
for (const opp of opportunities) {
|
|
129
|
+
(0, eventBus_1.emit)('arb:opportunity', opp);
|
|
130
|
+
}
|
|
131
|
+
return opportunities;
|
|
132
|
+
}
|
|
133
|
+
async getMarketCached(id) {
|
|
134
|
+
if (this.marketCache.has(id)) {
|
|
135
|
+
return this.marketCache.get(id);
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const m = await this.manifold.getMarket(id);
|
|
139
|
+
this.marketCache.set(id, m);
|
|
140
|
+
return m;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
clearCache() {
|
|
147
|
+
this.marketCache.clear();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.ArbitrageDetector = ArbitrageDetector;
|
|
151
|
+
exports.default = ArbitrageDetector;
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "apexbot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ApexBot - Your free, private AI assistant. 100% free with Ollama (local AI). Multi-channel: Telegram, Discord, WebChat. Like Clawdbot but open-source!",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"apexbot": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"scripts",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"preferGlobal": true,
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"start": "ts-node src/cli/index.ts gateway",
|
|
19
|
+
"cli": "ts-node src/cli/index.ts",
|
|
20
|
+
"gateway": "ts-node src/cli/index.ts gateway",
|
|
21
|
+
"onboard": "ts-node src/cli/index.ts onboard",
|
|
22
|
+
"dev": "ts-node-dev --respawn src/cli/index.ts gateway --verbose",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"lint": "echo 'TODO: add eslint'",
|
|
25
|
+
"test": "echo 'TODO: add tests' && exit 0"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"ai-assistant",
|
|
29
|
+
"telegram-bot",
|
|
30
|
+
"discord-bot",
|
|
31
|
+
"chatbot",
|
|
32
|
+
"ollama",
|
|
33
|
+
"local-llm",
|
|
34
|
+
"gemini",
|
|
35
|
+
"claude",
|
|
36
|
+
"openai",
|
|
37
|
+
"typescript",
|
|
38
|
+
"free",
|
|
39
|
+
"open-source"
|
|
40
|
+
],
|
|
41
|
+
"author": "ApexBot Team",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"homepage": "https://github.com/YOUR_USERNAME/apexbot#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/YOUR_USERNAME/apexbot/issues"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/YOUR_USERNAME/apexbot.git"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@anthropic-ai/sdk": "^0.71.2",
|
|
56
|
+
"@google/generative-ai": "^0.24.1",
|
|
57
|
+
"axios": "^1.5.0",
|
|
58
|
+
"boxen": "^5.1.2",
|
|
59
|
+
"chalk": "^4.1.2",
|
|
60
|
+
"commander": "^14.0.2",
|
|
61
|
+
"discord.js": "^14.25.1",
|
|
62
|
+
"dotenv": "^17.2.3",
|
|
63
|
+
"eventemitter3": "^4.0.7",
|
|
64
|
+
"figlet": "^1.10.0",
|
|
65
|
+
"grammy": "^1.39.3",
|
|
66
|
+
"inquirer": "^8.2.7",
|
|
67
|
+
"ora": "^5.4.1",
|
|
68
|
+
"ws": "^8.19.0"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/figlet": "^1.7.0",
|
|
72
|
+
"@types/inquirer": "^9.0.9",
|
|
73
|
+
"@types/node": "^20.0.0",
|
|
74
|
+
"@types/ws": "^8.18.1",
|
|
75
|
+
"ts-node": "^10.9.1",
|
|
76
|
+
"typescript": "^5.2.2"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env pwsh
|
|
2
|
+
# ApexBot Installer for Windows
|
|
3
|
+
# Usage: iwr -useb https://raw.githubusercontent.com/YOUR_USERNAME/apexbot/main/scripts/install.ps1 | iex
|
|
4
|
+
|
|
5
|
+
$ErrorActionPreference = "Stop"
|
|
6
|
+
|
|
7
|
+
# Colors
|
|
8
|
+
function Write-Color($Text, $Color) {
|
|
9
|
+
Write-Host $Text -ForegroundColor $Color
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# ASCII Art
|
|
13
|
+
$logo = @"
|
|
14
|
+
|
|
15
|
+
___ ____ _______ ______ ____ ______
|
|
16
|
+
/ | / __ \/ ____/ |/ / __ )/ __ \/_ __/
|
|
17
|
+
/ /| | / /_/ / __/ | / __ / / / / / /
|
|
18
|
+
/ ___ |/ ____/ /___ / / /_/ / /_/ / / /
|
|
19
|
+
/_/ |_/_/ /_____//_/|_/_____/\____/ /_/
|
|
20
|
+
|
|
21
|
+
🦊 Your Free AI Assistant 🦊
|
|
22
|
+
"@
|
|
23
|
+
|
|
24
|
+
Write-Color $logo "Cyan"
|
|
25
|
+
Write-Host ""
|
|
26
|
+
Write-Color "Welcome to ApexBot Installer!" "Green"
|
|
27
|
+
Write-Host ""
|
|
28
|
+
|
|
29
|
+
# Check Node.js
|
|
30
|
+
Write-Host "Checking prerequisites..." -NoNewline
|
|
31
|
+
if (!(Get-Command node -ErrorAction SilentlyContinue)) {
|
|
32
|
+
Write-Color " FAILED" "Red"
|
|
33
|
+
Write-Host ""
|
|
34
|
+
Write-Color "Node.js is required but not installed." "Yellow"
|
|
35
|
+
Write-Host "Install Node.js from: https://nodejs.org/"
|
|
36
|
+
Write-Host ""
|
|
37
|
+
$install = Read-Host "Would you like to install Node.js via winget? (Y/n)"
|
|
38
|
+
if ($install -ne "n" -and $install -ne "N") {
|
|
39
|
+
winget install OpenJS.NodeJS.LTS
|
|
40
|
+
Write-Color "Please restart your terminal and run this installer again." "Yellow"
|
|
41
|
+
exit 0
|
|
42
|
+
}
|
|
43
|
+
exit 1
|
|
44
|
+
}
|
|
45
|
+
Write-Color " OK" "Green"
|
|
46
|
+
|
|
47
|
+
# Check npm
|
|
48
|
+
if (!(Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
49
|
+
Write-Color "npm is required but not installed." "Red"
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Check Ollama (optional but recommended)
|
|
54
|
+
Write-Host "Checking Ollama (recommended for free AI)..." -NoNewline
|
|
55
|
+
$ollamaInstalled = Get-Command ollama -ErrorAction SilentlyContinue
|
|
56
|
+
if ($ollamaInstalled) {
|
|
57
|
+
Write-Color " INSTALLED" "Green"
|
|
58
|
+
} else {
|
|
59
|
+
Write-Color " NOT FOUND" "Yellow"
|
|
60
|
+
Write-Host ""
|
|
61
|
+
Write-Color "Ollama is recommended for 100% free, local AI." "Yellow"
|
|
62
|
+
Write-Host "Install from: https://ollama.com"
|
|
63
|
+
Write-Host ""
|
|
64
|
+
$installOllama = Read-Host "Would you like to install Ollama via winget? (Y/n)"
|
|
65
|
+
if ($installOllama -ne "n" -and $installOllama -ne "N") {
|
|
66
|
+
winget install Ollama.Ollama
|
|
67
|
+
Write-Color "Ollama installed! Pull a model with: ollama pull llama3.2" "Green"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Write-Host ""
|
|
72
|
+
Write-Color "Installing ApexBot..." "Cyan"
|
|
73
|
+
Write-Host ""
|
|
74
|
+
|
|
75
|
+
# Install ApexBot globally
|
|
76
|
+
try {
|
|
77
|
+
npm install -g apexbot
|
|
78
|
+
Write-Color "ApexBot installed successfully!" "Green"
|
|
79
|
+
} catch {
|
|
80
|
+
Write-Color "npm global install failed, trying with sudo..." "Yellow"
|
|
81
|
+
npm install -g apexbot --unsafe-perm
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Write-Host ""
|
|
85
|
+
Write-Color "================================================" "Cyan"
|
|
86
|
+
Write-Host ""
|
|
87
|
+
Write-Color "🎉 Installation Complete!" "Green"
|
|
88
|
+
Write-Host ""
|
|
89
|
+
Write-Host "Next steps:"
|
|
90
|
+
Write-Host " 1. Run: " -NoNewline
|
|
91
|
+
Write-Color "apexbot onboard" "Yellow"
|
|
92
|
+
Write-Host " 2. Follow the setup wizard"
|
|
93
|
+
Write-Host " 3. Start your bot: " -NoNewline
|
|
94
|
+
Write-Color "apexbot daemon start" "Yellow"
|
|
95
|
+
Write-Host ""
|
|
96
|
+
|
|
97
|
+
# Check if Ollama needs setup
|
|
98
|
+
if ($ollamaInstalled) {
|
|
99
|
+
Write-Host "Ollama detected! Make sure to:"
|
|
100
|
+
Write-Host " - Pull a model: " -NoNewline
|
|
101
|
+
Write-Color "ollama pull llama3.2" "Yellow"
|
|
102
|
+
Write-Host " - Start Ollama: " -NoNewline
|
|
103
|
+
Write-Color "ollama serve" "Yellow"
|
|
104
|
+
Write-Host ""
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Write-Color "Documentation: https://github.com/YOUR_USERNAME/apexbot" "Cyan"
|
|
108
|
+
Write-Host ""
|
|
109
|
+
|
|
110
|
+
# Offer to run onboard
|
|
111
|
+
$runOnboard = Read-Host "Would you like to run the setup wizard now? (Y/n)"
|
|
112
|
+
if ($runOnboard -ne "n" -and $runOnboard -ne "N") {
|
|
113
|
+
apexbot onboard
|
|
114
|
+
}
|