teleton 0.6.0 → 0.7.1

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.
Files changed (39) hide show
  1. package/README.md +34 -31
  2. package/dist/{chunk-6L6KGATM.js → chunk-3YM57ZAV.js} +1638 -1749
  3. package/dist/{chunk-D5I7GBV7.js → chunk-FNV5FF35.js} +22 -13
  4. package/dist/{chunk-4IPJ25HE.js → chunk-HZNZT4TG.js} +1106 -711
  5. package/dist/chunk-LRCPA7SC.js +149 -0
  6. package/dist/chunk-ND2X5FWB.js +368 -0
  7. package/dist/chunk-NERLQY2H.js +421 -0
  8. package/dist/{chunk-YBA6IBGT.js → chunk-OCLG5GKI.js} +24 -24
  9. package/dist/{chunk-ADCMUNYU.js → chunk-RBU6JXD3.js} +60 -55
  10. package/dist/chunk-RCMD3U65.js +141 -0
  11. package/dist/{chunk-ECSCVEQQ.js → chunk-UCN6TI25.js} +7 -3
  12. package/dist/{chunk-WL2Q3VRD.js → chunk-UDD7FYOU.js} +12 -4
  13. package/dist/chunk-VAUJSSD3.js +20 -0
  14. package/dist/chunk-XBE4JB7C.js +8 -0
  15. package/dist/{chunk-GDCODBNO.js → chunk-XBKSS6DM.js} +2 -16
  16. package/dist/cli/index.js +878 -433
  17. package/dist/client-3VWE7NC4.js +29 -0
  18. package/dist/{get-my-gifts-KVULMBJ3.js → get-my-gifts-RI7FAXAL.js} +3 -1
  19. package/dist/index.js +17 -11
  20. package/dist/{memory-TVDOGQXS.js → memory-5SS3Q5EA.js} +7 -5
  21. package/dist/{migrate-QIEMPOMT.js → migrate-M7SJMDOL.js} +14 -9
  22. package/dist/{server-RSWVCVY3.js → server-DS5OARW6.js} +174 -85
  23. package/dist/setup-server-C7ZTPHD5.js +934 -0
  24. package/dist/{task-dependency-resolver-72DLY2HV.js → task-dependency-resolver-WKZWJLLM.js} +19 -15
  25. package/dist/{task-executor-VXB6DAV2.js → task-executor-PD3H4MLO.js} +4 -1
  26. package/dist/tool-adapter-Y3TCEQOC.js +145 -0
  27. package/dist/{tool-index-DKI2ZNOU.js → tool-index-MIVK3D7H.js} +14 -9
  28. package/dist/{transcript-7V4UNID4.js → transcript-UDJZP6NK.js} +2 -1
  29. package/dist/web/assets/complete-fZLnb5Ot.js +1 -0
  30. package/dist/web/assets/index-BqwoDycr.js +72 -0
  31. package/dist/web/assets/index-CRDIf07k.css +1 -0
  32. package/dist/web/assets/index.es-D81xLR29.js +11 -0
  33. package/dist/web/assets/login-telegram-BP7CJDmx.js +1 -0
  34. package/dist/web/assets/run-DOrDowjK.js +1 -0
  35. package/dist/web/index.html +2 -2
  36. package/package.json +7 -3
  37. package/dist/chunk-2QUJLHCZ.js +0 -362
  38. package/dist/web/assets/index-BNhrx9S1.js +0 -67
  39. package/dist/web/assets/index-CqrrRLOh.css +0 -1
package/dist/cli/index.js CHANGED
@@ -1,176 +1,519 @@
1
+ import {
2
+ TelegramUserClient,
3
+ main
4
+ } from "../chunk-3YM57ZAV.js";
5
+ import "../chunk-UDD7FYOU.js";
6
+ import "../chunk-EHEV7FJ7.js";
7
+ import "../chunk-U7FQYCBQ.js";
1
8
  import {
2
9
  CONFIGURABLE_KEYS,
10
+ configExists,
3
11
  deleteNestedValue,
12
+ getDefaultConfigPath,
4
13
  getNestedValue,
5
14
  readRawConfig,
6
15
  setNestedValue,
7
16
  writeRawConfig
8
- } from "../chunk-2QUJLHCZ.js";
9
- import {
10
- TelegramUserClient,
11
- ensureWorkspace,
12
- isNewWorkspace,
13
- main
14
- } from "../chunk-6L6KGATM.js";
15
- import "../chunk-WL2Q3VRD.js";
16
- import "../chunk-EHEV7FJ7.js";
17
- import "../chunk-U7FQYCBQ.js";
17
+ } from "../chunk-HZNZT4TG.js";
18
18
  import {
19
19
  ConfigSchema,
20
20
  DealsConfigSchema,
21
- configExists,
21
+ ensureWorkspace,
22
22
  generateWallet,
23
- getDefaultConfigPath,
24
- getProviderMetadata,
25
- getSupportedProviders,
26
23
  importWallet,
24
+ isNewWorkspace,
27
25
  loadWallet,
28
26
  saveWallet,
29
- validateApiKeyFormat,
30
27
  walletExists
31
- } from "../chunk-4IPJ25HE.js";
32
- import "../chunk-YBA6IBGT.js";
33
- import "../chunk-ADCMUNYU.js";
34
- import "../chunk-D5I7GBV7.js";
35
- import "../chunk-ECSCVEQQ.js";
28
+ } from "../chunk-NERLQY2H.js";
29
+ import "../chunk-QUAPFI2N.js";
30
+ import "../chunk-TSKJCWQQ.js";
36
31
  import {
37
- fetchWithTimeout
38
- } from "../chunk-GDCODBNO.js";
39
- import "../chunk-4DU3C27M.js";
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-RBU6JXD3.js";
42
+ import "../chunk-UCN6TI25.js";
43
+ import "../chunk-FNV5FF35.js";
44
+ import "../chunk-XBKSS6DM.js";
40
45
  import {
41
46
  TELEGRAM_MAX_MESSAGE_LENGTH
42
47
  } from "../chunk-RO62LO6Z.js";
