@t2000/cli 0.18.4 → 0.18.6
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/{chunk-XIQ5PRLT.js → chunk-5TJJMPLT.js} +3 -3
- package/dist/{chunk-UKFUFU5T.js → chunk-7KJXKMEM.js} +2 -2
- package/dist/{chunk-2P7CF3JO.js → chunk-YPWSCLE3.js} +2 -2
- package/dist/{dist-PSN4YXQP.js → dist-ALKOTWPF.js} +296 -60
- package/dist/{dist-PSN4YXQP.js.map → dist-ALKOTWPF.js.map} +1 -1
- package/dist/{dist-5DMINJAS.js → dist-NNID5OEY.js} +4 -4
- package/dist/{fileFromPath-5M6WXDSO.js → fileFromPath-TJOXOKGY.js} +4 -4
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- /package/dist/{chunk-XIQ5PRLT.js.map → chunk-5TJJMPLT.js.map} +0 -0
- /package/dist/{chunk-UKFUFU5T.js.map → chunk-7KJXKMEM.js.map} +0 -0
- /package/dist/{chunk-2P7CF3JO.js.map → chunk-YPWSCLE3.js.map} +0 -0
- /package/dist/{dist-5DMINJAS.js.map → dist-NNID5OEY.js.map} +0 -0
- /package/dist/{fileFromPath-5M6WXDSO.js.map → fileFromPath-TJOXOKGY.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);
|
|
1
|
+
import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
|
|
2
2
|
import {
|
|
3
3
|
__export
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YPWSCLE3.js";
|
|
5
5
|
|
|
6
6
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
7
7
|
var external_exports = {};
|
|
@@ -5334,4 +5334,4 @@ export {
|
|
|
5334
5334
|
external_exports,
|
|
5335
5335
|
zodToJsonSchema
|
|
5336
5336
|
};
|
|
5337
|
-
//# sourceMappingURL=chunk-
|
|
5337
|
+
//# sourceMappingURL=chunk-5TJJMPLT.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);
|
|
1
|
+
import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
|
|
2
2
|
|
|
3
3
|
// ../../node_modules/.pnpm/web-streams-polyfill@4.0.0-beta.3/node_modules/web-streams-polyfill/dist/ponyfill.mjs
|
|
4
4
|
var e = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? Symbol : (e2) => `Symbol(${e2})`;
|
|
@@ -2052,4 +2052,4 @@ formdata-node/lib/esm/blobHelpers.js:
|
|
|
2052
2052
|
formdata-node/lib/esm/Blob.js:
|
|
2053
2053
|
(*! Based on fetch-blob. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> & David Frank *)
|
|
2054
2054
|
*/
|
|
2055
|
-
//# sourceMappingURL=chunk-
|
|
2055
|
+
//# sourceMappingURL=chunk-7KJXKMEM.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);
|
|
1
|
+
import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -47,4 +47,4 @@ export {
|
|
|
47
47
|
__toESM,
|
|
48
48
|
__toCommonJS
|
|
49
49
|
};
|
|
50
|
-
//# sourceMappingURL=chunk-
|
|
50
|
+
//# sourceMappingURL=chunk-YPWSCLE3.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);
|
|
1
|
+
import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename);
|
|
2
2
|
import {
|
|
3
3
|
external_exports,
|
|
4
4
|
zodToJsonSchema
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5TJJMPLT.js";
|
|
6
6
|
import {
|
|
7
7
|
Blob,
|
|
8
8
|
File,
|
|
9
9
|
isFile,
|
|
10
10
|
isFunction
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7KJXKMEM.js";
|
|
12
12
|
import {
|
|
13
13
|
__commonJS,
|
|
14
14
|
__esm,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
__require,
|
|
17
17
|
__toCommonJS,
|
|
18
18
|
__toESM
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-YPWSCLE3.js";
|
|
20
20
|
|
|
21
21
|
// ../../node_modules/.pnpm/webidl-conversions@3.0.1/node_modules/webidl-conversions/lib/index.js
|
|
22
22
|
var require_lib = __commonJS({
|
|
@@ -12650,7 +12650,7 @@ var require_keyboard = __commonJS({
|
|
|
12650
12650
|
}
|
|
12651
12651
|
};
|
|
12652
12652
|
exports.Keyboard = Keyboard;
|
|
12653
|
-
var
|
|
12653
|
+
var InlineKeyboard2 = class _InlineKeyboard {
|
|
12654
12654
|
/**
|
|
12655
12655
|
* Initialize a new `InlineKeyboard` with an optional two-dimensional array
|
|
12656
12656
|
* of `InlineKeyboardButton` objects. This is the nested array that holds
|
|
@@ -13162,7 +13162,7 @@ var require_keyboard = __commonJS({
|
|
|
13162
13162
|
return new _InlineKeyboard(source.map((row) => row.slice()));
|
|
13163
13163
|
}
|
|
13164
13164
|
};
|
|
13165
|
-
exports.InlineKeyboard =
|
|
13165
|
+
exports.InlineKeyboard = InlineKeyboard2;
|
|
13166
13166
|
function transpose(grid) {
|
|
13167
13167
|
var _a3;
|
|
13168
13168
|
const transposed = [];
|
|
@@ -14987,6 +14987,9 @@ var require_node_cron = __commonJS({
|
|
|
14987
14987
|
|
|
14988
14988
|
// ../gateway/dist/index.js
|
|
14989
14989
|
import { createRequire } from "module";
|
|
14990
|
+
import { fileURLToPath } from "url";
|
|
14991
|
+
import { dirname, resolve, join } from "path";
|
|
14992
|
+
import { T2000Error, INVESTMENT_ASSETS } from "@t2000/sdk";
|
|
14990
14993
|
|
|
14991
14994
|
// ../../node_modules/.pnpm/@anthropic-ai+sdk@0.39.0/node_modules/@anthropic-ai/sdk/version.mjs
|
|
14992
14995
|
var VERSION = "0.39.0";
|
|
@@ -15358,7 +15361,7 @@ var MultipartBody = class {
|
|
|
15358
15361
|
import { ReadableStream as ReadableStream2 } from "stream/web";
|
|
15359
15362
|
var fileFromPathWarned = false;
|
|
15360
15363
|
async function fileFromPath2(path, ...args) {
|
|
15361
|
-
const { fileFromPath: _fileFromPath } = await import("./fileFromPath-
|
|
15364
|
+
const { fileFromPath: _fileFromPath } = await import("./fileFromPath-TJOXOKGY.js");
|
|
15362
15365
|
if (!fileFromPathWarned) {
|
|
15363
15366
|
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path)}) instead`);
|
|
15364
15367
|
fileFromPathWarned = true;
|
|
@@ -19008,7 +19011,7 @@ var MultipartBody2 = class {
|
|
|
19008
19011
|
import { ReadableStream as ReadableStream4 } from "stream/web";
|
|
19009
19012
|
var fileFromPathWarned2 = false;
|
|
19010
19013
|
async function fileFromPath4(path, ...args) {
|
|
19011
|
-
const { fileFromPath: _fileFromPath } = await import("./fileFromPath-
|
|
19014
|
+
const { fileFromPath: _fileFromPath } = await import("./fileFromPath-TJOXOKGY.js");
|
|
19012
19015
|
if (!fileFromPathWarned2) {
|
|
19013
19016
|
console.warn(`fileFromPath is deprecated; use fs.createReadStream(${JSON.stringify(path)}) instead`);
|
|
19014
19017
|
fileFromPathWarned2 = true;
|
|
@@ -24861,12 +24864,12 @@ var import_grammy = __toESM(require_mod2(), 1);
|
|
|
24861
24864
|
import { Hono } from "hono";
|
|
24862
24865
|
import { serve } from "@hono/node-server";
|
|
24863
24866
|
var import_node_cron = __toESM(require_node_cron(), 1);
|
|
24864
|
-
import { INVESTMENT_ASSETS } from "@t2000/sdk";
|
|
24865
24867
|
import { readFile, writeFile } from "fs/promises";
|
|
24866
|
-
import { resolve, join } from "path";
|
|
24867
24868
|
import { homedir } from "os";
|
|
24868
24869
|
import { existsSync, mkdirSync, appendFileSync, statSync, readdirSync, unlinkSync } from "fs";
|
|
24869
24870
|
var require$1 = createRequire(import.meta.url);
|
|
24871
|
+
var __filename$1 = fileURLToPath(import.meta.url);
|
|
24872
|
+
dirname(__filename$1);
|
|
24870
24873
|
var __require2 = /* @__PURE__ */ ((x) => typeof require$1 !== "undefined" ? require$1 : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
24871
24874
|
get: (a, b) => (typeof require$1 !== "undefined" ? require$1 : a)[b]
|
|
24872
24875
|
}) : x)(function(x) {
|
|
@@ -25235,8 +25238,8 @@ var WebChatChannel = class {
|
|
|
25235
25238
|
try {
|
|
25236
25239
|
const { readFile: readFile2 } = await import("fs/promises");
|
|
25237
25240
|
const { resolve: resolve2, join: join2 } = await import("path");
|
|
25238
|
-
const { fileURLToPath } = await import("url");
|
|
25239
|
-
const __dirname2 = resolve2(
|
|
25241
|
+
const { fileURLToPath: fileURLToPath2 } = await import("url");
|
|
25242
|
+
const __dirname2 = resolve2(fileURLToPath2(import.meta.url), "..");
|
|
25240
25243
|
const distPath = join2(__dirname2, "..", "web", "dist");
|
|
25241
25244
|
const filePath = join2(distPath, path);
|
|
25242
25245
|
if (!filePath.startsWith(distPath)) return c.text("Forbidden", 403);
|
|
@@ -25291,6 +25294,17 @@ function getInlineHTML() {
|
|
|
25291
25294
|
.confirm-bar .accept { background: var(--green); color: white; }
|
|
25292
25295
|
.confirm-bar .cancel { background: var(--surface); color: var(--text-muted); border: 1px solid var(--border); }
|
|
25293
25296
|
.typing { align-self: flex-start; color: var(--text-muted); font-size: 13px; padding: 8px 16px; }
|
|
25297
|
+
#welcome { padding: 40px 20px; text-align: center; flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 16px; }
|
|
25298
|
+
#welcome h2 { font-size: 18px; font-weight: 600; color: var(--text); }
|
|
25299
|
+
#welcome p { font-size: 14px; color: var(--text-muted); max-width: 320px; }
|
|
25300
|
+
.quick-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; max-width: 380px; }
|
|
25301
|
+
.quick-btn { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 10px 16px; color: var(--text); font-size: 13px; cursor: pointer; transition: border-color 0.15s; }
|
|
25302
|
+
.quick-btn:hover { border-color: var(--accent); }
|
|
25303
|
+
.msg a { color: var(--accent); text-decoration: none; }
|
|
25304
|
+
.msg a:hover { text-decoration: underline; }
|
|
25305
|
+
.msg code { background: rgba(255,255,255,0.08); padding: 1px 5px; border-radius: 4px; font-family: 'SF Mono', Monaco, monospace; font-size: 0.9em; }
|
|
25306
|
+
.msg ul, .msg ol { margin: 4px 0; padding-left: 20px; }
|
|
25307
|
+
.msg li { margin: 2px 0; }
|
|
25294
25308
|
footer { padding: 16px 20px; border-top: 1px solid var(--border); }
|
|
25295
25309
|
#input-form { display: flex; gap: 8px; }
|
|
25296
25310
|
#input { flex: 1; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 12px 16px; color: var(--text); font-size: 14px; outline: none; font-family: inherit; }
|
|
@@ -25305,7 +25319,17 @@ function getInlineHTML() {
|
|
|
25305
25319
|
<div class="status" id="status"></div>
|
|
25306
25320
|
<h1>t2000</h1>
|
|
25307
25321
|
</header>
|
|
25308
|
-
<div id="
|
|
25322
|
+
<div id="welcome">
|
|
25323
|
+
<h2>t2000</h2>
|
|
25324
|
+
<p>Your AI financial advisor. Ask me anything about your accounts.</p>
|
|
25325
|
+
<div class="quick-actions">
|
|
25326
|
+
<button class="quick-btn" onclick="quickAction('What\\'s my balance?')">\u{1F4B0} Balance</button>
|
|
25327
|
+
<button class="quick-btn" onclick="quickAction('Show my portfolio')">\u{1F4CA} Portfolio</button>
|
|
25328
|
+
<button class="quick-btn" onclick="quickAction('What are the best rates?')">\u{1F4C8} Rates</button>
|
|
25329
|
+
<button class="quick-btn" onclick="quickAction('Show recent transactions')">\u{1F4B8} Transactions</button>
|
|
25330
|
+
</div>
|
|
25331
|
+
</div>
|
|
25332
|
+
<div id="messages" style="display:none"></div>
|
|
25309
25333
|
<footer>
|
|
25310
25334
|
<form id="input-form">
|
|
25311
25335
|
<input id="input" placeholder="Message your AI financial advisor..." autocomplete="off" />
|
|
@@ -25314,11 +25338,25 @@ function getInlineHTML() {
|
|
|
25314
25338
|
</footer>
|
|
25315
25339
|
<script>
|
|
25316
25340
|
const messages = document.getElementById('messages');
|
|
25341
|
+
const welcome = document.getElementById('welcome');
|
|
25317
25342
|
const input = document.getElementById('input');
|
|
25318
25343
|
const form = document.getElementById('input-form');
|
|
25319
25344
|
const statusDot = document.getElementById('status');
|
|
25320
25345
|
let currentAssistantMsg = null;
|
|
25321
25346
|
let connected = false;
|
|
25347
|
+
let welcomeVisible = true;
|
|
25348
|
+
|
|
25349
|
+
function hideWelcome() {
|
|
25350
|
+
if (!welcomeVisible) return;
|
|
25351
|
+
welcomeVisible = false;
|
|
25352
|
+
welcome.style.display = 'none';
|
|
25353
|
+
messages.style.display = 'flex';
|
|
25354
|
+
}
|
|
25355
|
+
|
|
25356
|
+
function quickAction(text) {
|
|
25357
|
+
hideWelcome();
|
|
25358
|
+
sendMsg(text);
|
|
25359
|
+
}
|
|
25322
25360
|
|
|
25323
25361
|
function connect() {
|
|
25324
25362
|
const es = new EventSource('/api/events');
|
|
@@ -25327,12 +25365,14 @@ function connect() {
|
|
|
25327
25365
|
es.onmessage = (e) => {
|
|
25328
25366
|
const data = JSON.parse(e.data);
|
|
25329
25367
|
if (data.type === 'token') {
|
|
25368
|
+
hideWelcome();
|
|
25330
25369
|
if (!currentAssistantMsg) {
|
|
25331
25370
|
currentAssistantMsg = addMessage('', 'assistant');
|
|
25332
25371
|
}
|
|
25333
25372
|
currentAssistantMsg.textContent += data.text;
|
|
25334
25373
|
scrollToBottom();
|
|
25335
25374
|
} else if (data.type === 'message') {
|
|
25375
|
+
hideWelcome();
|
|
25336
25376
|
if (currentAssistantMsg) {
|
|
25337
25377
|
currentAssistantMsg.innerHTML = renderMarkdown(data.text);
|
|
25338
25378
|
} else {
|
|
@@ -25341,6 +25381,7 @@ function connect() {
|
|
|
25341
25381
|
}
|
|
25342
25382
|
currentAssistantMsg = null;
|
|
25343
25383
|
} else if (data.type === 'tool_call') {
|
|
25384
|
+
hideWelcome();
|
|
25344
25385
|
if (!currentAssistantMsg) currentAssistantMsg = addMessage('', 'assistant');
|
|
25345
25386
|
const badge = document.createElement('span');
|
|
25346
25387
|
badge.className = 'tool-badge';
|
|
@@ -25369,20 +25410,30 @@ function addMessage(text, role) {
|
|
|
25369
25410
|
function scrollToBottom() { messages.scrollTop = messages.scrollHeight; }
|
|
25370
25411
|
|
|
25371
25412
|
function renderMarkdown(text) {
|
|
25372
|
-
|
|
25373
|
-
.replace(
|
|
25374
|
-
|
|
25375
|
-
if (rows.length < 2) return match;
|
|
25376
|
-
const headers = rows[0].split('|').filter(c => c.trim()).map(c => '<th>' + c.trim() + '</th>');
|
|
25377
|
-
const body = rows.slice(2).map(r => '<tr>' + r.split('|').filter(c => c.trim()).map(c => '<td>' + c.trim() + '</td>').join('') + '</tr>');
|
|
25378
|
-
return '<table><tr>' + headers.join('') + '</tr>' + body.join('') + '</table>';
|
|
25379
|
-
})
|
|
25413
|
+
let html = text
|
|
25414
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
25415
|
+
.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>')
|
|
25380
25416
|
.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')
|
|
25381
|
-
.replace(
|
|
25417
|
+
.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
|
|
25418
|
+
const lines = html.split('\\n');
|
|
25419
|
+
let result = [];
|
|
25420
|
+
let inList = false;
|
|
25421
|
+
for (const line of lines) {
|
|
25422
|
+
if (line.match(/^\\s*[-*]\\s+/)) {
|
|
25423
|
+
if (!inList) { result.push('<ul>'); inList = true; }
|
|
25424
|
+
result.push('<li>' + line.replace(/^\\s*[-*]\\s+/, '') + '</li>');
|
|
25425
|
+
} else {
|
|
25426
|
+
if (inList) { result.push('</ul>'); inList = false; }
|
|
25427
|
+
result.push(line);
|
|
25428
|
+
}
|
|
25429
|
+
}
|
|
25430
|
+
if (inList) result.push('</ul>');
|
|
25431
|
+
return result.join('<br>').replace(/<br><ul>/g, '<ul>').replace(/<\\/ul><br>/g, '</ul>');
|
|
25382
25432
|
}
|
|
25383
25433
|
|
|
25384
25434
|
function sendMsg(text) {
|
|
25385
25435
|
if (!text.trim()) return;
|
|
25436
|
+
hideWelcome();
|
|
25386
25437
|
addMessage(text, 'user');
|
|
25387
25438
|
input.value = '';
|
|
25388
25439
|
currentAssistantMsg = null;
|
|
@@ -25399,6 +25450,7 @@ connect();
|
|
|
25399
25450
|
</html>`;
|
|
25400
25451
|
}
|
|
25401
25452
|
var TELEGRAM_MAX_LENGTH = 4096;
|
|
25453
|
+
var TYPING_INTERVAL_MS = 4e3;
|
|
25402
25454
|
var TelegramChannel = class {
|
|
25403
25455
|
id = "telegram";
|
|
25404
25456
|
name = "Telegram";
|
|
@@ -25406,7 +25458,9 @@ var TelegramChannel = class {
|
|
|
25406
25458
|
allowedUsers;
|
|
25407
25459
|
messageHandler = null;
|
|
25408
25460
|
pinUnlockHandler = null;
|
|
25461
|
+
startHandler = null;
|
|
25409
25462
|
awaitingPin = /* @__PURE__ */ new Set();
|
|
25463
|
+
typingIntervals = /* @__PURE__ */ new Map();
|
|
25410
25464
|
constructor(config) {
|
|
25411
25465
|
this.bot = new import_grammy.Bot(config.botToken);
|
|
25412
25466
|
this.allowedUsers = new Set(config.allowedUsers);
|
|
@@ -25424,25 +25478,101 @@ var TelegramChannel = class {
|
|
|
25424
25478
|
}
|
|
25425
25479
|
async send(userId, message) {
|
|
25426
25480
|
const chatId = parseInt(userId, 10);
|
|
25427
|
-
const
|
|
25481
|
+
const html = markdownToTelegramHTML(message);
|
|
25482
|
+
const chunks = splitMessage(html);
|
|
25428
25483
|
for (const chunk of chunks) {
|
|
25429
25484
|
try {
|
|
25430
|
-
await this.bot.api.sendMessage(chatId, chunk, { parse_mode: "
|
|
25485
|
+
await this.bot.api.sendMessage(chatId, chunk, { parse_mode: "HTML" });
|
|
25431
25486
|
} catch {
|
|
25432
|
-
await this.bot.api.sendMessage(chatId, chunk);
|
|
25487
|
+
await this.bot.api.sendMessage(chatId, stripHtml(chunk));
|
|
25433
25488
|
}
|
|
25434
25489
|
}
|
|
25435
25490
|
}
|
|
25491
|
+
async sendWithConfirmation(userId, message) {
|
|
25492
|
+
const chatId = parseInt(userId, 10);
|
|
25493
|
+
const html = markdownToTelegramHTML(message);
|
|
25494
|
+
const keyboard = new import_grammy.InlineKeyboard().text("\u2705 Confirm", "confirm:yes").text("\u274C Cancel", "confirm:no");
|
|
25495
|
+
try {
|
|
25496
|
+
await this.bot.api.sendMessage(chatId, html, { parse_mode: "HTML", reply_markup: keyboard });
|
|
25497
|
+
} catch {
|
|
25498
|
+
await this.bot.api.sendMessage(chatId, stripHtml(html), { reply_markup: keyboard });
|
|
25499
|
+
}
|
|
25500
|
+
}
|
|
25436
25501
|
onMessage(handler) {
|
|
25437
25502
|
this.messageHandler = handler;
|
|
25438
25503
|
}
|
|
25439
25504
|
onPinUnlock(handler) {
|
|
25440
25505
|
this.pinUnlockHandler = handler;
|
|
25441
25506
|
}
|
|
25507
|
+
onStart(handler) {
|
|
25508
|
+
this.startHandler = handler;
|
|
25509
|
+
}
|
|
25442
25510
|
requestPin(userId) {
|
|
25443
25511
|
this.awaitingPin.add(userId);
|
|
25444
25512
|
}
|
|
25513
|
+
startTyping(userId) {
|
|
25514
|
+
const chatId = parseInt(userId, 10);
|
|
25515
|
+
this.bot.api.sendChatAction(chatId, "typing").catch(() => {
|
|
25516
|
+
});
|
|
25517
|
+
const interval = setInterval(() => {
|
|
25518
|
+
this.bot.api.sendChatAction(chatId, "typing").catch(() => {
|
|
25519
|
+
});
|
|
25520
|
+
}, TYPING_INTERVAL_MS);
|
|
25521
|
+
this.typingIntervals.set(userId, interval);
|
|
25522
|
+
}
|
|
25523
|
+
stopTyping(userId) {
|
|
25524
|
+
const interval = this.typingIntervals.get(userId);
|
|
25525
|
+
if (interval) {
|
|
25526
|
+
clearInterval(interval);
|
|
25527
|
+
this.typingIntervals.delete(userId);
|
|
25528
|
+
}
|
|
25529
|
+
}
|
|
25445
25530
|
setupHandlers() {
|
|
25531
|
+
this.bot.command("start", async (ctx) => {
|
|
25532
|
+
if (!this.isAllowed(ctx)) {
|
|
25533
|
+
await ctx.reply("This is a private financial agent. Access is restricted to the account owner.");
|
|
25534
|
+
return;
|
|
25535
|
+
}
|
|
25536
|
+
const userId = ctx.from.id.toString();
|
|
25537
|
+
let welcomeText = "Welcome to t2000 \u2014 your AI financial advisor.\n\nAsk me anything about your accounts.";
|
|
25538
|
+
if (this.startHandler) {
|
|
25539
|
+
try {
|
|
25540
|
+
welcomeText = await this.startHandler(userId);
|
|
25541
|
+
} catch {
|
|
25542
|
+
}
|
|
25543
|
+
}
|
|
25544
|
+
const keyboard = new import_grammy.InlineKeyboard().text("\u{1F4B0} Balance", "quick:What's my balance?").text("\u{1F4CA} Portfolio", "quick:Show my portfolio").row().text("\u{1F4C8} Rates", "quick:What are the best rates?").text("\u2753 Help", "quick:What can you do?");
|
|
25545
|
+
await ctx.reply(welcomeText, { reply_markup: keyboard });
|
|
25546
|
+
});
|
|
25547
|
+
this.bot.on("callback_query:data", async (ctx) => {
|
|
25548
|
+
if (!this.isAllowed(ctx)) {
|
|
25549
|
+
await ctx.answerCallbackQuery({ text: "Unauthorized" });
|
|
25550
|
+
return;
|
|
25551
|
+
}
|
|
25552
|
+
const data = ctx.callbackQuery.data;
|
|
25553
|
+
const userId = ctx.from.id.toString();
|
|
25554
|
+
if (data.startsWith("confirm:")) {
|
|
25555
|
+
const answer = data.slice(8);
|
|
25556
|
+
await ctx.answerCallbackQuery();
|
|
25557
|
+
try {
|
|
25558
|
+
await ctx.editMessageReplyMarkup({ reply_markup: void 0 });
|
|
25559
|
+
} catch {
|
|
25560
|
+
}
|
|
25561
|
+
if (this.messageHandler) {
|
|
25562
|
+
await this.messageHandler({ channelId: this.id, userId, text: answer });
|
|
25563
|
+
}
|
|
25564
|
+
return;
|
|
25565
|
+
}
|
|
25566
|
+
if (data.startsWith("quick:")) {
|
|
25567
|
+
await ctx.answerCallbackQuery();
|
|
25568
|
+
if (this.messageHandler) {
|
|
25569
|
+
const text = data.slice(6);
|
|
25570
|
+
await this.messageHandler({ channelId: this.id, userId, text });
|
|
25571
|
+
}
|
|
25572
|
+
return;
|
|
25573
|
+
}
|
|
25574
|
+
await ctx.answerCallbackQuery({ text: "Unknown action" });
|
|
25575
|
+
});
|
|
25446
25576
|
this.bot.on(["message:photo", "message:video", "message:voice", "message:sticker", "message:document"], async (ctx) => {
|
|
25447
25577
|
if (!this.isAllowed(ctx)) return;
|
|
25448
25578
|
await ctx.reply("I can only process text messages. How can I help with your finances?");
|
|
@@ -25509,6 +25639,19 @@ function splitMessage(text) {
|
|
|
25509
25639
|
}
|
|
25510
25640
|
return chunks;
|
|
25511
25641
|
}
|
|
25642
|
+
function escapeHtml(text) {
|
|
25643
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
25644
|
+
}
|
|
25645
|
+
function markdownToTelegramHTML(text) {
|
|
25646
|
+
let result = escapeHtml(text);
|
|
25647
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
25648
|
+
result = result.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
|
|
25649
|
+
result = result.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
25650
|
+
return result;
|
|
25651
|
+
}
|
|
25652
|
+
function stripHtml(text) {
|
|
25653
|
+
return text.replace(/<[^>]+>/g, "");
|
|
25654
|
+
}
|
|
25512
25655
|
var investAssets = Object.keys(INVESTMENT_ASSETS);
|
|
25513
25656
|
function createToolRegistry() {
|
|
25514
25657
|
return [
|
|
@@ -26011,23 +26154,66 @@ You manage their bank accounts on the Sui blockchain:
|
|
|
26011
26154
|
- Exchange (swap tokens via Cetus DEX)
|
|
26012
26155
|
- Investment (crypto & commodities \u2014 BTC, ETH, SUI, GOLD)
|
|
26013
26156
|
|
|
26014
|
-
You have
|
|
26157
|
+
You have ${toolCount} tools. Use them to check balances, execute transactions, manage investments, and optimize yield.
|
|
26015
26158
|
|
|
26016
26159
|
RULES:
|
|
26017
|
-
- Always confirm before
|
|
26018
|
-
- Show
|
|
26019
|
-
- For read-only queries (balance, rates, portfolio), respond immediately
|
|
26020
|
-
-
|
|
26021
|
-
|
|
26022
|
-
|
|
26023
|
-
-
|
|
26024
|
-
-
|
|
26160
|
+
- Always confirm before state-changing actions (send, save, invest, borrow, etc.)
|
|
26161
|
+
- Show what you'll do and ask "should I proceed?"
|
|
26162
|
+
- For read-only queries (balance, rates, portfolio), respond immediately \u2014 no confirmation needed
|
|
26163
|
+
- If the user asks something outside finance, briefly redirect
|
|
26164
|
+
|
|
26165
|
+
FORMATTING \u2014 follow these exactly:
|
|
26166
|
+
- Use standard markdown only: **bold**, \`code\`, [links](url), - lists
|
|
26167
|
+
- NEVER use markdown tables \u2014 they render poorly on mobile
|
|
26168
|
+
- One data point per line with emoji prefix for scannability
|
|
26169
|
+
- Currency: always 2 decimal places ($52.67, not $52.6723)
|
|
26170
|
+
- APY: always show % with 1-2 decimals (4.2%, not 0.0423)
|
|
26171
|
+
- Addresses: wrap in backticks \`0xabc...def\`
|
|
26172
|
+
- Transaction links: [View on explorer](https://suiscan.xyz/testnet/tx/DIGEST)
|
|
26173
|
+
- No Unicode box-drawing characters (\u2500, \u2502, \u250C, etc.) \u2014 they render inconsistently
|
|
26174
|
+
- No column-aligned text with spaces \u2014 breaks on proportional fonts
|
|
26025
26175
|
|
|
26026
26176
|
PERSONALITY:
|
|
26027
|
-
-
|
|
26028
|
-
- Brief \u2014
|
|
26029
|
-
-
|
|
26030
|
-
-
|
|
26177
|
+
- You are a knowledgeable financial advisor, not a chatbot
|
|
26178
|
+
- Brief \u2014 3-5 lines for simple queries, never an essay
|
|
26179
|
+
- Lead with the number \u2014 users scan for amounts
|
|
26180
|
+
- Be opinionated \u2014 if you notice idle funds, bad rates, or risky positions, say so
|
|
26181
|
+
- Suggest next actions only when something is genuinely actionable (idle funds, rate changes, better yield, risky health factor). Do NOT add a suggestion to every response
|
|
26182
|
+
- Never start with "Sure!" or "Of course!" \u2014 just answer
|
|
26183
|
+
|
|
26184
|
+
RESPONSE EXAMPLES \u2014 match this style:
|
|
26185
|
+
|
|
26186
|
+
When showing balances:
|
|
26187
|
+
\u{1F4B3} Checking: **$52.67**
|
|
26188
|
+
\u{1F3E6} Savings: **$19.24** (earning 4.2%)
|
|
26189
|
+
\u{1F4B8} Debt: **-$2.01**
|
|
26190
|
+
\u{1F4C8} Investment: **$0.05**
|
|
26191
|
+
|
|
26192
|
+
Net: **$70.95**
|
|
26193
|
+
|
|
26194
|
+
Your debt ($2.01) costs more than it earns. Pay it off from checking? Just say "repay all."
|
|
26195
|
+
|
|
26196
|
+
When showing a transaction receipt:
|
|
26197
|
+
\u2705 Saved **$80.00**
|
|
26198
|
+
|
|
26199
|
+
Protocol: NAVI
|
|
26200
|
+
APY: 5.57%
|
|
26201
|
+
Monthly yield: ~$3.71
|
|
26202
|
+
[View on explorer](https://suiscan.xyz/testnet/tx/abc123)
|
|
26203
|
+
|
|
26204
|
+
Savings balance: **$99.24** (+$80.00)
|
|
26205
|
+
|
|
26206
|
+
When showing portfolio:
|
|
26207
|
+
Your portfolio: **$152.30** (+2.3%)
|
|
26208
|
+
|
|
26209
|
+
\u{1F4C8} **SUI** \u2014 45.2 tokens ($48.00, +3.1%) \u2014 earning 2.6% on Suilend
|
|
26210
|
+
\u{1F4C8} **BTC** \u2014 0.0012 ($89.30, +1.8%)
|
|
26211
|
+
\u{1F4C9} **ETH** \u2014 0.025 ($15.00, -0.5%)
|
|
26212
|
+
|
|
26213
|
+
\u{1F4A1} ETH is the only position losing. Rebalance into SUI?
|
|
26214
|
+
|
|
26215
|
+
When showing an error:
|
|
26216
|
+
\u274C Not enough funds. You have **$12.50** available but need $50.00. Try a smaller amount?`;
|
|
26031
26217
|
}
|
|
26032
26218
|
async function buildContextInjection(agent) {
|
|
26033
26219
|
try {
|
|
@@ -26563,7 +26749,7 @@ var Logger = class _Logger {
|
|
|
26563
26749
|
if (!existsSync(this.logDir)) mkdirSync(this.logDir, { recursive: true });
|
|
26564
26750
|
this.logPath = join(this.logDir, LOG_FILE);
|
|
26565
26751
|
this.level = opts?.level ?? "info";
|
|
26566
|
-
this.toConsole =
|
|
26752
|
+
this.toConsole = true;
|
|
26567
26753
|
}
|
|
26568
26754
|
debug(msg, data) {
|
|
26569
26755
|
this.write("debug", msg, data);
|
|
@@ -26749,13 +26935,32 @@ var Gateway = class _Gateway {
|
|
|
26749
26935
|
allowedUsers: telegramConfig.allowedUsers ?? []
|
|
26750
26936
|
});
|
|
26751
26937
|
const loop = new AgentLoop({ agent: this.agent, llm: this.llm, tools, toolDefinitions: toolDefs });
|
|
26938
|
+
telegram.onStart(async (_userId) => {
|
|
26939
|
+
try {
|
|
26940
|
+
const balance = await this.agent.balance();
|
|
26941
|
+
return [
|
|
26942
|
+
"Welcome to t2000 \u2014 your AI financial advisor.\n",
|
|
26943
|
+
`\u{1F4B3} Checking: $${balance.available.toFixed(2)}`,
|
|
26944
|
+
`\u{1F3E6} Savings: $${balance.savings.toFixed(2)}`,
|
|
26945
|
+
`Net: $${(balance.available + balance.savings - balance.debt).toFixed(2)}`,
|
|
26946
|
+
"\nAsk me anything, or tap a button below."
|
|
26947
|
+
].join("\n");
|
|
26948
|
+
} catch {
|
|
26949
|
+
return "Welcome to t2000 \u2014 your AI financial advisor.\n\nAsk me anything about your accounts.";
|
|
26950
|
+
}
|
|
26951
|
+
});
|
|
26752
26952
|
telegram.onMessage(async (msg) => {
|
|
26753
26953
|
if (this.agent.enforcer.getConfig().locked) {
|
|
26754
26954
|
telegram.requestPin(msg.userId);
|
|
26755
26955
|
await telegram.send(msg.userId, "Agent is locked. Enter your PIN to unlock.");
|
|
26756
26956
|
return;
|
|
26757
26957
|
}
|
|
26758
|
-
|
|
26958
|
+
telegram.startTyping(msg.userId);
|
|
26959
|
+
try {
|
|
26960
|
+
await this.handleMessage(msg, loop, telegram);
|
|
26961
|
+
} finally {
|
|
26962
|
+
telegram.stopTyping(msg.userId);
|
|
26963
|
+
}
|
|
26759
26964
|
});
|
|
26760
26965
|
telegram.onPinUnlock(async (_pin) => {
|
|
26761
26966
|
try {
|
|
@@ -26802,6 +27007,9 @@ var Gateway = class _Gateway {
|
|
|
26802
27007
|
}
|
|
26803
27008
|
async handleMessage(msg, loop, channel) {
|
|
26804
27009
|
const isWebChat = channel instanceof WebChatChannel;
|
|
27010
|
+
const isTelegram = channel instanceof TelegramChannel;
|
|
27011
|
+
const startTime = Date.now();
|
|
27012
|
+
const queryPreview = msg.text.length > 40 ? msg.text.slice(0, 40) + "..." : msg.text;
|
|
26805
27013
|
try {
|
|
26806
27014
|
const response = await loop.processMessage(msg.text, {
|
|
26807
27015
|
stream: isWebChat,
|
|
@@ -26815,30 +27023,58 @@ var Gateway = class _Gateway {
|
|
|
26815
27023
|
channel.sendConfirmation(response.needsConfirmation.preview);
|
|
26816
27024
|
}
|
|
26817
27025
|
}
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
this.logger.debug(`Message handled`, {
|
|
26821
|
-
channel: channel.name,
|
|
26822
|
-
tools: response.toolCalls.length,
|
|
26823
|
-
inputTokens: response.usage.inputTokens,
|
|
26824
|
-
outputTokens: response.usage.outputTokens,
|
|
26825
|
-
cost: `$${cost.toFixed(4)}`
|
|
26826
|
-
});
|
|
26827
|
-
} catch (err) {
|
|
26828
|
-
const errorMsg = err instanceof Error ? err.message : "Internal error";
|
|
26829
|
-
if (this.isLLMError(err)) {
|
|
26830
|
-
this.logger.error("LLM call failed", { error: errorMsg });
|
|
26831
|
-
await channel.send(msg.userId, "AI is temporarily unavailable. Please try again in a moment.");
|
|
27026
|
+
if (isTelegram && response.needsConfirmation) {
|
|
27027
|
+
await channel.sendWithConfirmation(msg.userId, response.text);
|
|
26832
27028
|
} else {
|
|
26833
|
-
|
|
26834
|
-
|
|
27029
|
+
await channel.send(msg.userId, response.text);
|
|
27030
|
+
}
|
|
27031
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
27032
|
+
const toolCount = response.toolCalls.length;
|
|
27033
|
+
const suffix = response.needsConfirmation ? "confirmation pending" : `${toolCount} tool${toolCount !== 1 ? "s" : ""}, ${elapsed}s`;
|
|
27034
|
+
this.logger.info(`${channel.id} \xB7 "${queryPreview}" \u2192 ${suffix}`);
|
|
27035
|
+
if (this.options.verbose) {
|
|
27036
|
+
const cost = this.estimateCost(response.usage);
|
|
27037
|
+
this.logger.debug(` tokens: ${response.usage.inputTokens}in/${response.usage.outputTokens}out, ~$${cost.toFixed(4)}`);
|
|
26835
27038
|
}
|
|
27039
|
+
} catch (err) {
|
|
27040
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
27041
|
+
const friendlyMsg = this.friendlyError(err);
|
|
27042
|
+
this.logger.error(`${channel.id} \xB7 "${queryPreview}" \u2192 error (${elapsed}s): ${err instanceof Error ? err.message : String(err)}`);
|
|
27043
|
+
await channel.send(msg.userId, friendlyMsg);
|
|
26836
27044
|
}
|
|
26837
27045
|
}
|
|
26838
|
-
|
|
26839
|
-
if (!(err instanceof Error)) return
|
|
27046
|
+
friendlyError(err) {
|
|
27047
|
+
if (!(err instanceof Error)) return "Something went wrong. Try again?";
|
|
26840
27048
|
const msg = err.message.toLowerCase();
|
|
26841
|
-
|
|
27049
|
+
if (msg.includes("rate limit") || msg.includes("429") || msg.includes("overloaded")) {
|
|
27050
|
+
return "AI is busy. Try again in a moment.";
|
|
27051
|
+
}
|
|
27052
|
+
if (msg.includes("api") || msg.includes("500") || msg.includes("503") || msg.includes("timeout")) {
|
|
27053
|
+
return "AI is temporarily unavailable. Please try again in a moment.";
|
|
27054
|
+
}
|
|
27055
|
+
if (err instanceof T2000Error) {
|
|
27056
|
+
switch (err.code) {
|
|
27057
|
+
case "INSUFFICIENT_BALANCE":
|
|
27058
|
+
case "INSUFFICIENT_GAS":
|
|
27059
|
+
return `Not enough funds. ${err.message}`;
|
|
27060
|
+
case "SAFEGUARD_BLOCKED":
|
|
27061
|
+
return `${err.message}`;
|
|
27062
|
+
case "HEALTH_FACTOR_TOO_LOW":
|
|
27063
|
+
case "WITHDRAW_WOULD_LIQUIDATE":
|
|
27064
|
+
return "That would put your health factor below safe levels. Try a smaller amount.";
|
|
27065
|
+
case "SLIPPAGE_EXCEEDED":
|
|
27066
|
+
return "Price moved too much during the swap. Try again or increase slippage.";
|
|
27067
|
+
case "PROTOCOL_PAUSED":
|
|
27068
|
+
return "The protocol is temporarily paused. Try again later.";
|
|
27069
|
+
case "INVALID_ADDRESS":
|
|
27070
|
+
return "That address doesn't look right. Check it and try again.";
|
|
27071
|
+
case "INVALID_AMOUNT":
|
|
27072
|
+
return "Invalid amount. Please enter a positive number.";
|
|
27073
|
+
default:
|
|
27074
|
+
return `${err.message}`;
|
|
27075
|
+
}
|
|
27076
|
+
}
|
|
27077
|
+
return `Something went wrong: ${err.message}`;
|
|
26842
27078
|
}
|
|
26843
27079
|
estimateCost(usage) {
|
|
26844
27080
|
if (this.llm.id === "anthropic") {
|
|
@@ -26876,4 +27112,4 @@ humanize-ms/index.js:
|
|
|
26876
27112
|
* MIT Licensed
|
|
26877
27113
|
*)
|
|
26878
27114
|
*/
|
|
26879
|
-
//# sourceMappingURL=dist-
|
|
27115
|
+
//# sourceMappingURL=dist-ALKOTWPF.js.map
|