talon-agent 4.3.2 → 4.3.3
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/package.json +1 -1
- package/src/backend/claude-sdk/doctor.ts +1 -1
- package/src/backend/codex/doctor.ts +1 -1
- package/src/backend/openai-agents/doctor.ts +1 -1
- package/src/bootstrap.ts +1 -1
- package/src/cli/doctor.ts +1 -1
- package/src/core/agent-runtime/backend-registry.ts +1 -1
- package/src/core/auth/expiry-monitor.ts +1 -1
- package/src/core/{doctor.ts → doctor/index.ts} +11 -11
- package/src/core/{notify.ts → frontend-runtime/admin-notify.ts} +1 -1
- package/src/core/plugin/native-runtimes.ts +1 -1
- package/src/frontend/discord/commands/admin.ts +1 -1
- package/src/frontend/discord/render.ts +1 -1
- package/src/frontend/telegram/actions/index.ts +1 -1
- package/src/frontend/telegram/actions/moderation/chat.ts +63 -0
- package/src/frontend/telegram/actions/moderation/index.ts +74 -0
- package/src/frontend/telegram/actions/moderation/members.ts +77 -0
- package/src/frontend/telegram/actions/moderation/permissions.ts +69 -0
- package/src/frontend/telegram/actions/moderation/topics.ts +48 -0
- package/src/frontend/telegram/actions/moderation/types.ts +16 -0
- package/src/frontend/telegram/admin/background.ts +66 -0
- package/src/frontend/telegram/admin/chunked-reply.ts +20 -0
- package/src/frontend/telegram/admin/health.ts +81 -0
- package/src/frontend/telegram/admin/sessions.ts +105 -0
- package/src/frontend/telegram/admin.ts +56 -261
- package/src/frontend/telegram/callbacks/model/backend.ts +156 -0
- package/src/frontend/telegram/callbacks/model/control.ts +29 -0
- package/src/frontend/telegram/callbacks/model/nav.ts +54 -0
- package/src/frontend/telegram/callbacks/model/select.ts +100 -0
- package/src/frontend/telegram/callbacks/model/types.ts +39 -0
- package/src/frontend/telegram/callbacks/model/views.ts +116 -0
- package/src/frontend/telegram/callbacks/model.ts +61 -331
- package/src/frontend/telegram/commands/admin.ts +108 -77
- package/src/frontend/telegram/commands/settings.ts +65 -43
- package/src/frontend/telegram/commands/whatsapp-pairing.ts +1 -1
- package/src/frontend/telegram/helpers/diagnostics.ts +1 -1
- package/src/frontend/telegram/middleware.ts +166 -201
- package/src/frontend/terminal/builtins/context.ts +184 -0
- package/src/frontend/terminal/builtins/help.ts +35 -0
- package/src/frontend/terminal/builtins/index.ts +23 -0
- package/src/frontend/terminal/builtins/model.ts +198 -0
- package/src/frontend/terminal/builtins/session.ts +101 -0
- package/src/frontend/terminal/builtins/status.ts +202 -0
- package/src/frontend/terminal/command-registry.ts +80 -0
- package/src/frontend/terminal/commands.ts +11 -674
- package/src/frontend/terminal/input/history.ts +64 -0
- package/src/frontend/terminal/input/keys.ts +143 -0
- package/src/frontend/terminal/input/parts.ts +33 -0
- package/src/frontend/terminal/input/paste.ts +53 -0
- package/src/frontend/terminal/input/render.ts +42 -0
- package/src/frontend/terminal/input/state.ts +60 -0
- package/src/frontend/terminal/input.ts +27 -342
- package/src/frontend/whatsapp/connection.ts +1 -1
- package/src/frontend/whatsapp/index.ts +1 -1
- package/src/frontend/whatsapp/pairing-service.ts +1 -1
- package/src/plugins/github/provision.ts +1 -1
- package/src/plugins/mempalace/provision.ts +1 -1
- package/src/plugins/playwright/provision.ts +1 -1
- package/src/frontend/telegram/actions/moderation.ts +0 -255
- /package/src/core/{doctor-types.ts → doctor/types.ts} +0 -0
- /package/src/core/{pairing-broker.ts → frontend-runtime/pairing-broker.ts} +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Bot } from "grammy";
|
|
10
|
+
import type { TalonConfig } from "../../../util/config.js";
|
|
10
11
|
import { respawnSelf } from "../../../util/respawn.js";
|
|
11
12
|
import { isStaleCommand } from "../stale-command.js";
|
|
12
13
|
import {
|
|
@@ -25,16 +26,13 @@ import {
|
|
|
25
26
|
renderUsageMessage,
|
|
26
27
|
} from "../helpers/index.js";
|
|
27
28
|
import { collectPlanUsage } from "../../shared/plan-usage-report.js";
|
|
28
|
-
import { collectDoctorReport } from "../../../core/doctor.js";
|
|
29
|
+
import { collectDoctorReport } from "../../../core/doctor/index.js";
|
|
29
30
|
import { handleAdminCommand } from "../admin.js";
|
|
30
31
|
import { getTodayMetrics } from "../../../storage/metrics.js";
|
|
31
32
|
import { isAuthorizedAdmin, type RegisterDeps } from "./state.js";
|
|
32
33
|
import { telegramCommandMenu } from "./definitions.js";
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
bot: Bot,
|
|
36
|
-
{ config }: RegisterDeps,
|
|
37
|
-
): void {
|
|
35
|
+
function registerAdminCommand(bot: Bot, config: TalonConfig): void {
|
|
38
36
|
bot.command("admin", async (ctx) => {
|
|
39
37
|
if (!isAuthorizedAdmin(ctx)) {
|
|
40
38
|
await ctx.reply("Not authorized.");
|
|
@@ -42,7 +40,9 @@ export function registerAdminCommands(
|
|
|
42
40
|
}
|
|
43
41
|
await handleAdminCommand(ctx, bot, config);
|
|
44
42
|
});
|
|
43
|
+
}
|
|
45
44
|
|
|
45
|
+
function registerMetricsCommand(bot: Bot): void {
|
|
46
46
|
bot.command("metrics", async (ctx) => {
|
|
47
47
|
if (!isAuthorizedAdmin(ctx)) {
|
|
48
48
|
await ctx.reply("Not authorized.");
|
|
@@ -55,15 +55,19 @@ export function registerAdminCommands(
|
|
|
55
55
|
reply_markup: { inline_keyboard: renderMetricsKeyboard("today") },
|
|
56
56
|
});
|
|
57
57
|
});
|
|
58
|
+
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
// /usage — plan limits across every exposed backend, not just this
|
|
61
|
+
// chat's. Not admin-gated: it says how close the shared account is to a
|
|
62
|
+
// wall, which is exactly what a user hitting one needs to know.
|
|
63
|
+
function registerUsageCommand(bot: Bot, config: TalonConfig): void {
|
|
62
64
|
bot.command("usage", async (ctx) => {
|
|
63
65
|
const entries = await collectPlanUsage(config);
|
|
64
66
|
await ctx.reply(renderUsageMessage(entries), { parse_mode: "HTML" });
|
|
65
67
|
});
|
|
68
|
+
}
|
|
66
69
|
|
|
70
|
+
function registerDoctorCommand(bot: Bot, config: TalonConfig): void {
|
|
67
71
|
bot.command("doctor", async (ctx) => {
|
|
68
72
|
if (!isAuthorizedAdmin(ctx)) {
|
|
69
73
|
await ctx.reply("Not authorized.");
|
|
@@ -90,7 +94,9 @@ export function registerAdminCommands(
|
|
|
90
94
|
);
|
|
91
95
|
}
|
|
92
96
|
});
|
|
97
|
+
}
|
|
93
98
|
|
|
99
|
+
function registerDreamCommand(bot: Bot): void {
|
|
94
100
|
bot.command("dream", async (ctx) => {
|
|
95
101
|
if (!isAuthorizedAdmin(ctx)) {
|
|
96
102
|
await ctx.reply("Not authorized.");
|
|
@@ -118,10 +124,12 @@ export function registerAdminCommands(
|
|
|
118
124
|
);
|
|
119
125
|
});
|
|
120
126
|
});
|
|
127
|
+
}
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
// /soul — introspect the compiled identity (read-only). `/soul dream`
|
|
130
|
+
// (admin) runs the organic maintenance pass. Inert when the soul is
|
|
131
|
+
// disabled (TALON_SOUL_ENABLED), so it's safe to ship dormant.
|
|
132
|
+
function registerSoulCommand(bot: Bot): void {
|
|
125
133
|
bot.command("soul", async (ctx) => {
|
|
126
134
|
const soul = getSoul();
|
|
127
135
|
if (!soul.enabled) {
|
|
@@ -149,7 +157,9 @@ export function registerAdminCommands(
|
|
|
149
157
|
}
|
|
150
158
|
await ctx.reply(soul.introspect());
|
|
151
159
|
});
|
|
160
|
+
}
|
|
152
161
|
|
|
162
|
+
function registerRestartCommand(bot: Bot): void {
|
|
153
163
|
bot.command("restart", async (ctx) => {
|
|
154
164
|
if (!isAuthorizedAdmin(ctx)) {
|
|
155
165
|
await ctx.reply("Not authorized.");
|
|
@@ -162,77 +172,82 @@ export function registerAdminCommands(
|
|
|
162
172
|
await ctx.reply("♻️ Restarting...");
|
|
163
173
|
respawnSelf("telegram /restart");
|
|
164
174
|
});
|
|
175
|
+
}
|
|
165
176
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
177
|
+
// /update — pull latest, reinstall, run setup, restart. Only wired
|
|
178
|
+
// up for developer builds running from a git checkout; packaged
|
|
179
|
+
// binaries have no source tree (getRepoRoot() === null) so the
|
|
180
|
+
// command stays absent entirely.
|
|
181
|
+
function registerUpdateCommand(
|
|
182
|
+
bot: Bot,
|
|
183
|
+
config: TalonConfig,
|
|
184
|
+
updateRepoRoot: string,
|
|
185
|
+
): void {
|
|
186
|
+
bot.command("update", async (ctx) => {
|
|
187
|
+
if (!isAuthorizedAdmin(ctx)) {
|
|
188
|
+
await ctx.reply("Not authorized.");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Same redelivery hazard as /restart — it also ends the process.
|
|
192
|
+
if (isStaleCommand(ctx.message?.date, "/update")) return;
|
|
193
|
+
const remote = config.update?.remote ?? "origin";
|
|
194
|
+
const branch = config.update?.branch ?? "main";
|
|
195
|
+
const sent = await ctx.reply(
|
|
196
|
+
`⏳ Updating from <code>${escapeHtml(remote)}/${escapeHtml(branch)}</code>…`,
|
|
197
|
+
{ parse_mode: "HTML" },
|
|
198
|
+
);
|
|
199
|
+
const edit = (text: string) =>
|
|
200
|
+
bot.api
|
|
201
|
+
.editMessageText(ctx.chat.id, sent.message_id, text, {
|
|
202
|
+
parse_mode: "HTML",
|
|
203
|
+
})
|
|
204
|
+
.catch(() => {});
|
|
191
205
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
await edit(
|
|
203
|
-
`⚠️ Update failed: ${escapeHtml(res.error ?? "unknown error")}` +
|
|
204
|
-
(tail ? `\n\n<pre>${escapeHtml(tail.slice(-1500))}</pre>` : ""),
|
|
205
|
-
);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if (!res.changed) {
|
|
209
|
-
await edit(
|
|
210
|
-
`✅ Already up to date at <code>${escapeHtml(res.before ?? "?")}</code> — no restart needed.`,
|
|
211
|
-
);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
206
|
+
// Fire-and-forget so grammY keeps processing other updates.
|
|
207
|
+
runSelfUpdate({
|
|
208
|
+
remote,
|
|
209
|
+
branch,
|
|
210
|
+
setup: config.update?.setup,
|
|
211
|
+
repoRoot: updateRepoRoot,
|
|
212
|
+
})
|
|
213
|
+
.then(async (res) => {
|
|
214
|
+
if (!res.ok) {
|
|
215
|
+
const tail = res.steps[res.steps.length - 1]?.output ?? "";
|
|
214
216
|
await edit(
|
|
215
|
-
|
|
217
|
+
`⚠️ Update failed: ${escapeHtml(res.error ?? "unknown error")}` +
|
|
218
|
+
(tail ? `\n\n<pre>${escapeHtml(tail.slice(-1500))}</pre>` : ""),
|
|
216
219
|
);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!res.changed) {
|
|
223
|
+
await edit(
|
|
224
|
+
`✅ Already up to date at <code>${escapeHtml(res.before ?? "?")}</code> — no restart needed.`,
|
|
225
|
+
);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
await edit(
|
|
229
|
+
`✅ Updated <code>${escapeHtml(res.before ?? "?")}</code> → <code>${escapeHtml(res.after ?? "?")}</code>. ♻️ Restarting…`,
|
|
230
|
+
);
|
|
231
|
+
// The successor documents any provisioning changes (plugin
|
|
232
|
+
// runtime upgrades, migrations) back to this chat once it's up.
|
|
233
|
+
const { armProvisionReport } =
|
|
234
|
+
await import("../../../core/plugin/provision-journal.js");
|
|
235
|
+
armProvisionReport("telegram", String(ctx.chat.id));
|
|
236
|
+
respawnSelf("telegram /update");
|
|
237
|
+
})
|
|
238
|
+
.catch(async (err: unknown) => {
|
|
239
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
240
|
+
await edit(`⚠️ Update crashed: ${escapeHtml(msg)}`);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
}
|
|
230
244
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
245
|
+
// Unknown /command → "did you mean ...?" via the C similarity core
|
|
246
|
+
// (native/strsim-wasm). Registered after every real command, so grammY
|
|
247
|
+
// only reaches this when nothing above matched. Only bare commands
|
|
248
|
+
// are intercepted — a close miss gets a suggestion, anything else
|
|
249
|
+
// keeps flowing to the agent as a normal message.
|
|
250
|
+
function registerUnknownCommandSuggester(bot: Bot, config: TalonConfig): void {
|
|
236
251
|
const commandNames = telegramCommandMenu(config).map((c) => c.command);
|
|
237
252
|
bot.on("message::bot_command", async (ctx, next) => {
|
|
238
253
|
const typed = /^\/([a-zA-Z0-9_]+)(?:@(\w+))?\s*$/.exec(ctx.msg.text ?? "");
|
|
@@ -250,3 +265,19 @@ export function registerAdminCommands(
|
|
|
250
265
|
);
|
|
251
266
|
});
|
|
252
267
|
}
|
|
268
|
+
|
|
269
|
+
export function registerAdminCommands(
|
|
270
|
+
bot: Bot,
|
|
271
|
+
{ config }: RegisterDeps,
|
|
272
|
+
): void {
|
|
273
|
+
registerAdminCommand(bot, config);
|
|
274
|
+
registerMetricsCommand(bot);
|
|
275
|
+
registerUsageCommand(bot, config);
|
|
276
|
+
registerDoctorCommand(bot, config);
|
|
277
|
+
registerDreamCommand(bot);
|
|
278
|
+
registerSoulCommand(bot);
|
|
279
|
+
registerRestartCommand(bot);
|
|
280
|
+
const updateRepoRoot = config.devBuild ? getRepoRoot() : null;
|
|
281
|
+
if (updateRepoRoot) registerUpdateCommand(bot, config, updateRepoRoot);
|
|
282
|
+
registerUnknownCommandSuggester(bot, config);
|
|
283
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Settings commands — /model, /effort, /pulse, /settings.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { Bot } from "grammy";
|
|
5
|
+
import type { Bot, Context } from "grammy";
|
|
6
6
|
import {
|
|
7
7
|
getChatSettings,
|
|
8
8
|
setChatModelForBackend,
|
|
@@ -43,10 +43,56 @@ import {
|
|
|
43
43
|
} from "../../shared/reasoning-levels.js";
|
|
44
44
|
import type { RegisterDeps } from "./state.js";
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
/** `/model <query>` — resolve the query against the per-chat backend and
|
|
47
|
+
* persist the pick. `be` + `beId` are the caller's (override-aware). */
|
|
48
|
+
async function replyModelSet(
|
|
49
|
+
ctx: Context,
|
|
50
|
+
arg: string,
|
|
51
|
+
cid: string,
|
|
52
|
+
be: ReturnType<typeof resolveBackendForChat>,
|
|
53
|
+
beId: string,
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
if (be?.models?.resolveModelInfo) {
|
|
56
|
+
const resolution = await be.models?.resolveModelInfo(arg);
|
|
57
|
+
if (resolution.kind !== "exact") {
|
|
58
|
+
// Every backend's formatModelError returns plain text and
|
|
59
|
+
// interpolates the raw query, so escape at this boundary — the
|
|
60
|
+
// same fix as the model menu's status lines. `/model <name>`
|
|
61
|
+
// (which the OpenCode/Kilo hint literally tells you to type)
|
|
62
|
+
// otherwise reaches Telegram as an unsupported `<name>` tag and
|
|
63
|
+
// the whole reply 400s, leaving the command looking dead.
|
|
64
|
+
const msg =
|
|
65
|
+
be.models?.formatModelError?.(arg, resolution) ??
|
|
66
|
+
`No model matched "${arg}".`;
|
|
67
|
+
await ctx.reply(escapeHtml(msg), { parse_mode: "HTML" });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!resolution.model.selectable) {
|
|
71
|
+
const msg =
|
|
72
|
+
resolution.model.unavailableReason ??
|
|
73
|
+
`${resolution.model.providerName} is not connected.`;
|
|
74
|
+
await ctx.reply(escapeHtml(msg), { parse_mode: "HTML" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setChatModelForBackend(cid, beId, resolution.storedValue);
|
|
78
|
+
setChatBackend(cid, beId);
|
|
79
|
+
await ctx.reply(
|
|
80
|
+
`Model set to <code>${escapeHtml(resolution.storedValue)}</code> (${escapeHtml(resolution.model.providerName)}${resolution.model.free ? " · free" : ""}).`,
|
|
81
|
+
{ parse_mode: "HTML" },
|
|
82
|
+
);
|
|
83
|
+
} else {
|
|
84
|
+
// Fallback for backends without model resolution
|
|
85
|
+
const model = resolveModelName(arg);
|
|
86
|
+
setChatModelForBackend(cid, beId, model);
|
|
87
|
+
setChatBackend(cid, beId);
|
|
88
|
+
await ctx.reply(
|
|
89
|
+
`Model set to <code>${escapeHtml(formatModelLabel(model))}</code>.`,
|
|
90
|
+
{ parse_mode: "HTML" },
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function registerModelCommand(bot: Bot, { config, gateway }: RegisterDeps) {
|
|
50
96
|
bot.command("model", async (ctx) => {
|
|
51
97
|
const cid = String(ctx.chat.id);
|
|
52
98
|
const arg = ctx.match?.trim();
|
|
@@ -109,46 +155,11 @@ export function registerSettingsCommands(
|
|
|
109
155
|
|
|
110
156
|
// `be` + `beId` already resolved above for the activeModel lookup.
|
|
111
157
|
// Reuse them — they point at the per-chat backend (override-aware).
|
|
112
|
-
|
|
113
|
-
const resolution = await be.models?.resolveModelInfo(arg);
|
|
114
|
-
if (resolution.kind !== "exact") {
|
|
115
|
-
// Every backend's formatModelError returns plain text and
|
|
116
|
-
// interpolates the raw query, so escape at this boundary — the
|
|
117
|
-
// same fix as the model menu's status lines. `/model <name>`
|
|
118
|
-
// (which the OpenCode/Kilo hint literally tells you to type)
|
|
119
|
-
// otherwise reaches Telegram as an unsupported `<name>` tag and
|
|
120
|
-
// the whole reply 400s, leaving the command looking dead.
|
|
121
|
-
const msg =
|
|
122
|
-
be.models?.formatModelError?.(arg, resolution) ??
|
|
123
|
-
`No model matched "${arg}".`;
|
|
124
|
-
await ctx.reply(escapeHtml(msg), { parse_mode: "HTML" });
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (!resolution.model.selectable) {
|
|
128
|
-
const msg =
|
|
129
|
-
resolution.model.unavailableReason ??
|
|
130
|
-
`${resolution.model.providerName} is not connected.`;
|
|
131
|
-
await ctx.reply(escapeHtml(msg), { parse_mode: "HTML" });
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
setChatModelForBackend(cid, beId, resolution.storedValue);
|
|
135
|
-
setChatBackend(cid, beId);
|
|
136
|
-
await ctx.reply(
|
|
137
|
-
`Model set to <code>${escapeHtml(resolution.storedValue)}</code> (${escapeHtml(resolution.model.providerName)}${resolution.model.free ? " · free" : ""}).`,
|
|
138
|
-
{ parse_mode: "HTML" },
|
|
139
|
-
);
|
|
140
|
-
} else {
|
|
141
|
-
// Fallback for backends without model resolution
|
|
142
|
-
const model = resolveModelName(arg);
|
|
143
|
-
setChatModelForBackend(cid, beId, model);
|
|
144
|
-
setChatBackend(cid, beId);
|
|
145
|
-
await ctx.reply(
|
|
146
|
-
`Model set to <code>${escapeHtml(formatModelLabel(model))}</code>.`,
|
|
147
|
-
{ parse_mode: "HTML" },
|
|
148
|
-
);
|
|
149
|
-
}
|
|
158
|
+
await replyModelSet(ctx, arg, cid, be, beId);
|
|
150
159
|
});
|
|
160
|
+
}
|
|
151
161
|
|
|
162
|
+
function registerEffortCommand(bot: Bot, { config, gateway }: RegisterDeps) {
|
|
152
163
|
bot.command("effort", async (ctx) => {
|
|
153
164
|
const cid = String(ctx.chat.id);
|
|
154
165
|
const arg = ctx.match?.trim().toLowerCase();
|
|
@@ -207,7 +218,9 @@ export function registerSettingsCommands(
|
|
|
207
218
|
`Unknown level for this model. Valid: ${reasoning.levels.join(", ")}, or adaptive.`,
|
|
208
219
|
);
|
|
209
220
|
});
|
|
221
|
+
}
|
|
210
222
|
|
|
223
|
+
function registerPulseCommand(bot: Bot) {
|
|
211
224
|
bot.command("pulse", async (ctx) => {
|
|
212
225
|
const cid = String(ctx.chat.id);
|
|
213
226
|
const arg = ctx.match?.trim().toLowerCase();
|
|
@@ -270,7 +283,9 @@ export function registerSettingsCommands(
|
|
|
270
283
|
|
|
271
284
|
await ctx.reply("Use: /pulse on, /pulse off, /pulse 30m, /pulse 2h");
|
|
272
285
|
});
|
|
286
|
+
}
|
|
273
287
|
|
|
288
|
+
function registerSettingsCommand(bot: Bot, { config, gateway }: RegisterDeps) {
|
|
274
289
|
bot.command("settings", async (ctx) => {
|
|
275
290
|
const cid = String(ctx.chat.id);
|
|
276
291
|
const chatSets = getChatSettings(cid);
|
|
@@ -349,3 +364,10 @@ export function registerSettingsCommands(
|
|
|
349
364
|
);
|
|
350
365
|
});
|
|
351
366
|
}
|
|
367
|
+
|
|
368
|
+
export function registerSettingsCommands(bot: Bot, deps: RegisterDeps): void {
|
|
369
|
+
registerModelCommand(bot, deps);
|
|
370
|
+
registerEffortCommand(bot, deps);
|
|
371
|
+
registerPulseCommand(bot);
|
|
372
|
+
registerSettingsCommand(bot, deps);
|
|
373
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { InputFile, type Bot, type Context } from "grammy";
|
|
14
|
-
import { getPairingProvider } from "../../../core/pairing-broker.js";
|
|
14
|
+
import { getPairingProvider } from "../../../core/frontend-runtime/pairing-broker.js";
|
|
15
15
|
import { log } from "../../../util/log.js";
|
|
16
16
|
import { isAuthorizedAdmin } from "./state.js";
|
|
17
17
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { escapeHtml } from "../formatting.js";
|
|
6
|
-
import type { DoctorReport } from "../../../core/doctor.js";
|
|
6
|
+
import type { DoctorReport } from "../../../core/doctor/index.js";
|
|
7
7
|
import type { MeshPingResult } from "../../../core/mesh/service.js";
|
|
8
8
|
import type { BackendUsageEntry } from "../../shared/plan-usage-report.js";
|
|
9
9
|
import type { SettingsButton } from "./menu.js";
|