48
+ import {
49
+ fetchWithTimeout
50
+ } from "../chunk-VAUJSSD3.js";
51
+ import "../chunk-4DU3C27M.js";
43
52
  import {
44
53
  TELETON_ROOT
45
54
  } from "../chunk-EYWNOHMJ.js";
55
+ import "../chunk-RCMD3U65.js";
46
56
  import "../chunk-NUGDTPE4.js";
47
- import "../chunk-QUAPFI2N.js";
48
- import "../chunk-TSKJCWQQ.js";
49
57
  import "../chunk-QGM4M3NI.js";
50
58
 
51
59
  // src/cli/index.ts
52
60
  import { Command } from "commander";
53
61
 
54
62
  // src/cli/prompts.ts
55
- import * as clack from "@clack/prompts";
56
- var ClackPrompter = class {
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 {
57
251
  async intro(title) {
58
- clack.intro(title);
252
+ console.log(`
253
+ ${TON.bold(title)}
254
+ `);
59
255
  }
60
256
  async outro(message) {
61
- clack.outro(message);
257
+ console.log(`
258
+ ${DIM(message)}
259
+ `);
62
260
  }
63
261
  async note(message, title) {
64
- clack.note(message, title);
262
+ noteBox(message, title ?? "Note", YELLOW);
65
263
  }
66
264
  async text(options) {
67
- const result = await clack.text({
68
- message: options.message,
69
- placeholder: options.placeholder,
70
- initialValue: options.initialValue,
71
- validate: options.validate
72
- });
73
- if (clack.isCancel(result)) {
74
- throw new CancelledError();
75
- }
76
- 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
+ );
77
273
  }
78
274
  async password(options) {
79
- const result = await clack.password({
80
- message: options.message,
81
- validate: options.validate
82
- });
83
- if (clack.isCancel(result)) {
84
- throw new CancelledError();
85
- }
86
- return result;
275
+ return wrapExitPromptError(
276
+ password({
277
+ message: options.message,
278
+ theme: inquirerTheme,
279
+ validate: adaptValidate(options.validate)
280
+ })
281
+ );
87
282
  }
88
283
  async select(options) {
89
- const result = await clack.select({
90
- message: options.message,
91
- options: options.options.map((opt) => {
92
- const mapped = {
284
+ return wrapExitPromptError(
285
+ select({
286
+ message: options.message,
287
+ default: options.initialValue,
288
+ theme: inquirerTheme,
289
+ choices: options.options.map((opt) => ({
93
290
  value: opt.value,
94
- label: opt.label
95
- };
96
- if (opt.hint) {
97
- mapped.hint = opt.hint;
98
- }
99
- return mapped;
100
- }),
101
- initialValue: options.initialValue
102
- });
103
- if (clack.isCancel(result)) {
104
- throw new CancelledError();
105
- }
106
- return result;
291
+ name: opt.label,
292
+ description: opt.hint
293
+ }))
294
+ })
295
+ );
107
296
  }
108
297
  async confirm(options) {
109
- const result = await clack.confirm({
110
- message: options.message,
111
- initialValue: options.initialValue ?? false
112
- });
113
- if (clack.isCancel(result)) {
114
- throw new CancelledError();
115
- }
116
- return result;
298
+ return wrapExitPromptError(
299
+ confirm({
300
+ message: options.message,
301
+ default: options.initialValue ?? false,
302
+ theme: inquirerTheme
303
+ })
304
+ );
117
305
  }
118
306
  async multiselect(options) {
119
- const result = await clack.multiselect({
120
- message: options.message,
121
- options: options.options.map((opt) => {
122
- const mapped = {
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) => ({
123
315
  value: opt.value,
124
- label: opt.label
125
- };
126
- if (opt.hint) {
127
- mapped.hint = opt.hint;
128
- }
129
- return mapped;
130
- }),
131
- required: options.required
132
- });
133
- if (clack.isCancel(result)) {
134
- throw new CancelledError();
135
- }
136
- return result;
316
+ name: opt.label,
317
+ description: opt.hint
318
+ })),
319
+ required: options.required
320
+ })
321
+ );
137
322
  }
138
323
  spinner() {
139
- const s = clack.spinner();
324
+ let s = null;
140
325
  return {
141
- start: (message) => s.start(message),
142
- stop: (message) => s.stop(message),
143
- message: (message) => s.message(message)
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
+ }
144
336
  };
145
337
  }
146
338
  log(message) {
147
- clack.log.message(message);
339
+ console.log(` ${DIM("\u25CB")} ${message}`);
148
340
  }
149
341
  warn(message) {
150
- clack.log.warn(message);
342
+ console.log(` ${YELLOW("\u26A0")} ${YELLOW(message)}`);
151
343
  }
152
344
  error(message) {
153
- clack.log.error(message);
345
+ console.log(` ${RED("\u2717")} ${RED(message)}`);
154
346
  }
155
347
  success(message) {
156
- clack.log.success(message);
157
- }
158
- };
159
- var CancelledError = class extends Error {
160
- constructor() {
161
- super("Operation cancelled by user");
162
- this.name = "CancelledError";
348
+ console.log(` ${GREEN("\u2713")} ${GREEN(message)}`);
163
349
  }
164
350
  };
165
351
  function createPrompter() {
166
- return new ClackPrompter();
352
+ return new InquirerPrompter();
167
353
  }
168
354
 
169
355
  // src/cli/commands/onboard.ts
170
- import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
356
+ import { writeFileSync, readFileSync, existsSync } from "fs";
171
357
  import { join } from "path";
172
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
+ };
173
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
+ }
174
517
  const prompter = createPrompter();
175
518
  try {
176
519
  if (options.nonInteractive) {
@@ -180,386 +523,515 @@ async function onboardCommand(options = {}) {
180
523
  }
181
524
  } catch (err) {
182
525
  if (err instanceof CancelledError) {
183
- prompter.outro("Onboarding cancelled");
526
+ console.log(`
527
+ ${DIM("Setup cancelled. No changes were made.")}
528
+ `);
184
529
  process.exit(0);
185
530
  }
186
531
  throw err;
187
532
  }
188
533
  }
