teleton 0.5.2 → 0.7.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 +243 -105
- package/dist/chunk-FNV5FF35.js +331 -0
- package/dist/chunk-LRCPA7SC.js +149 -0
- package/dist/{chunk-WUTMT6DW.js → chunk-N3F7E7DR.js} +114 -566
- package/dist/chunk-ND2X5FWB.js +368 -0
- package/dist/chunk-NERLQY2H.js +421 -0
- package/dist/{chunk-YBA6IBGT.js → chunk-OCLG5GKI.js} +24 -24
- package/dist/{chunk-WOXBZOQX.js → chunk-OGIG552S.js} +2152 -4488
- package/dist/chunk-RCMD3U65.js +141 -0
- package/dist/{chunk-O4R7V5Y2.js → chunk-RO62LO6Z.js} +11 -1
- package/dist/chunk-TCD4NZDA.js +3226 -0
- package/dist/chunk-UCN6TI25.js +143 -0
- package/dist/{chunk-WL2Q3VRD.js → chunk-UDD7FYOU.js} +12 -4
- package/dist/chunk-VAUJSSD3.js +20 -0
- package/dist/chunk-XBE4JB7C.js +8 -0
- package/dist/chunk-XBKSS6DM.js +58 -0
- package/dist/cli/index.js +1179 -412
- package/dist/client-3VWE7NC4.js +29 -0
- package/dist/{get-my-gifts-KVULMBJ3.js → get-my-gifts-RI7FAXAL.js} +3 -1
- package/dist/index.js +17 -8
- package/dist/{memory-Y5J7CXAR.js → memory-RD7ZSTRV.js} +16 -10
- package/dist/{migrate-UEQCDWL2.js → migrate-GO4NOBT7.js} +14 -6
- package/dist/{server-BQY7CM2N.js → server-OWVEZTR3.js} +869 -93
- package/dist/setup-server-C7ZTPHD5.js +934 -0
- package/dist/{task-dependency-resolver-TRPILAHM.js → task-dependency-resolver-WKZWJLLM.js} +19 -15
- package/dist/{task-executor-N7XNVK5N.js → task-executor-PD3H4MLO.js} +5 -2
- package/dist/tool-adapter-Y3TCEQOC.js +145 -0
- package/dist/tool-index-MIVK3D7H.js +250 -0
- package/dist/{transcript-7V4UNID4.js → transcript-UDJZP6NK.js} +2 -1
- package/dist/web/assets/complete-fZLnb5Ot.js +1 -0
- package/dist/web/assets/index-B_FcaX5D.css +1 -0
- package/dist/web/assets/index-CbeAP4_n.js +67 -0
- package/dist/web/assets/index.es-oXiZF7Hc.js +11 -0
- package/dist/web/assets/login-telegram-BP7CJDmx.js +1 -0
- package/dist/web/assets/run-DOrDowjK.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +21 -15
- package/dist/chunk-5WWR4CU3.js +0 -124
- package/dist/web/assets/index-CDMbujHf.css +0 -1
- package/dist/web/assets/index-DDX8oQ2z.js +0 -67
package/dist/cli/index.js
CHANGED
|
@@ -1,164 +1,519 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ConfigSchema,
|
|
3
|
-
DealsConfigSchema,
|
|
4
2
|
TelegramUserClient,
|
|
3
|
+
main
|
|
4
|
+
} from "../chunk-OGIG552S.js";
|
|
5
|
+
import "../chunk-UDD7FYOU.js";
|
|
6
|
+
import "../chunk-EHEV7FJ7.js";
|
|
7
|
+
import "../chunk-U7FQYCBQ.js";
|
|
8
|
+
import {
|
|
9
|
+
CONFIGURABLE_KEYS,
|
|
5
10
|
configExists,
|
|
11
|
+
deleteNestedValue,
|
|
12
|
+
getDefaultConfigPath,
|
|
13
|
+
getNestedValue,
|
|
14
|
+
readRawConfig,
|
|
15
|
+
setNestedValue,
|
|
16
|
+
writeRawConfig
|
|
17
|
+
} from "../chunk-TCD4NZDA.js";
|
|
18
|
+
import {
|
|
19
|
+
ConfigSchema,
|
|
20
|
+
DealsConfigSchema,
|
|
6
21
|
ensureWorkspace,
|
|
7
22
|
generateWallet,
|
|
8
|
-
getDefaultConfigPath,
|
|
9
|
-
getProviderMetadata,
|
|
10
|
-
getSupportedProviders,
|
|
11
23
|
importWallet,
|
|
12
24
|
isNewWorkspace,
|
|
13
25
|
loadWallet,
|
|
14
|
-
main,
|
|
15
26
|
saveWallet,
|
|
16
|
-
validateApiKeyFormat,
|
|
17
27
|
walletExists
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-
|
|
21
|
-
import "../chunk-U7FQYCBQ.js";
|
|
22
|
-
import "../chunk-5WWR4CU3.js";
|
|
23
|
-
import "../chunk-YBA6IBGT.js";
|
|
28
|
+
} from "../chunk-NERLQY2H.js";
|
|
29
|
+
import "../chunk-QUAPFI2N.js";
|
|
30
|
+
import "../chunk-TSKJCWQQ.js";
|
|
24
31
|
import {
|
|
25
|
-
|
|
26
|
-
} from "../chunk-
|
|
27
|
-
import "../chunk-
|
|
32
|
+
getErrorMessage
|
|
33
|
+
} from "../chunk-XBE4JB7C.js";
|
|
34
|
+
import "../chunk-ND2X5FWB.js";
|
|
35
|
+
import {
|
|
36
|
+
getProviderMetadata,
|
|
37
|
+
getSupportedProviders,
|
|
38
|
+
validateApiKeyFormat
|
|
39
|
+
} from "../chunk-LRCPA7SC.js";
|
|
40
|
+
import "../chunk-OCLG5GKI.js";
|
|
41
|
+
import "../chunk-N3F7E7DR.js";
|
|
42
|
+
import "../chunk-FNV5FF35.js";
|
|
43
|
+
import "../chunk-UCN6TI25.js";
|
|
44
|
+
import "../chunk-XBKSS6DM.js";
|
|
28
45
|
import {
|
|
29
46
|
TELEGRAM_MAX_MESSAGE_LENGTH
|
|
30
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-RO62LO6Z.js";
|
|
48
|
+
import {
|
|
49
|
+
fetchWithTimeout
|
|
50
|
+
} from "../chunk-VAUJSSD3.js";
|
|
51
|
+
import "../chunk-4DU3C27M.js";
|
|
31
52
|
import {
|
|
32
53
|
TELETON_ROOT
|
|
33
54
|
} from "../chunk-EYWNOHMJ.js";
|
|
55
|
+
import "../chunk-RCMD3U65.js";
|
|
34
56
|
import "../chunk-NUGDTPE4.js";
|
|
35
|
-
import "../chunk-QUAPFI2N.js";
|
|
36
|
-
import "../chunk-TSKJCWQQ.js";
|
|
37
57
|
import "../chunk-QGM4M3NI.js";
|
|
38
58
|
|
|
39
59
|
// src/cli/index.ts
|
|
40
60
|
import { Command } from "commander";
|
|
41
61
|
|
|
42
62
|
// src/cli/prompts.ts
|
|
43
|
-
import
|
|
44
|
-
|
|
63
|
+
import { input, select, checkbox, confirm, password } from "@inquirer/prompts";
|
|
64
|
+
import chalk from "chalk";
|
|
65
|
+
import ora from "ora";
|
|
66
|
+
var TON = chalk.hex("#0098EA");
|
|
67
|
+
var GREEN = chalk.green;
|
|
68
|
+
var CYAN = chalk.cyan;
|
|
69
|
+
var DIM = chalk.dim;
|
|
70
|
+
var BOLD = chalk.bold;
|
|
71
|
+
var RED = chalk.red;
|
|
72
|
+
var YELLOW = chalk.yellow;
|
|
73
|
+
var WHITE = chalk.white;
|
|
74
|
+
var inquirerTheme = {
|
|
75
|
+
prefix: { idle: TON("\u203A"), done: GREEN("\u2714") },
|
|
76
|
+
style: {
|
|
77
|
+
answer: (t) => CYAN.bold(t),
|
|
78
|
+
message: (t, status) => status === "done" ? DIM(t) : BOLD(t),
|
|
79
|
+
error: (t) => RED.bold(` \u2718 ${t}`),
|
|
80
|
+
help: (t) => DIM(t),
|
|
81
|
+
highlight: (t) => TON.bold(t),
|
|
82
|
+
description: (t) => DIM.italic(t)
|
|
83
|
+
},
|
|
84
|
+
icon: { cursor: TON("\u276F") }
|
|
85
|
+
};
|
|
86
|
+
function stripAnsi(s) {
|
|
87
|
+
return s.replace(/\x1B\[[0-9;]*m/g, "");
|
|
88
|
+
}
|
|
89
|
+
function padRight(s, len) {
|
|
90
|
+
return s + " ".repeat(Math.max(0, len - s.length));
|
|
91
|
+
}
|
|
92
|
+
function padRightAnsi(s, len) {
|
|
93
|
+
const visible = stripAnsi(s).length;
|
|
94
|
+
return s + " ".repeat(Math.max(0, len - visible));
|
|
95
|
+
}
|
|
96
|
+
function centerIn(text, width) {
|
|
97
|
+
const vis = stripAnsi(text).length;
|
|
98
|
+
const pad = width - vis;
|
|
99
|
+
const left = Math.floor(pad / 2);
|
|
100
|
+
const right = pad - left;
|
|
101
|
+
return " ".repeat(Math.max(0, left)) + text + " ".repeat(Math.max(0, right));
|
|
102
|
+
}
|
|
103
|
+
var BOX_WIDTH = 56;
|
|
104
|
+
var ASCII_ART_RAW = [
|
|
105
|
+
" ______ __ __ ___ __",
|
|
106
|
+
" /_ __/__ / /__ / /_____ ____ / | ____ ____ ____ / /_",
|
|
107
|
+
" / / / _ \\/ / _ \\/ __/ __ \\/ __ \\ / /| |/ __ `/ _ \\/ __ \\/ __/",
|
|
108
|
+
" / / / __/ / __/ /_/ /_/ / / / / / ___ / /_/ / __/ / / / /_",
|
|
109
|
+
" /_/ \\___/_/\\___/\\__/\\____/_/ /_/ /_/ |_\\__, /\\___/_/ /_/\\__/",
|
|
110
|
+
" /____/"
|
|
111
|
+
];
|
|
112
|
+
var ART_WIDTH = Math.max(...ASCII_ART_RAW.map((l) => l.length));
|
|
113
|
+
var ASCII_ART = ASCII_ART_RAW.map((l) => l + " ".repeat(ART_WIDTH - l.length));
|
|
114
|
+
var FRAME_WIDTH = Math.max(BOX_WIDTH, ART_WIDTH + 4);
|
|
115
|
+
function frameRow(content, border = TON) {
|
|
116
|
+
const pad = FRAME_WIDTH - stripAnsi(content).length;
|
|
117
|
+
return ` ${border("\u2551")}${content}${" ".repeat(Math.max(0, pad))}${border("\u2551")}`;
|
|
118
|
+
}
|
|
119
|
+
function emptyRow(border = TON) {
|
|
120
|
+
return ` ${border("\u2551")}${" ".repeat(FRAME_WIDTH)}${border("\u2551")}`;
|
|
121
|
+
}
|
|
122
|
+
function wizardFrame(currentStep, steps) {
|
|
123
|
+
const W = FRAME_WIDTH;
|
|
124
|
+
const out = [];
|
|
125
|
+
out.push(` ${TON("\u2554" + "\u2550".repeat(W) + "\u2557")}`);
|
|
126
|
+
out.push(emptyRow());
|
|
127
|
+
for (const line of ASCII_ART) {
|
|
128
|
+
out.push(frameRow(TON.bold(centerIn(line, W))));
|
|
129
|
+
}
|
|
130
|
+
out.push(emptyRow());
|
|
131
|
+
const subtitle = "Autonomous AI agent on Telegram with native TON blockchain integration";
|
|
132
|
+
out.push(frameRow(DIM(centerIn(subtitle, W))));
|
|
133
|
+
out.push(frameRow(DIM(centerIn("t.me/TeletonAgents | v0.7.0", W))));
|
|
134
|
+
out.push(` ${TON("\u2560" + "\u2550".repeat(W) + "\u2563")}`);
|
|
135
|
+
out.push(emptyRow());
|
|
136
|
+
const labelWidth = 14;
|
|
137
|
+
for (let i = 0; i < steps.length; i++) {
|
|
138
|
+
const s = steps[i];
|
|
139
|
+
let line;
|
|
140
|
+
if (i < currentStep) {
|
|
141
|
+
const val = s.value ?? "";
|
|
142
|
+
line = ` ${GREEN("\u2714")} ${WHITE(padRight(s.label, labelWidth))}${CYAN(val)}`;
|
|
143
|
+
} else if (i === currentStep) {
|
|
144
|
+
line = ` ${TON.bold("\u25B8")} ${TON.bold(padRight(s.label, labelWidth))}${DIM(s.desc)}`;
|
|
145
|
+
} else {
|
|
146
|
+
line = ` ${DIM("\u25CB")} ${DIM(padRight(s.label, labelWidth))}${DIM(s.desc)}`;
|
|
147
|
+
}
|
|
148
|
+
out.push(frameRow(padRightAnsi(line, W)));
|
|
149
|
+
}
|
|
150
|
+
out.push(emptyRow());
|
|
151
|
+
const pct = Math.round(currentStep / steps.length * 100);
|
|
152
|
+
const barLen = Math.max(10, W - 36);
|
|
153
|
+
const filled = Math.round(currentStep / steps.length * barLen);
|
|
154
|
+
const bar = TON("\u2588".repeat(filled)) + DIM("\u2591".repeat(barLen - filled));
|
|
155
|
+
const footer = ` ${bar} ${DIM(`${pct}% \xB7 Step ${currentStep + 1} of ${steps.length}`)}`;
|
|
156
|
+
out.push(frameRow(padRightAnsi(footer, W)));
|
|
157
|
+
out.push(emptyRow());
|
|
158
|
+
out.push(` ${TON("\u255A" + "\u2550".repeat(W) + "\u255D")}`);
|
|
159
|
+
return out.join("\n");
|
|
160
|
+
}
|
|
161
|
+
function noteBox(text, title, color = YELLOW) {
|
|
162
|
+
const W = FRAME_WIDTH;
|
|
163
|
+
const titleStr = ` ${title} `;
|
|
164
|
+
const titlePad = W - stripAnsi(titleStr).length - 1;
|
|
165
|
+
console.log(
|
|
166
|
+
` ${color("\u250C\u2500")}${color.bold(titleStr)}${color("\u2500".repeat(Math.max(0, titlePad)) + "\u2510")}`
|
|
167
|
+
);
|
|
168
|
+
const lines = text.split("\n");
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
const pad = W - stripAnsi(line).length - 2;
|
|
171
|
+
console.log(` ${color("\u2502")} ${line}${" ".repeat(Math.max(0, pad))}${color("\u2502")}`);
|
|
172
|
+
}
|
|
173
|
+
console.log(` ${color("\u2514" + "\u2500".repeat(W) + "\u2518")}`);
|
|
174
|
+
console.log();
|
|
175
|
+
}
|
|
176
|
+
function finalSummaryBox(steps, connected) {
|
|
177
|
+
const W = FRAME_WIDTH;
|
|
178
|
+
const B = GREEN;
|
|
179
|
+
const gRow = (content) => {
|
|
180
|
+
const pad = W - stripAnsi(content).length;
|
|
181
|
+
return ` ${B("\u2551")}${content}${" ".repeat(Math.max(0, pad))}${B("\u2551")}`;
|
|
182
|
+
};
|
|
183
|
+
const gEmpty = () => ` ${B("\u2551")}${" ".repeat(W)}${B("\u2551")}`;
|
|
184
|
+
const out = [];
|
|
185
|
+
const t1 = " Configuration Summary ";
|
|
186
|
+
const t1Pad = W - stripAnsi(t1).length - 1;
|
|
187
|
+
out.push(` ${B("\u2554\u2550" + B.bold(t1) + "\u2550".repeat(Math.max(0, t1Pad)) + "\u2557")}`);
|
|
188
|
+
out.push(gEmpty());
|
|
189
|
+
for (const s of steps) {
|
|
190
|
+
const val = s.value ?? DIM("not set");
|
|
191
|
+
const entry = ` ${GREEN("\u2714")} ${WHITE(padRight(s.label, 14))}${CYAN(val)}`;
|
|
192
|
+
out.push(gRow(entry));
|
|
193
|
+
}
|
|
194
|
+
const t2 = " Next Steps ";
|
|
195
|
+
const t2Pad = W - stripAnsi(t2).length - 1;
|
|
196
|
+
out.push(gEmpty());
|
|
197
|
+
out.push(` ${B("\u2560\u2550" + B.bold(t2) + "\u2550".repeat(Math.max(0, t2Pad)) + "\u2563")}`);
|
|
198
|
+
out.push(gEmpty());
|
|
199
|
+
const items = connected ? [
|
|
200
|
+
`${TON("1.")} Start the agent`,
|
|
201
|
+
` ${CYAN("$ teleton start")}`,
|
|
202
|
+
"",
|
|
203
|
+
`${TON("2.")} Send ${CYAN("/boot")} as your first message`,
|
|
204
|
+
` to bootstrap the agent's personality`,
|
|
205
|
+
"",
|
|
206
|
+
`${TON("3.")} Customize ${DIM("~/.teleton/workspace/SOUL.md")}`,
|
|
207
|
+
` to shape your agent's behavior`,
|
|
208
|
+
"",
|
|
209
|
+
`${TON("4.")} Read the docs`,
|
|
210
|
+
` ${TON.underline("https://github.com/TONresistor/teleton-agent")}`
|
|
211
|
+
] : [
|
|
212
|
+
`${TON("1.")} Start the agent`,
|
|
213
|
+
` ${CYAN("$ teleton start")}`,
|
|
214
|
+
"",
|
|
215
|
+
`${TON("2.")} On first launch, you will be asked for:`,
|
|
216
|
+
` - Telegram verification code`,
|
|
217
|
+
` - 2FA password (if enabled)`,
|
|
218
|
+
"",
|
|
219
|
+
`${TON("3.")} Send a message to your Telegram account to test`
|
|
220
|
+
];
|
|
221
|
+
for (const item of items) {
|
|
222
|
+
const pad = W - stripAnsi(item).length - 2;
|
|
223
|
+
out.push(` ${B("\u2551")} ${item}${" ".repeat(Math.max(0, pad))}${B("\u2551")}`);
|
|
224
|
+
}
|
|
225
|
+
out.push(gEmpty());
|
|
226
|
+
out.push(` ${B("\u255A" + "\u2550".repeat(W) + "\u255D")}`);
|
|
227
|
+
return out.join("\n");
|
|
228
|
+
}
|
|
229
|
+
var CancelledError = class extends Error {
|
|
230
|
+
constructor() {
|
|
231
|
+
super("Operation cancelled by user");
|
|
232
|
+
this.name = "CancelledError";
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
function wrapExitPromptError(promise) {
|
|
236
|
+
return promise.catch((err) => {
|
|
237
|
+
if (err?.name === "ExitPromptError") throw new CancelledError();
|
|
238
|
+
throw err;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function adaptValidate(fn) {
|
|
242
|
+
if (!fn) return void 0;
|
|
243
|
+
return (value) => {
|
|
244
|
+
const result = fn(value);
|
|
245
|
+
if (result === void 0) return true;
|
|
246
|
+
if (result instanceof Error) return result.message;
|
|
247
|
+
return result;
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
var InquirerPrompter = class {
|
|
45
251
|
async intro(title) {
|
|
46
|
-
|
|
252
|
+
console.log(`
|
|
253
|
+
${TON.bold(title)}
|
|
254
|
+
`);
|
|
47
255
|
}
|
|
48
256
|
async outro(message) {
|
|
49
|
-
|
|
257
|
+
console.log(`
|
|
258
|
+
${DIM(message)}
|
|
259
|
+
`);
|
|
50
260
|
}
|
|
51
261
|
async note(message, title) {
|
|
52
|
-
|
|
262
|
+
noteBox(message, title ?? "Note", YELLOW);
|
|
53
263
|
}
|
|
54
264
|
async text(options) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
return result;
|
|
265
|
+
return wrapExitPromptError(
|
|
266
|
+
input({
|
|
267
|
+
message: options.message,
|
|
268
|
+
default: options.initialValue,
|
|
269
|
+
theme: inquirerTheme,
|
|
270
|
+
validate: adaptValidate(options.validate)
|
|
271
|
+
})
|
|
272
|
+
);
|
|
65
273
|
}
|
|
66
274
|
async password(options) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return result;
|
|
275
|
+
return wrapExitPromptError(
|
|
276
|
+
password({
|
|
277
|
+
message: options.message,
|
|
278
|
+
theme: inquirerTheme,
|
|
279
|
+
validate: adaptValidate(options.validate)
|
|
280
|
+
})
|
|
281
|
+
);
|
|
75
282
|
}
|
|
76
283
|
async select(options) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
284
|
+
return wrapExitPromptError(
|
|
285
|
+
select({
|
|
286
|
+
message: options.message,
|
|
287
|
+
default: options.initialValue,
|
|
288
|
+
theme: inquirerTheme,
|
|
289
|
+
choices: options.options.map((opt) => ({
|
|
81
290
|
value: opt.value,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return mapped;
|
|
88
|
-
}),
|
|
89
|
-
initialValue: options.initialValue
|
|
90
|
-
});
|
|
91
|
-
if (clack.isCancel(result)) {
|
|
92
|
-
throw new CancelledError();
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
291
|
+
name: opt.label,
|
|
292
|
+
description: opt.hint
|
|
293
|
+
}))
|
|
294
|
+
})
|
|
295
|
+
);
|
|
95
296
|
}
|
|
96
297
|
async confirm(options) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return result;
|
|
298
|
+
return wrapExitPromptError(
|
|
299
|
+
confirm({
|
|
300
|
+
message: options.message,
|
|
301
|
+
default: options.initialValue ?? false,
|
|
302
|
+
theme: inquirerTheme
|
|
303
|
+
})
|
|
304
|
+
);
|
|
105
305
|
}
|
|
106
306
|
async multiselect(options) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
307
|
+
return wrapExitPromptError(
|
|
308
|
+
checkbox({
|
|
309
|
+
message: options.message,
|
|
310
|
+
theme: {
|
|
311
|
+
...inquirerTheme,
|
|
312
|
+
icon: { cursor: TON("\u276F"), checked: GREEN("\u2714"), unchecked: DIM("\u25CB") }
|
|
313
|
+
},
|
|
314
|
+
choices: options.options.map((opt) => ({
|
|
111
315
|
value: opt.value,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}),
|
|
119
|
-
required: options.required
|
|
120
|
-
});
|
|
121
|
-
if (clack.isCancel(result)) {
|
|
122
|
-
throw new CancelledError();
|
|
123
|
-
}
|
|
124
|
-
return result;
|
|
316
|
+
name: opt.label,
|
|
317
|
+
description: opt.hint
|
|
318
|
+
})),
|
|
319
|
+
required: options.required
|
|
320
|
+
})
|
|
321
|
+
);
|
|
125
322
|
}
|
|
126
323
|
spinner() {
|
|
127
|
-
|
|
324
|
+
let s = null;
|
|
128
325
|
return {
|
|
129
|
-
start: (message) =>
|
|
130
|
-
|
|
131
|
-
|
|
326
|
+
start: (message) => {
|
|
327
|
+
s = ora({ text: DIM(message), color: "cyan" }).start();
|
|
328
|
+
},
|
|
329
|
+
stop: (message) => {
|
|
330
|
+
if (s) s.succeed(DIM(message));
|
|
331
|
+
s = null;
|
|
332
|
+
},
|
|
333
|
+
message: (message) => {
|
|
334
|
+
if (s) s.text = DIM(message);
|
|
335
|
+
}
|
|
132
336
|
};
|
|
133
337
|
}
|
|
134
338
|
log(message) {
|
|
135
|
-
|
|
339
|
+
console.log(` ${DIM("\u25CB")} ${message}`);
|
|
136
340
|
}
|
|
137
341
|
warn(message) {
|
|
138
|
-
|
|
342
|
+
console.log(` ${YELLOW("\u26A0")} ${YELLOW(message)}`);
|
|
139
343
|
}
|
|
140
344
|
error(message) {
|
|
141
|
-
|
|
345
|
+
console.log(` ${RED("\u2717")} ${RED(message)}`);
|
|
142
346
|
}
|
|
143
347
|
success(message) {
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
var CancelledError = class extends Error {
|
|
148
|
-
constructor() {
|
|
149
|
-
super("Operation cancelled by user");
|
|
150
|
-
this.name = "CancelledError";
|
|
348
|
+
console.log(` ${GREEN("\u2713")} ${GREEN(message)}`);
|
|
151
349
|
}
|
|
152
350
|
};
|
|
153
351
|
function createPrompter() {
|
|
154
|
-
return new
|
|
352
|
+
return new InquirerPrompter();
|
|
155
353
|
}
|
|
156
354
|
|
|
157
355
|
// src/cli/commands/onboard.ts
|
|
158
|
-
import { writeFileSync, readFileSync, existsSync
|
|
356
|
+
import { writeFileSync, readFileSync, existsSync } from "fs";
|
|
159
357
|
import { join } from "path";
|
|
160
358
|
import YAML from "yaml";
|
|
359
|
+
import ora2 from "ora";
|
|
360
|
+
var STEPS = [
|
|
361
|
+
{ label: "Agent", desc: "Name & mode" },
|
|
362
|
+
{ label: "Provider", desc: "LLM & API key" },
|
|
363
|
+
{ label: "Telegram", desc: "Credentials" },
|
|
364
|
+
{ label: "Config", desc: "Model & policies" },
|
|
365
|
+
{ label: "Modules", desc: "Optional features" },
|
|
366
|
+
{ label: "Wallet", desc: "TON blockchain" },
|
|
367
|
+
{ label: "Connect", desc: "Telegram auth" }
|
|
368
|
+
];
|
|
369
|
+
function redraw(currentStep) {
|
|
370
|
+
console.clear();
|
|
371
|
+
console.log();
|
|
372
|
+
console.log(wizardFrame(currentStep, STEPS));
|
|
373
|
+
console.log();
|
|
374
|
+
}
|
|
375
|
+
function sleep(ms) {
|
|
376
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
377
|
+
}
|
|
378
|
+
var MODEL_OPTIONS = {
|
|
379
|
+
anthropic: [
|
|
380
|
+
{
|
|
381
|
+
value: "claude-opus-4-5-20251101",
|
|
382
|
+
name: "Claude Opus 4.5",
|
|
383
|
+
description: "Most capable, $5/M"
|
|
384
|
+
},
|
|
385
|
+
{ value: "claude-sonnet-4-0", name: "Claude Sonnet 4", description: "Balanced, $3/M" },
|
|
386
|
+
{
|
|
387
|
+
value: "claude-haiku-4-5-20251001",
|
|
388
|
+
name: "Claude Haiku 4.5",
|
|
389
|
+
description: "Fast & cheap, $1/M"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
value: "claude-3-5-haiku-20241022",
|
|
393
|
+
name: "Claude 3.5 Haiku",
|
|
394
|
+
description: "Cheapest, $0.80/M"
|
|
395
|
+
}
|
|
396
|
+
],
|
|
397
|
+
openai: [
|
|
398
|
+
{ value: "gpt-5", name: "GPT-5", description: "Most capable, 400K ctx, $1.25/M" },
|
|
399
|
+
{ value: "gpt-4o", name: "GPT-4o", description: "Balanced, 128K ctx, $2.50/M" },
|
|
400
|
+
{ value: "gpt-4.1", name: "GPT-4.1", description: "1M ctx, $2/M" },
|
|
401
|
+
{ value: "gpt-4.1-mini", name: "GPT-4.1 Mini", description: "1M ctx, cheap, $0.40/M" },
|
|
402
|
+
{ value: "o3", name: "o3", description: "Reasoning, 200K ctx, $2/M" }
|
|
403
|
+
],
|
|
404
|
+
google: [
|
|
405
|
+
{ value: "gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "Fast, 1M ctx, $0.30/M" },
|
|
406
|
+
{
|
|
407
|
+
value: "gemini-2.5-pro",
|
|
408
|
+
name: "Gemini 2.5 Pro",
|
|
409
|
+
description: "Most capable, 1M ctx, $1.25/M"
|
|
410
|
+
},
|
|
411
|
+
{ value: "gemini-2.0-flash", name: "Gemini 2.0 Flash", description: "Cheap, 1M ctx, $0.10/M" }
|
|
412
|
+
],
|
|
413
|
+
xai: [
|
|
414
|
+
{ value: "grok-4-fast", name: "Grok 4 Fast", description: "Vision, 2M ctx, $0.20/M" },
|
|
415
|
+
{ value: "grok-4", name: "Grok 4", description: "Reasoning, 256K ctx, $3/M" },
|
|
416
|
+
{ value: "grok-3", name: "Grok 3", description: "Stable, 131K ctx, $3/M" }
|
|
417
|
+
],
|
|
418
|
+
groq: [
|
|
419
|
+
{
|
|
420
|
+
value: "meta-llama/llama-4-maverick-17b-128e-instruct",
|
|
421
|
+
name: "Llama 4 Maverick",
|
|
422
|
+
description: "Vision, 131K ctx, $0.20/M"
|
|
423
|
+
},
|
|
424
|
+
{ value: "qwen/qwen3-32b", name: "Qwen3 32B", description: "Reasoning, 131K ctx, $0.29/M" },
|
|
425
|
+
{
|
|
426
|
+
value: "deepseek-r1-distill-llama-70b",
|
|
427
|
+
name: "DeepSeek R1 70B",
|
|
428
|
+
description: "Reasoning, 131K ctx, $0.75/M"
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
value: "llama-3.3-70b-versatile",
|
|
432
|
+
name: "Llama 3.3 70B",
|
|
433
|
+
description: "General purpose, 131K ctx, $0.59/M"
|
|
434
|
+
}
|
|
435
|
+
],
|
|
436
|
+
openrouter: [
|
|
437
|
+
{ value: "anthropic/claude-opus-4.5", name: "Claude Opus 4.5", description: "200K ctx, $5/M" },
|
|
438
|
+
{ value: "openai/gpt-5", name: "GPT-5", description: "400K ctx, $1.25/M" },
|
|
439
|
+
{ value: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "1M ctx, $0.30/M" },
|
|
440
|
+
{
|
|
441
|
+
value: "deepseek/deepseek-r1",
|
|
442
|
+
name: "DeepSeek R1",
|
|
443
|
+
description: "Reasoning, 64K ctx, $0.70/M"
|
|
444
|
+
},
|
|
445
|
+
{ value: "x-ai/grok-4", name: "Grok 4", description: "256K ctx, $3/M" }
|
|
446
|
+
],
|
|
447
|
+
moonshot: [
|
|
448
|
+
{ value: "kimi-k2.5", name: "Kimi K2.5", description: "Free, 256K ctx, multimodal" },
|
|
449
|
+
{
|
|
450
|
+
value: "kimi-k2-thinking",
|
|
451
|
+
name: "Kimi K2 Thinking",
|
|
452
|
+
description: "Free, 256K ctx, reasoning"
|
|
453
|
+
}
|
|
454
|
+
],
|
|
455
|
+
mistral: [
|
|
456
|
+
{
|
|
457
|
+
value: "devstral-small-2507",
|
|
458
|
+
name: "Devstral Small",
|
|
459
|
+
description: "Coding, 128K ctx, $0.10/M"
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
value: "devstral-medium-latest",
|
|
463
|
+
name: "Devstral Medium",
|
|
464
|
+
description: "Coding, 262K ctx, $0.40/M"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
value: "mistral-large-latest",
|
|
468
|
+
name: "Mistral Large",
|
|
469
|
+
description: "General, 128K ctx, $2/M"
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
value: "magistral-small",
|
|
473
|
+
name: "Magistral Small",
|
|
474
|
+
description: "Reasoning, 128K ctx, $0.50/M"
|
|
475
|
+
}
|
|
476
|
+
]
|
|
477
|
+
};
|
|
161
478
|
async function onboardCommand(options = {}) {
|
|
479
|
+
if (options.ui) {
|
|
480
|
+
const { SetupServer } = await import("../setup-server-C7ZTPHD5.js");
|
|
481
|
+
const port = parseInt(options.uiPort || "7777") || 7777;
|
|
482
|
+
const url = `http://localhost:${port}/setup`;
|
|
483
|
+
const blue2 = "\x1B[34m";
|
|
484
|
+
const reset2 = "\x1B[0m";
|
|
485
|
+
const dim = "\x1B[2m";
|
|
486
|
+
console.log(`
|
|
487
|
+
${blue2} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
488
|
+
\u2502 \u2502
|
|
489
|
+
\u2502 ______________ ________________ _ __ ___ _____________ ________ \u2502
|
|
490
|
+
\u2502 /_ __/ ____/ / / ____/_ __/ __ \\/ | / / / | / ____/ ____/ | / /_ __/ \u2502
|
|
491
|
+
\u2502 / / / __/ / / / __/ / / / / / / |/ / / /| |/ / __/ __/ / |/ / / / \u2502
|
|
492
|
+
\u2502 / / / /___/ /___/ /___ / / / /_/ / /| / / ___ / /_/ / /___/ /| / / / \u2502
|
|
493
|
+
\u2502 /_/ /_____/_____/_____/ /_/ \\____/_/ |_/ /_/ |_\\____/_____/_/ |_/ /_/ \u2502
|
|
494
|
+
\u2502 \u2502
|
|
495
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 DEV: ZKPROOF.T.ME \u2500\u2500\u2518${reset2}
|
|
496
|
+
|
|
497
|
+
${dim}Setup wizard running at${reset2} ${url}
|
|
498
|
+
${dim}Opening in your default browser...${reset2}
|
|
499
|
+
${dim}Press Ctrl+C to cancel.${reset2}
|
|
500
|
+
`);
|
|
501
|
+
const server = new SetupServer(port);
|
|
502
|
+
await server.start();
|
|
503
|
+
process.on("SIGINT", async () => {
|
|
504
|
+
await server.stop();
|
|
505
|
+
process.exit(0);
|
|
506
|
+
});
|
|
507
|
+
await server.waitForLaunch();
|
|
508
|
+
console.log("\n Launch signal received \u2014 stopping setup server");
|
|
509
|
+
await server.stop();
|
|
510
|
+
console.log(" Starting TonnetApp...\n");
|
|
511
|
+
const { TeletonApp } = await import("../index.js");
|
|
512
|
+
const configPath = join(TELETON_ROOT, "config.yaml");
|
|
513
|
+
const app = new TeletonApp(configPath);
|
|
514
|
+
await app.start();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
162
517
|
const prompter = createPrompter();
|
|
163
518
|
try {
|
|
164
519
|
if (options.nonInteractive) {
|
|
@@ -168,368 +523,515 @@ async function onboardCommand(options = {}) {
|
|
|
168
523
|
}
|
|
169
524
|
} catch (err) {
|
|
170
525
|
if (err instanceof CancelledError) {
|
|
171
|
-
|
|
526
|
+
console.log(`
|
|
527
|
+
${DIM("Setup cancelled. No changes were made.")}
|
|
528
|
+
`);
|
|
172
529
|
process.exit(0);
|
|
173
530
|
}
|
|
174
531
|
throw err;
|
|
175
532
|
}
|
|
176
533
|
}
|
|
177
534
|
async function runInteractiveOnboarding(options, prompter) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
535
|
+
let selectedFlow = "quick";
|
|
536
|
+
let selectedProvider = "anthropic";
|
|
537
|
+
const dealsEnabled = true;
|
|
538
|
+
let selectedModel = "";
|
|
539
|
+
let apiKey = "";
|
|
540
|
+
let apiId = 0;
|
|
541
|
+
let apiHash = "";
|
|
542
|
+
let phone = "";
|
|
543
|
+
let userId = 0;
|
|
544
|
+
let tonapiKey;
|
|
545
|
+
let tavilyApiKey;
|
|
546
|
+
let botToken;
|
|
547
|
+
let botUsername;
|
|
548
|
+
let dmPolicy = "open";
|
|
549
|
+
let groupPolicy = "open";
|
|
550
|
+
let requireMention = true;
|
|
551
|
+
let maxAgenticIterations = "5";
|
|
552
|
+
let cocoonInstance = 1e4;
|
|
553
|
+
let buyMaxFloorPercent = 100;
|
|
554
|
+
let sellMinFloorPercent = 105;
|
|
555
|
+
console.clear();
|
|
556
|
+
console.log();
|
|
557
|
+
console.log(wizardFrame(0, STEPS));
|
|
558
|
+
console.log();
|
|
559
|
+
await sleep(800);
|
|
560
|
+
redraw(0);
|
|
561
|
+
noteBox(
|
|
562
|
+
"Your Teleton agent will have FULL CONTROL over:\n\n \u2022 TELEGRAM: Read, send, and delete messages on your behalf\n \u2022 TON WALLET: A new wallet will be generated that the agent\n can use to send transactions autonomously\n\nWe strongly recommend using a dedicated Telegram account.\nOnly fund the generated wallet with amounts you're comfortable\nletting the agent manage.",
|
|
563
|
+
"Security Warning",
|
|
564
|
+
RED
|
|
196
565
|
);
|
|
197
|
-
const acceptRisk = await
|
|
566
|
+
const acceptRisk = await confirm({
|
|
198
567
|
message: "I understand the risks and want to continue",
|
|
199
|
-
|
|
568
|
+
default: false,
|
|
569
|
+
theme: inquirerTheme
|
|
200
570
|
});
|
|
201
571
|
if (!acceptRisk) {
|
|
202
|
-
|
|
572
|
+
console.log(`
|
|
573
|
+
${DIM("Setup cancelled \u2014 you must accept the risks to continue.")}
|
|
574
|
+
`);
|
|
203
575
|
process.exit(1);
|
|
204
576
|
}
|
|
205
|
-
const
|
|
206
|
-
|
|
577
|
+
const spinner = ora2({ color: "cyan" });
|
|
578
|
+
spinner.start(DIM("Creating workspace..."));
|
|
207
579
|
const workspace = await ensureWorkspace({
|
|
208
580
|
workspaceDir: options.workspace,
|
|
209
581
|
ensureTemplates: true
|
|
210
582
|
});
|
|
211
583
|
const isNew = isNewWorkspace(workspace);
|
|
212
|
-
|
|
584
|
+
spinner.succeed(DIM(`Workspace: ${workspace.root}`));
|
|
213
585
|
if (!isNew) {
|
|
214
586
|
prompter.warn("Existing configuration detected");
|
|
215
|
-
const shouldOverwrite = await
|
|
587
|
+
const shouldOverwrite = await confirm({
|
|
216
588
|
message: "Overwrite existing configuration?",
|
|
217
|
-
|
|
589
|
+
default: false,
|
|
590
|
+
theme: inquirerTheme
|
|
218
591
|
});
|
|
219
592
|
if (!shouldOverwrite) {
|
|
220
|
-
|
|
593
|
+
console.log(`
|
|
594
|
+
${DIM("Setup cancelled \u2014 existing configuration preserved.")}
|
|
595
|
+
`);
|
|
221
596
|
return;
|
|
222
597
|
}
|
|
223
598
|
}
|
|
224
|
-
const agentName = await
|
|
599
|
+
const agentName = await input({
|
|
225
600
|
message: "Give your agent a name (optional)",
|
|
226
|
-
|
|
601
|
+
default: "Nova",
|
|
602
|
+
theme: inquirerTheme
|
|
227
603
|
});
|
|
228
604
|
if (agentName && agentName.trim() && existsSync(workspace.identityPath)) {
|
|
229
605
|
const identity = readFileSync(workspace.identityPath, "utf-8");
|
|
230
606
|
const updated = identity.replace("[Your name - pick one or ask your human]", agentName.trim());
|
|
231
607
|
writeFileSync(workspace.identityPath, updated, "utf-8");
|
|
232
608
|
}
|
|
233
|
-
|
|
609
|
+
selectedFlow = await select({
|
|
234
610
|
message: "Installation mode",
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
],
|
|
239
|
-
initialValue: "quick"
|
|
240
|
-
});
|
|
241
|
-
const enabledModules = await prompter.multiselect({
|
|
242
|
-
message: "Enable optional modules (Space to toggle, Enter to confirm)",
|
|
243
|
-
options: [
|
|
611
|
+
default: "quick",
|
|
612
|
+
theme: inquirerTheme,
|
|
613
|
+
choices: [
|
|
244
614
|
{
|
|
245
|
-
value: "
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
615
|
+
value: "quick",
|
|
616
|
+
name: "\u26A1 QuickStart",
|
|
617
|
+
description: "Minimal configuration (recommended)"
|
|
618
|
+
},
|
|
619
|
+
{ value: "advanced", name: "\u2699 Advanced", description: "Detailed configuration" }
|
|
620
|
+
]
|
|
251
621
|
});
|
|
252
|
-
|
|
622
|
+
STEPS[0].value = `${agentName} (${selectedFlow})`;
|
|
623
|
+
redraw(1);
|
|
253
624
|
const providers = getSupportedProviders();
|
|
254
|
-
|
|
625
|
+
selectedProvider = await select({
|
|
255
626
|
message: "AI Provider",
|
|
256
|
-
|
|
627
|
+
default: "anthropic",
|
|
628
|
+
theme: inquirerTheme,
|
|
629
|
+
choices: providers.map((p) => ({
|
|
257
630
|
value: p.id,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}))
|
|
261
|
-
initialValue: "anthropic"
|
|
631
|
+
name: p.displayName,
|
|
632
|
+
description: p.toolLimit !== null ? `${p.defaultModel} (max ${p.toolLimit} tools)` : `${p.defaultModel}`
|
|
633
|
+
}))
|
|
262
634
|
});
|
|
263
635
|
const providerMeta = getProviderMetadata(selectedProvider);
|
|
264
636
|
if (providerMeta.toolLimit !== null) {
|
|
265
|
-
|
|
637
|
+
noteBox(
|
|
266
638
|
`${providerMeta.displayName} supports max ${providerMeta.toolLimit} tools.
|
|
267
639
|
Teleton currently has ~116 tools. If more tools are added,
|
|
268
640
|
some may be truncated.`,
|
|
269
641
|
"Tool Limit"
|
|
270
642
|
);
|
|
271
643
|
}
|
|
272
|
-
|
|
644
|
+
let localBaseUrl = "";
|
|
645
|
+
if (selectedProvider === "cocoon") {
|
|
646
|
+
apiKey = "";
|
|
647
|
+
const cocoonPort = await input({
|
|
648
|
+
message: "Cocoon proxy HTTP port",
|
|
649
|
+
default: "10000",
|
|
650
|
+
theme: inquirerTheme,
|
|
651
|
+
validate: (value = "") => {
|
|
652
|
+
const n = parseInt(value.trim(), 10);
|
|
653
|
+
return n >= 1 && n <= 65535 ? true : "Must be a port number (1-65535)";
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
cocoonInstance = parseInt(cocoonPort.trim(), 10);
|
|
657
|
+
noteBox(
|
|
658
|
+
`Cocoon Network \u2014 Decentralized LLM on TON
|
|
659
|
+
No API key needed. Requires cocoon-cli running externally.
|
|
660
|
+
Teleton will connect to http://localhost:${cocoonInstance}/v1/`,
|
|
661
|
+
"Cocoon Network",
|
|
662
|
+
TON
|
|
663
|
+
);
|
|
664
|
+
STEPS[1].value = `${providerMeta.displayName} ${DIM(`port ${cocoonInstance}`)}`;
|
|
665
|
+
} else if (selectedProvider === "local") {
|
|
666
|
+
apiKey = "";
|
|
667
|
+
localBaseUrl = await input({
|
|
668
|
+
message: "Local LLM server URL",
|
|
669
|
+
default: "http://localhost:11434/v1",
|
|
670
|
+
theme: inquirerTheme,
|
|
671
|
+
validate: (value = "") => {
|
|
672
|
+
try {
|
|
673
|
+
new URL(value.trim());
|
|
674
|
+
return true;
|
|
675
|
+
} catch {
|
|
676
|
+
return "Must be a valid URL (e.g. http://localhost:11434/v1)";
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
localBaseUrl = localBaseUrl.trim();
|
|
681
|
+
noteBox(
|
|
682
|
+
`Local LLM \u2014 OpenAI-compatible server
|
|
683
|
+
No API key needed. Models auto-discovered at startup.
|
|
684
|
+
Teleton will connect to ${localBaseUrl}`,
|
|
685
|
+
"Local LLM",
|
|
686
|
+
TON
|
|
687
|
+
);
|
|
688
|
+
STEPS[1].value = `${providerMeta.displayName} ${DIM(localBaseUrl)}`;
|
|
689
|
+
} else {
|
|
690
|
+
const envApiKey = process.env.TELETON_API_KEY;
|
|
691
|
+
if (options.apiKey) {
|
|
692
|
+
apiKey = options.apiKey;
|
|
693
|
+
} else if (envApiKey) {
|
|
694
|
+
const validationError = validateApiKeyFormat(selectedProvider, envApiKey);
|
|
695
|
+
if (validationError) {
|
|
696
|
+
prompter.warn(`TELETON_API_KEY env var found but invalid: ${validationError}`);
|
|
697
|
+
apiKey = await password({
|
|
698
|
+
message: `${providerMeta.displayName} API Key (${providerMeta.keyHint})`,
|
|
699
|
+
theme: inquirerTheme,
|
|
700
|
+
validate: (value = "") => validateApiKeyFormat(selectedProvider, value) ?? true
|
|
701
|
+
});
|
|
702
|
+
} else {
|
|
703
|
+
prompter.log(`Using API key from TELETON_API_KEY env var`);
|
|
704
|
+
apiKey = envApiKey;
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
noteBox(
|
|
708
|
+
`${providerMeta.displayName} API key required.
|
|
709
|
+
Get it at: ${providerMeta.consoleUrl}`,
|
|
710
|
+
"API Key",
|
|
711
|
+
TON
|
|
712
|
+
);
|
|
713
|
+
apiKey = await password({
|
|
714
|
+
message: `${providerMeta.displayName} API Key (${providerMeta.keyHint})`,
|
|
715
|
+
theme: inquirerTheme,
|
|
716
|
+
validate: (value = "") => validateApiKeyFormat(selectedProvider, value) ?? true
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
const maskedKey = apiKey.length > 10 ? apiKey.slice(0, 6) + "..." + apiKey.slice(-4) : "***";
|
|
720
|
+
STEPS[1].value = `${providerMeta.displayName} ${DIM(maskedKey)}`;
|
|
721
|
+
}
|
|
722
|
+
redraw(2);
|
|
723
|
+
noteBox(
|
|
273
724
|
"You need Telegram credentials from https://my.telegram.org/apps\nCreate an application and note the API ID and API Hash",
|
|
274
|
-
"Telegram"
|
|
725
|
+
"Telegram",
|
|
726
|
+
TON
|
|
275
727
|
);
|
|
276
728
|
const envApiId = process.env.TELETON_TG_API_ID;
|
|
277
729
|
const envApiHash = process.env.TELETON_TG_API_HASH;
|
|
278
730
|
const envPhone = process.env.TELETON_TG_PHONE;
|
|
279
|
-
const
|
|
280
|
-
const apiIdStr = options.apiId ? options.apiId.toString() : await prompter.text({
|
|
731
|
+
const apiIdStr = options.apiId ? options.apiId.toString() : await input({
|
|
281
732
|
message: envApiId ? "API ID (from env)" : "API ID (from my.telegram.org)",
|
|
282
|
-
|
|
733
|
+
default: envApiId,
|
|
734
|
+
theme: inquirerTheme,
|
|
283
735
|
validate: (value) => {
|
|
284
736
|
if (!value || isNaN(parseInt(value))) return "Invalid API ID (must be a number)";
|
|
737
|
+
return true;
|
|
285
738
|
}
|
|
286
739
|
});
|
|
287
|
-
|
|
288
|
-
|
|
740
|
+
apiId = parseInt(apiIdStr);
|
|
741
|
+
apiHash = options.apiHash ? options.apiHash : await input({
|
|
289
742
|
message: envApiHash ? "API Hash (from env)" : "API Hash (from my.telegram.org)",
|
|
290
|
-
|
|
743
|
+
default: envApiHash,
|
|
744
|
+
theme: inquirerTheme,
|
|
291
745
|
validate: (value) => {
|
|
292
746
|
if (!value || value.length < 10) return "Invalid API Hash";
|
|
747
|
+
return true;
|
|
293
748
|
}
|
|
294
749
|
});
|
|
295
|
-
|
|
296
|
-
message: envPhone ? "Phone number (from env)" : "Phone number (international format
|
|
297
|
-
|
|
298
|
-
|
|
750
|
+
phone = options.phone ? options.phone : await input({
|
|
751
|
+
message: envPhone ? "Phone number (from env)" : "Phone number (international format)",
|
|
752
|
+
default: envPhone,
|
|
753
|
+
theme: inquirerTheme,
|
|
299
754
|
validate: (value) => {
|
|
300
|
-
if (!value || !value.startsWith("+")) return "
|
|
755
|
+
if (!value || !value.startsWith("+")) return "Must start with +";
|
|
756
|
+
return true;
|
|
301
757
|
}
|
|
302
758
|
});
|
|
303
|
-
|
|
759
|
+
noteBox(
|
|
304
760
|
"To get your Telegram User ID:\n1. Open @userinfobot on Telegram\n2. Send /start\n3. Note the ID displayed",
|
|
305
|
-
"User ID"
|
|
761
|
+
"User ID",
|
|
762
|
+
TON
|
|
306
763
|
);
|
|
307
|
-
const userIdStr = options.userId ? options.userId.toString() : await
|
|
764
|
+
const userIdStr = options.userId ? options.userId.toString() : await input({
|
|
308
765
|
message: "Your Telegram User ID (for admin rights)",
|
|
766
|
+
theme: inquirerTheme,
|
|
309
767
|
validate: (value) => {
|
|
310
768
|
if (!value || isNaN(parseInt(value))) return "Invalid User ID";
|
|
769
|
+
return true;
|
|
311
770
|
}
|
|
312
771
|
});
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
);
|
|
319
|
-
let apiKey;
|
|
320
|
-
if (options.apiKey) {
|
|
321
|
-
apiKey = options.apiKey;
|
|
322
|
-
} else if (envApiKey) {
|
|
323
|
-
const validationError = validateApiKeyFormat(selectedProvider, envApiKey);
|
|
324
|
-
if (validationError) {
|
|
325
|
-
prompter.warn(`TELETON_API_KEY env var found but invalid: ${validationError}`);
|
|
326
|
-
apiKey = await prompter.password({
|
|
327
|
-
message: `${providerMeta.displayName} API Key (${providerMeta.keyHint})`,
|
|
328
|
-
validate: (value) => validateApiKeyFormat(selectedProvider, value)
|
|
329
|
-
});
|
|
330
|
-
} else {
|
|
331
|
-
prompter.log(`Using API key from TELETON_API_KEY env var`);
|
|
332
|
-
apiKey = envApiKey;
|
|
333
|
-
}
|
|
334
|
-
} else {
|
|
335
|
-
apiKey = await prompter.password({
|
|
336
|
-
message: `${providerMeta.displayName} API Key (${providerMeta.keyHint})`,
|
|
337
|
-
validate: (value) => validateApiKeyFormat(selectedProvider, value)
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
const MODEL_OPTIONS = {
|
|
341
|
-
anthropic: [
|
|
342
|
-
{ value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5", hint: "Most capable, $5/M" },
|
|
343
|
-
{ value: "claude-sonnet-4-0", label: "Claude Sonnet 4", hint: "Balanced, $3/M" },
|
|
344
|
-
{ value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "Fast & cheap, $1/M" },
|
|
345
|
-
{ value: "claude-3-5-haiku-20241022", label: "Claude 3.5 Haiku", hint: "Cheapest, $0.80/M" }
|
|
346
|
-
],
|
|
347
|
-
openai: [
|
|
348
|
-
{ value: "gpt-5", label: "GPT-5", hint: "Most capable, 400K ctx, $1.25/M" },
|
|
349
|
-
{ value: "gpt-4o", label: "GPT-4o", hint: "Balanced, 128K ctx, $2.50/M" },
|
|
350
|
-
{ value: "gpt-4.1", label: "GPT-4.1", hint: "1M ctx, $2/M" },
|
|
351
|
-
{ value: "gpt-4.1-mini", label: "GPT-4.1 Mini", hint: "1M ctx, cheap, $0.40/M" },
|
|
352
|
-
{ value: "o3", label: "o3", hint: "Reasoning, 200K ctx, $2/M" }
|
|
353
|
-
],
|
|
354
|
-
google: [
|
|
355
|
-
{ value: "gemini-2.5-flash", label: "Gemini 2.5 Flash", hint: "Fast, 1M ctx, $0.30/M" },
|
|
356
|
-
{ value: "gemini-2.5-pro", label: "Gemini 2.5 Pro", hint: "Most capable, 1M ctx, $1.25/M" },
|
|
357
|
-
{ value: "gemini-2.0-flash", label: "Gemini 2.0 Flash", hint: "Cheap, 1M ctx, $0.10/M" }
|
|
358
|
-
],
|
|
359
|
-
xai: [
|
|
360
|
-
{ value: "grok-4-fast", label: "Grok 4 Fast", hint: "Vision, 2M ctx, $0.20/M" },
|
|
361
|
-
{ value: "grok-4", label: "Grok 4", hint: "Reasoning, 256K ctx, $3/M" },
|
|
362
|
-
{ value: "grok-3", label: "Grok 3", hint: "Stable, 131K ctx, $3/M" }
|
|
363
|
-
],
|
|
364
|
-
groq: [
|
|
365
|
-
{
|
|
366
|
-
value: "meta-llama/llama-4-maverick-17b-128e-instruct",
|
|
367
|
-
label: "Llama 4 Maverick",
|
|
368
|
-
hint: "Vision, 131K ctx, $0.20/M"
|
|
369
|
-
},
|
|
370
|
-
{ value: "qwen/qwen3-32b", label: "Qwen3 32B", hint: "Reasoning, 131K ctx, $0.29/M" },
|
|
371
|
-
{
|
|
372
|
-
value: "deepseek-r1-distill-llama-70b",
|
|
373
|
-
label: "DeepSeek R1 70B",
|
|
374
|
-
hint: "Reasoning, 131K ctx, $0.75/M"
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
value: "llama-3.3-70b-versatile",
|
|
378
|
-
label: "Llama 3.3 70B",
|
|
379
|
-
hint: "General purpose, 131K ctx, $0.59/M"
|
|
380
|
-
}
|
|
381
|
-
],
|
|
382
|
-
openrouter: [
|
|
383
|
-
{ value: "anthropic/claude-opus-4.5", label: "Claude Opus 4.5", hint: "200K ctx, $5/M" },
|
|
384
|
-
{ value: "openai/gpt-5", label: "GPT-5", hint: "400K ctx, $1.25/M" },
|
|
385
|
-
{ value: "google/gemini-2.5-flash", label: "Gemini 2.5 Flash", hint: "1M ctx, $0.30/M" },
|
|
386
|
-
{ value: "deepseek/deepseek-r1", label: "DeepSeek R1", hint: "Reasoning, 64K ctx, $0.70/M" },
|
|
387
|
-
{ value: "x-ai/grok-4", label: "Grok 4", hint: "256K ctx, $3/M" }
|
|
388
|
-
]
|
|
389
|
-
};
|
|
390
|
-
let selectedModel = providerMeta.defaultModel;
|
|
391
|
-
if (flow === "advanced") {
|
|
772
|
+
userId = parseInt(userIdStr);
|
|
773
|
+
STEPS[2].value = `${phone} (ID: ${userId})`;
|
|
774
|
+
redraw(3);
|
|
775
|
+
selectedModel = providerMeta.defaultModel;
|
|
776
|
+
if (selectedFlow === "advanced" && selectedProvider !== "cocoon" && selectedProvider !== "local") {
|
|
392
777
|
const providerModels = MODEL_OPTIONS[selectedProvider] || [];
|
|
393
|
-
const
|
|
778
|
+
const modelChoices = [
|
|
394
779
|
...providerModels,
|
|
395
|
-
{ value: "__custom__",
|
|
780
|
+
{ value: "__custom__", name: "Custom", description: "Enter a model ID manually" }
|
|
396
781
|
];
|
|
397
|
-
const modelChoice = await
|
|
782
|
+
const modelChoice = await select({
|
|
398
783
|
message: "Model",
|
|
399
|
-
|
|
400
|
-
|
|
784
|
+
default: providerMeta.defaultModel,
|
|
785
|
+
theme: inquirerTheme,
|
|
786
|
+
choices: modelChoices
|
|
401
787
|
});
|
|
402
788
|
if (modelChoice === "__custom__") {
|
|
403
|
-
const customModel = await
|
|
789
|
+
const customModel = await input({
|
|
404
790
|
message: "Model ID",
|
|
405
|
-
|
|
406
|
-
|
|
791
|
+
default: providerMeta.defaultModel,
|
|
792
|
+
theme: inquirerTheme
|
|
407
793
|
});
|
|
408
|
-
if (customModel
|
|
409
|
-
selectedModel = customModel.trim();
|
|
410
|
-
}
|
|
794
|
+
if (customModel?.trim()) selectedModel = customModel.trim();
|
|
411
795
|
} else {
|
|
412
796
|
selectedModel = modelChoice;
|
|
413
797
|
}
|
|
414
|
-
|
|
415
|
-
let dmPolicy = "open";
|
|
416
|
-
let groupPolicy = "open";
|
|
417
|
-
let requireMention = true;
|
|
418
|
-
let maxAgenticIterations = "5";
|
|
419
|
-
if (flow === "advanced") {
|
|
420
|
-
dmPolicy = await prompter.select({
|
|
798
|
+
dmPolicy = await select({
|
|
421
799
|
message: "DM policy (private messages)",
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
{ value: "
|
|
426
|
-
|
|
427
|
-
|
|
800
|
+
default: "open",
|
|
801
|
+
theme: inquirerTheme,
|
|
802
|
+
choices: [
|
|
803
|
+
{ value: "open", name: "Open", description: "Reply to everyone" },
|
|
804
|
+
{ value: "allowlist", name: "Allowlist", description: "Only specific users" },
|
|
805
|
+
{ value: "disabled", name: "Disabled", description: "No DM replies" }
|
|
806
|
+
]
|
|
428
807
|
});
|
|
429
|
-
groupPolicy = await
|
|
808
|
+
groupPolicy = await select({
|
|
430
809
|
message: "Group policy",
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
{ value: "
|
|
435
|
-
|
|
436
|
-
|
|
810
|
+
default: "open",
|
|
811
|
+
theme: inquirerTheme,
|
|
812
|
+
choices: [
|
|
813
|
+
{ value: "open", name: "Open", description: "Reply in all groups" },
|
|
814
|
+
{ value: "allowlist", name: "Allowlist", description: "Only specific groups" },
|
|
815
|
+
{ value: "disabled", name: "Disabled", description: "No group replies" }
|
|
816
|
+
]
|
|
437
817
|
});
|
|
438
|
-
requireMention = await
|
|
818
|
+
requireMention = await confirm({
|
|
439
819
|
message: "Require @mention in groups?",
|
|
440
|
-
|
|
820
|
+
default: true,
|
|
821
|
+
theme: inquirerTheme
|
|
441
822
|
});
|
|
442
|
-
maxAgenticIterations = await
|
|
823
|
+
maxAgenticIterations = await input({
|
|
443
824
|
message: "Max agentic iterations (tool call loops per message)",
|
|
444
|
-
|
|
825
|
+
default: "5",
|
|
826
|
+
theme: inquirerTheme,
|
|
445
827
|
validate: (v) => {
|
|
446
828
|
const n = parseInt(v, 10);
|
|
447
|
-
|
|
829
|
+
return !isNaN(n) && n >= 1 && n <= 50 ? true : "Must be 1\u201350";
|
|
448
830
|
}
|
|
449
831
|
});
|
|
832
|
+
const modelLabel = providerModels.find((m) => m.value === selectedModel)?.name ?? selectedModel;
|
|
833
|
+
STEPS[3].value = `${modelLabel}, ${dmPolicy}/${groupPolicy}`;
|
|
834
|
+
} else {
|
|
835
|
+
STEPS[3].value = `${selectedModel} (defaults)`;
|
|
450
836
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
let buyMaxFloorPercent = 100;
|
|
454
|
-
let sellMinFloorPercent = 105;
|
|
837
|
+
redraw(4);
|
|
838
|
+
const extras = [];
|
|
455
839
|
if (dealsEnabled) {
|
|
456
|
-
const customizeStrategy = await
|
|
457
|
-
message:
|
|
458
|
-
|
|
840
|
+
const customizeStrategy = await confirm({
|
|
841
|
+
message: `Customize trading thresholds? ${DIM("(default: buy \u2264 floor, sell \u2265 floor +5%)")}`,
|
|
842
|
+
default: false,
|
|
843
|
+
theme: inquirerTheme
|
|
459
844
|
});
|
|
460
845
|
if (customizeStrategy) {
|
|
461
|
-
const buyInput = await
|
|
846
|
+
const buyInput = await input({
|
|
462
847
|
message: "Max buy price (% of floor price)",
|
|
463
|
-
|
|
848
|
+
default: "100",
|
|
849
|
+
theme: inquirerTheme,
|
|
464
850
|
validate: (v) => {
|
|
465
851
|
const n = parseInt(v, 10);
|
|
466
|
-
|
|
852
|
+
return !isNaN(n) && n >= 50 && n <= 150 ? true : "Must be 50\u2013150";
|
|
467
853
|
}
|
|
468
854
|
});
|
|
469
855
|
buyMaxFloorPercent = parseInt(buyInput, 10);
|
|
470
|
-
const sellInput = await
|
|
856
|
+
const sellInput = await input({
|
|
471
857
|
message: "Min sell price (% of floor price)",
|
|
472
|
-
|
|
858
|
+
default: "105",
|
|
859
|
+
theme: inquirerTheme,
|
|
473
860
|
validate: (v) => {
|
|
474
861
|
const n = parseInt(v, 10);
|
|
475
|
-
|
|
862
|
+
return !isNaN(n) && n >= 100 && n <= 200 ? true : "Must be 100\u2013200";
|
|
476
863
|
}
|
|
477
864
|
});
|
|
478
865
|
sellMinFloorPercent = parseInt(sellInput, 10);
|
|
479
866
|
}
|
|
480
|
-
|
|
481
|
-
if (dealsEnabled) {
|
|
482
|
-
prompter.note(
|
|
867
|
+
noteBox(
|
|
483
868
|
"Create a bot with @BotFather on Telegram:\n1. Send /newbot and follow the instructions\n2. Copy the bot token\n3. Enable inline mode: /setinline on the bot",
|
|
484
|
-
"Deals Bot"
|
|
869
|
+
"Deals Bot",
|
|
870
|
+
TON
|
|
485
871
|
);
|
|
486
|
-
const tokenInput = await
|
|
872
|
+
const tokenInput = await password({
|
|
487
873
|
message: "Bot token (from @BotFather)",
|
|
874
|
+
theme: inquirerTheme,
|
|
488
875
|
validate: (value) => {
|
|
489
|
-
if (!value || !value.includes(":")) return "Invalid
|
|
876
|
+
if (!value || !value.includes(":")) return "Invalid format (expected id:hash)";
|
|
877
|
+
return true;
|
|
490
878
|
}
|
|
491
879
|
});
|
|
492
|
-
|
|
880
|
+
spinner.start(DIM("Validating bot token..."));
|
|
493
881
|
try {
|
|
494
882
|
const res = await fetchWithTimeout(`https://api.telegram.org/bot${tokenInput}/getMe`);
|
|
495
883
|
const data = await res.json();
|
|
496
884
|
if (!data.ok) {
|
|
497
|
-
|
|
885
|
+
spinner.warn(DIM("Bot token is invalid \u2014 skipping bot setup"));
|
|
498
886
|
} else {
|
|
499
887
|
botToken = tokenInput;
|
|
500
888
|
botUsername = data.result.username;
|
|
501
|
-
|
|
889
|
+
spinner.succeed(DIM(`Bot verified: @${botUsername}`));
|
|
502
890
|
}
|
|
503
891
|
} catch {
|
|
504
|
-
|
|
892
|
+
spinner.warn(DIM("Could not validate bot token (network error) \u2014 saving anyway"));
|
|
505
893
|
botToken = tokenInput;
|
|
506
|
-
const usernameInput = await
|
|
894
|
+
const usernameInput = await input({
|
|
507
895
|
message: "Bot username (without @)",
|
|
896
|
+
theme: inquirerTheme,
|
|
508
897
|
validate: (value) => {
|
|
509
898
|
if (!value || value.length < 3) return "Username too short";
|
|
899
|
+
return true;
|
|
510
900
|
}
|
|
511
901
|
});
|
|
512
902
|
botUsername = usernameInput;
|
|
513
903
|
}
|
|
904
|
+
extras.push("Deals");
|
|
514
905
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
906
|
+
const setupTonapi = await confirm({
|
|
907
|
+
message: `Add a TonAPI key? ${DIM("(optional, recommended for 10x rate limits)")}`,
|
|
908
|
+
default: false,
|
|
909
|
+
theme: inquirerTheme
|
|
519
910
|
});
|
|
520
911
|
if (setupTonapi) {
|
|
521
|
-
|
|
912
|
+
noteBox(
|
|
522
913
|
"Without key: 1 req/s (you will hit rate limits)\nWith free key: 10 req/s (recommended)\n\nOpen @tonapibot on Telegram \u2192 tap the mini app \u2192 generate a server key",
|
|
523
|
-
"TonAPI"
|
|
914
|
+
"TonAPI",
|
|
915
|
+
TON
|
|
524
916
|
);
|
|
525
|
-
const keyInput = await
|
|
917
|
+
const keyInput = await input({
|
|
526
918
|
message: "TonAPI key",
|
|
919
|
+
theme: inquirerTheme,
|
|
527
920
|
validate: (v) => {
|
|
528
921
|
if (!v || v.length < 10) return "Key too short";
|
|
922
|
+
return true;
|
|
529
923
|
}
|
|
530
924
|
});
|
|
531
925
|
tonapiKey = keyInput;
|
|
926
|
+
extras.push("TonAPI");
|
|
927
|
+
}
|
|
928
|
+
const setupTavily = await confirm({
|
|
929
|
+
message: `Enable web search? ${DIM("(free Tavily key \u2014 1,000 req/month)")}`,
|
|
930
|
+
default: false,
|
|
931
|
+
theme: inquirerTheme
|
|
932
|
+
});
|
|
933
|
+
if (setupTavily) {
|
|
934
|
+
noteBox(
|
|
935
|
+
"Web search lets your agent search the internet and read web pages.\n\nTo get your free API key (takes 30 seconds):\n\n 1. Go to https://app.tavily.com/sign-in\n 2. Create an account (email or Google/GitHub)\n 3. Your API key is displayed on the dashboard\n (starts with tvly-)\n\nFree plan: 1,000 requests/month \u2014 no credit card required.",
|
|
936
|
+
"Tavily \u2014 Web Search API",
|
|
937
|
+
TON
|
|
938
|
+
);
|
|
939
|
+
const keyInput = await input({
|
|
940
|
+
message: "Tavily API key (starts with tvly-)",
|
|
941
|
+
theme: inquirerTheme,
|
|
942
|
+
validate: (v) => {
|
|
943
|
+
if (!v || !v.startsWith("tvly-")) return "Should start with tvly-";
|
|
944
|
+
return true;
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
tavilyApiKey = keyInput;
|
|
948
|
+
extras.push("Tavily");
|
|
949
|
+
}
|
|
950
|
+
STEPS[4].value = extras.length ? extras.join(", ") : "defaults";
|
|
951
|
+
redraw(5);
|
|
952
|
+
let wallet;
|
|
953
|
+
const existingWallet = walletExists() ? loadWallet() : null;
|
|
954
|
+
if (existingWallet) {
|
|
955
|
+
noteBox(`Existing wallet found: ${existingWallet.address}`, "TON Wallet", TON);
|
|
956
|
+
const walletAction = await select({
|
|
957
|
+
message: "A TON wallet already exists. What do you want to do?",
|
|
958
|
+
default: "keep",
|
|
959
|
+
theme: inquirerTheme,
|
|
960
|
+
choices: [
|
|
961
|
+
{ value: "keep", name: "Keep existing", description: existingWallet.address },
|
|
962
|
+
{
|
|
963
|
+
value: "regenerate",
|
|
964
|
+
name: "Generate new",
|
|
965
|
+
description: "WARNING: old wallet will be lost"
|
|
966
|
+
},
|
|
967
|
+
{ value: "import", name: "Import mnemonic", description: "Restore from 24-word seed" }
|
|
968
|
+
]
|
|
969
|
+
});
|
|
970
|
+
if (walletAction === "keep") {
|
|
971
|
+
wallet = existingWallet;
|
|
972
|
+
} else if (walletAction === "import") {
|
|
973
|
+
const mnemonicInput = await input({
|
|
974
|
+
message: "Enter your 24-word mnemonic (space-separated)",
|
|
975
|
+
theme: inquirerTheme,
|
|
976
|
+
validate: (value = "") => {
|
|
977
|
+
const words = value.trim().split(/\s+/);
|
|
978
|
+
return words.length === 24 ? true : `Expected 24 words, got ${words.length}`;
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
spinner.start(DIM("Importing wallet..."));
|
|
982
|
+
wallet = await importWallet(mnemonicInput.trim().split(/\s+/));
|
|
983
|
+
saveWallet(wallet);
|
|
984
|
+
spinner.succeed(DIM(`Wallet imported: ${wallet.address}`));
|
|
985
|
+
} else {
|
|
986
|
+
spinner.start(DIM("Generating new TON wallet..."));
|
|
987
|
+
wallet = await generateWallet();
|
|
988
|
+
saveWallet(wallet);
|
|
989
|
+
spinner.succeed(DIM("New TON wallet generated"));
|
|
990
|
+
}
|
|
991
|
+
} else {
|
|
992
|
+
spinner.start(DIM("Generating TON wallet..."));
|
|
993
|
+
wallet = await generateWallet();
|
|
994
|
+
saveWallet(wallet);
|
|
995
|
+
spinner.succeed(DIM("TON wallet generated"));
|
|
996
|
+
}
|
|
997
|
+
if (!existingWallet || wallet !== existingWallet) {
|
|
998
|
+
const W = FRAME_WIDTH;
|
|
999
|
+
const mnTitle = " \u26A0 BACKUP REQUIRED \u2014 WRITE DOWN THESE 24 WORDS";
|
|
1000
|
+
console.log();
|
|
1001
|
+
console.log(RED(` \u250C${"\u2500".repeat(W)}\u2510`));
|
|
1002
|
+
console.log(RED(" \u2502") + RED.bold(padRight(mnTitle, W)) + RED("\u2502"));
|
|
1003
|
+
console.log(RED(` \u251C${"\u2500".repeat(W)}\u2524`));
|
|
1004
|
+
console.log(RED(" \u2502") + " ".repeat(W) + RED("\u2502"));
|
|
1005
|
+
const cols = 4;
|
|
1006
|
+
const wordWidth = Math.max(10, Math.floor((W - 8) / cols) - 5);
|
|
1007
|
+
const words = wallet.mnemonic;
|
|
1008
|
+
for (let r = 0; r < 6; r++) {
|
|
1009
|
+
const parts = [];
|
|
1010
|
+
for (let c = 0; c < cols; c++) {
|
|
1011
|
+
const idx = r * cols + c;
|
|
1012
|
+
const num = String(idx + 1).padStart(2, " ");
|
|
1013
|
+
parts.push(`${DIM(num + ".")} ${WHITE(padRight(words[idx], wordWidth))}`);
|
|
1014
|
+
}
|
|
1015
|
+
const line = ` ${parts.join(" ")}`;
|
|
1016
|
+
const visPad = W - stripAnsi(line).length;
|
|
1017
|
+
console.log(RED(" \u2502") + line + " ".repeat(Math.max(0, visPad)) + RED("\u2502"));
|
|
1018
|
+
}
|
|
1019
|
+
console.log(RED(" \u2502") + " ".repeat(W) + RED("\u2502"));
|
|
1020
|
+
console.log(
|
|
1021
|
+
RED(" \u2502") + padRightAnsi(DIM(" These words allow you to recover your wallet."), W) + RED("\u2502")
|
|
1022
|
+
);
|
|
1023
|
+
console.log(
|
|
1024
|
+
RED(" \u2502") + padRightAnsi(DIM(" Without them, you will lose access to your TON."), W) + RED("\u2502")
|
|
1025
|
+
);
|
|
1026
|
+
console.log(
|
|
1027
|
+
RED(" \u2502") + padRightAnsi(DIM(" Write them on paper and keep them safe."), W) + RED("\u2502")
|
|
1028
|
+
);
|
|
1029
|
+
console.log(RED(" \u2502") + " ".repeat(W) + RED("\u2502"));
|
|
1030
|
+
console.log(RED(` \u2514${"\u2500".repeat(W)}\u2518`));
|
|
1031
|
+
console.log();
|
|
532
1032
|
}
|
|
1033
|
+
STEPS[5].value = `${wallet.address.slice(0, 8)}...${wallet.address.slice(-4)}`;
|
|
1034
|
+
redraw(6);
|
|
533
1035
|
const config = {
|
|
534
1036
|
meta: {
|
|
535
1037
|
version: "1.0.0",
|
|
@@ -539,6 +1041,7 @@ Get it at: ${providerMeta.consoleUrl}`,
|
|
|
539
1041
|
agent: {
|
|
540
1042
|
provider: selectedProvider,
|
|
541
1043
|
api_key: apiKey,
|
|
1044
|
+
...selectedProvider === "local" && localBaseUrl ? { base_url: localBaseUrl } : {},
|
|
542
1045
|
model: selectedModel,
|
|
543
1046
|
max_tokens: 4096,
|
|
544
1047
|
temperature: 0.7,
|
|
@@ -593,66 +1096,42 @@ Get it at: ${providerMeta.consoleUrl}`,
|
|
|
593
1096
|
log_requests: false
|
|
594
1097
|
},
|
|
595
1098
|
dev: { hot_reload: false },
|
|
1099
|
+
tool_rag: {
|
|
1100
|
+
enabled: true,
|
|
1101
|
+
top_k: 25,
|
|
1102
|
+
always_include: [
|
|
1103
|
+
"telegram_send_message",
|
|
1104
|
+
"telegram_reply_message",
|
|
1105
|
+
"telegram_send_photo",
|
|
1106
|
+
"telegram_send_document",
|
|
1107
|
+
"journal_*",
|
|
1108
|
+
"workspace_*",
|
|
1109
|
+
"web_*"
|
|
1110
|
+
],
|
|
1111
|
+
skip_unlimited_providers: false
|
|
1112
|
+
},
|
|
1113
|
+
logging: { level: "info", pretty: true },
|
|
1114
|
+
mcp: { servers: {} },
|
|
596
1115
|
plugins: {},
|
|
597
|
-
|
|
1116
|
+
...selectedProvider === "cocoon" ? { cocoon: { port: cocoonInstance } } : {},
|
|
1117
|
+
tonapi_key: tonapiKey,
|
|
1118
|
+
tavily_api_key: tavilyApiKey
|
|
598
1119
|
};
|
|
599
|
-
|
|
1120
|
+
spinner.start(DIM("Saving configuration..."));
|
|
600
1121
|
const configYaml = YAML.stringify(config);
|
|
601
|
-
writeFileSync(workspace.configPath, configYaml, "utf-8");
|
|
602
|
-
|
|
603
|
-
spinner2.stop("\u2713 Configuration saved");
|
|
604
|
-
let wallet;
|
|
605
|
-
const existingWallet = walletExists() ? loadWallet() : null;
|
|
606
|
-
if (existingWallet) {
|
|
607
|
-
prompter.note(`Existing wallet found: ${existingWallet.address}`, "TON Wallet");
|
|
608
|
-
const walletAction = await prompter.select({
|
|
609
|
-
message: "A TON wallet already exists. What do you want to do?",
|
|
610
|
-
options: [
|
|
611
|
-
{ value: "keep", label: "Keep existing", hint: `${existingWallet.address}` },
|
|
612
|
-
{ value: "regenerate", label: "Generate new", hint: "WARNING: old wallet will be lost" },
|
|
613
|
-
{ value: "import", label: "Import mnemonic", hint: "Restore from 24-word seed" }
|
|
614
|
-
],
|
|
615
|
-
initialValue: "keep"
|
|
616
|
-
});
|
|
617
|
-
if (walletAction === "keep") {
|
|
618
|
-
wallet = existingWallet;
|
|
619
|
-
} else if (walletAction === "import") {
|
|
620
|
-
const mnemonicInput = await prompter.text({
|
|
621
|
-
message: "Enter your 24-word mnemonic (space-separated)",
|
|
622
|
-
validate: (value) => {
|
|
623
|
-
const words = value.trim().split(/\s+/);
|
|
624
|
-
if (words.length !== 24) return `Expected 24 words, got ${words.length}`;
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
spinner2.start("Importing wallet...");
|
|
628
|
-
wallet = await importWallet(mnemonicInput.trim().split(/\s+/));
|
|
629
|
-
saveWallet(wallet);
|
|
630
|
-
spinner2.stop(`\u2713 Wallet imported: ${wallet.address}`);
|
|
631
|
-
} else {
|
|
632
|
-
spinner2.start("Generating new TON wallet...");
|
|
633
|
-
wallet = await generateWallet();
|
|
634
|
-
saveWallet(wallet);
|
|
635
|
-
spinner2.stop("\u2713 New TON wallet generated");
|
|
636
|
-
}
|
|
637
|
-
} else {
|
|
638
|
-
spinner2.start("Generating TON wallet...");
|
|
639
|
-
wallet = await generateWallet();
|
|
640
|
-
saveWallet(wallet);
|
|
641
|
-
spinner2.stop("\u2713 TON wallet generated");
|
|
642
|
-
}
|
|
643
|
-
if (!existingWallet || wallet !== existingWallet) {
|
|
644
|
-
prompter.note(
|
|
645
|
-
"BACKUP REQUIRED - WRITE DOWN THESE 24 WORDS:\n\n" + wallet.mnemonic.join(" ") + "\n\nThese words allow you to recover your wallet.\nWithout them, you will lose access to your TON.\nWrite them on paper and keep them safe.",
|
|
646
|
-
"Mnemonic Seed (24 words)"
|
|
647
|
-
);
|
|
648
|
-
}
|
|
1122
|
+
writeFileSync(workspace.configPath, configYaml, { encoding: "utf-8", mode: 384 });
|
|
1123
|
+
spinner.succeed(DIM(`Configuration saved: ${workspace.configPath}`));
|
|
649
1124
|
let telegramConnected = false;
|
|
650
|
-
const connectNow = await
|
|
651
|
-
message:
|
|
652
|
-
|
|
1125
|
+
const connectNow = await confirm({
|
|
1126
|
+
message: `Connect to Telegram now? ${DIM("(verification code will be sent to your phone)")}`,
|
|
1127
|
+
default: true,
|
|
1128
|
+
theme: inquirerTheme
|
|
653
1129
|
});
|
|
654
1130
|
if (connectNow) {
|
|
655
|
-
|
|
1131
|
+
console.log(
|
|
1132
|
+
`
|
|
1133
|
+
${DIM("Connecting to Telegram... Check your phone for the verification code.")}`
|
|
1134
|
+
);
|
|
656
1135
|
try {
|
|
657
1136
|
const sessionPath = join(TELETON_ROOT, "telegram_session.txt");
|
|
658
1137
|
const client = new TelegramUserClient({
|
|
@@ -665,52 +1144,54 @@ Get it at: ${providerMeta.consoleUrl}`,
|
|
|
665
1144
|
const me = client.getMe();
|
|
666
1145
|
await client.disconnect();
|
|
667
1146
|
telegramConnected = true;
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
1147
|
+
const displayName = `${me?.firstName || ""}${me?.username ? ` (@${me.username})` : ""}`;
|
|
1148
|
+
console.log(` ${GREEN("\u2713")} ${DIM("Telegram connected as")} ${CYAN(displayName)}
|
|
1149
|
+
`);
|
|
1150
|
+
STEPS[6].value = `Connected${me?.username ? ` (@${me.username})` : ""}`;
|
|
671
1151
|
} catch (err) {
|
|
672
1152
|
prompter.warn(
|
|
673
1153
|
`Telegram connection failed: ${err instanceof Error ? err.message : String(err)}
|
|
674
1154
|
You can authenticate later when running: teleton start`
|
|
675
1155
|
);
|
|
1156
|
+
STEPS[6].value = "Auth on first start";
|
|
676
1157
|
}
|
|
677
|
-
}
|
|
678
|
-
prompter.note(
|
|
679
|
-
`Workspace: ${workspace.root}
|
|
680
|
-
Config: ${workspace.configPath}
|
|
681
|
-
Templates: SOUL.md, MEMORY.md, IDENTITY.md, USER.md
|
|
682
|
-
Telegram: ${phone} (API ID: ${apiId})${telegramConnected ? " \u2713 connected" : ""}
|
|
683
|
-
Admin: User ID ${userId}
|
|
684
|
-
Provider: ${providerMeta.displayName}
|
|
685
|
-
Model: ${selectedModel}
|
|
686
|
-
TON Wallet: ${wallet.address}`,
|
|
687
|
-
"Setup complete"
|
|
688
|
-
);
|
|
689
|
-
if (telegramConnected) {
|
|
690
|
-
prompter.note(
|
|
691
|
-
"Next steps:\n\n1. Start the agent:\n $ teleton start\n\n2. Send /boot as your first message to bootstrap\n the agent's personality and get to know each other",
|
|
692
|
-
"Ready"
|
|
693
|
-
);
|
|
694
1158
|
} else {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
1159
|
+
console.log(`
|
|
1160
|
+
${DIM("You can authenticate later when running: teleton start")}
|
|
1161
|
+
`);
|
|
1162
|
+
STEPS[6].value = "Auth on first start";
|
|
699
1163
|
}
|
|
700
|
-
|
|
1164
|
+
console.clear();
|
|
1165
|
+
console.log();
|
|
1166
|
+
console.log(wizardFrame(STEPS.length, STEPS));
|
|
1167
|
+
console.log();
|
|
1168
|
+
console.log(finalSummaryBox(STEPS, telegramConnected));
|
|
1169
|
+
console.log();
|
|
1170
|
+
console.log(
|
|
1171
|
+
` ${GREEN.bold("\u2714")} ${GREEN.bold("Setup complete!")} ${DIM(`Config saved to ${workspace.configPath}`)}`
|
|
1172
|
+
);
|
|
1173
|
+
console.log(` ${TON.bold("\u26A1")} Good luck!
|
|
1174
|
+
`);
|
|
701
1175
|
}
|
|
702
1176
|
async function runNonInteractiveOnboarding(options, prompter) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
);
|
|
1177
|
+
const selectedProvider = options.provider || "anthropic";
|
|
1178
|
+
const needsApiKey = selectedProvider !== "cocoon" && selectedProvider !== "local";
|
|
1179
|
+
if (!options.apiId || !options.apiHash || !options.phone || !options.userId) {
|
|
1180
|
+
prompter.error("Non-interactive mode requires: --api-id, --api-hash, --phone, --user-id");
|
|
1181
|
+
process.exit(1);
|
|
1182
|
+
}
|
|
1183
|
+
if (needsApiKey && !options.apiKey) {
|
|
1184
|
+
prompter.error(`Non-interactive mode requires --api-key for provider "${selectedProvider}"`);
|
|
1185
|
+
process.exit(1);
|
|
1186
|
+
}
|
|
1187
|
+
if (selectedProvider === "local" && !options.baseUrl) {
|
|
1188
|
+
prompter.error("Non-interactive mode requires --base-url for local provider");
|
|
707
1189
|
process.exit(1);
|
|
708
1190
|
}
|
|
709
1191
|
const workspace = await ensureWorkspace({
|
|
710
1192
|
workspaceDir: options.workspace,
|
|
711
1193
|
ensureTemplates: true
|
|
712
1194
|
});
|
|
713
|
-
const selectedProvider = options.provider || "anthropic";
|
|
714
1195
|
const providerMeta = getProviderMetadata(selectedProvider);
|
|
715
1196
|
const config = {
|
|
716
1197
|
meta: {
|
|
@@ -720,7 +1201,8 @@ async function runNonInteractiveOnboarding(options, prompter) {
|
|
|
720
1201
|
},
|
|
721
1202
|
agent: {
|
|
722
1203
|
provider: selectedProvider,
|
|
723
|
-
api_key: options.apiKey,
|
|
1204
|
+
api_key: options.apiKey || "",
|
|
1205
|
+
...options.baseUrl ? { base_url: options.baseUrl } : {},
|
|
724
1206
|
model: providerMeta.defaultModel,
|
|
725
1207
|
max_tokens: 4096,
|
|
726
1208
|
temperature: 0.7,
|
|
@@ -771,12 +1253,28 @@ async function runNonInteractiveOnboarding(options, prompter) {
|
|
|
771
1253
|
log_requests: false
|
|
772
1254
|
},
|
|
773
1255
|
dev: { hot_reload: false },
|
|
774
|
-
|
|
1256
|
+
tool_rag: {
|
|
1257
|
+
enabled: true,
|
|
1258
|
+
top_k: 25,
|
|
1259
|
+
always_include: [
|
|
1260
|
+
"telegram_send_message",
|
|
1261
|
+
"telegram_reply_message",
|
|
1262
|
+
"telegram_send_photo",
|
|
1263
|
+
"telegram_send_document",
|
|
1264
|
+
"journal_*",
|
|
1265
|
+
"workspace_*",
|
|
1266
|
+
"web_*"
|
|
1267
|
+
],
|
|
1268
|
+
skip_unlimited_providers: false
|
|
1269
|
+
},
|
|
1270
|
+
logging: { level: "info", pretty: true },
|
|
1271
|
+
mcp: { servers: {} },
|
|
1272
|
+
plugins: {},
|
|
1273
|
+
tavily_api_key: options.tavilyApiKey
|
|
775
1274
|
};
|
|
776
1275
|
const configYaml = YAML.stringify(config);
|
|
777
|
-
writeFileSync(workspace.configPath, configYaml, "utf-8");
|
|
778
|
-
|
|
779
|
-
prompter.success(`\u2713 Configuration created: ${workspace.configPath}`);
|
|
1276
|
+
writeFileSync(workspace.configPath, configYaml, { encoding: "utf-8", mode: 384 });
|
|
1277
|
+
prompter.success(`Configuration created: ${workspace.configPath}`);
|
|
780
1278
|
}
|
|
781
1279
|
|
|
782
1280
|
// src/cli/commands/doctor.ts
|
|
@@ -888,6 +1386,13 @@ async function checkApiKey(workspaceDir) {
|
|
|
888
1386
|
message: `Unknown provider: ${provider}`
|
|
889
1387
|
};
|
|
890
1388
|
}
|
|
1389
|
+
if (provider === "cocoon" || provider === "local") {
|
|
1390
|
+
return {
|
|
1391
|
+
name: `${meta.displayName}`,
|
|
1392
|
+
status: "ok",
|
|
1393
|
+
message: "No API key needed"
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
891
1396
|
if (!apiKey) {
|
|
892
1397
|
return {
|
|
893
1398
|
name: `${meta.displayName} API key`,
|
|
@@ -903,8 +1408,8 @@ async function checkApiKey(workspaceDir) {
|
|
|
903
1408
|
message: validationError
|
|
904
1409
|
};
|
|
905
1410
|
}
|
|
906
|
-
const maskLen = Math.min(
|
|
907
|
-
const masked = apiKey.substring(0, maskLen) + "
|
|
1411
|
+
const maskLen = Math.min(4, Math.max(0, apiKey.length - 4));
|
|
1412
|
+
const masked = apiKey.substring(0, maskLen) + "****" + apiKey.substring(apiKey.length - 4);
|
|
908
1413
|
return {
|
|
909
1414
|
name: `${meta.displayName} API key`,
|
|
910
1415
|
status: "ok",
|
|
@@ -1159,6 +1664,224 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
|
|
|
1159
1664
|
console.log("");
|
|
1160
1665
|
}
|
|
1161
1666
|
|
|
1667
|
+
// src/cli/commands/mcp.ts
|
|
1668
|
+
function ensureMcpSection(raw) {
|
|
1669
|
+
if (!raw.mcp || typeof raw.mcp !== "object") {
|
|
1670
|
+
raw.mcp = { servers: {} };
|
|
1671
|
+
}
|
|
1672
|
+
const mcp2 = raw.mcp;
|
|
1673
|
+
if (!mcp2.servers || typeof mcp2.servers !== "object") {
|
|
1674
|
+
mcp2.servers = {};
|
|
1675
|
+
}
|
|
1676
|
+
return mcp2.servers;
|
|
1677
|
+
}
|
|
1678
|
+
function deriveServerName(pkg) {
|
|
1679
|
+
const unscoped = pkg.includes("/") ? pkg.split("/").pop() : pkg;
|
|
1680
|
+
return unscoped.replace(/^server-/, "").replace(/^mcp-server-/, "").replace(/^mcp-/, "");
|
|
1681
|
+
}
|
|
1682
|
+
async function mcpAddCommand(pkg, extraArgs, options) {
|
|
1683
|
+
const configPath = options.config || getDefaultConfigPath();
|
|
1684
|
+
const raw = readRawConfig(configPath);
|
|
1685
|
+
const servers = ensureMcpSection(raw);
|
|
1686
|
+
const serverName = options.name || deriveServerName(pkg);
|
|
1687
|
+
if (servers[serverName]) {
|
|
1688
|
+
console.error(`\u274C MCP server "${serverName}" already exists. Remove it first or use --name.`);
|
|
1689
|
+
process.exit(1);
|
|
1690
|
+
}
|
|
1691
|
+
const entry = {};
|
|
1692
|
+
if (options.url) {
|
|
1693
|
+
entry.url = pkg;
|
|
1694
|
+
} else {
|
|
1695
|
+
entry.command = "npx";
|
|
1696
|
+
entry.args = ["-y", pkg, ...extraArgs];
|
|
1697
|
+
}
|
|
1698
|
+
if (options.scope && options.scope !== "always") {
|
|
1699
|
+
entry.scope = options.scope;
|
|
1700
|
+
}
|
|
1701
|
+
if (options.env && options.env.length > 0) {
|
|
1702
|
+
const envMap = {};
|
|
1703
|
+
for (const pair of options.env) {
|
|
1704
|
+
const eq = pair.indexOf("=");
|
|
1705
|
+
if (eq === -1) {
|
|
1706
|
+
console.error(`\u274C Invalid --env format: "${pair}" (expected KEY=VALUE)`);
|
|
1707
|
+
process.exit(1);
|
|
1708
|
+
}
|
|
1709
|
+
envMap[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
1710
|
+
}
|
|
1711
|
+
entry.env = envMap;
|
|
1712
|
+
}
|
|
1713
|
+
servers[serverName] = entry;
|
|
1714
|
+
writeRawConfig(raw, configPath);
|
|
1715
|
+
console.log(`\u2705 Added MCP server "${serverName}"`);
|
|
1716
|
+
if (entry.command) {
|
|
1717
|
+
console.log(` Command: ${entry.command}`);
|
|
1718
|
+
} else {
|
|
1719
|
+
console.log(` URL: ${entry.url}`);
|
|
1720
|
+
}
|
|
1721
|
+
console.log(`
|
|
1722
|
+
Restart teleton to connect: teleton start`);
|
|
1723
|
+
}
|
|
1724
|
+
async function mcpRemoveCommand(name, options) {
|
|
1725
|
+
const configPath = options.config || getDefaultConfigPath();
|
|
1726
|
+
const raw = readRawConfig(configPath);
|
|
1727
|
+
const servers = ensureMcpSection(raw);
|
|
1728
|
+
if (!servers[name]) {
|
|
1729
|
+
console.error(`\u274C MCP server "${name}" not found.`);
|
|
1730
|
+
const names = Object.keys(servers);
|
|
1731
|
+
if (names.length > 0) {
|
|
1732
|
+
console.error(` Available: ${names.join(", ")}`);
|
|
1733
|
+
}
|
|
1734
|
+
process.exit(1);
|
|
1735
|
+
}
|
|
1736
|
+
delete servers[name];
|
|
1737
|
+
writeRawConfig(raw, configPath);
|
|
1738
|
+
console.log(`\u2705 Removed MCP server "${name}"`);
|
|
1739
|
+
}
|
|
1740
|
+
async function mcpListCommand(options) {
|
|
1741
|
+
const configPath = options.config || getDefaultConfigPath();
|
|
1742
|
+
const raw = readRawConfig(configPath);
|
|
1743
|
+
const servers = ensureMcpSection(raw);
|
|
1744
|
+
const entries = Object.entries(servers);
|
|
1745
|
+
if (entries.length === 0) {
|
|
1746
|
+
console.log("No MCP servers configured.");
|
|
1747
|
+
console.log("\n Add one: teleton mcp add @modelcontextprotocol/server-filesystem /tmp");
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
console.log(`MCP servers (${entries.length}):
|
|
1751
|
+
`);
|
|
1752
|
+
for (const [name, cfg] of entries) {
|
|
1753
|
+
const type = cfg.command ? "stdio" : "sse";
|
|
1754
|
+
const target = cfg.command || cfg.url || "?";
|
|
1755
|
+
const scope = cfg.scope || "always";
|
|
1756
|
+
const enabled = cfg.enabled !== false ? "\u2713" : "\u2717";
|
|
1757
|
+
console.log(` ${enabled} ${name} (${type}, ${scope})`);
|
|
1758
|
+
console.log(` ${target}`);
|
|
1759
|
+
if (cfg.env && typeof cfg.env === "object") {
|
|
1760
|
+
const keys = Object.keys(cfg.env);
|
|
1761
|
+
console.log(` env: ${keys.join(", ")}`);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// src/cli/commands/config.ts
|
|
1767
|
+
function requireWhitelisted(key) {
|
|
1768
|
+
const meta = CONFIGURABLE_KEYS[key];
|
|
1769
|
+
if (!meta) {
|
|
1770
|
+
const allowed = Object.keys(CONFIGURABLE_KEYS).join(", ");
|
|
1771
|
+
console.error(`Key "${key}" is not configurable.
|
|
1772
|
+
Allowed keys: ${allowed}`);
|
|
1773
|
+
process.exit(1);
|
|
1774
|
+
}
|
|
1775
|
+
return meta;
|
|
1776
|
+
}
|
|
1777
|
+
async function actionSet(key, value, configPath) {
|
|
1778
|
+
const meta = requireWhitelisted(key);
|
|
1779
|
+
if (!value) {
|
|
1780
|
+
const prompter = createPrompter();
|
|
1781
|
+
try {
|
|
1782
|
+
if (meta.sensitive) {
|
|
1783
|
+
value = await prompter.password({
|
|
1784
|
+
message: `Enter value for ${key}:`,
|
|
1785
|
+
validate: (v) => {
|
|
1786
|
+
if (!v) return "Value is required";
|
|
1787
|
+
const err2 = meta.validate(v);
|
|
1788
|
+
return err2 ? new Error(err2) : void 0;
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
} else {
|
|
1792
|
+
value = await prompter.text({
|
|
1793
|
+
message: `Enter value for ${key}:`,
|
|
1794
|
+
validate: (v) => {
|
|
1795
|
+
if (!v) return "Value is required";
|
|
1796
|
+
const err2 = meta.validate(v);
|
|
1797
|
+
return err2 ? new Error(err2) : void 0;
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
} catch (e) {
|
|
1802
|
+
if (e instanceof CancelledError) {
|
|
1803
|
+
console.log("Cancelled.");
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
throw e;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const err = meta.validate(value);
|
|
1810
|
+
if (err) {
|
|
1811
|
+
console.error(`Invalid value for ${key}: ${err}`);
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
}
|
|
1814
|
+
const raw = readRawConfig(configPath);
|
|
1815
|
+
setNestedValue(raw, key, meta.parse(value));
|
|
1816
|
+
writeRawConfig(raw, configPath);
|
|
1817
|
+
console.log(`\u2713 ${key} = ${meta.mask(value)}`);
|
|
1818
|
+
}
|
|
1819
|
+
function actionGet(key, configPath) {
|
|
1820
|
+
const meta = requireWhitelisted(key);
|
|
1821
|
+
const raw = readRawConfig(configPath);
|
|
1822
|
+
const value = getNestedValue(raw, key);
|
|
1823
|
+
if (value == null || value === "") {
|
|
1824
|
+
console.log(`\u2717 ${key} (not set)`);
|
|
1825
|
+
} else {
|
|
1826
|
+
const display = meta.sensitive ? meta.mask(String(value)) : String(value);
|
|
1827
|
+
console.log(`\u2713 ${key} = ${display}`);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function actionList(configPath) {
|
|
1831
|
+
const raw = readRawConfig(configPath);
|
|
1832
|
+
console.log("\nConfigurable keys:\n");
|
|
1833
|
+
for (const [key, meta] of Object.entries(CONFIGURABLE_KEYS)) {
|
|
1834
|
+
const value = getNestedValue(raw, key);
|
|
1835
|
+
if (value != null && value !== "") {
|
|
1836
|
+
const display = meta.sensitive ? meta.mask(String(value)) : String(value);
|
|
1837
|
+
console.log(` \u2713 ${key.padEnd(24)} = ${display}`);
|
|
1838
|
+
} else {
|
|
1839
|
+
console.log(` \u2717 ${key.padEnd(24)} (not set)`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
console.log();
|
|
1843
|
+
}
|
|
1844
|
+
function actionUnset(key, configPath) {
|
|
1845
|
+
requireWhitelisted(key);
|
|
1846
|
+
const raw = readRawConfig(configPath);
|
|
1847
|
+
deleteNestedValue(raw, key);
|
|
1848
|
+
writeRawConfig(raw, configPath);
|
|
1849
|
+
console.log(`\u2713 ${key} unset`);
|
|
1850
|
+
}
|
|
1851
|
+
async function configCommand(action, key, value, options) {
|
|
1852
|
+
const configPath = options.config ?? getDefaultConfigPath();
|
|
1853
|
+
switch (action) {
|
|
1854
|
+
case "list":
|
|
1855
|
+
actionList(configPath);
|
|
1856
|
+
break;
|
|
1857
|
+
case "get":
|
|
1858
|
+
if (!key) {
|
|
1859
|
+
console.error("Usage: teleton config get <key>");
|
|
1860
|
+
process.exit(1);
|
|
1861
|
+
}
|
|
1862
|
+
actionGet(key, configPath);
|
|
1863
|
+
break;
|
|
1864
|
+
case "set":
|
|
1865
|
+
if (!key) {
|
|
1866
|
+
console.error("Usage: teleton config set <key> [value]");
|
|
1867
|
+
process.exit(1);
|
|
1868
|
+
}
|
|
1869
|
+
await actionSet(key, value, configPath);
|
|
1870
|
+
break;
|
|
1871
|
+
case "unset":
|
|
1872
|
+
if (!key) {
|
|
1873
|
+
console.error("Usage: teleton config unset <key>");
|
|
1874
|
+
process.exit(1);
|
|
1875
|
+
}
|
|
1876
|
+
actionUnset(key, configPath);
|
|
1877
|
+
break;
|
|
1878
|
+
default:
|
|
1879
|
+
console.error(`Unknown action: ${action}
|
|
1880
|
+
Available: set, get, list, unset`);
|
|
1881
|
+
process.exit(1);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1162
1885
|
// src/cli/index.ts
|
|
1163
1886
|
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
|
|
1164
1887
|
import { dirname, join as join3 } from "path";
|
|
@@ -1177,19 +1900,23 @@ function findPackageJson() {
|
|
|
1177
1900
|
var packageJson = findPackageJson();
|
|
1178
1901
|
var program = new Command();
|
|
1179
1902
|
program.name("teleton").description("Teleton Agent - Personal AI Agent for Telegram").version(packageJson.version);
|
|
1180
|
-
program.command("setup").description("Interactive wizard to set up Teleton").option("--workspace <dir>", "Workspace directory").option("--non-interactive", "Non-interactive mode").option("--api-id <id>", "Telegram API ID").option("--api-hash <hash>", "Telegram API Hash").option("--phone <number>", "Phone number").option("--api-key <key>", "
|
|
1903
|
+
program.command("setup").description("Interactive wizard to set up Teleton").option("--workspace <dir>", "Workspace directory").option("--non-interactive", "Non-interactive mode").option("--ui", "Launch web-based setup wizard").option("--ui-port <port>", "Port for setup WebUI", "7777").option("--api-id <id>", "Telegram API ID").option("--api-hash <hash>", "Telegram API Hash").option("--phone <number>", "Phone number").option("--api-key <key>", "LLM provider API key").option("--base-url <url>", "Base URL for local LLM server").option("--user-id <id>", "Telegram User ID").option("--tavily-api-key <key>", "Tavily API key for web search").action(async (options) => {
|
|
1181
1904
|
try {
|
|
1182
1905
|
await onboardCommand({
|
|
1183
1906
|
workspace: options.workspace,
|
|
1184
1907
|
nonInteractive: options.nonInteractive,
|
|
1908
|
+
ui: options.ui,
|
|
1909
|
+
uiPort: options.uiPort,
|
|
1185
1910
|
apiId: options.apiId ? parseInt(options.apiId) : void 0,
|
|
1186
1911
|
apiHash: options.apiHash,
|
|
1187
1912
|
phone: options.phone,
|
|
1188
1913
|
apiKey: options.apiKey,
|
|
1189
|
-
|
|
1914
|
+
baseUrl: options.baseUrl,
|
|
1915
|
+
userId: options.userId ? parseInt(options.userId) : void 0,
|
|
1916
|
+
tavilyApiKey: options.tavilyApiKey
|
|
1190
1917
|
});
|
|
1191
1918
|
} catch (error) {
|
|
1192
|
-
console.error("Error:",
|
|
1919
|
+
console.error("Error:", getErrorMessage(error));
|
|
1193
1920
|
process.exit(1);
|
|
1194
1921
|
}
|
|
1195
1922
|
});
|
|
@@ -1209,7 +1936,7 @@ program.command("start").description("Start the Teleton agent").option("-c, --co
|
|
|
1209
1936
|
}
|
|
1210
1937
|
await main(options.config);
|
|
1211
1938
|
} catch (error) {
|
|
1212
|
-
console.error("Error:",
|
|
1939
|
+
console.error("Error:", getErrorMessage(error));
|
|
1213
1940
|
process.exit(1);
|
|
1214
1941
|
}
|
|
1215
1942
|
});
|
|
@@ -1217,7 +1944,47 @@ program.command("doctor").description("Run system health checks").action(async (
|
|
|
1217
1944
|
try {
|
|
1218
1945
|
await doctorCommand();
|
|
1219
1946
|
} catch (error) {
|
|
1220
|
-
console.error("Error:",
|
|
1947
|
+
console.error("Error:", getErrorMessage(error));
|
|
1948
|
+
process.exit(1);
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
var mcp = program.command("mcp").description("Manage MCP (Model Context Protocol) servers");
|
|
1952
|
+
mcp.command("add <package> [args...]").description(
|
|
1953
|
+
"Add an MCP server (e.g. teleton mcp add @modelcontextprotocol/server-filesystem /tmp)"
|
|
1954
|
+
).option("-n, --name <name>", "Server name (auto-derived from package if omitted)").option("-s, --scope <scope>", "Tool scope: always | dm-only | group-only | admin-only", "always").option(
|
|
1955
|
+
"-e, --env <KEY=VALUE...>",
|
|
1956
|
+
"Environment variables (repeatable)",
|
|
1957
|
+
(v, prev) => [...prev, v],
|
|
1958
|
+
[]
|
|
1959
|
+
).option("--url", "Treat <package> as an SSE/HTTP URL instead of an npx package").option("-c, --config <path>", "Config file path").action(async (pkg, args, options) => {
|
|
1960
|
+
try {
|
|
1961
|
+
await mcpAddCommand(pkg, args, options);
|
|
1962
|
+
} catch (error) {
|
|
1963
|
+
console.error("Error:", getErrorMessage(error));
|
|
1964
|
+
process.exit(1);
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
mcp.command("remove <name>").description("Remove an MCP server by name").option("-c, --config <path>", "Config file path").action(async (name, options) => {
|
|
1968
|
+
try {
|
|
1969
|
+
await mcpRemoveCommand(name, options);
|
|
1970
|
+
} catch (error) {
|
|
1971
|
+
console.error("Error:", getErrorMessage(error));
|
|
1972
|
+
process.exit(1);
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
mcp.command("list").description("List configured MCP servers").option("-c, --config <path>", "Config file path").action(async (options) => {
|
|
1976
|
+
try {
|
|
1977
|
+
await mcpListCommand(options);
|
|
1978
|
+
} catch (error) {
|
|
1979
|
+
console.error("Error:", getErrorMessage(error));
|
|
1980
|
+
process.exit(1);
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
program.command("config").description("Manage configuration keys (set, get, list, unset)").argument("<action>", "set | get | list | unset").argument("[key]", "Config key (e.g., tavily_api_key, telegram.bot_token)").argument("[value]", "Value to set (prompts interactively if omitted)").option("-c, --config <path>", "Config file path").action(async (action, key, value, options) => {
|
|
1984
|
+
try {
|
|
1985
|
+
await configCommand(action, key, value, options);
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
console.error("Error:", getErrorMessage(error));
|
|
1221
1988
|
process.exit(1);
|
|
1222
1989
|
}
|
|
1223
1990
|
});
|