teleton 0.5.1 → 0.6.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.
@@ -0,0 +1,362 @@
1
+ import {
2
+ ConfigSchema,
3
+ expandPath
4
+ } from "./chunk-4IPJ25HE.js";
5
+
6
+ // src/config/configurable-keys.ts
7
+ import { readFileSync, writeFileSync, existsSync, chmodSync } from "fs";
8
+ import { parse, stringify } from "yaml";
9
+ var noValidation = () => void 0;
10
+ var identity = (v) => v;
11
+ var nonEmpty = (v) => v.length > 0 ? void 0 : "Must not be empty";
12
+ function numberInRange(min, max) {
13
+ return (v) => {
14
+ const n = Number(v);
15
+ if (isNaN(n)) return "Must be a number";
16
+ if (n < min || n > max) return `Must be between ${min} and ${max}`;
17
+ return void 0;
18
+ };
19
+ }
20
+ function enumValidator(options) {
21
+ return (v) => options.includes(v) ? void 0 : `Must be one of: ${options.join(", ")}`;
22
+ }
23
+ var CONFIGURABLE_KEYS = {
24
+ // ─── API Keys ──────────────────────────────────────────────────────
25
+ "agent.api_key": {
26
+ type: "string",
27
+ category: "API Keys",
28
+ description: "LLM provider API key",
29
+ sensitive: true,
30
+ validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
31
+ mask: (v) => v.slice(0, 8) + "****",
32
+ parse: identity
33
+ },
34
+ tavily_api_key: {
35
+ type: "string",
36
+ category: "API Keys",
37
+ description: "Tavily API key for web search",
38
+ sensitive: true,
39
+ validate: (v) => v.startsWith("tvly-") ? void 0 : "Must start with 'tvly-'",
40
+ mask: (v) => v.slice(0, 9) + "****",
41
+ parse: identity
42
+ },
43
+ tonapi_key: {
44
+ type: "string",
45
+ category: "API Keys",
46
+ description: "TonAPI key for higher rate limits",
47
+ sensitive: true,
48
+ validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
49
+ mask: (v) => v.slice(0, 10) + "****",
50
+ parse: identity
51
+ },
52
+ "telegram.bot_token": {
53
+ type: "string",
54
+ category: "API Keys",
55
+ description: "Bot token from @BotFather",
56
+ sensitive: true,
57
+ validate: (v) => v.includes(":") ? void 0 : "Must contain ':' (e.g., 123456:ABC...)",
58
+ mask: (v) => v.split(":")[0] + ":****",
59
+ parse: identity
60
+ },
61
+ // ─── Agent ─────────────────────────────────────────────────────────
62
+ "agent.provider": {
63
+ type: "enum",
64
+ category: "Agent",
65
+ description: "LLM provider",
66
+ sensitive: false,
67
+ options: ["anthropic", "openai", "google", "xai", "groq", "openrouter"],
68
+ validate: enumValidator(["anthropic", "openai", "google", "xai", "groq", "openrouter"]),
69
+ mask: identity,
70
+ parse: identity
71
+ },
72
+ "agent.model": {
73
+ type: "string",
74
+ category: "Agent",
75
+ description: "Main LLM model ID",
76
+ sensitive: false,
77
+ validate: nonEmpty,
78
+ mask: identity,
79
+ parse: identity
80
+ },
81
+ "agent.utility_model": {
82
+ type: "string",
83
+ category: "Agent",
84
+ description: "Cheap model for summarization (auto-detected if empty)",
85
+ sensitive: false,
86
+ validate: noValidation,
87
+ mask: identity,
88
+ parse: identity
89
+ },
90
+ "agent.temperature": {
91
+ type: "number",
92
+ category: "Agent",
93
+ description: "Response creativity (0.0 = deterministic, 2.0 = max)",
94
+ sensitive: false,
95
+ validate: numberInRange(0, 2),
96
+ mask: identity,
97
+ parse: (v) => Number(v)
98
+ },
99
+ "agent.max_tokens": {
100
+ type: "number",
101
+ category: "Agent",
102
+ description: "Maximum response length in tokens",
103
+ sensitive: false,
104
+ validate: numberInRange(256, 128e3),
105
+ mask: identity,
106
+ parse: (v) => Number(v)
107
+ },
108
+ "agent.max_agentic_iterations": {
109
+ type: "number",
110
+ category: "Agent",
111
+ description: "Max tool-call loop iterations per message",
112
+ sensitive: false,
113
+ validate: numberInRange(1, 20),
114
+ mask: identity,
115
+ parse: (v) => Number(v)
116
+ },
117
+ // ─── Session ───────────────────────────────────────────────────
118
+ "agent.session_reset_policy.daily_reset_enabled": {
119
+ type: "boolean",
120
+ category: "Session",
121
+ description: "Enable daily session reset at specified hour",
122
+ sensitive: false,
123
+ validate: enumValidator(["true", "false"]),
124
+ mask: identity,
125
+ parse: (v) => v === "true"
126
+ },
127
+ "agent.session_reset_policy.daily_reset_hour": {
128
+ type: "number",
129
+ category: "Session",
130
+ description: "Hour (0-23 UTC) for daily session reset",
131
+ sensitive: false,
132
+ validate: numberInRange(0, 23),
133
+ mask: identity,
134
+ parse: (v) => Number(v)
135
+ },
136
+ "agent.session_reset_policy.idle_expiry_enabled": {
137
+ type: "boolean",
138
+ category: "Session",
139
+ description: "Enable automatic session expiry after idle period",
140
+ sensitive: false,
141
+ validate: enumValidator(["true", "false"]),
142
+ mask: identity,
143
+ parse: (v) => v === "true"
144
+ },
145
+ "agent.session_reset_policy.idle_expiry_minutes": {
146
+ type: "number",
147
+ category: "Session",
148
+ description: "Idle minutes before session expires (minimum 1)",
149
+ sensitive: false,
150
+ validate: numberInRange(1, Number.MAX_SAFE_INTEGER),
151
+ mask: identity,
152
+ parse: (v) => Number(v)
153
+ },
154
+ // ─── Telegram ──────────────────────────────────────────────────────
155
+ "telegram.bot_username": {
156
+ type: "string",
157
+ category: "Telegram",
158
+ description: "Bot username without @",
159
+ sensitive: false,
160
+ validate: (v) => v.length >= 3 ? void 0 : "Must be at least 3 characters",
161
+ mask: identity,
162
+ parse: identity
163
+ },
164
+ "telegram.dm_policy": {
165
+ type: "enum",
166
+ category: "Telegram",
167
+ description: "DM access policy",
168
+ sensitive: false,
169
+ options: ["pairing", "allowlist", "open", "disabled"],
170
+ validate: enumValidator(["pairing", "allowlist", "open", "disabled"]),
171
+ mask: identity,
172
+ parse: identity
173
+ },
174
+ "telegram.group_policy": {
175
+ type: "enum",
176
+ category: "Telegram",
177
+ description: "Group access policy",
178
+ sensitive: false,
179
+ options: ["open", "allowlist", "disabled"],
180
+ validate: enumValidator(["open", "allowlist", "disabled"]),
181
+ mask: identity,
182
+ parse: identity
183
+ },
184
+ "telegram.require_mention": {
185
+ type: "boolean",
186
+ category: "Telegram",
187
+ description: "Require @mention in groups to respond",
188
+ sensitive: false,
189
+ validate: enumValidator(["true", "false"]),
190
+ mask: identity,
191
+ parse: (v) => v === "true"
192
+ },
193
+ "telegram.owner_name": {
194
+ type: "string",
195
+ category: "Telegram",
196
+ description: "Owner's first name (used in system prompt)",
197
+ sensitive: false,
198
+ validate: noValidation,
199
+ mask: identity,
200
+ parse: identity
201
+ },
202
+ "telegram.owner_username": {
203
+ type: "string",
204
+ category: "Telegram",
205
+ description: "Owner's Telegram username (without @)",
206
+ sensitive: false,
207
+ validate: noValidation,
208
+ mask: identity,
209
+ parse: identity
210
+ },
211
+ "telegram.debounce_ms": {
212
+ type: "number",
213
+ category: "Telegram",
214
+ description: "Group message debounce delay in ms (0 = disabled)",
215
+ sensitive: false,
216
+ validate: numberInRange(0, 1e4),
217
+ mask: identity,
218
+ parse: (v) => Number(v)
219
+ },
220
+ "telegram.agent_channel": {
221
+ type: "string",
222
+ category: "Telegram",
223
+ description: "Channel username for auto-publishing",
224
+ sensitive: false,
225
+ validate: noValidation,
226
+ mask: identity,
227
+ parse: identity
228
+ },
229
+ "telegram.typing_simulation": {
230
+ type: "boolean",
231
+ category: "Telegram",
232
+ description: "Simulate typing indicator before sending replies",
233
+ sensitive: false,
234
+ validate: enumValidator(["true", "false"]),
235
+ mask: identity,
236
+ parse: (v) => v === "true"
237
+ },
238
+ // ─── Embedding ─────────────────────────────────────────────────────
239
+ "embedding.provider": {
240
+ type: "enum",
241
+ category: "Embedding",
242
+ description: "Embedding provider for RAG",
243
+ sensitive: false,
244
+ options: ["local", "anthropic", "none"],
245
+ validate: enumValidator(["local", "anthropic", "none"]),
246
+ mask: identity,
247
+ parse: identity
248
+ },
249
+ // ─── WebUI ─────────────────────────────────────────────────────────
250
+ "webui.port": {
251
+ type: "number",
252
+ category: "WebUI",
253
+ description: "HTTP server port (requires restart)",
254
+ sensitive: false,
255
+ validate: numberInRange(1024, 65535),
256
+ mask: identity,
257
+ parse: (v) => Number(v)
258
+ },
259
+ "webui.log_requests": {
260
+ type: "boolean",
261
+ category: "WebUI",
262
+ description: "Log all HTTP requests to console",
263
+ sensitive: false,
264
+ validate: enumValidator(["true", "false"]),
265
+ mask: identity,
266
+ parse: (v) => v === "true"
267
+ },
268
+ // ─── Deals ─────────────────────────────────────────────────────────
269
+ "deals.enabled": {
270
+ type: "boolean",
271
+ category: "Deals",
272
+ description: "Enable the deals/escrow module",
273
+ sensitive: false,
274
+ validate: enumValidator(["true", "false"]),
275
+ mask: identity,
276
+ parse: (v) => v === "true"
277
+ },
278
+ // ─── Developer ─────────────────────────────────────────────────────
279
+ "dev.hot_reload": {
280
+ type: "boolean",
281
+ category: "Developer",
282
+ description: "Watch ~/.teleton/plugins/ for live changes",
283
+ sensitive: false,
284
+ validate: enumValidator(["true", "false"]),
285
+ mask: identity,
286
+ parse: (v) => v === "true"
287
+ }
288
+ };
289
+ var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
290
+ function assertSafePath(parts) {
291
+ if (parts.some((p) => FORBIDDEN_SEGMENTS.has(p))) {
292
+ throw new Error("Invalid config path: forbidden segment");
293
+ }
294
+ }
295
+ function getNestedValue(obj, path) {
296
+ const parts = path.split(".");
297
+ assertSafePath(parts);
298
+ let current = obj;
299
+ for (const part of parts) {
300
+ if (current == null || typeof current !== "object") return void 0;
301
+ current = current[part];
302
+ }
303
+ return current;
304
+ }
305
+ function setNestedValue(obj, path, value) {
306
+ const parts = path.split(".");
307
+ assertSafePath(parts);
308
+ let current = obj;
309
+ for (let i = 0; i < parts.length - 1; i++) {
310
+ if (current[parts[i]] == null || typeof current[parts[i]] !== "object") {
311
+ current[parts[i]] = {};
312
+ }
313
+ current = current[parts[i]];
314
+ }
315
+ current[parts[parts.length - 1]] = value;
316
+ }
317
+ function deleteNestedValue(obj, path) {
318
+ const parts = path.split(".");
319
+ assertSafePath(parts);
320
+ let current = obj;
321
+ for (let i = 0; i < parts.length - 1; i++) {
322
+ if (current == null || typeof current !== "object") return;
323
+ current = current[parts[i]];
324
+ }
325
+ if (current != null && typeof current === "object") {
326
+ delete current[parts[parts.length - 1]];
327
+ }
328
+ }
329
+ function readRawConfig(configPath) {
330
+ const fullPath = expandPath(configPath);
331
+ if (!existsSync(fullPath)) {
332
+ throw new Error(`Config file not found: ${fullPath}
333
+ Run 'teleton setup' to create one.`);
334
+ }
335
+ const raw = parse(readFileSync(fullPath, "utf-8"));
336
+ if (!raw || typeof raw !== "object") {
337
+ throw new Error(`Invalid config file: ${fullPath}`);
338
+ }
339
+ return raw;
340
+ }
341
+ function writeRawConfig(raw, configPath) {
342
+ const clone = { ...raw };
343
+ delete clone.market;
344
+ const result = ConfigSchema.safeParse(clone);
345
+ if (!result.success) {
346
+ throw new Error(`Refusing to save invalid config: ${result.error.message}`);
347
+ }
348
+ raw.meta = raw.meta ?? {};
349
+ raw.meta.last_modified_at = (/* @__PURE__ */ new Date()).toISOString();
350
+ const fullPath = expandPath(configPath);
351
+ writeFileSync(fullPath, stringify(raw), "utf-8");
352
+ chmodSync(fullPath, 384);
353
+ }
354
+
355
+ export {
356
+ CONFIGURABLE_KEYS,
357
+ getNestedValue,
358
+ setNestedValue,
359
+ deleteNestedValue,
360
+ readRawConfig,
361
+ writeRawConfig
362
+ };