189
534
  async function runInteractiveOnboarding(options, prompter) {
190
- const blue2 = "\x1B[34m";
191
- const reset2 = "\x1B[0m";
192
- console.log(`
193
- ${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
194
- \u2502 \u2502
195
- \u2502 ______________ ________________ _ __ ___ _____________ ________ \u2502
196
- \u2502 /_ __/ ____/ / / ____/_ __/ __ \\/ | / / / | / ____/ ____/ | / /_ __/ \u2502
197
- \u2502 / / / __/ / / / __/ / / / / / / |/ / / /| |/ / __/ __/ / |/ / / / \u2502
198
- \u2502 / / / /___/ /___/ /___ / / / /_/ / /| / / ___ / /_/ / /___/ /| / / / \u2502
199
- \u2502 /_/ /_____/_____/_____/ /_/ \\____/_/ |_/ /_/ |_\\____/_____/_/ |_/ /_/ \u2502
200
- \u2502 \u2502
201
- \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 SETUP \u2500\u2500\u2518${reset2}
202
-
203
- Need help? Join @ResistanceForum on Telegram
204
- `);
205
- prompter.note(
206
- "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.",
207
- "Security Warning"
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
208
565
  );
209
- const acceptRisk = await prompter.confirm({
566
+ const acceptRisk = await confirm({
210
567
  message: "I understand the risks and want to continue",
211
- initialValue: false
568
+ default: false,
569
+ theme: inquirerTheme
212
570
  });
213
571
  if (!acceptRisk) {
214
- prompter.outro("Setup cancelled - you must accept the risks to continue");
572
+ console.log(`
573
+ ${DIM("Setup cancelled \u2014 you must accept the risks to continue.")}
574
+ `);
215
575
  process.exit(1);
216
576
  }
217
- const spinner2 = prompter.spinner();
218
- spinner2.start("Creating workspace...");
577
+ const spinner = ora2({ color: "cyan" });
578
+ spinner.start(DIM("Creating workspace..."));
219
579
  const workspace = await ensureWorkspace({
220
580
  workspaceDir: options.workspace,
221
581
  ensureTemplates: true
222
582
  });
223
583
  const isNew = isNewWorkspace(workspace);
224
- spinner2.stop(`\u2713 Workspace: ${workspace.root}`);
584
+ spinner.succeed(DIM(`Workspace: ${workspace.root}`));
225
585
  if (!isNew) {
226
586
  prompter.warn("Existing configuration detected");
227
- const shouldOverwrite = await prompter.confirm({
587
+ const shouldOverwrite = await confirm({
228
588
  message: "Overwrite existing configuration?",
229
- initialValue: false
589
+ default: false,
590
+ theme: inquirerTheme
230
591
  });
231
592
  if (!shouldOverwrite) {
232
- prompter.outro("Setup cancelled - existing configuration preserved");
593
+ console.log(`
594
+ ${DIM("Setup cancelled \u2014 existing configuration preserved.")}
595
+ `);
233
596
  return;
234
597
  }
235
598
  }
236
- const agentName = await prompter.text({
599
+ const agentName = await input({
237
600
  message: "Give your agent a name (optional)",
238
- placeholder: "e.g. Nova, Kai, Echo..."
601
+ default: "Nova",
602
+ theme: inquirerTheme
239
603
  });
240
604
  if (agentName && agentName.trim() && existsSync(workspace.identityPath)) {
241
605
  const identity = readFileSync(workspace.identityPath, "utf-8");
242
606
  const updated = identity.replace("[Your name - pick one or ask your human]", agentName.trim());
243
607
  writeFileSync(workspace.identityPath, updated, "utf-8");
244
608
  }
245
- const flow = await prompter.select({
609
+ selectedFlow = await select({
246
610
  message: "Installation mode",
247
- options: [
248
- { value: "quick", label: "QuickStart", hint: "Minimal configuration (recommended)" },
249
- { value: "advanced", label: "Advanced", hint: "Detailed configuration" }
250
- ],
251
- initialValue: "quick"
252
- });
253
- const enabledModules = await prompter.multiselect({
254
- message: "Enable optional modules (Space to toggle, Enter to confirm)",
255
- options: [
611
+ default: "quick",
612
+ theme: inquirerTheme,
613
+ choices: [
256
614
  {
257
- value: "deals",
258
- label: "Gifts & Deals",
259
- hint: "Gift/TON trading with escrow system"
260
- }
261
- ],
262
- required: false
615
+ value: "quick",
616
+ name: "\u26A1 QuickStart",
617
+ description: "Minimal configuration (recommended)"
618
+ },
619
+ { value: "advanced", name: "\u2699 Advanced", description: "Detailed configuration" }
620
+ ]
263
621
  });
264
- const dealsEnabled = enabledModules.includes("deals");
622
+ STEPS[0].value = `${agentName} (${selectedFlow})`;
623
+ redraw(1);
265
624
  const providers = getSupportedProviders();
266
- const selectedProvider = await prompter.select({
625
+ selectedProvider = await select({
267
626
  message: "AI Provider",
268
- options: providers.map((p) => ({
627
+ default: "anthropic",
628
+ theme: inquirerTheme,
629
+ choices: providers.map((p) => ({
269
630
  value: p.id,
270
- label: p.displayName,
271
- hint: p.toolLimit !== null ? `${p.defaultModel} (max ${p.toolLimit} tools)` : `${p.defaultModel}`
272
- })),
273
- initialValue: "anthropic"
631
+ name: p.displayName,
632
+ description: p.toolLimit !== null ? `${p.defaultModel} (max ${p.toolLimit} tools)` : `${p.defaultModel}`
633
+ }))
274
634
  });
275
635
  const providerMeta = getProviderMetadata(selectedProvider);
276
636
  if (providerMeta.toolLimit !== null) {
277
- prompter.note(
637
+ noteBox(
278
638
  `${providerMeta.displayName} supports max ${providerMeta.toolLimit} tools.
279
639
  Teleton currently has ~116 tools. If more tools are added,
280
640
  some may be truncated.`,
281
641
  "Tool Limit"
282
642
  );
283
643
  }
284
- prompter.note(
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(
285
724
  "You need Telegram credentials from https://my.telegram.org/apps\nCreate an application and note the API ID and API Hash",
286
- "Telegram"
725
+ "Telegram",
726
+ TON
287
727
  );
288
728
  const envApiId = process.env.TELETON_TG_API_ID;
289
729
  const envApiHash = process.env.TELETON_TG_API_HASH;
290
730
  const envPhone = process.env.TELETON_TG_PHONE;
291
- const envApiKey = process.env.TELETON_API_KEY;
292
- const apiIdStr = options.apiId ? options.apiId.toString() : await prompter.text({
731
+ const apiIdStr = options.apiId ? options.apiId.toString() : await input({
293
732
  message: envApiId ? "API ID (from env)" : "API ID (from my.telegram.org)",
294
- initialValue: envApiId,
733
+ default: envApiId,
734
+ theme: inquirerTheme,
295
735
  validate: (value) => {
296
736
  if (!value || isNaN(parseInt(value))) return "Invalid API ID (must be a number)";
737
+ return true;
297
738
  }
298
739
  });
299
- const apiId = parseInt(apiIdStr);
300
- const apiHash = options.apiHash ? options.apiHash : await prompter.text({
740
+ apiId = parseInt(apiIdStr);
741
+ apiHash = options.apiHash ? options.apiHash : await input({
301
742
  message: envApiHash ? "API Hash (from env)" : "API Hash (from my.telegram.org)",
302
- initialValue: envApiHash,
743
+ default: envApiHash,
744
+ theme: inquirerTheme,
303
745
  validate: (value) => {
304
746
  if (!value || value.length < 10) return "Invalid API Hash";
747
+ return true;
305
748
  }
306
749
  });
307
- const phone = options.phone ? options.phone : await prompter.text({
308
- message: envPhone ? "Phone number (from env)" : "Phone number (international format, e.g. +1234567890)",
309
- placeholder: "+1234567890",
310
- initialValue: envPhone,
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,
311
754
  validate: (value) => {
312
- if (!value || !value.startsWith("+")) return "Invalid format (must start with +)";
755
+ if (!value || !value.startsWith("+")) return "Must start with +";
756
+ return true;
313
757
  }
314
758
  });
315
- prompter.note(
759
+ noteBox(
316
760
  "To get your Telegram User ID:\n1. Open @userinfobot on Telegram\n2. Send /start\n3. Note the ID displayed",
317
- "User ID"
761
+ "User ID",
762
+ TON
318
763
  );
319
- const userIdStr = options.userId ? options.userId.toString() : await prompter.text({
764
+ const userIdStr = options.userId ? options.userId.toString() : await input({
320
765
  message: "Your Telegram User ID (for admin rights)",
766
+ theme: inquirerTheme,
321
767
  validate: (value) => {
322
768
  if (!value || isNaN(parseInt(value))) return "Invalid User ID";
769
+ return true;
323
770
  }
324
771
  });
325
- const userId = parseInt(userIdStr);
326
- prompter.note(
327
- `${providerMeta.displayName} API key required.
328
- Get it at: ${providerMeta.consoleUrl}`,
329
- "API Key"
330
- );
331
- let apiKey;
332
- if (options.apiKey) {
333
- apiKey = options.apiKey;
334
- } else if (envApiKey) {
335
- const validationError = validateApiKeyFormat(selectedProvider, envApiKey);
336
- if (validationError) {
337
- prompter.warn(`TELETON_API_KEY env var found but invalid: ${validationError}`);
338
- apiKey = await prompter.password({
339
- message: `${providerMeta.displayName} API Key (${providerMeta.keyHint})`,
340
- validate: (value = "") => validateApiKeyFormat(selectedProvider, value)
341
- });
342
- } else {
343
- prompter.log(`Using API key from TELETON_API_KEY env var`);
344
- apiKey = envApiKey;
345
- }
346
- } else {
347
- apiKey = await prompter.password({
348
- message: `${providerMeta.displayName} API Key (${providerMeta.keyHint})`,
349
- validate: (value = "") => validateApiKeyFormat(selectedProvider, value)
350
- });
351
- }
352
- const MODEL_OPTIONS = {
353
- anthropic: [
354
- { value: "claude-opus-4-5-20251101", label: "Claude Opus 4.5", hint: "Most capable, $5/M" },
355
- { value: "claude-sonnet-4-0", label: "Claude Sonnet 4", hint: "Balanced, $3/M" },
356
- { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "Fast & cheap, $1/M" },
357
- { value: "claude-3-5-haiku-20241022", label: "Claude 3.5 Haiku", hint: "Cheapest, $0.80/M" }
358
- ],
359
- openai: [
360
- { value: "gpt-5", label: "GPT-5", hint: "Most capable, 400K ctx, $1.25/M" },
361
- { value: "gpt-4o", label: "GPT-4o", hint: "Balanced, 128K ctx, $2.50/M" },
362
- { value: "gpt-4.1", label: "GPT-4.1", hint: "1M ctx, $2/M" },
363
- { value: "gpt-4.1-mini", label: "GPT-4.1 Mini", hint: "1M ctx, cheap, $0.40/M" },
364
- { value: "o3", label: "o3", hint: "Reasoning, 200K ctx, $2/M" }
365
- ],
366
- google: [
367
- { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash", hint: "Fast, 1M ctx, $0.30/M" },
368
- { value: "gemini-2.5-pro", label: "Gemini 2.5 Pro", hint: "Most capable, 1M ctx, $1.25/M" },
369
- { value: "gemini-2.0-flash", label: "Gemini 2.0 Flash", hint: "Cheap, 1M ctx, $0.10/M" }
370
- ],
371
- xai: [
372
- { value: "grok-4-fast", label: "Grok 4 Fast", hint: "Vision, 2M ctx, $0.20/M" },
373
- { value: "grok-4", label: "Grok 4", hint: "Reasoning, 256K ctx, $3/M" },
374
- { value: "grok-3", label: "Grok 3", hint: "Stable, 131K ctx, $3/M" }
375
- ],
376
- groq: [
377
- {
378
- value: "meta-llama/llama-4-maverick-17b-128e-instruct",
379
- label: "Llama 4 Maverick",
380
- hint: "Vision, 131K ctx, $0.20/M"
381
- },
382
- { value: "qwen/qwen3-32b", label: "Qwen3 32B", hint: "Reasoning, 131K ctx, $0.29/M" },
383
- {
384
- value: "deepseek-r1-distill-llama-70b",
385
- label: "DeepSeek R1 70B",
386
- hint: "Reasoning, 131K ctx, $0.75/M"
387
- },
388
- {
389
- value: "llama-3.3-70b-versatile",
390
- label: "Llama 3.3 70B",
391
- hint: "General purpose, 131K ctx, $0.59/M"
392
- }
393
- ],
394
- openrouter: [
395
- { value: "anthropic/claude-opus-4.5", label: "Claude Opus 4.5", hint: "200K ctx, $5/M" },
396
- { value: "openai/gpt-5", label: "GPT-5", hint: "400K ctx, $1.25/M" },
397
- { value: "google/gemini-2.5-flash", label: "Gemini 2.5 Flash", hint: "1M ctx, $0.30/M" },
398
- { value: "deepseek/deepseek-r1", label: "DeepSeek R1", hint: "Reasoning, 64K ctx, $0.70/M" },
399
- { value: "x-ai/grok-4", label: "Grok 4", hint: "256K ctx, $3/M" }
400
- ]
401
- };
402
- let selectedModel = providerMeta.defaultModel;
403
- 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") {
404
777
  const providerModels = MODEL_OPTIONS[selectedProvider] || [];
405
- const modelOptions = [
778
+ const modelChoices = [
406
779
  ...providerModels,
407
- { value: "__custom__", label: "Custom", hint: "Enter a model ID manually" }
780
+ { value: "__custom__", name: "Custom", description: "Enter a model ID manually" }
408
781
  ];
409
- const modelChoice = await prompter.select({
782
+ const modelChoice = await select({
410
783
  message: "Model",
411
- options: modelOptions,
412
- initialValue: providerMeta.defaultModel
784
+ default: providerMeta.defaultModel,
785
+ theme: inquirerTheme,
786
+ choices: modelChoices
413
787
  });
414
788
  if (modelChoice === "__custom__") {
415
- const customModel = await prompter.text({
789
+ const customModel = await input({
416
790
  message: "Model ID",
417
- placeholder: providerMeta.defaultModel,
418
- initialValue: providerMeta.defaultModel
791
+ default: providerMeta.defaultModel,
792
+ theme: inquirerTheme
419
793
  });
420
- if (customModel && customModel.trim()) {
421
- selectedModel = customModel.trim();
422
- }
794
+ if (customModel?.trim()) selectedModel = customModel.trim();
423
795
  } else {
424
796
  selectedModel = modelChoice;
425
797
  }
426
- }
427
- let dmPolicy = "open";
428
- let groupPolicy = "open";
429
- let requireMention = true;
430
- let maxAgenticIterations = "5";
431
- if (flow === "advanced") {
432
- dmPolicy = await prompter.select({
798
+ dmPolicy = await select({
433
799
  message: "DM policy (private messages)",
434
- options: [
435
- { value: "open", label: "Open", hint: "Reply to everyone" },
436
- { value: "allowlist", label: "Allowlist", hint: "Only specific users" },
437
- { value: "disabled", label: "Disabled", hint: "No DM replies" }
438
- ],
439
- initialValue: "open"
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
+ ]
440
807
  });
441
- groupPolicy = await prompter.select({
808
+ groupPolicy = await select({
442
809
  message: "Group policy",
443
- options: [
444
- { value: "open", label: "Open", hint: "Reply in all groups" },
445
- { value: "allowlist", label: "Allowlist", hint: "Only specific groups" },
446
- { value: "disabled", label: "Disabled", hint: "No group replies" }
447
- ],
448
- initialValue: "open"
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
+ ]
449
817
  });
450
- requireMention = await prompter.confirm({
818
+ requireMention = await confirm({
451
819
  message: "Require @mention in groups?",
452
- initialValue: true
820
+ default: true,
821
+ theme: inquirerTheme
453
822
  });
454
- maxAgenticIterations = await prompter.text({
823
+ maxAgenticIterations = await input({
455
824
  message: "Max agentic iterations (tool call loops per message)",
456
- initialValue: "5",
457
- validate: (v = "") => {
825
+ default: "5",
826
+ theme: inquirerTheme,
827
+ validate: (v) => {
458
828
  const n = parseInt(v, 10);
459
- if (isNaN(n) || n < 1 || n > 50) return "Must be a number between 1 and 50";
829
+ return !isNaN(n) && n >= 1 && n <= 50 ? true : "Must be 1\u201350";
460
830
  }
461
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)`;
462
836
  }
463
- let botToken;
464
- let botUsername;
465
- let buyMaxFloorPercent = 100;
466
- let sellMinFloorPercent = 105;
837
+ redraw(4);
838
+ const extras = [];
467
839
  if (dealsEnabled) {
468
- const customizeStrategy = await prompter.confirm({
469
- message: "Customize trading thresholds? (default: buy \u2264 floor, sell \u2265 floor +5%)",
470
- initialValue: false
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
471
844
  });
472
845
  if (customizeStrategy) {
473
- const buyInput = await prompter.text({
846
+ const buyInput = await input({
474
847
  message: "Max buy price (% of floor price)",
475
- initialValue: "100",
476
- validate: (v = "") => {
848
+ default: "100",
849
+ theme: inquirerTheme,
850
+ validate: (v) => {
477
851
  const n = parseInt(v, 10);
478
- if (isNaN(n) || n < 50 || n > 150) return "Must be between 50 and 150";
852
+ return !isNaN(n) && n >= 50 && n <= 150 ? true : "Must be 50\u2013150";
479
853
  }
480
854
  });
481
855
  buyMaxFloorPercent = parseInt(buyInput, 10);
482
- const sellInput = await prompter.text({
856
+ const sellInput = await input({
483
857
  message: "Min sell price (% of floor price)",
484
- initialValue: "105",
485
- validate: (v = "") => {
858
+ default: "105",
859
+ theme: inquirerTheme,
860
+ validate: (v) => {
486
861
  const n = parseInt(v, 10);
487
- if (isNaN(n) || n < 100 || n > 200) return "Must be between 100 and 200";
862
+ return !isNaN(n) && n >= 100 && n <= 200 ? true : "Must be 100\u2013200";
488
863
  }
489
864
  });
490
865
  sellMinFloorPercent = parseInt(sellInput, 10);
491
866
  }
492
- }
493
- if (dealsEnabled) {
494
- prompter.note(
867
+ noteBox(
495
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",
496
- "Deals Bot"
869
+ "Deals Bot",
870
+ TON
497
871
  );
498
- const tokenInput = await prompter.password({
872
+ const tokenInput = await password({
499
873
  message: "Bot token (from @BotFather)",
874
+ theme: inquirerTheme,
500
875
  validate: (value) => {
501
- if (!value || !value.includes(":")) return "Invalid bot token format (expected id:hash)";
876
+ if (!value || !value.includes(":")) return "Invalid format (expected id:hash)";
877
+ return true;
502
878
  }
503
879
  });
504
- spinner2.start("Validating bot token...");
880
+ spinner.start(DIM("Validating bot token..."));
505
881
  try {
506
882
  const res = await fetchWithTimeout(`https://api.telegram.org/bot${tokenInput}/getMe`);
507
883
  const data = await res.json();
508
884
  if (!data.ok) {
509
- spinner2.stop("\u26A0 Bot token is invalid - skipping bot setup");
885
+ spinner.warn(DIM("Bot token is invalid \u2014 skipping bot setup"));
510
886
  } else {
511
887
  botToken = tokenInput;
512
888
  botUsername = data.result.username;
513
- spinner2.stop(`\u2713 Bot verified: @${botUsername}`);
889
+ spinner.succeed(DIM(`Bot verified: @${botUsername}`));
514
890
  }
515
891
  } catch {
516
- spinner2.stop("\u26A0 Could not validate bot token (network error) - saving anyway");
892
+ spinner.warn(DIM("Could not validate bot token (network error) \u2014 saving anyway"));
517
893
  botToken = tokenInput;
518
- const usernameInput = await prompter.text({
894
+ const usernameInput = await input({
519
895
  message: "Bot username (without @)",
896
+ theme: inquirerTheme,
520
897
  validate: (value) => {
521
898
  if (!value || value.length < 3) return "Username too short";
899
+ return true;
522
900
  }
523
901
  });
524
902
  botUsername = usernameInput;
525
903
  }
904
+ extras.push("Deals");
526
905
  }
527
- let tonapiKey;
528
- const setupTonapi = await prompter.confirm({
529
- message: "Add a TonAPI key? (optional, recommended)",
530
- initialValue: false
906
+ const setupTonapi = await confirm({
907
+ message: `Add a TonAPI key? ${DIM("(optional, recommended for 10x rate limits)")}`,
908
+ default: false,
909
+ theme: inquirerTheme
531
910
  });
532
911
  if (setupTonapi) {
533
- prompter.note(
912
+ noteBox(
534
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",
535
- "TonAPI"
914
+ "TonAPI",
915
+ TON
536
916
  );
537
- const keyInput = await prompter.text({
917
+ const keyInput = await input({
538
918
  message: "TonAPI key",
919
+ theme: inquirerTheme,
539
920
  validate: (v) => {
540
921
  if (!v || v.length < 10) return "Key too short";
922
+ return true;
541
923
  }
542
924
  });
543
925
  tonapiKey = keyInput;
926
+ extras.push("TonAPI");
544
927
  }
545
- let tavilyApiKey;
546
- const setupTavily = await prompter.confirm({
547
- message: "Enable web search? (requires a free Tavily API key \u2014 1,000 req/month, no credit card)",
548
- initialValue: false
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
549
932
  });
550
933
  if (setupTavily) {
551
- prompter.note(
934
+ noteBox(
552
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.",
553
- "Tavily \u2014 Web Search API"
936
+ "Tavily \u2014 Web Search API",
937
+ TON
554
938
  );
555
- const keyInput = await prompter.text({
939
+ const keyInput = await input({
556
940
  message: "Tavily API key (starts with tvly-)",
941
+ theme: inquirerTheme,
557
942
  validate: (v) => {
558
- if (!v || !v.startsWith("tvly-")) return "Invalid key (should start with tvly-)";
943
+ if (!v || !v.startsWith("tvly-")) return "Should start with tvly-";
944
+ return true;
559
945
  }
560
946
  });
561
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"));
562
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();
1032
+ }
1033
+ STEPS[5].value = `${wallet.address.slice(0, 8)}...${wallet.address.slice(-4)}`;
1034
+ redraw(6);
563
1035
  const config = {
564
1036
  meta: {
565
1037
  version: "1.0.0",
@@ -569,6 +1041,7 @@ Get it at: ${providerMeta.consoleUrl}`,
569
1041
  agent: {
570
1042
  provider: selectedProvider,
571
1043
  api_key: apiKey,
1044
+ ...selectedProvider === "local" && localBaseUrl ? { base_url: localBaseUrl } : {},
572
1045
  model: selectedModel,
573
1046
  max_tokens: 4096,
574
1047
  temperature: 0.7,
@@ -637,68 +1110,28 @@ Get it at: ${providerMeta.consoleUrl}`,
637
1110
  ],
638
1111
  skip_unlimited_providers: false
639
1112
  },
1113
+ logging: { level: "info", pretty: true },
640
1114
  mcp: { servers: {} },
641
1115
  plugins: {},
1116
+ ...selectedProvider === "cocoon" ? { cocoon: { port: cocoonInstance } } : {},
642
1117
  tonapi_key: tonapiKey,
643
1118
  tavily_api_key: tavilyApiKey
644
1119
  };
645
- spinner2.start("Saving configuration...");
1120
+ spinner.start(DIM("Saving configuration..."));
646
1121
  const configYaml = YAML.stringify(config);
647
- writeFileSync(workspace.configPath, configYaml, "utf-8");
648
- chmodSync(workspace.configPath, 384);
649
- spinner2.stop("\u2713 Configuration saved");
650
- let wallet;
651
- const existingWallet = walletExists() ? loadWallet() : null;
652
- if (existingWallet) {
653
- prompter.note(`Existing wallet found: ${existingWallet.address}`, "TON Wallet");
654
- const walletAction = await prompter.select({
655
- message: "A TON wallet already exists. What do you want to do?",
656
- options: [
657
- { value: "keep", label: "Keep existing", hint: `${existingWallet.address}` },
658
- { value: "regenerate", label: "Generate new", hint: "WARNING: old wallet will be lost" },
659
- { value: "import", label: "Import mnemonic", hint: "Restore from 24-word seed" }
660
- ],
661
- initialValue: "keep"
662
- });
663
- if (walletAction === "keep") {
664
- wallet = existingWallet;
665
- } else if (walletAction === "import") {
666
- const mnemonicInput = await prompter.text({
667
- message: "Enter your 24-word mnemonic (space-separated)",
668
- validate: (value = "") => {
669
- const words = value.trim().split(/\s+/);
670
- if (words.length !== 24) return `Expected 24 words, got ${words.length}`;
671
- }
672
- });
673
- spinner2.start("Importing wallet...");
674
- wallet = await importWallet(mnemonicInput.trim().split(/\s+/));
675
- saveWallet(wallet);
676
- spinner2.stop(`\u2713 Wallet imported: ${wallet.address}`);
677
- } else {
678
- spinner2.start("Generating new TON wallet...");
679
- wallet = await generateWallet();
680
- saveWallet(wallet);
681
- spinner2.stop("\u2713 New TON wallet generated");
682
- }
683
- } else {
684
- spinner2.start("Generating TON wallet...");
685
- wallet = await generateWallet();
686
- saveWallet(wallet);
687
- spinner2.stop("\u2713 TON wallet generated");
688
- }
689
- if (!existingWallet || wallet !== existingWallet) {
690
- prompter.note(
691
- "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.",
692
- "Mnemonic Seed (24 words)"
693
- );
694
- }
1122
+ writeFileSync(workspace.configPath, configYaml, { encoding: "utf-8", mode: 384 });
1123
+ spinner.succeed(DIM(`Configuration saved: ${workspace.configPath}`));
695
1124
  let telegramConnected = false;
696
- const connectNow = await prompter.confirm({
697
- message: "Connect to Telegram now? (you'll need the verification code sent to your phone)",
698
- initialValue: true
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
699
1129
  });
700
1130
  if (connectNow) {
701
- prompter.log("Connecting to Telegram... Check your phone for the verification code.");
1131
+ console.log(
1132
+ `
1133
+ ${DIM("Connecting to Telegram... Check your phone for the verification code.")}`
1134
+ );
702
1135
  try {
703
1136
  const sessionPath = join(TELETON_ROOT, "telegram_session.txt");
704
1137
  const client = new TelegramUserClient({
@@ -711,53 +1144,54 @@ Get it at: ${providerMeta.consoleUrl}`,
711
1144
  const me = client.getMe();
712
1145
  await client.disconnect();
713
1146
  telegramConnected = true;
714
- prompter.success(
715
- `\u2713 Telegram connected as ${me?.firstName || ""}${me?.username ? ` (@${me.username})` : ""}`
716
- );
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})` : ""}`;
717
1151
  } catch (err) {
718
1152
  prompter.warn(
719
1153
  `Telegram connection failed: ${err instanceof Error ? err.message : String(err)}
720
1154
  You can authenticate later when running: teleton start`
721
1155
  );
1156
+ STEPS[6].value = "Auth on first start";
722
1157
  }
723
- }
724
- prompter.note(
725
- `Workspace: ${workspace.root}
726
- Config: ${workspace.configPath}
727
- Templates: SOUL.md, MEMORY.md, IDENTITY.md, USER.md
728
- Telegram: ${phone} (API ID: ${apiId})${telegramConnected ? " \u2713 connected" : ""}
729
- Admin: User ID ${userId}
730
- Provider: ${providerMeta.displayName}
731
- Model: ${selectedModel}
732
- TON Wallet: ${wallet.address}
733
- Web Search: ${tavilyApiKey ? "Tavily \u2713" : "disabled (no Tavily key)"}`,
734
- "Setup complete"
735
- );
736
- if (telegramConnected) {
737
- prompter.note(
738
- "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",
739
- "Ready"
740
- );
741
1158
  } else {
742
- prompter.note(
743
- "Next steps:\n\n1. Start the agent:\n $ teleton start\n\n2. On first launch, you will be asked for:\n - Telegram verification code\n - 2FA password (if enabled)\n\n3. Send a message to your Telegram account to test",
744
- "Ready"
745
- );
1159
+ console.log(`
1160
+ ${DIM("You can authenticate later when running: teleton start")}
1161
+ `);
1162
+ STEPS[6].value = "Auth on first start";
746
1163
  }
747
- prompter.outro("Good luck!");
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
+ `);
748
1175
  }
749
1176
  async function runNonInteractiveOnboarding(options, prompter) {
750
- if (!options.apiId || !options.apiHash || !options.phone || !options.apiKey || !options.userId) {
751
- prompter.error(
752
- "Non-interactive mode requires: --api-id, --api-hash, --phone, --api-key, --user-id"
753
- );
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");
754
1189
  process.exit(1);
755
1190
  }
756
1191
  const workspace = await ensureWorkspace({
757
1192
  workspaceDir: options.workspace,
758
1193
  ensureTemplates: true
759
1194
  });
760
- const selectedProvider = options.provider || "anthropic";
761
1195
  const providerMeta = getProviderMetadata(selectedProvider);
762
1196
  const config = {
763
1197
  meta: {
@@ -767,7 +1201,8 @@ async function runNonInteractiveOnboarding(options, prompter) {
767
1201
  },
768
1202
  agent: {
769
1203
  provider: selectedProvider,
770
- api_key: options.apiKey,
1204
+ api_key: options.apiKey || "",
1205
+ ...options.baseUrl ? { base_url: options.baseUrl } : {},
771
1206
  model: providerMeta.defaultModel,
772
1207
  max_tokens: 4096,
773
1208
  temperature: 0.7,
@@ -832,14 +1267,14 @@ async function runNonInteractiveOnboarding(options, prompter) {
832
1267
  ],
833
1268
  skip_unlimited_providers: false
834
1269
  },
1270
+ logging: { level: "info", pretty: true },
835
1271
  mcp: { servers: {} },
836
1272
  plugins: {},
837
1273
  tavily_api_key: options.tavilyApiKey
838
1274
  };
839
1275
  const configYaml = YAML.stringify(config);
840
- writeFileSync(workspace.configPath, configYaml, "utf-8");
841
- chmodSync(workspace.configPath, 384);
842
- prompter.success(`\u2713 Configuration created: ${workspace.configPath}`);
1276
+ writeFileSync(workspace.configPath, configYaml, { encoding: "utf-8", mode: 384 });
1277
+ prompter.success(`Configuration created: ${workspace.configPath}`);
843
1278
  }
844
1279
 
845
1280
  // src/cli/commands/doctor.ts
@@ -951,6 +1386,13 @@ async function checkApiKey(workspaceDir) {
951
1386
  message: `Unknown provider: ${provider}`
952
1387
  };
953
1388
  }
1389
+ if (provider === "cocoon" || provider === "local") {
1390
+ return {
1391
+ name: `${meta.displayName}`,
1392
+ status: "ok",
1393
+ message: "No API key needed"
1394
+ };
1395
+ }
954
1396
  if (!apiKey) {
955
1397
  return {
956
1398
  name: `${meta.displayName} API key`,
@@ -966,8 +1408,8 @@ async function checkApiKey(workspaceDir) {
966
1408
  message: validationError
967
1409
  };
968
1410
  }
969
- const maskLen = Math.min(10, apiKey.length - 4);
970
- const masked = apiKey.substring(0, maskLen) + "..." + apiKey.substring(apiKey.length - 4);
1411
+ const maskLen = Math.min(4, Math.max(0, apiKey.length - 4));
1412
+ const masked = apiKey.substring(0, maskLen) + "****" + apiKey.substring(apiKey.length - 4);
971
1413
  return {
972
1414
  name: `${meta.displayName} API key`,
973
1415
  status: "ok",
@@ -1250,8 +1692,8 @@ async function mcpAddCommand(pkg, extraArgs, options) {
1250
1692
  if (options.url) {
1251
1693
  entry.url = pkg;
1252
1694
  } else {
1253
- const cmdParts = ["npx", "-y", pkg, ...extraArgs];
1254
- entry.command = cmdParts.join(" ");
1695
+ entry.command = "npx";
1696
+ entry.args = ["-y", pkg, ...extraArgs];
1255
1697
  }
1256
1698
  if (options.scope && options.scope !== "always") {
1257
1699
  entry.scope = options.scope;
@@ -1458,20 +1900,23 @@ function findPackageJson() {
1458
1900
  var packageJson = findPackageJson();
1459
1901
  var program = new Command();
1460
1902
  program.name("teleton").description("Teleton Agent - Personal AI Agent for Telegram").version(packageJson.version);
1461
- 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>", "Anthropic API key").option("--user-id <id>", "Telegram User ID").option("--tavily-api-key <key>", "Tavily API key for web search").action(async (options) => {
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) => {
1462
1904
  try {
1463
1905
  await onboardCommand({
1464
1906
  workspace: options.workspace,
1465
1907
  nonInteractive: options.nonInteractive,
1908
+ ui: options.ui,
1909
+ uiPort: options.uiPort,
1466
1910
  apiId: options.apiId ? parseInt(options.apiId) : void 0,
1467
1911
  apiHash: options.apiHash,
1468
1912
  phone: options.phone,
1469
1913
  apiKey: options.apiKey,
1914
+ baseUrl: options.baseUrl,
1470
1915
  userId: options.userId ? parseInt(options.userId) : void 0,
1471
1916
  tavilyApiKey: options.tavilyApiKey
1472
1917
  });
1473
1918
  } catch (error) {
1474
- console.error("Error:", error instanceof Error ? error.message : String(error));
1919
+ console.error("Error:", getErrorMessage(error));
1475
1920
  process.exit(1);
1476
1921
  }
1477
1922
  });
@@ -1491,7 +1936,7 @@ program.command("start").description("Start the Teleton agent").option("-c, --co
1491
1936
  }
1492
1937
  await main(options.config);
1493
1938
  } catch (error) {
1494
- console.error("Error:", error instanceof Error ? error.message : String(error));
1939
+ console.error("Error:", getErrorMessage(error));
1495
1940
  process.exit(1);
1496
1941
  }
1497
1942
  });
@@ -1499,7 +1944,7 @@ program.command("doctor").description("Run system health checks").action(async (
1499
1944
  try {
1500
1945
  await doctorCommand();
1501
1946
  } catch (error) {
1502
- console.error("Error:", error instanceof Error ? error.message : String(error));
1947
+ console.error("Error:", getErrorMessage(error));
1503
1948
  process.exit(1);
1504
1949
  }
1505
1950
  });
@@ -1515,7 +1960,7 @@ mcp.command("add <package> [args...]").description(
1515
1960
  try {
1516
1961
  await mcpAddCommand(pkg, args, options);
1517
1962
  } catch (error) {
1518
- console.error("Error:", error instanceof Error ? error.message : String(error));
1963
+ console.error("Error:", getErrorMessage(error));
1519
1964
  process.exit(1);
1520
1965
  }
1521
1966
  });
@@ -1523,7 +1968,7 @@ mcp.command("remove <name>").description("Remove an MCP server by name").option(
1523
1968
  try {
1524
1969
  await mcpRemoveCommand(name, options);
1525
1970
  } catch (error) {
1526
- console.error("Error:", error instanceof Error ? error.message : String(error));
1971
+ console.error("Error:", getErrorMessage(error));
1527
1972
  process.exit(1);
1528
1973
  }
1529
1974
  });
@@ -1531,7 +1976,7 @@ mcp.command("list").description("List configured MCP servers").option("-c, --con
1531
1976
  try {
1532
1977
  await mcpListCommand(options);
1533
1978
  } catch (error) {
1534
- console.error("Error:", error instanceof Error ? error.message : String(error));
1979
+ console.error("Error:", getErrorMessage(error));
1535
1980
  process.exit(1);
1536
1981
  }
1537
1982
  });
@@ -1539,7 +1984,7 @@ program.command("config").description("Manage configuration keys (set, get, list
1539
1984
  try {
1540
1985
  await configCommand(action, key, value, options);
1541
1986
  } catch (error) {
1542
- console.error("Error:", error instanceof Error ? error.message : String(error));
1987
+ console.error("Error:", getErrorMessage(error));
1543
1988
  process.exit(1);
1544
1989
  }
1545
1990
  });