cc-claw 0.19.2 → 0.19.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/dist/cli.js +1460 -1171
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.19.
|
|
36
|
+
VERSION = true ? "0.19.3" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -8470,6 +8470,17 @@ function buildContextPrefix(msg) {
|
|
|
8470
8470
|
}
|
|
8471
8471
|
return parts.length > 0 ? parts.join("\n") + "\n\n" : "";
|
|
8472
8472
|
}
|
|
8473
|
+
async function sendOrEditKeyboard(chatId, channel, messageId, text, buttons) {
|
|
8474
|
+
if (messageId && typeof channel.editKeyboard === "function") {
|
|
8475
|
+
const ok = await channel.editKeyboard(chatId, messageId, text, buttons);
|
|
8476
|
+
if (ok) return messageId;
|
|
8477
|
+
}
|
|
8478
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
8479
|
+
return channel.sendKeyboard(chatId, text, buttons);
|
|
8480
|
+
}
|
|
8481
|
+
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
8482
|
+
return void 0;
|
|
8483
|
+
}
|
|
8473
8484
|
var TONE_PATTERNS, ALLOWED_REACTION_EMOJIS, BLOCKED_PATH_PATTERNS, CLI_INSTALL_HINTS, PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, SKILLS_PER_PAGE, MAX_SIDE_QUESTS;
|
|
8474
8485
|
var init_helpers = __esm({
|
|
8475
8486
|
"src/router/helpers.ts"() {
|
|
@@ -15314,160 +15325,568 @@ var init_wizard = __esm({
|
|
|
15314
15325
|
}
|
|
15315
15326
|
});
|
|
15316
15327
|
|
|
15317
|
-
// src/
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15328
|
+
// src/router/ollama.ts
|
|
15329
|
+
var ollama_exports2 = {};
|
|
15330
|
+
__export(ollama_exports2, {
|
|
15331
|
+
handleOllamaCallback: () => handleOllamaCallback,
|
|
15332
|
+
handleOllamaCommand: () => handleOllamaCommand,
|
|
15333
|
+
handleOllamaWizardText: () => handleOllamaWizardText,
|
|
15334
|
+
hasOllamaWizard: () => hasOllamaWizard
|
|
15335
|
+
});
|
|
15336
|
+
function hasOllamaWizard(chatId) {
|
|
15337
|
+
return pendingAdds.has(chatId);
|
|
15338
|
+
}
|
|
15339
|
+
function cancelOllamaWizard(chatId) {
|
|
15340
|
+
pendingAdds.delete(chatId);
|
|
15341
|
+
const timer = wizardTimers2.get(chatId);
|
|
15342
|
+
if (timer) {
|
|
15343
|
+
clearTimeout(timer);
|
|
15344
|
+
wizardTimers2.delete(chatId);
|
|
15332
15345
|
}
|
|
15333
|
-
return NOT_DETECTED;
|
|
15334
15346
|
}
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15353
|
-
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
/\bclaude\b.*\bagents?\b.*\bcodex\b/i,
|
|
15359
|
-
/\bgemini\b.*\bagents?\b.*\bcodex\b/i,
|
|
15360
|
-
/\bcodex\b.*\bagents?\b.*\bgemini\b/i
|
|
15361
|
-
];
|
|
15362
|
-
NEGATIVE_PATTERNS = [
|
|
15363
|
-
/\bhow\s+does\s+.*\bwork\b/i,
|
|
15364
|
-
/\bwhat\s+is\s+.*\bfunction\b/i,
|
|
15365
|
-
/\bexplain\s+.*\bagent/i,
|
|
15366
|
-
/src\/agents?\//
|
|
15367
|
-
];
|
|
15347
|
+
function resetWizardTimeout2(chatId) {
|
|
15348
|
+
const existing = wizardTimers2.get(chatId);
|
|
15349
|
+
if (existing) clearTimeout(existing);
|
|
15350
|
+
wizardTimers2.set(chatId, setTimeout(() => {
|
|
15351
|
+
pendingAdds.delete(chatId);
|
|
15352
|
+
wizardTimers2.delete(chatId);
|
|
15353
|
+
log(`[ollama-wizard] Auto-cancelled stale wizard for chat ${chatId}`);
|
|
15354
|
+
}, WIZARD_TIMEOUT_MS2));
|
|
15355
|
+
}
|
|
15356
|
+
async function startAddWizard(chatId, channel) {
|
|
15357
|
+
cancelOllamaWizard(chatId);
|
|
15358
|
+
pendingAdds.set(chatId, { step: "name" });
|
|
15359
|
+
resetWizardTimeout2(chatId);
|
|
15360
|
+
await channel.sendText(chatId, '\u{1F999} Add Ollama Server\n\nWhat should this server be called?\n(A short name like "local", "mac-studio", "gpu-box")', { parseMode: "plain" });
|
|
15361
|
+
}
|
|
15362
|
+
async function handleOllamaWizardText(chatId, text, channel) {
|
|
15363
|
+
const pending = pendingAdds.get(chatId);
|
|
15364
|
+
if (!pending) return;
|
|
15365
|
+
const lower = text.toLowerCase().trim();
|
|
15366
|
+
if (lower === "cancel") {
|
|
15367
|
+
cancelOllamaWizard(chatId);
|
|
15368
|
+
await channel.sendText(chatId, "Server setup cancelled.", { parseMode: "plain" });
|
|
15369
|
+
return;
|
|
15368
15370
|
}
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
// src/router/session-log.ts
|
|
15372
|
-
var session_log_exports2 = {};
|
|
15373
|
-
__export(session_log_exports2, {
|
|
15374
|
-
SessionLogFile: () => SessionLogFile,
|
|
15375
|
-
cleanupSessionLogs: () => cleanupSessionLogs,
|
|
15376
|
-
getRetentionDays: () => getRetentionDays,
|
|
15377
|
-
listSessionLogs: () => listSessionLogs,
|
|
15378
|
-
startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
|
|
15379
|
-
tailSessionLog: () => tailSessionLog
|
|
15380
|
-
});
|
|
15381
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync6, createReadStream } from "fs";
|
|
15382
|
-
import { join as join15, basename } from "path";
|
|
15383
|
-
import { createInterface as createInterface6 } from "readline";
|
|
15384
|
-
function getRetentionDays() {
|
|
15385
|
-
const env = process.env.SESSION_LOG_RETENTION_DAYS;
|
|
15386
|
-
if (env) {
|
|
15387
|
-
const n = parseInt(env, 10);
|
|
15388
|
-
if (!isNaN(n) && n > 0) return n;
|
|
15371
|
+
if (lower === "confirm" && pending.step === "confirm") {
|
|
15372
|
+
return finalizeAddWizard(chatId, channel);
|
|
15389
15373
|
}
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15396
|
-
|
|
15397
|
-
try {
|
|
15398
|
-
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
15399
|
-
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
15400
|
-
const filePath = join15(SESSION_LOGS_PATH, file);
|
|
15401
|
-
try {
|
|
15402
|
-
const { mtimeMs } = statSync6(filePath);
|
|
15403
|
-
if (mtimeMs < cutoff) {
|
|
15404
|
-
unlinkSync6(filePath);
|
|
15405
|
-
cleaned++;
|
|
15406
|
-
}
|
|
15407
|
-
} catch {
|
|
15374
|
+
resetWizardTimeout2(chatId);
|
|
15375
|
+
switch (pending.step) {
|
|
15376
|
+
case "name": {
|
|
15377
|
+
const name = text.trim().replace(/\s+/g, "-").toLowerCase();
|
|
15378
|
+
if (!name || name.length > 30) {
|
|
15379
|
+
await channel.sendText(chatId, "Name should be 1-30 characters. Try again:", { parseMode: "plain" });
|
|
15380
|
+
return;
|
|
15408
15381
|
}
|
|
15382
|
+
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15383
|
+
if (OllamaStore.getServer(name)) {
|
|
15384
|
+
await channel.sendText(chatId, `Server "${name}" already exists. Pick a different name:`, { parseMode: "plain" });
|
|
15385
|
+
return;
|
|
15386
|
+
}
|
|
15387
|
+
pending.name = name;
|
|
15388
|
+
pending.step = "host";
|
|
15389
|
+
await channel.sendText(chatId, `Server name: ${name}
|
|
15390
|
+
|
|
15391
|
+
Enter the host or IP address:
|
|
15392
|
+
(e.g. 192.168.1.100, mac-studio.local, 10.0.0.5)`, { parseMode: "plain" });
|
|
15393
|
+
break;
|
|
15409
15394
|
}
|
|
15410
|
-
|
|
15411
|
-
|
|
15395
|
+
case "host": {
|
|
15396
|
+
const host = text.trim();
|
|
15397
|
+
if (!host || host.includes(" ")) {
|
|
15398
|
+
await channel.sendText(chatId, "Invalid host. Enter an IP address or hostname (no spaces):", { parseMode: "plain" });
|
|
15399
|
+
return;
|
|
15400
|
+
}
|
|
15401
|
+
pending.host = host;
|
|
15402
|
+
pending.step = "port";
|
|
15403
|
+
await promptPort(chatId, channel);
|
|
15404
|
+
break;
|
|
15405
|
+
}
|
|
15406
|
+
case "port": {
|
|
15407
|
+
const val = parseInt(text.trim(), 10);
|
|
15408
|
+
if (isNaN(val) || val < 1 || val > 65535) {
|
|
15409
|
+
await channel.sendText(chatId, "Invalid port (1-65535). Try again or tap Default:", { parseMode: "plain" });
|
|
15410
|
+
return;
|
|
15411
|
+
}
|
|
15412
|
+
pending.port = val;
|
|
15413
|
+
pending.step = "confirm";
|
|
15414
|
+
await promptAddConfirm(chatId, channel);
|
|
15415
|
+
break;
|
|
15416
|
+
}
|
|
15417
|
+
default:
|
|
15418
|
+
await channel.sendText(chatId, "Use the buttons to confirm, edit, or cancel.", { parseMode: "plain" });
|
|
15412
15419
|
}
|
|
15413
|
-
|
|
15414
|
-
|
|
15420
|
+
}
|
|
15421
|
+
async function promptPort(chatId, channel) {
|
|
15422
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
15423
|
+
await channel.sendKeyboard(chatId, "Which port is Ollama running on?", [
|
|
15424
|
+
[{ label: "Default (11434)", data: "ollama:wizard_port:11434" }],
|
|
15425
|
+
[{ label: "Custom (443)", data: "ollama:wizard_port:443" }]
|
|
15426
|
+
]);
|
|
15427
|
+
await channel.sendText(chatId, "Or type a custom port number:", { parseMode: "plain" });
|
|
15428
|
+
} else {
|
|
15429
|
+
await channel.sendText(chatId, "Enter the port (default: 11434):", { parseMode: "plain" });
|
|
15415
15430
|
}
|
|
15416
|
-
return cleaned;
|
|
15417
15431
|
}
|
|
15418
|
-
function
|
|
15419
|
-
const
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15432
|
+
async function promptAddConfirm(chatId, channel) {
|
|
15433
|
+
const pending = pendingAdds.get(chatId);
|
|
15434
|
+
if (!pending) return;
|
|
15435
|
+
const lines = [
|
|
15436
|
+
"\u{1F999} Add Server \u2014 Confirm",
|
|
15437
|
+
"",
|
|
15438
|
+
` Name: ${pending.name}`,
|
|
15439
|
+
` Host: ${pending.host}`,
|
|
15440
|
+
` Port: ${pending.port ?? 11434}`
|
|
15441
|
+
];
|
|
15442
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
15443
|
+
await channel.sendKeyboard(chatId, lines.join("\n"), [
|
|
15444
|
+
[
|
|
15445
|
+
{ label: "\u2705 Add", data: "ollama:wizard_confirm", style: "success" },
|
|
15446
|
+
{ label: "\u270F\uFE0F Start Over", data: "ollama:wizard_restart" },
|
|
15447
|
+
{ label: "\u274C Cancel", data: "ollama:wizard_cancel" }
|
|
15448
|
+
]
|
|
15449
|
+
]);
|
|
15450
|
+
} else {
|
|
15451
|
+
await channel.sendText(chatId, lines.join("\n") + "\n\nSend 'confirm' to add or 'cancel' to abort.", { parseMode: "plain" });
|
|
15452
|
+
}
|
|
15424
15453
|
}
|
|
15425
|
-
function
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
|
|
15439
|
-
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
|
|
15454
|
+
async function finalizeAddWizard(chatId, channel) {
|
|
15455
|
+
const pending = pendingAdds.get(chatId);
|
|
15456
|
+
if (!pending?.name || !pending?.host) return;
|
|
15457
|
+
cancelOllamaWizard(chatId);
|
|
15458
|
+
await handleAdd(chatId, channel, pending.name, pending.host, pending.port);
|
|
15459
|
+
}
|
|
15460
|
+
async function handleOllamaCommand(chatId, commandArgs, channel) {
|
|
15461
|
+
const [sub, ...rest] = (commandArgs || "").trim().split(/\s+/);
|
|
15462
|
+
switch (sub) {
|
|
15463
|
+
case "models":
|
|
15464
|
+
return sendModelList(chatId, channel, rest[0]);
|
|
15465
|
+
case "health":
|
|
15466
|
+
return sendHealthCheck(chatId, channel);
|
|
15467
|
+
case "discover":
|
|
15468
|
+
return sendDiscover(chatId, channel, rest[0]);
|
|
15469
|
+
case "add":
|
|
15470
|
+
if (rest.length >= 2) return handleAdd(chatId, channel, rest[0], rest[1], rest[2] ? parseInt(rest[2], 10) : void 0);
|
|
15471
|
+
if (rest.length === 1) return handleAdd(chatId, channel, rest[0], rest[0]);
|
|
15472
|
+
return startAddWizard(chatId, channel);
|
|
15473
|
+
case "remove":
|
|
15474
|
+
if (rest[0]) return sendRemoveConfirm(chatId, channel, rest[0]);
|
|
15475
|
+
await channel.sendText(chatId, "Usage: /ollama remove <server-name>", { parseMode: "plain" });
|
|
15476
|
+
return;
|
|
15477
|
+
default:
|
|
15478
|
+
return sendOllamaDashboard(chatId, channel);
|
|
15444
15479
|
}
|
|
15445
|
-
logs.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
15446
|
-
return logs;
|
|
15447
15480
|
}
|
|
15448
|
-
async function
|
|
15449
|
-
|
|
15450
|
-
|
|
15481
|
+
async function sendOllamaDashboard(chatId, channel) {
|
|
15482
|
+
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15483
|
+
const servers = OllamaStore.listServers();
|
|
15484
|
+
if (servers.length === 0) {
|
|
15485
|
+
const text = [
|
|
15486
|
+
"<b>\u{1F999} Ollama</b>",
|
|
15487
|
+
"",
|
|
15488
|
+
"No servers configured.",
|
|
15489
|
+
"",
|
|
15490
|
+
"Add your first server:",
|
|
15491
|
+
" <code>/ollama add <name> <host> [port]</code>",
|
|
15492
|
+
"",
|
|
15493
|
+
"Example:",
|
|
15494
|
+
" <code>/ollama add local 192.168.1.100</code>"
|
|
15495
|
+
].join("\n");
|
|
15496
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
15497
|
+
await channel.sendKeyboard(chatId, text, [
|
|
15498
|
+
[{ label: "\u{1F4D6} Help", data: "ollama:help" }]
|
|
15499
|
+
]);
|
|
15500
|
+
} else {
|
|
15501
|
+
await channel.sendText(chatId, text, { parseMode: "html" });
|
|
15502
|
+
}
|
|
15451
15503
|
return;
|
|
15452
15504
|
}
|
|
15453
|
-
const
|
|
15454
|
-
const
|
|
15455
|
-
|
|
15456
|
-
|
|
15457
|
-
});
|
|
15458
|
-
|
|
15459
|
-
allLines.push(line);
|
|
15505
|
+
const lines = ["<b>\u{1F999} Ollama Servers</b>", ""];
|
|
15506
|
+
for (const s of servers) {
|
|
15507
|
+
const dot = s.status === "online" ? "\u{1F7E2}" : "\u{1F534}";
|
|
15508
|
+
const modelCount = OllamaStore.listModels(s.id).length;
|
|
15509
|
+
lines.push(`${dot} <b>${s.name}</b> <code>${s.host}:${s.port}</code>`);
|
|
15510
|
+
lines.push(` ${modelCount} model${modelCount !== 1 ? "s" : ""} \xB7 ${s.status}`);
|
|
15460
15511
|
}
|
|
15461
|
-
const
|
|
15462
|
-
|
|
15463
|
-
|
|
15512
|
+
const onlineCount = servers.filter((s) => s.status === "online").length;
|
|
15513
|
+
lines.push("", `${onlineCount}/${servers.length} online`);
|
|
15514
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
15515
|
+
const buttons = [
|
|
15516
|
+
[
|
|
15517
|
+
{ label: "\u{1F50D} Discover", data: "ollama:discover" },
|
|
15518
|
+
{ label: "\u{1F49A} Health", data: "ollama:health", style: "success" }
|
|
15519
|
+
],
|
|
15520
|
+
[
|
|
15521
|
+
{ label: "\u{1F4CB} Models", data: "ollama:models" },
|
|
15522
|
+
{ label: "\u2795 Add", data: "ollama:add_prompt" }
|
|
15523
|
+
]
|
|
15524
|
+
];
|
|
15525
|
+
if (servers.length <= 4) {
|
|
15526
|
+
const removeRow = servers.map((s) => ({
|
|
15527
|
+
label: `\u{1F5D1} ${s.name}`,
|
|
15528
|
+
data: `ollama:remove_confirm:${s.name}`,
|
|
15529
|
+
style: "danger"
|
|
15530
|
+
}));
|
|
15531
|
+
buttons.push(removeRow);
|
|
15532
|
+
}
|
|
15533
|
+
await channel.sendKeyboard(chatId, lines.join("\n"), buttons);
|
|
15534
|
+
} else {
|
|
15535
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
15464
15536
|
}
|
|
15465
15537
|
}
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15538
|
+
async function handleAdd(chatId, channel, name, host, port) {
|
|
15539
|
+
const { OllamaStore, OllamaClient, OllamaService } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15540
|
+
const existing = OllamaStore.getServer(name);
|
|
15541
|
+
if (existing) {
|
|
15542
|
+
await channel.sendText(chatId, `Server "${name}" already exists. Remove it first.`, { parseMode: "plain" });
|
|
15543
|
+
return;
|
|
15544
|
+
}
|
|
15545
|
+
const actualPort = port ?? 11434;
|
|
15546
|
+
await channel.sendText(chatId, `Adding ${name} (${host}:${actualPort})...`, { parseMode: "plain" });
|
|
15547
|
+
const online = await OllamaClient.ping(`http://${host}:${actualPort}`, { timeoutMs: 5e3 });
|
|
15548
|
+
const server = OllamaStore.addServer(name, host, actualPort, null);
|
|
15549
|
+
OllamaStore.updateServerStatus(server.id, online ? "online" : "offline");
|
|
15550
|
+
if (!online) {
|
|
15551
|
+
await channel.sendText(chatId, `\u26A0\uFE0F Server "${name}" added but not responding. Check connectivity.`, { parseMode: "plain" });
|
|
15552
|
+
return;
|
|
15553
|
+
}
|
|
15554
|
+
const models = await OllamaService.discoverModels(name);
|
|
15555
|
+
const lines = [
|
|
15556
|
+
`\u2705 Added "${name}" (${host}:${actualPort})`,
|
|
15557
|
+
"",
|
|
15558
|
+
`Found ${models.length} model(s):`
|
|
15559
|
+
];
|
|
15560
|
+
for (const m of models) {
|
|
15561
|
+
lines.push(` \u2022 ${m.name}${m.parameterSize ? ` (${m.parameterSize})` : ""}`);
|
|
15562
|
+
}
|
|
15563
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
15564
|
+
}
|
|
15565
|
+
async function sendModelList(chatId, channel, serverName) {
|
|
15566
|
+
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15567
|
+
let models;
|
|
15568
|
+
if (serverName) {
|
|
15569
|
+
const server = OllamaStore.getServer(serverName);
|
|
15570
|
+
if (!server) {
|
|
15571
|
+
await channel.sendText(chatId, `Server "${serverName}" not found.`, { parseMode: "plain" });
|
|
15572
|
+
return;
|
|
15573
|
+
}
|
|
15574
|
+
models = OllamaStore.listModels(server.id);
|
|
15575
|
+
} else {
|
|
15576
|
+
models = OllamaStore.getAvailableModels();
|
|
15577
|
+
}
|
|
15578
|
+
if (models.length === 0) {
|
|
15579
|
+
await channel.sendText(chatId, "No models discovered. Run /ollama discover first.", { parseMode: "plain" });
|
|
15580
|
+
return;
|
|
15581
|
+
}
|
|
15582
|
+
const lines = [
|
|
15583
|
+
`<b>\u{1F9E0} Ollama Models</b>${serverName ? ` (${serverName})` : ""}`,
|
|
15584
|
+
""
|
|
15585
|
+
];
|
|
15586
|
+
for (const m of models) {
|
|
15587
|
+
const sizeGB = m.sizeBytes > 0 ? `${(m.sizeBytes / 1e9).toFixed(1)}GB` : "";
|
|
15588
|
+
const ctxK = m.contextWindow ? `${Math.round(m.contextWindow / 1e3)}K ctx` : "";
|
|
15589
|
+
const meta = [m.parameterSize, m.quantization, sizeGB, ctxK].filter(Boolean).join(" \xB7 ");
|
|
15590
|
+
lines.push(` \u2022 <b>${m.name}</b>`);
|
|
15591
|
+
if (meta) lines.push(` <i>${meta}</i>`);
|
|
15592
|
+
}
|
|
15593
|
+
lines.push("", `${models.length} model${models.length !== 1 ? "s" : ""} total`);
|
|
15594
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
15595
|
+
}
|
|
15596
|
+
async function sendDiscover(chatId, channel, serverName) {
|
|
15597
|
+
await channel.sendText(chatId, "\u{1F50D} Discovering models...", { parseMode: "plain" });
|
|
15598
|
+
try {
|
|
15599
|
+
const { OllamaService } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15600
|
+
const models = await OllamaService.discoverModels(serverName);
|
|
15601
|
+
if (models.length === 0) {
|
|
15602
|
+
await channel.sendText(chatId, "No models found. Check server connectivity.", { parseMode: "plain" });
|
|
15603
|
+
return;
|
|
15604
|
+
}
|
|
15605
|
+
const lines = [`\u2705 Found ${models.length} model(s):`, ""];
|
|
15606
|
+
for (const m of models) {
|
|
15607
|
+
const meta = [m.parameterSize, m.contextWindow ? `${Math.round(m.contextWindow / 1e3)}K ctx` : ""].filter(Boolean).join(" \xB7 ");
|
|
15608
|
+
lines.push(` \u2022 ${m.name}${meta ? ` (${meta})` : ""}`);
|
|
15609
|
+
}
|
|
15610
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
15611
|
+
} catch (err) {
|
|
15612
|
+
await channel.sendText(chatId, `Discovery failed: ${err instanceof Error ? err.message : String(err)}`, { parseMode: "plain" });
|
|
15613
|
+
}
|
|
15614
|
+
}
|
|
15615
|
+
async function sendHealthCheck(chatId, channel) {
|
|
15616
|
+
await channel.sendText(chatId, "\u{1F49A} Pinging servers...", { parseMode: "plain" });
|
|
15617
|
+
try {
|
|
15618
|
+
const { OllamaService } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15619
|
+
const results = await OllamaService.healthCheck();
|
|
15620
|
+
if (results.length === 0) {
|
|
15621
|
+
await channel.sendText(chatId, "No servers configured.", { parseMode: "plain" });
|
|
15622
|
+
return;
|
|
15623
|
+
}
|
|
15624
|
+
const lines = ["Health Check Results:", ""];
|
|
15625
|
+
for (const s of results) {
|
|
15626
|
+
const dot = s.status === "online" ? "\u{1F7E2}" : "\u{1F534}";
|
|
15627
|
+
lines.push(`${dot} ${s.name} (${s.host}:${s.port}) \u2014 ${s.status}`);
|
|
15628
|
+
}
|
|
15629
|
+
const online = results.filter((s) => s.status === "online").length;
|
|
15630
|
+
lines.push("", `${online}/${results.length} online`);
|
|
15631
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
15632
|
+
} catch (err) {
|
|
15633
|
+
await channel.sendText(chatId, `Health check failed: ${err instanceof Error ? err.message : String(err)}`, { parseMode: "plain" });
|
|
15634
|
+
}
|
|
15635
|
+
}
|
|
15636
|
+
async function sendRemoveConfirm(chatId, channel, name) {
|
|
15637
|
+
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15638
|
+
const server = OllamaStore.getServer(name);
|
|
15639
|
+
if (!server) {
|
|
15640
|
+
await channel.sendText(chatId, `Server "${name}" not found.`, { parseMode: "plain" });
|
|
15641
|
+
return;
|
|
15642
|
+
}
|
|
15643
|
+
const modelCount = OllamaStore.listModels(server.id).length;
|
|
15644
|
+
const text = [
|
|
15645
|
+
`Remove server <b>${name}</b>?`,
|
|
15646
|
+
"",
|
|
15647
|
+
`Host: ${server.host}:${server.port}`,
|
|
15648
|
+
`Status: ${server.status}`,
|
|
15649
|
+
`Models: ${modelCount}`,
|
|
15650
|
+
"",
|
|
15651
|
+
"This will also remove all cached model data."
|
|
15652
|
+
].join("\n");
|
|
15653
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
15654
|
+
await channel.sendKeyboard(chatId, text, [
|
|
15655
|
+
[
|
|
15656
|
+
{ label: "\u2705 Confirm", data: `ollama:remove:${name}`, style: "danger" },
|
|
15657
|
+
{ label: "\u274C Cancel", data: "ollama:dashboard" }
|
|
15658
|
+
]
|
|
15659
|
+
]);
|
|
15660
|
+
} else {
|
|
15661
|
+
await channel.sendText(chatId, text + "\n\nUse /ollama remove <name> to confirm.", { parseMode: "html" });
|
|
15662
|
+
}
|
|
15663
|
+
}
|
|
15664
|
+
async function handleOllamaCallback(chatId, data, channel) {
|
|
15665
|
+
const parts = data.split(":");
|
|
15666
|
+
const action = parts[1];
|
|
15667
|
+
switch (action) {
|
|
15668
|
+
case "dashboard":
|
|
15669
|
+
return sendOllamaDashboard(chatId, channel);
|
|
15670
|
+
case "models":
|
|
15671
|
+
return sendModelList(chatId, channel);
|
|
15672
|
+
case "discover":
|
|
15673
|
+
return sendDiscover(chatId, channel);
|
|
15674
|
+
case "health":
|
|
15675
|
+
return sendHealthCheck(chatId, channel);
|
|
15676
|
+
case "remove_confirm": {
|
|
15677
|
+
const name = parts.slice(2).join(":");
|
|
15678
|
+
return sendRemoveConfirm(chatId, channel, name);
|
|
15679
|
+
}
|
|
15680
|
+
case "remove": {
|
|
15681
|
+
const name = parts.slice(2).join(":");
|
|
15682
|
+
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15683
|
+
const removed = OllamaStore.removeServer(name);
|
|
15684
|
+
if (removed) {
|
|
15685
|
+
await channel.sendText(chatId, `\u2705 Removed server "${name}"`, { parseMode: "plain" });
|
|
15686
|
+
} else {
|
|
15687
|
+
await channel.sendText(chatId, `Server "${name}" not found.`, { parseMode: "plain" });
|
|
15688
|
+
}
|
|
15689
|
+
return sendOllamaDashboard(chatId, channel);
|
|
15690
|
+
}
|
|
15691
|
+
case "add_prompt":
|
|
15692
|
+
return startAddWizard(chatId, channel);
|
|
15693
|
+
case "wizard_port": {
|
|
15694
|
+
const pending = pendingAdds.get(chatId);
|
|
15695
|
+
if (!pending) return;
|
|
15696
|
+
pending.port = parseInt(parts[2], 10) || 11434;
|
|
15697
|
+
pending.step = "confirm";
|
|
15698
|
+
await channel.sendText(chatId, `Port: ${pending.port}`, { parseMode: "plain" });
|
|
15699
|
+
return promptAddConfirm(chatId, channel);
|
|
15700
|
+
}
|
|
15701
|
+
case "wizard_confirm":
|
|
15702
|
+
return finalizeAddWizard(chatId, channel);
|
|
15703
|
+
case "wizard_restart":
|
|
15704
|
+
return startAddWizard(chatId, channel);
|
|
15705
|
+
case "wizard_cancel":
|
|
15706
|
+
cancelOllamaWizard(chatId);
|
|
15707
|
+
await channel.sendText(chatId, "Server setup cancelled.", { parseMode: "plain" });
|
|
15708
|
+
return;
|
|
15709
|
+
case "help":
|
|
15710
|
+
await channel.sendText(chatId, [
|
|
15711
|
+
"\u{1F999} Ollama Commands:",
|
|
15712
|
+
"",
|
|
15713
|
+
"/ollama \u2014 Server dashboard",
|
|
15714
|
+
"/ollama add <name> <host> [port] \u2014 Add server",
|
|
15715
|
+
"/ollama remove <name> \u2014 Remove server",
|
|
15716
|
+
"/ollama models [server] \u2014 List models",
|
|
15717
|
+
"/ollama discover [server] \u2014 Discover models",
|
|
15718
|
+
"/ollama health \u2014 Ping all servers"
|
|
15719
|
+
].join("\n"), { parseMode: "plain" });
|
|
15720
|
+
return;
|
|
15721
|
+
default:
|
|
15722
|
+
await channel.sendText(chatId, "Unknown action.", { parseMode: "plain" });
|
|
15723
|
+
}
|
|
15724
|
+
}
|
|
15725
|
+
var pendingAdds, wizardTimers2, WIZARD_TIMEOUT_MS2;
|
|
15726
|
+
var init_ollama3 = __esm({
|
|
15727
|
+
"src/router/ollama.ts"() {
|
|
15728
|
+
"use strict";
|
|
15729
|
+
init_log();
|
|
15730
|
+
pendingAdds = /* @__PURE__ */ new Map();
|
|
15731
|
+
wizardTimers2 = /* @__PURE__ */ new Map();
|
|
15732
|
+
WIZARD_TIMEOUT_MS2 = 5 * 60 * 1e3;
|
|
15733
|
+
}
|
|
15734
|
+
});
|
|
15735
|
+
|
|
15736
|
+
// src/agents/classify.ts
|
|
15737
|
+
function classifyAgentIntent(message) {
|
|
15738
|
+
const NOT_DETECTED = { detected: false, suggestedMode: "native" };
|
|
15739
|
+
for (const pat of NEGATIVE_PATTERNS) {
|
|
15740
|
+
if (pat.test(message)) return NOT_DETECTED;
|
|
15741
|
+
}
|
|
15742
|
+
for (const pat of CLAW_SIGNAL_PATTERNS) {
|
|
15743
|
+
if (pat.test(message)) {
|
|
15744
|
+
return { detected: true, suggestedMode: "claw", reason: "cross-backend or inter-agent communication detected" };
|
|
15745
|
+
}
|
|
15746
|
+
}
|
|
15747
|
+
for (const pat of AGENT_SIGNAL_PATTERNS) {
|
|
15748
|
+
if (pat.test(message)) {
|
|
15749
|
+
return { detected: true, suggestedMode: "native" };
|
|
15750
|
+
}
|
|
15751
|
+
}
|
|
15752
|
+
return NOT_DETECTED;
|
|
15753
|
+
}
|
|
15754
|
+
var AGENT_SIGNAL_PATTERNS, CLAW_SIGNAL_PATTERNS, NEGATIVE_PATTERNS;
|
|
15755
|
+
var init_classify2 = __esm({
|
|
15756
|
+
"src/agents/classify.ts"() {
|
|
15757
|
+
"use strict";
|
|
15758
|
+
AGENT_SIGNAL_PATTERNS = [
|
|
15759
|
+
/\bspawn\s+(?:\d+\s+)?agents?\b/i,
|
|
15760
|
+
/\bsub-?agents?\b/i,
|
|
15761
|
+
/\bparallel\s+agents?\b/i,
|
|
15762
|
+
/\bfan\s+out\b/i,
|
|
15763
|
+
/\bdispatch\s+agents?\b/i,
|
|
15764
|
+
/\bmulti-?agent\b/i
|
|
15765
|
+
];
|
|
15766
|
+
CLAW_SIGNAL_PATTERNS = [
|
|
15767
|
+
/\breview\s+each\s+other/i,
|
|
15768
|
+
/\bdiscuss\s+between\b/i,
|
|
15769
|
+
/\bcross-?backend\b/i,
|
|
15770
|
+
/\bagent\s+team\b/i,
|
|
15771
|
+
/\bagent\s+inbox\b/i,
|
|
15772
|
+
/\bagent\s+whiteboard\b/i,
|
|
15773
|
+
/\bagents?\s+.*debate\b/i,
|
|
15774
|
+
/\bclaude\b.*\bagents?\b.*\bgemini\b/i,
|
|
15775
|
+
/\bgemini\b.*\bagents?\b.*\bclaude\b/i,
|
|
15776
|
+
/\bcodex\b.*\bagents?\b.*\bclaude\b/i,
|
|
15777
|
+
/\bclaude\b.*\bagents?\b.*\bcodex\b/i,
|
|
15778
|
+
/\bgemini\b.*\bagents?\b.*\bcodex\b/i,
|
|
15779
|
+
/\bcodex\b.*\bagents?\b.*\bgemini\b/i
|
|
15780
|
+
];
|
|
15781
|
+
NEGATIVE_PATTERNS = [
|
|
15782
|
+
/\bhow\s+does\s+.*\bwork\b/i,
|
|
15783
|
+
/\bwhat\s+is\s+.*\bfunction\b/i,
|
|
15784
|
+
/\bexplain\s+.*\bagent/i,
|
|
15785
|
+
/src\/agents?\//
|
|
15786
|
+
];
|
|
15787
|
+
}
|
|
15788
|
+
});
|
|
15789
|
+
|
|
15790
|
+
// src/router/session-log.ts
|
|
15791
|
+
var session_log_exports2 = {};
|
|
15792
|
+
__export(session_log_exports2, {
|
|
15793
|
+
SessionLogFile: () => SessionLogFile,
|
|
15794
|
+
cleanupSessionLogs: () => cleanupSessionLogs,
|
|
15795
|
+
getRetentionDays: () => getRetentionDays,
|
|
15796
|
+
listSessionLogs: () => listSessionLogs,
|
|
15797
|
+
startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
|
|
15798
|
+
tailSessionLog: () => tailSessionLog
|
|
15799
|
+
});
|
|
15800
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync6, createReadStream } from "fs";
|
|
15801
|
+
import { join as join15, basename } from "path";
|
|
15802
|
+
import { createInterface as createInterface6 } from "readline";
|
|
15803
|
+
function getRetentionDays() {
|
|
15804
|
+
const env = process.env.SESSION_LOG_RETENTION_DAYS;
|
|
15805
|
+
if (env) {
|
|
15806
|
+
const n = parseInt(env, 10);
|
|
15807
|
+
if (!isNaN(n) && n > 0) return n;
|
|
15808
|
+
}
|
|
15809
|
+
return DEFAULT_RETENTION_DAYS;
|
|
15810
|
+
}
|
|
15811
|
+
function cleanupSessionLogs(retentionDays) {
|
|
15812
|
+
const days = retentionDays ?? getRetentionDays();
|
|
15813
|
+
if (!existsSync15(SESSION_LOGS_PATH)) return 0;
|
|
15814
|
+
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
15815
|
+
let cleaned = 0;
|
|
15816
|
+
try {
|
|
15817
|
+
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
15818
|
+
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
15819
|
+
const filePath = join15(SESSION_LOGS_PATH, file);
|
|
15820
|
+
try {
|
|
15821
|
+
const { mtimeMs } = statSync6(filePath);
|
|
15822
|
+
if (mtimeMs < cutoff) {
|
|
15823
|
+
unlinkSync6(filePath);
|
|
15824
|
+
cleaned++;
|
|
15825
|
+
}
|
|
15826
|
+
} catch {
|
|
15827
|
+
}
|
|
15828
|
+
}
|
|
15829
|
+
} catch (err) {
|
|
15830
|
+
log(`[session-log] Cleanup failed: ${err}`);
|
|
15831
|
+
}
|
|
15832
|
+
if (cleaned > 0) {
|
|
15833
|
+
log(`[session-log] Cleaned ${cleaned} session log(s) older than ${days} day(s)`);
|
|
15834
|
+
}
|
|
15835
|
+
return cleaned;
|
|
15836
|
+
}
|
|
15837
|
+
function startSessionLogCleanupTimer() {
|
|
15838
|
+
const timer = setInterval(() => {
|
|
15839
|
+
cleanupSessionLogs();
|
|
15840
|
+
}, 24 * 60 * 60 * 1e3);
|
|
15841
|
+
timer.unref();
|
|
15842
|
+
return timer;
|
|
15843
|
+
}
|
|
15844
|
+
function listSessionLogs() {
|
|
15845
|
+
if (!existsSync15(SESSION_LOGS_PATH)) return [];
|
|
15846
|
+
const logs = [];
|
|
15847
|
+
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
15848
|
+
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
15849
|
+
const filePath = join15(SESSION_LOGS_PATH, file);
|
|
15850
|
+
try {
|
|
15851
|
+
const stat3 = statSync6(filePath);
|
|
15852
|
+
const match = file.match(/^session-(.+?)-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})\.log$/);
|
|
15853
|
+
logs.push({
|
|
15854
|
+
filename: file,
|
|
15855
|
+
filePath,
|
|
15856
|
+
chatId: match?.[1] ?? "unknown",
|
|
15857
|
+
timestamp: match?.[2]?.replace(/-/g, (m, i) => i > 9 ? ":" : m) ?? "unknown",
|
|
15858
|
+
sizeBytes: stat3.size,
|
|
15859
|
+
modifiedAt: stat3.mtime
|
|
15860
|
+
});
|
|
15861
|
+
} catch {
|
|
15862
|
+
}
|
|
15863
|
+
}
|
|
15864
|
+
logs.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
15865
|
+
return logs;
|
|
15866
|
+
}
|
|
15867
|
+
async function* tailSessionLog(filePath, lines = 50) {
|
|
15868
|
+
if (!existsSync15(filePath)) {
|
|
15869
|
+
yield `File not found: ${filePath}`;
|
|
15870
|
+
return;
|
|
15871
|
+
}
|
|
15872
|
+
const allLines = [];
|
|
15873
|
+
const rl2 = createInterface6({
|
|
15874
|
+
input: createReadStream(filePath, { encoding: "utf-8" }),
|
|
15875
|
+
crlfDelay: Infinity
|
|
15876
|
+
});
|
|
15877
|
+
for await (const line of rl2) {
|
|
15878
|
+
allLines.push(line);
|
|
15879
|
+
}
|
|
15880
|
+
const start = Math.max(0, allLines.length - lines);
|
|
15881
|
+
for (let i = start; i < allLines.length; i++) {
|
|
15882
|
+
yield allLines[i];
|
|
15883
|
+
}
|
|
15884
|
+
}
|
|
15885
|
+
var DEFAULT_RETENTION_DAYS, SessionLogFile;
|
|
15886
|
+
var init_session_log2 = __esm({
|
|
15887
|
+
"src/router/session-log.ts"() {
|
|
15888
|
+
"use strict";
|
|
15889
|
+
init_paths();
|
|
15471
15890
|
init_log();
|
|
15472
15891
|
DEFAULT_RETENTION_DAYS = 7;
|
|
15473
15892
|
SessionLogFile = class {
|
|
@@ -18060,7 +18479,7 @@ Status: ${config2.enabled ? "ON" : "OFF"}`;
|
|
|
18060
18479
|
}
|
|
18061
18480
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
18062
18481
|
}
|
|
18063
|
-
async function sendSkillsPage(chatId, channel, skills2, page) {
|
|
18482
|
+
async function sendSkillsPage(chatId, channel, skills2, page, messageId) {
|
|
18064
18483
|
const totalPages = Math.ceil(skills2.length / SKILLS_PER_PAGE);
|
|
18065
18484
|
const safePage = Math.max(1, Math.min(page, totalPages));
|
|
18066
18485
|
const start = (safePage - 1) * SKILLS_PER_PAGE;
|
|
@@ -18089,9 +18508,9 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
|
|
|
18089
18508
|
buttons.push(navRow);
|
|
18090
18509
|
}
|
|
18091
18510
|
const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
|
|
18092
|
-
await
|
|
18511
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
18093
18512
|
}
|
|
18094
|
-
async function sendMemoryPage(chatId, channel, page) {
|
|
18513
|
+
async function sendMemoryPage(chatId, channel, page, messageId) {
|
|
18095
18514
|
const memories = listMemories();
|
|
18096
18515
|
if (memories.length === 0) {
|
|
18097
18516
|
await channel.sendText(chatId, "No memories stored yet.", { parseMode: "plain" });
|
|
@@ -18133,7 +18552,7 @@ async function sendMemoryPage(chatId, channel, page) {
|
|
|
18133
18552
|
}
|
|
18134
18553
|
footerRow.push({ label: "Search: /memory <query>", data: "mem:page:noop" });
|
|
18135
18554
|
buttons.push(footerRow);
|
|
18136
|
-
await
|
|
18555
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
18137
18556
|
}
|
|
18138
18557
|
async function sendHeartbeatKeyboard(chatId, channel) {
|
|
18139
18558
|
const config2 = getHeartbeatConfig(chatId);
|
|
@@ -18167,7 +18586,7 @@ async function sendHeartbeatKeyboard(chatId, channel) {
|
|
|
18167
18586
|
[{ label: "Active Hours: /heartbeat hours <start>-<end>", data: "hb:noop" }]
|
|
18168
18587
|
]);
|
|
18169
18588
|
}
|
|
18170
|
-
async function sendForgetPicker(chatId, channel, page) {
|
|
18589
|
+
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
18171
18590
|
const memories = listMemories();
|
|
18172
18591
|
if (memories.length === 0) {
|
|
18173
18592
|
await channel.sendText(chatId, "No memories to forget.", { parseMode: "plain" });
|
|
@@ -18185,7 +18604,7 @@ async function sendForgetPicker(chatId, channel, page) {
|
|
|
18185
18604
|
headerText: (p, tp, total) => `Which memory to forget? (${total} total${tp > 1 ? `, page ${p}/${tp}` : ""})`,
|
|
18186
18605
|
footerButtons: [{ label: "Cancel", data: "forget:cancel" }]
|
|
18187
18606
|
});
|
|
18188
|
-
await
|
|
18607
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
18189
18608
|
}
|
|
18190
18609
|
function getJobScheduleText(job) {
|
|
18191
18610
|
if (job.cron) return humanizeCron(job.cron);
|
|
@@ -18203,7 +18622,7 @@ function getJobStatusLabel(job) {
|
|
|
18203
18622
|
if (!job.enabled) return "paused";
|
|
18204
18623
|
return "active";
|
|
18205
18624
|
}
|
|
18206
|
-
async function sendJobsBoard(chatId, channel, page) {
|
|
18625
|
+
async function sendJobsBoard(chatId, channel, page, messageId) {
|
|
18207
18626
|
const jobs = listJobs();
|
|
18208
18627
|
if (jobs.length === 0) {
|
|
18209
18628
|
await channel.sendText(chatId, "No scheduled jobs.\n\nCreate one: /schedule <description>", { parseMode: "plain" });
|
|
@@ -18245,9 +18664,9 @@ async function sendJobsBoard(chatId, channel, page) {
|
|
|
18245
18664
|
},
|
|
18246
18665
|
footerButtons: []
|
|
18247
18666
|
});
|
|
18248
|
-
await
|
|
18667
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
18249
18668
|
}
|
|
18250
|
-
async function sendJobDetail(chatId, jobId, channel) {
|
|
18669
|
+
async function sendJobDetail(chatId, jobId, channel, messageId) {
|
|
18251
18670
|
const job = getJobById(jobId);
|
|
18252
18671
|
if (!job) {
|
|
18253
18672
|
if (typeof channel.sendKeyboard === "function") {
|
|
@@ -18301,9 +18720,9 @@ async function sendJobDetail(chatId, jobId, channel) {
|
|
|
18301
18720
|
actionRow2.push({ label: "Cancel Job", data: `job:cancel:${job.id}`, style: "danger" });
|
|
18302
18721
|
}
|
|
18303
18722
|
const buttons = [actionRow1, actionRow2, [{ label: "\u2190 Back to List", data: "job:back" }]];
|
|
18304
|
-
await
|
|
18723
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
18305
18724
|
}
|
|
18306
|
-
async function sendJobRunsView(chatId, jobId, channel, page) {
|
|
18725
|
+
async function sendJobRunsView(chatId, jobId, channel, page, messageId) {
|
|
18307
18726
|
const runs = getJobRuns(jobId, 50);
|
|
18308
18727
|
if (runs.length === 0) {
|
|
18309
18728
|
const msg = `No runs for job #${jobId}.`;
|
|
@@ -18363,7 +18782,7 @@ async function sendJobRunsView(chatId, jobId, channel, page) {
|
|
|
18363
18782
|
},
|
|
18364
18783
|
footerButtons: [{ label: "\u2190 Back to Job", data: `job:view:${jobId}` }]
|
|
18365
18784
|
});
|
|
18366
|
-
await
|
|
18785
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
18367
18786
|
}
|
|
18368
18787
|
async function sendJobPicker(chatId, channel, action) {
|
|
18369
18788
|
const allJobs = listJobs();
|
|
@@ -18807,13 +19226,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
18807
19226
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
18808
19227
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
18809
19228
|
if (current === "frozen") {
|
|
18810
|
-
const { readFileSync:
|
|
19229
|
+
const { readFileSync: readFileSync28, existsSync: existsSync56 } = await import("fs");
|
|
18811
19230
|
const { join: join35 } = await import("path");
|
|
18812
19231
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
18813
19232
|
const soulPath = join35(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
18814
19233
|
const userPath = join35(CC_CLAW_HOME3, "identity/USER.md");
|
|
18815
|
-
const soul = existsSync56(soulPath) ?
|
|
18816
|
-
const user = existsSync56(userPath) ?
|
|
19234
|
+
const soul = existsSync56(soulPath) ? readFileSync28(soulPath, "utf-8") : "";
|
|
19235
|
+
const user = existsSync56(userPath) ? readFileSync28(userPath, "utf-8") : "";
|
|
18817
19236
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
18818
19237
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
18819
19238
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -20907,1081 +21326,809 @@ async function handleMemoryCommand(chatId, commandArgs, msg, channel) {
|
|
|
20907
21326
|
await channel.sendText(chatId, `Memory #${editId} updated.`, { parseMode: "plain" });
|
|
20908
21327
|
return;
|
|
20909
21328
|
}
|
|
20910
|
-
const memories = listMemories();
|
|
20911
|
-
if (memories.length === 0) {
|
|
20912
|
-
await channel.sendText(chatId, "No memories stored yet.", { parseMode: "plain" });
|
|
20913
|
-
return;
|
|
20914
|
-
}
|
|
20915
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
20916
|
-
await sendMemoryPage(chatId, channel, 1);
|
|
20917
|
-
} else {
|
|
20918
|
-
const lines = memories.map(
|
|
20919
|
-
(m) => `- ${m.trigger}: ${m.content} (salience: ${m.salience.toFixed(2)})`
|
|
20920
|
-
);
|
|
20921
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
20922
|
-
}
|
|
20923
|
-
}
|
|
20924
|
-
async function handleImagineCommand(chatId, commandArgs, msg, channel) {
|
|
20925
|
-
if (!commandArgs) {
|
|
20926
|
-
await channel.sendText(chatId, "Usage: /imagine <prompt>\nExample: /imagine a cat astronaut on Mars", { parseMode: "plain" });
|
|
20927
|
-
return;
|
|
20928
|
-
}
|
|
20929
|
-
if (!isImageGenAvailable()) {
|
|
20930
|
-
await channel.sendText(chatId, "Image generation requires GEMINI_API_KEY. Configure it in ~/.cc-claw/.env", { parseMode: "plain" });
|
|
20931
|
-
return;
|
|
20932
|
-
}
|
|
20933
|
-
await channel.sendText(chatId, "\u{1F3A8} Generating image\u2026", { parseMode: "plain" });
|
|
20934
|
-
try {
|
|
20935
|
-
const result = await generateImage(commandArgs);
|
|
20936
|
-
const file = await readFile6(result.filePath);
|
|
20937
|
-
const name = result.filePath.split("/").pop() ?? "image.png";
|
|
20938
|
-
await channel.sendFile(chatId, file, name);
|
|
20939
|
-
if (result.text) {
|
|
20940
|
-
await channel.sendText(chatId, result.text, { parseMode: "plain" });
|
|
20941
|
-
}
|
|
20942
|
-
} catch (err) {
|
|
20943
|
-
await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, { parseMode: "plain" });
|
|
20944
|
-
}
|
|
20945
|
-
}
|
|
20946
|
-
async function handleRunsCommand(chatId, commandArgs, msg, channel) {
|
|
20947
|
-
const runsJobId = commandArgs ? parseInt(commandArgs, 10) : void 0;
|
|
20948
|
-
if (runsJobId) {
|
|
20949
|
-
await sendJobRunsView(chatId, runsJobId, channel, 1);
|
|
20950
|
-
} else {
|
|
20951
|
-
const runs = getJobRuns(void 0, 10);
|
|
20952
|
-
if (runs.length === 0) {
|
|
20953
|
-
await channel.sendText(chatId, "No run history yet.", { parseMode: "plain" });
|
|
20954
|
-
return;
|
|
20955
|
-
}
|
|
20956
|
-
const lines = runs.map((r) => {
|
|
20957
|
-
const duration = r.durationMs ? ` (${(r.durationMs / 1e3).toFixed(1)}s)` : "";
|
|
20958
|
-
const error3 = r.error ? `
|
|
20959
|
-
Error: ${r.error.slice(0, 100)}` : "";
|
|
20960
|
-
const usage2 = r.usageInput ? `
|
|
20961
|
-
Tokens: ${r.usageInput}in / ${r.usageOutput}out` : "";
|
|
20962
|
-
return `#${r.jobId} [${r.status}] ${formatLocalDateTime(r.startedAt)}${duration}${error3}${usage2}`;
|
|
20963
|
-
});
|
|
20964
|
-
await channel.sendText(chatId, lines.join("\n\n"), { parseMode: "plain" });
|
|
20965
|
-
}
|
|
20966
|
-
}
|
|
20967
|
-
async function handleSkillsCommand(chatId, commandArgs, msg, channel) {
|
|
20968
|
-
const skills2 = await discoverAllSkills();
|
|
20969
|
-
if (skills2.length === 0) {
|
|
20970
|
-
await channel.sendText(chatId, "No skills found. Install skills with /skill-install <github-url> or place them in ~/.cc-claw/workspace/skills/", { parseMode: "plain" });
|
|
20971
|
-
return;
|
|
20972
|
-
}
|
|
20973
|
-
const page = commandArgs ? parseInt(commandArgs, 10) || 1 : 1;
|
|
20974
|
-
await sendSkillsPage(chatId, channel, skills2, page);
|
|
20975
|
-
}
|
|
20976
|
-
async function handleChatsCommand(chatId, commandArgs, msg, channel) {
|
|
20977
|
-
if (!commandArgs) {
|
|
20978
|
-
const aliases = getAllChatAliases();
|
|
20979
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
20980
|
-
if (aliases.length === 0) {
|
|
20981
|
-
await channel.sendKeyboard(
|
|
20982
|
-
chatId,
|
|
20983
|
-
"Authorized Chats\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nNo chat aliases configured yet.\nGroup chats are auto-discovered when the bot receives a message.",
|
|
20984
|
-
[[{ label: "Refresh", data: "chats:refresh", style: "primary" }]]
|
|
20985
|
-
);
|
|
20986
|
-
} else {
|
|
20987
|
-
const lines = aliases.map(
|
|
20988
|
-
(a) => `${a.alias.padEnd(12)} | ${a.chatId}${a.chatId === chatId ? " [Active]" : ""}`
|
|
20989
|
-
);
|
|
20990
|
-
const text = ["Authorized Chats", "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501", "", ...lines].join("\n");
|
|
20991
|
-
const buttons = [];
|
|
20992
|
-
const actionRow = [];
|
|
20993
|
-
if (aliases.length > 0) {
|
|
20994
|
-
actionRow.push({ label: "Remove", data: "chats:remove", style: "danger" });
|
|
20995
|
-
}
|
|
20996
|
-
actionRow.push({ label: "Refresh", data: "chats:refresh", style: "primary" });
|
|
20997
|
-
buttons.push(actionRow);
|
|
20998
|
-
await channel.sendKeyboard(chatId, text, buttons);
|
|
20999
|
-
}
|
|
21000
|
-
} else {
|
|
21001
|
-
if (aliases.length === 0) {
|
|
21002
|
-
await channel.sendText(chatId, "No chat aliases configured yet.\nUsage: /chats name <chat_id> <alias>\n\nGroup chats are auto-discovered when the bot receives a message from them.", { parseMode: "plain" });
|
|
21003
|
-
} else {
|
|
21004
|
-
const lines = ["Authorized chats:", "", ...aliases.map((a) => ` ${a.alias} -> ${a.chatId}`)];
|
|
21005
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
21006
|
-
}
|
|
21007
|
-
}
|
|
21008
|
-
return;
|
|
21009
|
-
}
|
|
21010
|
-
const parts = commandArgs.split(/\s+/);
|
|
21011
|
-
if (parts[0] === "name" && parts.length >= 3) {
|
|
21012
|
-
const targetChatId = parts[1];
|
|
21013
|
-
const alias = parts.slice(2).join("-").toLowerCase();
|
|
21014
|
-
setChatAlias(alias, targetChatId);
|
|
21015
|
-
await channel.sendText(chatId, `Alias set: ${alias} -> ${targetChatId}`, { parseMode: "plain" });
|
|
21016
|
-
} else if (parts[0] === "remove" && parts[1]) {
|
|
21017
|
-
const removed = removeChatAlias(parts[1]);
|
|
21018
|
-
await channel.sendText(chatId, removed ? `Alias "${parts[1]}" removed.` : `Alias "${parts[1]}" not found.`, { parseMode: "plain" });
|
|
21019
|
-
} else {
|
|
21020
|
-
await channel.sendText(chatId, "Usage:\n /chats \u2014 list all aliases\n /chats name <chat_id> <alias> \u2014 assign alias\n /chats remove <alias> \u2014 remove alias", { parseMode: "plain" });
|
|
21021
|
-
}
|
|
21022
|
-
}
|
|
21023
|
-
async function handleCwdCommand(chatId, commandArgs, msg, channel) {
|
|
21024
|
-
if (!commandArgs) {
|
|
21025
|
-
const current = getCwd(chatId);
|
|
21026
|
-
const recents = getRecentBookmarks(chatId, 10);
|
|
21027
|
-
if (recents.length === 0) {
|
|
21028
|
-
await channel.sendText(
|
|
21029
|
-
chatId,
|
|
21030
|
-
current ? `Working directory: ${current}
|
|
21031
|
-
|
|
21032
|
-
No saved bookmarks yet. Set a directory with /cwd <path> to auto-save.` : "No working directory set. Usage: /cwd ~/projects/my-app",
|
|
21033
|
-
{ parseMode: "plain" }
|
|
21034
|
-
);
|
|
21035
|
-
return;
|
|
21036
|
-
}
|
|
21037
|
-
const text = current ? `Current: ${current}
|
|
21038
|
-
|
|
21039
|
-
Recent directories:` : "Recent directories:";
|
|
21040
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21041
|
-
const buttons = recents.map((r) => [{ label: r.alias, data: `cwdpick:${r.alias}` }]);
|
|
21042
|
-
await channel.sendKeyboard(chatId, text, buttons);
|
|
21043
|
-
} else {
|
|
21044
|
-
const list = recents.map((r) => ` ${r.alias} \u2192 ${r.path}`).join("\n");
|
|
21045
|
-
await channel.sendText(chatId, `${text}
|
|
21046
|
-
${list}`, { parseMode: "plain" });
|
|
21047
|
-
}
|
|
21048
|
-
return;
|
|
21049
|
-
}
|
|
21050
|
-
if (commandArgs === "reset" || commandArgs === "clear") {
|
|
21051
|
-
clearCwd(chatId);
|
|
21052
|
-
await channel.sendText(chatId, "Working directory cleared. Using default.", { parseMode: "plain" });
|
|
21053
|
-
return;
|
|
21054
|
-
}
|
|
21055
|
-
if (commandArgs === "aliases") {
|
|
21056
|
-
const all = getAllBookmarks(chatId);
|
|
21057
|
-
if (all.length === 0) {
|
|
21058
|
-
await channel.sendText(chatId, "No bookmarks saved yet.", { parseMode: "plain" });
|
|
21059
|
-
return;
|
|
21060
|
-
}
|
|
21061
|
-
const lines = all.map((b) => ` ${b.manual ? "[manual]" : "[auto]"} ${b.alias} \u2192 ${b.path}`);
|
|
21062
|
-
await channel.sendText(chatId, `Directory bookmarks:
|
|
21063
|
-
${lines.join("\n")}`, { parseMode: "plain" });
|
|
21064
|
-
return;
|
|
21065
|
-
}
|
|
21066
|
-
if (commandArgs.startsWith("unalias ")) {
|
|
21067
|
-
const aliasName = commandArgs.slice(8).trim();
|
|
21068
|
-
if (!aliasName) {
|
|
21069
|
-
await channel.sendText(chatId, "Usage: /cwd unalias <name>", { parseMode: "plain" });
|
|
21070
|
-
return;
|
|
21071
|
-
}
|
|
21072
|
-
const deleted = deleteBookmark(chatId, aliasName);
|
|
21073
|
-
await channel.sendText(chatId, deleted ? `Bookmark '${aliasName}' removed.` : `Bookmark '${aliasName}' not found.`, { parseMode: "plain" });
|
|
21074
|
-
return;
|
|
21075
|
-
}
|
|
21076
|
-
if (commandArgs.startsWith("alias ")) {
|
|
21077
|
-
const parts = commandArgs.slice(6).trim().split(/\s+/);
|
|
21078
|
-
if (parts.length < 2) {
|
|
21079
|
-
await channel.sendText(chatId, "Usage: /cwd alias <name> <path>", { parseMode: "plain" });
|
|
21080
|
-
return;
|
|
21081
|
-
}
|
|
21082
|
-
const [aliasName, ...pathParts] = parts;
|
|
21083
|
-
const aliasPath = pathParts.join(" ").replace(/^~/, process.env.HOME ?? "");
|
|
21084
|
-
upsertBookmark(chatId, aliasName, aliasPath, true);
|
|
21085
|
-
await channel.sendText(chatId, `Bookmark saved: ${aliasName} \u2192 ${aliasPath}`, { parseMode: "plain" });
|
|
21086
|
-
return;
|
|
21087
|
-
}
|
|
21088
|
-
const arg = commandArgs;
|
|
21089
|
-
if (arg.startsWith("/") || arg.startsWith("~")) {
|
|
21090
|
-
const resolvedPath = arg.startsWith("~") ? arg.replace("~", process.env.HOME ?? "") : arg;
|
|
21091
|
-
setCwd(chatId, resolvedPath);
|
|
21092
|
-
const basename4 = resolvedPath.split("/").filter(Boolean).pop();
|
|
21093
|
-
if (basename4) upsertBookmark(chatId, basename4, resolvedPath, false);
|
|
21094
|
-
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${resolvedPath}`, detail: { field: "cwd", value: resolvedPath } });
|
|
21095
|
-
await sendCwdSessionChoice(chatId, resolvedPath, channel);
|
|
21096
|
-
return;
|
|
21097
|
-
}
|
|
21098
|
-
const exact = getBookmark(chatId, arg);
|
|
21099
|
-
if (exact) {
|
|
21100
|
-
setCwd(chatId, exact.path);
|
|
21101
|
-
touchBookmark(chatId, arg);
|
|
21102
|
-
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${exact.path}`, detail: { field: "cwd", value: exact.path } });
|
|
21103
|
-
await sendCwdSessionChoice(chatId, exact.path, channel);
|
|
21329
|
+
const memories = listMemories();
|
|
21330
|
+
if (memories.length === 0) {
|
|
21331
|
+
await channel.sendText(chatId, "No memories stored yet.", { parseMode: "plain" });
|
|
21104
21332
|
return;
|
|
21105
21333
|
}
|
|
21106
|
-
|
|
21107
|
-
|
|
21108
|
-
|
|
21109
|
-
|
|
21110
|
-
|
|
21111
|
-
|
|
21334
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21335
|
+
await sendMemoryPage(chatId, channel, 1);
|
|
21336
|
+
} else {
|
|
21337
|
+
const lines = memories.map(
|
|
21338
|
+
(m) => `- ${m.trigger}: ${m.content} (salience: ${m.salience.toFixed(2)})`
|
|
21339
|
+
);
|
|
21340
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
21341
|
+
}
|
|
21342
|
+
}
|
|
21343
|
+
async function handleImagineCommand(chatId, commandArgs, msg, channel) {
|
|
21344
|
+
if (!commandArgs) {
|
|
21345
|
+
await channel.sendText(chatId, "Usage: /imagine <prompt>\nExample: /imagine a cat astronaut on Mars", { parseMode: "plain" });
|
|
21112
21346
|
return;
|
|
21113
21347
|
}
|
|
21114
|
-
if (
|
|
21115
|
-
|
|
21116
|
-
await channel.sendKeyboard(chatId, `Multiple matches for "${arg}":`, buttons);
|
|
21348
|
+
if (!isImageGenAvailable()) {
|
|
21349
|
+
await channel.sendText(chatId, "Image generation requires GEMINI_API_KEY. Configure it in ~/.cc-claw/.env", { parseMode: "plain" });
|
|
21117
21350
|
return;
|
|
21118
21351
|
}
|
|
21119
|
-
await channel.sendText(chatId,
|
|
21120
|
-
}
|
|
21121
|
-
async function handleSummarizerCommand(chatId, commandArgs, msg, channel) {
|
|
21122
|
-
let adapter;
|
|
21352
|
+
await channel.sendText(chatId, "\u{1F3A8} Generating image\u2026", { parseMode: "plain" });
|
|
21123
21353
|
try {
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21129
|
-
|
|
21130
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21131
|
-
const isAuto = !current.backend && current.backend !== "off";
|
|
21132
|
-
const isOff = current.backend === "off";
|
|
21133
|
-
const buttons = [
|
|
21134
|
-
[{ label: `Auto (use active backend)${isAuto ? " \u2713" : ""}`, data: "summarizer:auto", ...isAuto ? { style: "primary" } : {} }],
|
|
21135
|
-
[{ label: `Off (disable)${isOff ? " \u2713" : ""}`, data: "summarizer:off", ...isOff ? { style: "primary" } : {} }]
|
|
21136
|
-
];
|
|
21137
|
-
for (const a of getAvailableAdapters()) {
|
|
21138
|
-
const pinned = current.backend === a.id;
|
|
21139
|
-
buttons.push([{
|
|
21140
|
-
label: `${pinned ? "\u2713 " : ""}${a.displayName}: ${a.summarizerModel}`,
|
|
21141
|
-
data: `summarizer:${a.id}:${a.summarizerModel}`,
|
|
21142
|
-
...pinned ? { style: "primary" } : {}
|
|
21143
|
-
}]);
|
|
21354
|
+
const result = await generateImage(commandArgs);
|
|
21355
|
+
const file = await readFile6(result.filePath);
|
|
21356
|
+
const name = result.filePath.split("/").pop() ?? "image.png";
|
|
21357
|
+
await channel.sendFile(chatId, file, name);
|
|
21358
|
+
if (result.text) {
|
|
21359
|
+
await channel.sendText(chatId, result.text, { parseMode: "plain" });
|
|
21144
21360
|
}
|
|
21145
|
-
|
|
21146
|
-
|
|
21147
|
-
await channel.sendText(chatId, `Summarizer: ${currentLabel}
|
|
21148
|
-
|
|
21149
|
-
Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, { parseMode: "plain" });
|
|
21361
|
+
} catch (err) {
|
|
21362
|
+
await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, { parseMode: "plain" });
|
|
21150
21363
|
}
|
|
21151
21364
|
}
|
|
21152
|
-
async function
|
|
21153
|
-
const
|
|
21154
|
-
if (
|
|
21155
|
-
await
|
|
21156
|
-
return;
|
|
21157
|
-
}
|
|
21158
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21159
|
-
const rows = buildAccountSlotKeyboard(
|
|
21160
|
-
slots,
|
|
21161
|
-
getChatGeminiSlotId(chatId),
|
|
21162
|
-
getGeminiRotationMode(),
|
|
21163
|
-
"gslot:",
|
|
21164
|
-
"grotation:"
|
|
21165
|
-
);
|
|
21166
|
-
await channel.sendKeyboard(chatId, "Gemini Accounts & Rotation:", rows);
|
|
21365
|
+
async function handleRunsCommand(chatId, commandArgs, msg, channel) {
|
|
21366
|
+
const runsJobId = commandArgs ? parseInt(commandArgs, 10) : void 0;
|
|
21367
|
+
if (runsJobId) {
|
|
21368
|
+
await sendJobRunsView(chatId, runsJobId, channel, 1);
|
|
21167
21369
|
} else {
|
|
21168
|
-
const
|
|
21169
|
-
|
|
21170
|
-
|
|
21171
|
-
return
|
|
21172
|
-
}
|
|
21173
|
-
|
|
21174
|
-
${
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21370
|
+
const runs = getJobRuns(void 0, 10);
|
|
21371
|
+
if (runs.length === 0) {
|
|
21372
|
+
await channel.sendText(chatId, "No run history yet.", { parseMode: "plain" });
|
|
21373
|
+
return;
|
|
21374
|
+
}
|
|
21375
|
+
const lines = runs.map((r) => {
|
|
21376
|
+
const duration = r.durationMs ? ` (${(r.durationMs / 1e3).toFixed(1)}s)` : "";
|
|
21377
|
+
const error3 = r.error ? `
|
|
21378
|
+
Error: ${r.error.slice(0, 100)}` : "";
|
|
21379
|
+
const usage2 = r.usageInput ? `
|
|
21380
|
+
Tokens: ${r.usageInput}in / ${r.usageOutput}out` : "";
|
|
21381
|
+
return `#${r.jobId} [${r.status}] ${formatLocalDateTime(r.startedAt)}${duration}${error3}${usage2}`;
|
|
21382
|
+
});
|
|
21383
|
+
await channel.sendText(chatId, lines.join("\n\n"), { parseMode: "plain" });
|
|
21178
21384
|
}
|
|
21179
21385
|
}
|
|
21180
|
-
async function
|
|
21181
|
-
const
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
const slots = getBackendSlots(slotBackend);
|
|
21185
|
-
if (slots.length === 0) {
|
|
21186
|
-
await channel.sendText(chatId, `No ${slotDisplayName} credentials configured.
|
|
21187
|
-
Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
|
|
21386
|
+
async function handleSkillsCommand(chatId, commandArgs, msg, channel) {
|
|
21387
|
+
const skills2 = await discoverAllSkills();
|
|
21388
|
+
if (skills2.length === 0) {
|
|
21389
|
+
await channel.sendText(chatId, "No skills found. Install skills with /skill-install <github-url> or place them in ~/.cc-claw/workspace/skills/", { parseMode: "plain" });
|
|
21188
21390
|
return;
|
|
21189
21391
|
}
|
|
21190
|
-
|
|
21191
|
-
|
|
21192
|
-
slots,
|
|
21193
|
-
getChatBackendSlotId(chatId, slotBackend),
|
|
21194
|
-
getBackendRotationMode(slotBackend),
|
|
21195
|
-
`bslot:${slotBackend}:`,
|
|
21196
|
-
`brotation:${slotBackend}:`
|
|
21197
|
-
);
|
|
21198
|
-
await channel.sendKeyboard(chatId, `${slotDisplayName} Accounts & Rotation:`, rows);
|
|
21199
|
-
} else {
|
|
21200
|
-
const currentMode = getBackendRotationMode(slotBackend);
|
|
21201
|
-
const list = slots.filter((s) => s.enabled).map((s) => {
|
|
21202
|
-
const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
21203
|
-
return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
|
|
21204
|
-
}).join("\n");
|
|
21205
|
-
await channel.sendText(chatId, `${slotDisplayName} Slots:
|
|
21206
|
-
${list}
|
|
21207
|
-
|
|
21208
|
-
Rotation mode: ${currentMode}
|
|
21209
|
-
Use: /${command} <name> to pin`, { parseMode: "plain" });
|
|
21210
|
-
}
|
|
21211
|
-
}
|
|
21212
|
-
async function handleDebugCommand(chatId, commandArgs, msg, channel) {
|
|
21213
|
-
const { getSessionLogEnabled: getSessionLogEnabled2, toggleSessionLogEnabled: toggleSessionLogEnabled2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
21214
|
-
const current = getSessionLogEnabled2(chatId);
|
|
21215
|
-
if (commandArgs === "on" || commandArgs === "off") {
|
|
21216
|
-
const { setSessionLogEnabled: setSessionLogEnabled2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
21217
|
-
const value = commandArgs === "on";
|
|
21218
|
-
setSessionLogEnabled2(chatId, value);
|
|
21219
|
-
await channel.sendText(chatId, `\u{1F52C} Session debug logging: ${value ? "ON" : "OFF"}
|
|
21220
|
-
|
|
21221
|
-
${value ? "Full tool inputs/results will be saved to ~/.cc-claw/logs/sessions/\nUse 'cc-claw logs session list' or 'cc-claw logs session tail' to inspect." : "Session logs disabled."}`, { parseMode: "plain" });
|
|
21222
|
-
} else if (typeof channel.sendKeyboard === "function") {
|
|
21223
|
-
await channel.sendKeyboard(
|
|
21224
|
-
chatId,
|
|
21225
|
-
`\u{1F52C} Session Debug Logging
|
|
21226
|
-
|
|
21227
|
-
Records full, untruncated tool inputs and results to disk for debugging.
|
|
21228
|
-
Logs: ~/.cc-claw/logs/sessions/
|
|
21229
|
-
Retention: ${process.env.SESSION_LOG_RETENTION_DAYS ?? "7"} day(s)
|
|
21230
|
-
|
|
21231
|
-
Currently: ${current ? "ON" : "OFF"}`,
|
|
21232
|
-
[[
|
|
21233
|
-
{ label: current ? "\u2713 ON" : "ON", data: "debug_log:on", ...current ? { style: "primary" } : {} },
|
|
21234
|
-
{ label: !current ? "\u2713 OFF" : "OFF", data: "debug_log:off", ...!current ? { style: "primary" } : {} }
|
|
21235
|
-
]]
|
|
21236
|
-
);
|
|
21237
|
-
} else {
|
|
21238
|
-
const next = toggleSessionLogEnabled2(chatId);
|
|
21239
|
-
await channel.sendText(chatId, `\u{1F52C} Session debug logging: ${next ? "ON" : "OFF"}`, { parseMode: "plain" });
|
|
21240
|
-
}
|
|
21392
|
+
const page = commandArgs ? parseInt(commandArgs, 10) || 1 : 1;
|
|
21393
|
+
await sendSkillsPage(chatId, channel, skills2, page);
|
|
21241
21394
|
}
|
|
21242
|
-
async function
|
|
21243
|
-
if (commandArgs) {
|
|
21244
|
-
const
|
|
21245
|
-
|
|
21246
|
-
|
|
21247
|
-
|
|
21248
|
-
|
|
21249
|
-
|
|
21250
|
-
|
|
21251
|
-
|
|
21252
|
-
|
|
21253
|
-
|
|
21254
|
-
|
|
21255
|
-
|
|
21256
|
-
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
if (
|
|
21260
|
-
|
|
21261
|
-
} else {
|
|
21262
|
-
await channel.sendText(chatId, "Heartbeat disabled.", { parseMode: "plain" });
|
|
21263
|
-
}
|
|
21264
|
-
return;
|
|
21265
|
-
}
|
|
21266
|
-
case "interval": {
|
|
21267
|
-
const ms = parseIntervalToMs(value ?? "30m");
|
|
21268
|
-
if (!ms || ms < 6e4) {
|
|
21269
|
-
await channel.sendText(chatId, "Invalid interval. Use e.g. 15m, 30m, 1h, 2h", { parseMode: "plain" });
|
|
21270
|
-
return;
|
|
21271
|
-
}
|
|
21272
|
-
setHeartbeatConfig(chatId, { intervalMs: ms });
|
|
21273
|
-
stopHeartbeatForChat(chatId);
|
|
21274
|
-
const hbConf = getHeartbeatConfig(chatId);
|
|
21275
|
-
if (hbConf?.enabled) startHeartbeatForChat(chatId);
|
|
21276
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21277
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
21278
|
-
} else {
|
|
21279
|
-
await channel.sendText(chatId, `Heartbeat interval set to ${ms / 6e4} minutes.`, { parseMode: "plain" });
|
|
21395
|
+
async function handleChatsCommand(chatId, commandArgs, msg, channel) {
|
|
21396
|
+
if (!commandArgs) {
|
|
21397
|
+
const aliases = getAllChatAliases();
|
|
21398
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21399
|
+
if (aliases.length === 0) {
|
|
21400
|
+
await channel.sendKeyboard(
|
|
21401
|
+
chatId,
|
|
21402
|
+
"Authorized Chats\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nNo chat aliases configured yet.\nGroup chats are auto-discovered when the bot receives a message.",
|
|
21403
|
+
[[{ label: "Refresh", data: "chats:refresh", style: "primary" }]]
|
|
21404
|
+
);
|
|
21405
|
+
} else {
|
|
21406
|
+
const lines = aliases.map(
|
|
21407
|
+
(a) => `${a.alias.padEnd(12)} | ${a.chatId}${a.chatId === chatId ? " [Active]" : ""}`
|
|
21408
|
+
);
|
|
21409
|
+
const text = ["Authorized Chats", "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501", "", ...lines].join("\n");
|
|
21410
|
+
const buttons = [];
|
|
21411
|
+
const actionRow = [];
|
|
21412
|
+
if (aliases.length > 0) {
|
|
21413
|
+
actionRow.push({ label: "Remove", data: "chats:remove", style: "danger" });
|
|
21280
21414
|
}
|
|
21281
|
-
|
|
21415
|
+
actionRow.push({ label: "Refresh", data: "chats:refresh", style: "primary" });
|
|
21416
|
+
buttons.push(actionRow);
|
|
21417
|
+
await channel.sendKeyboard(chatId, text, buttons);
|
|
21282
21418
|
}
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
}
|
|
21289
|
-
const hbStart = `${hourMatch[1].padStart(2, "0")}:00`;
|
|
21290
|
-
const hbEnd = `${hourMatch[2].padStart(2, "0")}:00`;
|
|
21291
|
-
setHeartbeatConfig(chatId, { activeStart: hbStart, activeEnd: hbEnd });
|
|
21292
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21293
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
21294
|
-
} else {
|
|
21295
|
-
await channel.sendText(chatId, `Active hours: ${hbStart} - ${hbEnd}`, { parseMode: "plain" });
|
|
21296
|
-
}
|
|
21297
|
-
return;
|
|
21419
|
+
} else {
|
|
21420
|
+
if (aliases.length === 0) {
|
|
21421
|
+
await channel.sendText(chatId, "No chat aliases configured yet.\nUsage: /chats name <chat_id> <alias>\n\nGroup chats are auto-discovered when the bot receives a message from them.", { parseMode: "plain" });
|
|
21422
|
+
} else {
|
|
21423
|
+
const lines = ["Authorized chats:", "", ...aliases.map((a) => ` ${a.alias} -> ${a.chatId}`)];
|
|
21424
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
21298
21425
|
}
|
|
21299
|
-
default:
|
|
21300
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21301
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
21302
|
-
} else {
|
|
21303
|
-
await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
|
|
21304
|
-
}
|
|
21305
|
-
return;
|
|
21306
21426
|
}
|
|
21427
|
+
return;
|
|
21307
21428
|
}
|
|
21308
|
-
|
|
21309
|
-
|
|
21429
|
+
const parts = commandArgs.split(/\s+/);
|
|
21430
|
+
if (parts[0] === "name" && parts.length >= 3) {
|
|
21431
|
+
const targetChatId = parts[1];
|
|
21432
|
+
const alias = parts.slice(2).join("-").toLowerCase();
|
|
21433
|
+
setChatAlias(alias, targetChatId);
|
|
21434
|
+
await channel.sendText(chatId, `Alias set: ${alias} -> ${targetChatId}`, { parseMode: "plain" });
|
|
21435
|
+
} else if (parts[0] === "remove" && parts[1]) {
|
|
21436
|
+
const removed = removeChatAlias(parts[1]);
|
|
21437
|
+
await channel.sendText(chatId, removed ? `Alias "${parts[1]}" removed.` : `Alias "${parts[1]}" not found.`, { parseMode: "plain" });
|
|
21310
21438
|
} else {
|
|
21311
|
-
await channel.sendText(chatId,
|
|
21439
|
+
await channel.sendText(chatId, "Usage:\n /chats \u2014 list all aliases\n /chats name <chat_id> <alias> \u2014 assign alias\n /chats remove <alias> \u2014 remove alias", { parseMode: "plain" });
|
|
21312
21440
|
}
|
|
21313
21441
|
}
|
|
21314
|
-
async function
|
|
21315
|
-
if (commandArgs
|
|
21316
|
-
const
|
|
21317
|
-
|
|
21318
|
-
|
|
21319
|
-
|
|
21320
|
-
|
|
21321
|
-
|
|
21322
|
-
}
|
|
21323
|
-
clearSession(chatId);
|
|
21324
|
-
await channel.sendText(chatId, `Agent mode set to <b>${modeArg}</b>. Session cleared.`, { parseMode: "html" });
|
|
21325
|
-
} else if (typeof channel.sendKeyboard === "function") {
|
|
21326
|
-
const current = getAgentMode(chatId);
|
|
21327
|
-
await channel.sendKeyboard(chatId, `Agent mode: <b>${current}</b>
|
|
21442
|
+
async function handleCwdCommand(chatId, commandArgs, msg, channel) {
|
|
21443
|
+
if (!commandArgs) {
|
|
21444
|
+
const current = getCwd(chatId);
|
|
21445
|
+
const recents = getRecentBookmarks(chatId, 10);
|
|
21446
|
+
if (recents.length === 0) {
|
|
21447
|
+
await channel.sendText(
|
|
21448
|
+
chatId,
|
|
21449
|
+
current ? `Working directory: ${current}
|
|
21328
21450
|
|
|
21329
|
-
|
|
21330
|
-
|
|
21331
|
-
|
|
21332
|
-
|
|
21333
|
-
{ label: "Orchestrated", data: "agentmode:claw", ...current === "claw" ? { style: "primary" } : {} }
|
|
21334
|
-
]
|
|
21335
|
-
]);
|
|
21336
|
-
} else {
|
|
21337
|
-
const current = getAgentMode(chatId);
|
|
21338
|
-
await channel.sendText(chatId, `Agent mode: ${current}. Use /agents mode <auto|native|claw> to change.`, { parseMode: "plain" });
|
|
21451
|
+
No saved bookmarks yet. Set a directory with /cwd <path> to auto-save.` : "No working directory set. Usage: /cwd ~/projects/my-app",
|
|
21452
|
+
{ parseMode: "plain" }
|
|
21453
|
+
);
|
|
21454
|
+
return;
|
|
21339
21455
|
}
|
|
21340
|
-
|
|
21341
|
-
}
|
|
21342
|
-
if (commandArgs?.startsWith("history")) {
|
|
21343
|
-
const db4 = getDb();
|
|
21344
|
-
const events = db4.prepare(`
|
|
21345
|
-
SELECT summary, detail, createdAt FROM activity_log
|
|
21346
|
-
WHERE chatId = ? AND eventType = 'native_subagent'
|
|
21347
|
-
AND createdAt > datetime('now', '-1 day')
|
|
21348
|
-
ORDER BY createdAt DESC LIMIT 20
|
|
21349
|
-
`).all(chatId);
|
|
21350
|
-
if (events.length === 0) {
|
|
21351
|
-
await channel.sendText(chatId, "No native sub-agent activity in the last 24h.", { parseMode: "plain" });
|
|
21352
|
-
} else {
|
|
21353
|
-
const lines2 = events.map((e) => {
|
|
21354
|
-
const d = e.detail ? JSON.parse(e.detail) : {};
|
|
21355
|
-
return `\u2022 ${e.summary} (${d.backend ?? "?"}, ${e.createdAt})`;
|
|
21356
|
-
});
|
|
21357
|
-
await channel.sendText(chatId, `<b>Native agent history (24h)</b>
|
|
21456
|
+
const text = current ? `Current: ${current}
|
|
21358
21457
|
|
|
21359
|
-
|
|
21458
|
+
Recent directories:` : "Recent directories:";
|
|
21459
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21460
|
+
const buttons = recents.map((r) => [{ label: r.alias, data: `cwdpick:${r.alias}` }]);
|
|
21461
|
+
await channel.sendKeyboard(chatId, text, buttons);
|
|
21462
|
+
} else {
|
|
21463
|
+
const list = recents.map((r) => ` ${r.alias} \u2192 ${r.path}`).join("\n");
|
|
21464
|
+
await channel.sendText(chatId, `${text}
|
|
21465
|
+
${list}`, { parseMode: "plain" });
|
|
21360
21466
|
}
|
|
21361
21467
|
return;
|
|
21362
21468
|
}
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
|
|
21366
|
-
running: "\u{1F7E2}",
|
|
21367
|
-
queued: "\u{1F7E1}",
|
|
21368
|
-
starting: "\u{1F535}",
|
|
21369
|
-
idle: "\u26AA",
|
|
21370
|
-
failed: "\u274C",
|
|
21371
|
-
cancelled: "\u{1F6AB}"
|
|
21372
|
-
};
|
|
21373
|
-
const runners2 = getAllRunners();
|
|
21374
|
-
const runnerMap = new Map(runners2.map((r) => [r.id, r]));
|
|
21375
|
-
if (agents2.length === 0) {
|
|
21376
|
-
await channel.sendText(chatId, "<b>Active Agents</b>\n\nNo active agents.", { parseMode: "html" });
|
|
21469
|
+
if (commandArgs === "reset" || commandArgs === "clear") {
|
|
21470
|
+
clearCwd(chatId);
|
|
21471
|
+
await channel.sendText(chatId, "Working directory cleared. Using default.", { parseMode: "plain" });
|
|
21377
21472
|
return;
|
|
21378
21473
|
}
|
|
21379
|
-
|
|
21380
|
-
|
|
21381
|
-
|
|
21382
|
-
|
|
21383
|
-
|
|
21384
|
-
const lines = ["<b>Active Agents</b>", ""];
|
|
21385
|
-
let runningCount = 0;
|
|
21386
|
-
let queuedCount = 0;
|
|
21387
|
-
for (const a of agents2) {
|
|
21388
|
-
const emoji = STATUS_EMOJI[a.status] ?? "\u26AA";
|
|
21389
|
-
const runner = runnerMap.get(a.runnerId);
|
|
21390
|
-
const displayName = runner?.displayName ?? a.runnerId;
|
|
21391
|
-
const modelId = a.model ?? defaultModelMap.get(a.runnerId) ?? "";
|
|
21392
|
-
const modelLabel = shortModelName(modelId);
|
|
21393
|
-
const shortId = a.id.slice(0, 8);
|
|
21394
|
-
if (a.status === "running") runningCount++;
|
|
21395
|
-
if (a.status === "queued") queuedCount++;
|
|
21396
|
-
const agentLabel = a.name ?? shortId;
|
|
21397
|
-
const header2 = modelLabel ? `${emoji} ${agentLabel} (${displayName} | ${modelLabel}) \u2014 ${a.status}` : `${emoji} ${agentLabel} (${displayName}) \u2014 ${a.status}`;
|
|
21398
|
-
lines.push(header2);
|
|
21399
|
-
if (a.name) lines.push(` ID: ${shortId}`);
|
|
21400
|
-
if (a.description) lines.push(` ${a.description}`);
|
|
21401
|
-
if (a.task) lines.push(` Task: ${a.task.slice(0, 200)}${a.task.length > 200 ? "\u2026" : ""}`);
|
|
21402
|
-
if (a.tokenInput > 0 || a.tokenOutput > 0) {
|
|
21403
|
-
const inK = (a.tokenInput / 1e3).toFixed(1);
|
|
21404
|
-
const outK = (a.tokenOutput / 1e3).toFixed(1);
|
|
21405
|
-
lines.push(` Tokens: ${inK}k in / ${outK}k out`);
|
|
21406
|
-
}
|
|
21407
|
-
lines.push("");
|
|
21408
|
-
}
|
|
21409
|
-
lines.push(`Total: ${agents2.length} agent(s) (${runningCount} running, ${queuedCount} queued)`);
|
|
21410
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
21411
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21412
|
-
const agentButtons = [];
|
|
21413
|
-
for (const a of agents2) {
|
|
21414
|
-
const shortId = a.id.slice(0, 8);
|
|
21415
|
-
const taskLabel = a.task ? a.task.slice(0, 20) : a.status;
|
|
21416
|
-
agentButtons.push([
|
|
21417
|
-
{ label: `\u{1F6D1} Stop ${shortId}: ${taskLabel}`, data: `agents:stop:${shortId}`, style: "danger" }
|
|
21418
|
-
]);
|
|
21474
|
+
if (commandArgs === "aliases") {
|
|
21475
|
+
const all = getAllBookmarks(chatId);
|
|
21476
|
+
if (all.length === 0) {
|
|
21477
|
+
await channel.sendText(chatId, "No bookmarks saved yet.", { parseMode: "plain" });
|
|
21478
|
+
return;
|
|
21419
21479
|
}
|
|
21420
|
-
|
|
21421
|
-
await channel.
|
|
21422
|
-
|
|
21423
|
-
}
|
|
21424
|
-
async function handleTasksCommand(chatId, commandArgs, msg, channel) {
|
|
21425
|
-
const db3 = getDb();
|
|
21426
|
-
const orch = getActiveOrchestration(db3, chatId);
|
|
21427
|
-
if (!orch) {
|
|
21428
|
-
await channel.sendText(chatId, "<b>Task Board</b>\n\nNo active orchestration. Start a task to create one.", { parseMode: "html" });
|
|
21480
|
+
const lines = all.map((b) => ` ${b.manual ? "[manual]" : "[auto]"} ${b.alias} \u2192 ${b.path}`);
|
|
21481
|
+
await channel.sendText(chatId, `Directory bookmarks:
|
|
21482
|
+
${lines.join("\n")}`, { parseMode: "plain" });
|
|
21429
21483
|
return;
|
|
21430
21484
|
}
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
failed: [],
|
|
21437
|
-
abandoned: []
|
|
21438
|
-
};
|
|
21439
|
-
for (const t of tasks) {
|
|
21440
|
-
if (byStatus[t.status]) byStatus[t.status].push(t);
|
|
21441
|
-
}
|
|
21442
|
-
const lines = ["<b>Task Board</b>", ""];
|
|
21443
|
-
const sections = [
|
|
21444
|
-
{ label: "Pending", emoji: "\u{1F4CB}", status: "pending" },
|
|
21445
|
-
{ label: "In Progress", emoji: "\u{1F504}", status: "in_progress" },
|
|
21446
|
-
{ label: "Completed", emoji: "\u2705", status: "completed" },
|
|
21447
|
-
{ label: "Failed", emoji: "\u274C", status: "failed" },
|
|
21448
|
-
{ label: "Abandoned", emoji: "\u{1F6AB}", status: "abandoned" }
|
|
21449
|
-
];
|
|
21450
|
-
for (const { label: label2, emoji, status } of sections) {
|
|
21451
|
-
const list = byStatus[status];
|
|
21452
|
-
if (list.length === 0) continue;
|
|
21453
|
-
lines.push(`${emoji} ${label2}:`);
|
|
21454
|
-
for (const t of list) {
|
|
21455
|
-
const assignee = t.assignee ? ` (\u2192 ${t.assignee.slice(0, 8)})` : "";
|
|
21456
|
-
lines.push(` #${t.id}: ${t.subject}${assignee}`);
|
|
21457
|
-
}
|
|
21458
|
-
lines.push("");
|
|
21459
|
-
}
|
|
21460
|
-
if (lines.length === 2) {
|
|
21461
|
-
lines.push("No tasks yet.");
|
|
21462
|
-
}
|
|
21463
|
-
await channel.sendText(chatId, lines.join("\n").trimEnd(), { parseMode: "html" });
|
|
21464
|
-
if (typeof channel.sendKeyboard === "function" && tasks.length > 0) {
|
|
21465
|
-
const taskButtons = [];
|
|
21466
|
-
const viewable = tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
|
|
21467
|
-
for (const t of viewable.slice(0, 10)) {
|
|
21468
|
-
const statusIcon = t.status === "in_progress" ? "\u{1F504}" : "\u{1F4CB}";
|
|
21469
|
-
taskButtons.push([
|
|
21470
|
-
{ label: `${statusIcon} #${t.id}: ${t.subject.slice(0, 30)}`, data: `tasks:view:${t.id}`, style: "primary" }
|
|
21471
|
-
]);
|
|
21472
|
-
}
|
|
21473
|
-
if (taskButtons.length > 0) {
|
|
21474
|
-
await channel.sendKeyboard(chatId, "View task details:", taskButtons);
|
|
21485
|
+
if (commandArgs.startsWith("unalias ")) {
|
|
21486
|
+
const aliasName = commandArgs.slice(8).trim();
|
|
21487
|
+
if (!aliasName) {
|
|
21488
|
+
await channel.sendText(chatId, "Usage: /cwd unalias <name>", { parseMode: "plain" });
|
|
21489
|
+
return;
|
|
21475
21490
|
}
|
|
21491
|
+
const deleted = deleteBookmark(chatId, aliasName);
|
|
21492
|
+
await channel.sendText(chatId, deleted ? `Bookmark '${aliasName}' removed.` : `Bookmark '${aliasName}' not found.`, { parseMode: "plain" });
|
|
21493
|
+
return;
|
|
21476
21494
|
}
|
|
21477
|
-
|
|
21478
|
-
|
|
21479
|
-
|
|
21480
|
-
|
|
21481
|
-
const agents3 = listActiveAgents(db4);
|
|
21482
|
-
if (agents3.length === 0) {
|
|
21483
|
-
await channel.sendText(chatId, "No active agents to stop.", { parseMode: "plain" });
|
|
21495
|
+
if (commandArgs.startsWith("alias ")) {
|
|
21496
|
+
const parts = commandArgs.slice(6).trim().split(/\s+/);
|
|
21497
|
+
if (parts.length < 2) {
|
|
21498
|
+
await channel.sendText(chatId, "Usage: /cwd alias <name> <path>", { parseMode: "plain" });
|
|
21484
21499
|
return;
|
|
21485
21500
|
}
|
|
21486
|
-
|
|
21487
|
-
|
|
21488
|
-
|
|
21489
|
-
|
|
21490
|
-
const shortId = a.id.slice(0, 8);
|
|
21491
|
-
const taskLabel = a.task ? a.task.slice(0, 40) : "no task";
|
|
21492
|
-
const runtimeMs = Date.now() - (/* @__PURE__ */ new Date(a.createdAt + "Z")).getTime();
|
|
21493
|
-
const mins = Math.floor(runtimeMs / 6e4);
|
|
21494
|
-
const runtime = mins < 1 ? "<1m" : `${mins}m`;
|
|
21495
|
-
return `${emoji} ${shortId} \u2014 "${taskLabel}" (${a.status}, ${runtime})`;
|
|
21496
|
-
});
|
|
21497
|
-
const buttons = agents3.map((a) => {
|
|
21498
|
-
const shortId = a.id.slice(0, 8);
|
|
21499
|
-
const taskLabel = a.task ? a.task.slice(0, 20) : a.status;
|
|
21500
|
-
return [{ label: `${shortId}: ${taskLabel}`, data: `stopagent:pick:${shortId}`, style: "danger" }];
|
|
21501
|
-
});
|
|
21502
|
-
buttons.push([
|
|
21503
|
-
{ label: "\u{1F6D1} Stop All", data: "stopagent:all", style: "danger" },
|
|
21504
|
-
{ label: "Cancel", data: "stopagent:cancel" }
|
|
21505
|
-
]);
|
|
21506
|
-
await channel.sendKeyboard(chatId, `Stop which agent?
|
|
21507
|
-
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
21508
|
-
|
|
21509
|
-
${agentLines.join("\n")}`, buttons);
|
|
21510
|
-
} else {
|
|
21511
|
-
await channel.sendText(chatId, "Usage: /stopagent <id>\nUse first 8 chars of agent ID or full ID.", { parseMode: "plain" });
|
|
21512
|
-
}
|
|
21501
|
+
const [aliasName, ...pathParts] = parts;
|
|
21502
|
+
const aliasPath = pathParts.join(" ").replace(/^~/, process.env.HOME ?? "");
|
|
21503
|
+
upsertBookmark(chatId, aliasName, aliasPath, true);
|
|
21504
|
+
await channel.sendText(chatId, `Bookmark saved: ${aliasName} \u2192 ${aliasPath}`, { parseMode: "plain" });
|
|
21513
21505
|
return;
|
|
21514
21506
|
}
|
|
21515
|
-
const
|
|
21516
|
-
|
|
21517
|
-
|
|
21518
|
-
|
|
21519
|
-
|
|
21520
|
-
|
|
21507
|
+
const arg = commandArgs;
|
|
21508
|
+
if (arg.startsWith("/") || arg.startsWith("~")) {
|
|
21509
|
+
const resolvedPath = arg.startsWith("~") ? arg.replace("~", process.env.HOME ?? "") : arg;
|
|
21510
|
+
setCwd(chatId, resolvedPath);
|
|
21511
|
+
const basename4 = resolvedPath.split("/").filter(Boolean).pop();
|
|
21512
|
+
if (basename4) upsertBookmark(chatId, basename4, resolvedPath, false);
|
|
21513
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${resolvedPath}`, detail: { field: "cwd", value: resolvedPath } });
|
|
21514
|
+
await sendCwdSessionChoice(chatId, resolvedPath, channel);
|
|
21521
21515
|
return;
|
|
21522
21516
|
}
|
|
21523
|
-
const
|
|
21524
|
-
|
|
21525
|
-
|
|
21526
|
-
|
|
21527
|
-
|
|
21528
|
-
|
|
21529
|
-
|
|
21530
|
-
let connectedCount = 0;
|
|
21531
|
-
const centralMcps = listRegisteredMcps(db3);
|
|
21532
|
-
if (centralMcps.length > 0) {
|
|
21533
|
-
lines.push("", "\u2501\u2501 <b>CC-Claw</b> \u2501\u2501");
|
|
21534
|
-
for (const m of centralMcps) {
|
|
21535
|
-
totalCount++;
|
|
21536
|
-
connectedCount++;
|
|
21537
|
-
const autoTag = m.enabledByDefault ? " \u{1F4CC}" : "";
|
|
21538
|
-
const desc = m.description ? ` <i>${m.description}</i>` : "";
|
|
21539
|
-
lines.push(` \u2705 <b>${m.name}</b>${autoTag}${desc}`);
|
|
21540
|
-
}
|
|
21517
|
+
const exact = getBookmark(chatId, arg);
|
|
21518
|
+
if (exact) {
|
|
21519
|
+
setCwd(chatId, exact.path);
|
|
21520
|
+
touchBookmark(chatId, arg);
|
|
21521
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${exact.path}`, detail: { field: "cwd", value: exact.path } });
|
|
21522
|
+
await sendCwdSessionChoice(chatId, exact.path, channel);
|
|
21523
|
+
return;
|
|
21541
21524
|
}
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21525
|
+
const matches = findBookmarksByPrefix(chatId, arg);
|
|
21526
|
+
if (matches.length === 1) {
|
|
21527
|
+
setCwd(chatId, matches[0].path);
|
|
21528
|
+
touchBookmark(chatId, matches[0].alias);
|
|
21529
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${matches[0].path}`, detail: { field: "cwd", value: matches[0].path } });
|
|
21530
|
+
await sendCwdSessionChoice(chatId, matches[0].path, channel);
|
|
21531
|
+
return;
|
|
21547
21532
|
}
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21557
|
-
|
|
21558
|
-
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
|
|
21562
|
-
|
|
21563
|
-
|
|
21564
|
-
|
|
21565
|
-
|
|
21566
|
-
|
|
21567
|
-
|
|
21568
|
-
|
|
21569
|
-
|
|
21570
|
-
|
|
21571
|
-
const
|
|
21572
|
-
|
|
21573
|
-
|
|
21574
|
-
|
|
21575
|
-
|
|
21576
|
-
|
|
21577
|
-
|
|
21578
|
-
}
|
|
21579
|
-
for (const srv of parsed) {
|
|
21580
|
-
totalCount++;
|
|
21581
|
-
if (srv.connected) connectedCount++;
|
|
21582
|
-
const icon = srv.connected ? "\u2705" : "\u274C";
|
|
21583
|
-
lines.push(` ${icon} <b>${srv.name}</b>`);
|
|
21533
|
+
if (matches.length > 1 && typeof channel.sendKeyboard === "function") {
|
|
21534
|
+
const buttons = matches.map((m) => [{ label: `${m.alias} \u2192 ${m.path}`, data: `cwdpick:${m.alias}` }]);
|
|
21535
|
+
await channel.sendKeyboard(chatId, `Multiple matches for "${arg}":`, buttons);
|
|
21536
|
+
return;
|
|
21537
|
+
}
|
|
21538
|
+
await channel.sendText(chatId, `Directory alias '${arg}' not found. Use /cwd aliases to see saved bookmarks.`, { parseMode: "plain" });
|
|
21539
|
+
}
|
|
21540
|
+
async function handleSummarizerCommand(chatId, commandArgs, msg, channel) {
|
|
21541
|
+
let adapter;
|
|
21542
|
+
try {
|
|
21543
|
+
adapter = getAdapterForChat(chatId);
|
|
21544
|
+
} catch {
|
|
21545
|
+
adapter = null;
|
|
21546
|
+
}
|
|
21547
|
+
const current = getSummarizer(chatId);
|
|
21548
|
+
const currentLabel = current.backend === "off" ? "Off" : current.backend ? `${current.backend}:${current.model ?? "default"}` : `Auto (${adapter?.summarizerModel ?? "default"})`;
|
|
21549
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21550
|
+
const isAuto = !current.backend && current.backend !== "off";
|
|
21551
|
+
const isOff = current.backend === "off";
|
|
21552
|
+
const buttons = [
|
|
21553
|
+
[{ label: `Auto (use active backend)${isAuto ? " \u2713" : ""}`, data: "summarizer:auto", ...isAuto ? { style: "primary" } : {} }],
|
|
21554
|
+
[{ label: `Off (disable)${isOff ? " \u2713" : ""}`, data: "summarizer:off", ...isOff ? { style: "primary" } : {} }]
|
|
21555
|
+
];
|
|
21556
|
+
for (const a of getAvailableAdapters()) {
|
|
21557
|
+
const pinned = current.backend === a.id;
|
|
21558
|
+
buttons.push([{
|
|
21559
|
+
label: `${pinned ? "\u2713 " : ""}${a.displayName}: ${a.summarizerModel}`,
|
|
21560
|
+
data: `summarizer:${a.id}:${a.summarizerModel}`,
|
|
21561
|
+
...pinned ? { style: "primary" } : {}
|
|
21562
|
+
}]);
|
|
21584
21563
|
}
|
|
21564
|
+
await channel.sendKeyboard(chatId, `Session summarizer (current: ${currentLabel}):`, buttons);
|
|
21565
|
+
} else {
|
|
21566
|
+
await channel.sendText(chatId, `Summarizer: ${currentLabel}
|
|
21567
|
+
|
|
21568
|
+
Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, { parseMode: "plain" });
|
|
21585
21569
|
}
|
|
21586
|
-
|
|
21587
|
-
|
|
21570
|
+
}
|
|
21571
|
+
async function handleGeminiAccountsCommand(chatId, commandArgs, msg, channel) {
|
|
21572
|
+
const slots = getGeminiSlots();
|
|
21573
|
+
if (slots.length === 0) {
|
|
21574
|
+
await channel.sendText(chatId, "No Gemini credentials configured.\nAdd with: <code>cc-claw gemini add-key</code> or <code>cc-claw gemini add-account</code>", { parseMode: "html" });
|
|
21575
|
+
return;
|
|
21576
|
+
}
|
|
21577
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21578
|
+
const rows = buildAccountSlotKeyboard(
|
|
21579
|
+
slots,
|
|
21580
|
+
getChatGeminiSlotId(chatId),
|
|
21581
|
+
getGeminiRotationMode(),
|
|
21582
|
+
"gslot:",
|
|
21583
|
+
"grotation:"
|
|
21584
|
+
);
|
|
21585
|
+
await channel.sendKeyboard(chatId, "Gemini Accounts & Rotation:", rows);
|
|
21588
21586
|
} else {
|
|
21589
|
-
|
|
21587
|
+
const currentMode = getGeminiRotationMode();
|
|
21588
|
+
const list = slots.filter((s) => s.enabled).map((s) => {
|
|
21589
|
+
const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
21590
|
+
return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
|
|
21591
|
+
}).join("\n");
|
|
21592
|
+
await channel.sendText(chatId, `Slots:
|
|
21593
|
+
${list}
|
|
21594
|
+
|
|
21595
|
+
Rotation mode: ${currentMode}
|
|
21596
|
+
Use: /gemini_accounts <name> to pin`, { parseMode: "plain" });
|
|
21590
21597
|
}
|
|
21591
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
21592
21598
|
}
|
|
21593
|
-
async function
|
|
21594
|
-
|
|
21595
|
-
|
|
21599
|
+
async function handleBackendAccountsCommand(chatId, commandArgs, msg, channel) {
|
|
21600
|
+
const command = msg.command;
|
|
21601
|
+
const slotBackend = command === "claude_accounts" ? "claude" : "codex";
|
|
21602
|
+
const slotDisplayName = command === "claude_accounts" ? "Claude" : "Codex";
|
|
21603
|
+
const slots = getBackendSlots(slotBackend);
|
|
21604
|
+
if (slots.length === 0) {
|
|
21605
|
+
await channel.sendText(chatId, `No ${slotDisplayName} credentials configured.
|
|
21606
|
+
Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
|
|
21596
21607
|
return;
|
|
21597
21608
|
}
|
|
21598
|
-
|
|
21599
|
-
|
|
21600
|
-
|
|
21601
|
-
|
|
21602
|
-
|
|
21603
|
-
|
|
21604
|
-
|
|
21605
|
-
|
|
21606
|
-
|
|
21607
|
-
|
|
21608
|
-
|
|
21609
|
-
|
|
21610
|
-
|
|
21611
|
-
|
|
21612
|
-
|
|
21613
|
-
|
|
21614
|
-
|
|
21609
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21610
|
+
const rows = buildAccountSlotKeyboard(
|
|
21611
|
+
slots,
|
|
21612
|
+
getChatBackendSlotId(chatId, slotBackend),
|
|
21613
|
+
getBackendRotationMode(slotBackend),
|
|
21614
|
+
`bslot:${slotBackend}:`,
|
|
21615
|
+
`brotation:${slotBackend}:`
|
|
21616
|
+
);
|
|
21617
|
+
await channel.sendKeyboard(chatId, `${slotDisplayName} Accounts & Rotation:`, rows);
|
|
21618
|
+
} else {
|
|
21619
|
+
const currentMode = getBackendRotationMode(slotBackend);
|
|
21620
|
+
const list = slots.filter((s) => s.enabled).map((s) => {
|
|
21621
|
+
const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
21622
|
+
return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
|
|
21623
|
+
}).join("\n");
|
|
21624
|
+
await channel.sendText(chatId, `${slotDisplayName} Slots:
|
|
21625
|
+
${list}
|
|
21626
|
+
|
|
21627
|
+
Rotation mode: ${currentMode}
|
|
21628
|
+
Use: /${command} <name> to pin`, { parseMode: "plain" });
|
|
21629
|
+
}
|
|
21630
|
+
}
|
|
21631
|
+
async function handleDebugCommand(chatId, commandArgs, msg, channel) {
|
|
21632
|
+
const { getSessionLogEnabled: getSessionLogEnabled2, toggleSessionLogEnabled: toggleSessionLogEnabled2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
21633
|
+
const current = getSessionLogEnabled2(chatId);
|
|
21634
|
+
if (commandArgs === "on" || commandArgs === "off") {
|
|
21635
|
+
const { setSessionLogEnabled: setSessionLogEnabled2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
21636
|
+
const value = commandArgs === "on";
|
|
21637
|
+
setSessionLogEnabled2(chatId, value);
|
|
21638
|
+
await channel.sendText(chatId, `\u{1F52C} Session debug logging: ${value ? "ON" : "OFF"}
|
|
21639
|
+
|
|
21640
|
+
${value ? "Full tool inputs/results will be saved to ~/.cc-claw/logs/sessions/\nUse 'cc-claw logs session list' or 'cc-claw logs session tail' to inspect." : "Session logs disabled."}`, { parseMode: "plain" });
|
|
21641
|
+
} else if (typeof channel.sendKeyboard === "function") {
|
|
21642
|
+
await channel.sendKeyboard(
|
|
21643
|
+
chatId,
|
|
21644
|
+
`\u{1F52C} Session Debug Logging
|
|
21645
|
+
|
|
21646
|
+
Records full, untruncated tool inputs and results to disk for debugging.
|
|
21647
|
+
Logs: ~/.cc-claw/logs/sessions/
|
|
21648
|
+
Retention: ${process.env.SESSION_LOG_RETENTION_DAYS ?? "7"} day(s)
|
|
21649
|
+
|
|
21650
|
+
Currently: ${current ? "ON" : "OFF"}`,
|
|
21651
|
+
[[
|
|
21652
|
+
{ label: current ? "\u2713 ON" : "ON", data: "debug_log:on", ...current ? { style: "primary" } : {} },
|
|
21653
|
+
{ label: !current ? "\u2713 OFF" : "OFF", data: "debug_log:off", ...!current ? { style: "primary" } : {} }
|
|
21654
|
+
]]
|
|
21655
|
+
);
|
|
21656
|
+
} else {
|
|
21657
|
+
const next = toggleSessionLogEnabled2(chatId);
|
|
21658
|
+
await channel.sendText(chatId, `\u{1F52C} Session debug logging: ${next ? "ON" : "OFF"}`, { parseMode: "plain" });
|
|
21659
|
+
}
|
|
21660
|
+
}
|
|
21661
|
+
async function handleHeartbeatCommand(chatId, commandArgs, msg, channel) {
|
|
21662
|
+
if (commandArgs) {
|
|
21663
|
+
const { action, value } = parseHeartbeatCommand(chatId, commandArgs);
|
|
21664
|
+
switch (action) {
|
|
21665
|
+
case "on": {
|
|
21666
|
+
setHeartbeatConfig(chatId, { enabled: true });
|
|
21667
|
+
startHeartbeatForChat(chatId);
|
|
21668
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21669
|
+
await sendHeartbeatKeyboard(chatId, channel);
|
|
21670
|
+
} else {
|
|
21671
|
+
await channel.sendText(chatId, "Heartbeat enabled. I'll check in periodically.", { parseMode: "plain" });
|
|
21672
|
+
}
|
|
21615
21673
|
return;
|
|
21616
21674
|
}
|
|
21617
|
-
|
|
21618
|
-
|
|
21619
|
-
|
|
21620
|
-
|
|
21621
|
-
|
|
21622
|
-
|
|
21623
|
-
|
|
21624
|
-
|
|
21675
|
+
case "off": {
|
|
21676
|
+
setHeartbeatConfig(chatId, { enabled: false });
|
|
21677
|
+
stopHeartbeatForChat(chatId);
|
|
21678
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21679
|
+
await sendHeartbeatKeyboard(chatId, channel);
|
|
21680
|
+
} else {
|
|
21681
|
+
await channel.sendText(chatId, "Heartbeat disabled.", { parseMode: "plain" });
|
|
21682
|
+
}
|
|
21625
21683
|
return;
|
|
21626
21684
|
}
|
|
21627
|
-
|
|
21628
|
-
|
|
21629
|
-
|
|
21630
|
-
|
|
21631
|
-
|
|
21632
|
-
|
|
21633
|
-
|
|
21634
|
-
|
|
21685
|
+
case "interval": {
|
|
21686
|
+
const ms = parseIntervalToMs(value ?? "30m");
|
|
21687
|
+
if (!ms || ms < 6e4) {
|
|
21688
|
+
await channel.sendText(chatId, "Invalid interval. Use e.g. 15m, 30m, 1h, 2h", { parseMode: "plain" });
|
|
21689
|
+
return;
|
|
21690
|
+
}
|
|
21691
|
+
setHeartbeatConfig(chatId, { intervalMs: ms });
|
|
21692
|
+
stopHeartbeatForChat(chatId);
|
|
21693
|
+
const hbConf = getHeartbeatConfig(chatId);
|
|
21694
|
+
if (hbConf?.enabled) startHeartbeatForChat(chatId);
|
|
21695
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21696
|
+
await sendHeartbeatKeyboard(chatId, channel);
|
|
21697
|
+
} else {
|
|
21698
|
+
await channel.sendText(chatId, `Heartbeat interval set to ${ms / 6e4} minutes.`, { parseMode: "plain" });
|
|
21699
|
+
}
|
|
21635
21700
|
return;
|
|
21636
21701
|
}
|
|
21637
|
-
|
|
21638
|
-
|
|
21639
|
-
|
|
21640
|
-
|
|
21641
|
-
break;
|
|
21642
|
-
}
|
|
21643
|
-
case "runs": {
|
|
21644
|
-
const runsId = cronSubArgs ? parseInt(cronSubArgs, 10) : void 0;
|
|
21645
|
-
if (runsId) {
|
|
21646
|
-
await sendJobRunsView(chatId, runsId, channel, 1);
|
|
21647
|
-
} else {
|
|
21648
|
-
const cronRuns2 = getJobRuns(void 0, 10);
|
|
21649
|
-
if (cronRuns2.length === 0) {
|
|
21650
|
-
await channel.sendText(chatId, "No run history.", { parseMode: "plain" });
|
|
21702
|
+
case "hours": {
|
|
21703
|
+
const hourMatch = (value ?? "").match(/^(\d{1,2})-(\d{1,2})$/);
|
|
21704
|
+
if (!hourMatch) {
|
|
21705
|
+
await channel.sendText(chatId, "Usage: /heartbeat hours 8-22", { parseMode: "plain" });
|
|
21651
21706
|
return;
|
|
21652
21707
|
}
|
|
21653
|
-
const
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
case "edit": {
|
|
21662
|
-
if (!cronSubArgs) {
|
|
21663
|
-
await channel.sendText(chatId, "Usage: /cron edit <id>", { parseMode: "plain" });
|
|
21708
|
+
const hbStart = `${hourMatch[1].padStart(2, "0")}:00`;
|
|
21709
|
+
const hbEnd = `${hourMatch[2].padStart(2, "0")}:00`;
|
|
21710
|
+
setHeartbeatConfig(chatId, { activeStart: hbStart, activeEnd: hbEnd });
|
|
21711
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21712
|
+
await sendHeartbeatKeyboard(chatId, channel);
|
|
21713
|
+
} else {
|
|
21714
|
+
await channel.sendText(chatId, `Active hours: ${hbStart} - ${hbEnd}`, { parseMode: "plain" });
|
|
21715
|
+
}
|
|
21664
21716
|
return;
|
|
21665
21717
|
}
|
|
21666
|
-
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
|
|
21670
|
-
|
|
21671
|
-
|
|
21672
|
-
|
|
21673
|
-
break;
|
|
21718
|
+
default:
|
|
21719
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21720
|
+
await sendHeartbeatKeyboard(chatId, channel);
|
|
21721
|
+
} else {
|
|
21722
|
+
await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
|
|
21723
|
+
}
|
|
21724
|
+
return;
|
|
21674
21725
|
}
|
|
21675
|
-
default:
|
|
21676
|
-
await startWizard(chatId, commandArgs, channel);
|
|
21677
|
-
}
|
|
21678
|
-
}
|
|
21679
|
-
var init_command_handlers = __esm({
|
|
21680
|
-
"src/router/command-handlers.ts"() {
|
|
21681
|
-
"use strict";
|
|
21682
|
-
init_format();
|
|
21683
|
-
init_log();
|
|
21684
|
-
init_format_time();
|
|
21685
|
-
init_version();
|
|
21686
|
-
init_image_gen();
|
|
21687
|
-
init_stt();
|
|
21688
|
-
init_agent();
|
|
21689
|
-
init_classify();
|
|
21690
|
-
init_install();
|
|
21691
|
-
init_profile();
|
|
21692
|
-
init_heartbeat2();
|
|
21693
|
-
init_discover();
|
|
21694
|
-
init_store5();
|
|
21695
|
-
init_summarize();
|
|
21696
|
-
init_session_log();
|
|
21697
|
-
init_backends();
|
|
21698
|
-
init_cron();
|
|
21699
|
-
init_wizard();
|
|
21700
|
-
init_health2();
|
|
21701
|
-
init_store();
|
|
21702
|
-
init_orchestrator();
|
|
21703
|
-
init_registry();
|
|
21704
|
-
init_registry2();
|
|
21705
|
-
init_types();
|
|
21706
|
-
init_store3();
|
|
21707
|
-
init_helpers();
|
|
21708
|
-
init_shell();
|
|
21709
|
-
init_ui();
|
|
21710
|
-
init_state();
|
|
21711
|
-
init_evolve2();
|
|
21712
|
-
init_optimize();
|
|
21713
21726
|
}
|
|
21714
|
-
|
|
21715
|
-
|
|
21716
|
-
|
|
21717
|
-
|
|
21718
|
-
__export(ollama_exports2, {
|
|
21719
|
-
handleOllamaCallback: () => handleOllamaCallback,
|
|
21720
|
-
handleOllamaCommand: () => handleOllamaCommand
|
|
21721
|
-
});
|
|
21722
|
-
async function handleOllamaCommand(chatId, commandArgs, channel) {
|
|
21723
|
-
const [sub, ...rest] = (commandArgs || "").trim().split(/\s+/);
|
|
21724
|
-
switch (sub) {
|
|
21725
|
-
case "models":
|
|
21726
|
-
return sendModelList(chatId, channel, rest[0]);
|
|
21727
|
-
case "health":
|
|
21728
|
-
return sendHealthCheck(chatId, channel);
|
|
21729
|
-
case "discover":
|
|
21730
|
-
return sendDiscover(chatId, channel, rest[0]);
|
|
21731
|
-
case "add":
|
|
21732
|
-
if (rest.length >= 2) return handleAdd(chatId, channel, rest[0], rest[1], rest[2] ? parseInt(rest[2], 10) : void 0);
|
|
21733
|
-
if (rest.length === 1) return handleAdd(chatId, channel, rest[0], rest[0]);
|
|
21734
|
-
await channel.sendText(chatId, "Usage: /ollama add <name> <host> [port]", { parseMode: "plain" });
|
|
21735
|
-
return;
|
|
21736
|
-
case "remove":
|
|
21737
|
-
if (rest[0]) return sendRemoveConfirm(chatId, channel, rest[0]);
|
|
21738
|
-
await channel.sendText(chatId, "Usage: /ollama remove <server-name>", { parseMode: "plain" });
|
|
21739
|
-
return;
|
|
21740
|
-
default:
|
|
21741
|
-
return sendOllamaDashboard(chatId, channel);
|
|
21727
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21728
|
+
await sendHeartbeatKeyboard(chatId, channel);
|
|
21729
|
+
} else {
|
|
21730
|
+
await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
|
|
21742
21731
|
}
|
|
21743
21732
|
}
|
|
21744
|
-
async function
|
|
21745
|
-
|
|
21746
|
-
|
|
21747
|
-
|
|
21748
|
-
|
|
21749
|
-
|
|
21750
|
-
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
|
|
21756
|
-
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
|
|
21760
|
-
|
|
21761
|
-
|
|
21733
|
+
async function handleAgentsCommand(chatId, commandArgs, msg, channel) {
|
|
21734
|
+
if (commandArgs?.startsWith("mode")) {
|
|
21735
|
+
const modeArg = commandArgs.slice(5).trim().toLowerCase();
|
|
21736
|
+
if (modeArg === "native" || modeArg === "claw" || modeArg === "auto") {
|
|
21737
|
+
setAgentMode(chatId, modeArg);
|
|
21738
|
+
try {
|
|
21739
|
+
await summarizeSession(chatId);
|
|
21740
|
+
} catch {
|
|
21741
|
+
}
|
|
21742
|
+
clearSession(chatId);
|
|
21743
|
+
await channel.sendText(chatId, `Agent mode set to <b>${modeArg}</b>. Session cleared.`, { parseMode: "html" });
|
|
21744
|
+
} else if (typeof channel.sendKeyboard === "function") {
|
|
21745
|
+
const current = getAgentMode(chatId);
|
|
21746
|
+
await channel.sendKeyboard(chatId, `Agent mode: <b>${current}</b>
|
|
21747
|
+
|
|
21748
|
+
Choose a mode:`, [
|
|
21749
|
+
[
|
|
21750
|
+
{ label: "Auto (recommended)", data: "agentmode:auto", ...current === "auto" ? { style: "primary" } : {} },
|
|
21751
|
+
{ label: "Native (fast)", data: "agentmode:native", ...current === "native" ? { style: "primary" } : {} },
|
|
21752
|
+
{ label: "Orchestrated", data: "agentmode:claw", ...current === "claw" ? { style: "primary" } : {} }
|
|
21753
|
+
]
|
|
21762
21754
|
]);
|
|
21763
21755
|
} else {
|
|
21764
|
-
|
|
21756
|
+
const current = getAgentMode(chatId);
|
|
21757
|
+
await channel.sendText(chatId, `Agent mode: ${current}. Use /agents mode <auto|native|claw> to change.`, { parseMode: "plain" });
|
|
21765
21758
|
}
|
|
21766
21759
|
return;
|
|
21767
21760
|
}
|
|
21768
|
-
|
|
21769
|
-
|
|
21770
|
-
const
|
|
21771
|
-
|
|
21772
|
-
|
|
21773
|
-
|
|
21761
|
+
if (commandArgs?.startsWith("history")) {
|
|
21762
|
+
const db4 = getDb();
|
|
21763
|
+
const events = db4.prepare(`
|
|
21764
|
+
SELECT summary, detail, createdAt FROM activity_log
|
|
21765
|
+
WHERE chatId = ? AND eventType = 'native_subagent'
|
|
21766
|
+
AND createdAt > datetime('now', '-1 day')
|
|
21767
|
+
ORDER BY createdAt DESC LIMIT 20
|
|
21768
|
+
`).all(chatId);
|
|
21769
|
+
if (events.length === 0) {
|
|
21770
|
+
await channel.sendText(chatId, "No native sub-agent activity in the last 24h.", { parseMode: "plain" });
|
|
21771
|
+
} else {
|
|
21772
|
+
const lines2 = events.map((e) => {
|
|
21773
|
+
const d = e.detail ? JSON.parse(e.detail) : {};
|
|
21774
|
+
return `\u2022 ${e.summary} (${d.backend ?? "?"}, ${e.createdAt})`;
|
|
21775
|
+
});
|
|
21776
|
+
await channel.sendText(chatId, `<b>Native agent history (24h)</b>
|
|
21777
|
+
|
|
21778
|
+
${lines2.join("\n")}`, { parseMode: "html" });
|
|
21779
|
+
}
|
|
21780
|
+
return;
|
|
21781
|
+
}
|
|
21782
|
+
const db3 = getDb();
|
|
21783
|
+
const agents2 = listActiveAgents(db3);
|
|
21784
|
+
const STATUS_EMOJI = {
|
|
21785
|
+
running: "\u{1F7E2}",
|
|
21786
|
+
queued: "\u{1F7E1}",
|
|
21787
|
+
starting: "\u{1F535}",
|
|
21788
|
+
idle: "\u26AA",
|
|
21789
|
+
failed: "\u274C",
|
|
21790
|
+
cancelled: "\u{1F6AB}"
|
|
21791
|
+
};
|
|
21792
|
+
const runners2 = getAllRunners();
|
|
21793
|
+
const runnerMap = new Map(runners2.map((r) => [r.id, r]));
|
|
21794
|
+
if (agents2.length === 0) {
|
|
21795
|
+
await channel.sendText(chatId, "<b>Active Agents</b>\n\nNo active agents.", { parseMode: "html" });
|
|
21796
|
+
return;
|
|
21797
|
+
}
|
|
21798
|
+
const { getAllAdapters: getAllAdaptersImport } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
21799
|
+
const defaultModelMap = /* @__PURE__ */ new Map();
|
|
21800
|
+
for (const adapterItem of getAllAdaptersImport()) {
|
|
21801
|
+
defaultModelMap.set(adapterItem.id, adapterItem.defaultModel);
|
|
21802
|
+
}
|
|
21803
|
+
const lines = ["<b>Active Agents</b>", ""];
|
|
21804
|
+
let runningCount = 0;
|
|
21805
|
+
let queuedCount = 0;
|
|
21806
|
+
for (const a of agents2) {
|
|
21807
|
+
const emoji = STATUS_EMOJI[a.status] ?? "\u26AA";
|
|
21808
|
+
const runner = runnerMap.get(a.runnerId);
|
|
21809
|
+
const displayName = runner?.displayName ?? a.runnerId;
|
|
21810
|
+
const modelId = a.model ?? defaultModelMap.get(a.runnerId) ?? "";
|
|
21811
|
+
const modelLabel = shortModelName(modelId);
|
|
21812
|
+
const shortId = a.id.slice(0, 8);
|
|
21813
|
+
if (a.status === "running") runningCount++;
|
|
21814
|
+
if (a.status === "queued") queuedCount++;
|
|
21815
|
+
const agentLabel = a.name ?? shortId;
|
|
21816
|
+
const header2 = modelLabel ? `${emoji} ${agentLabel} (${displayName} | ${modelLabel}) \u2014 ${a.status}` : `${emoji} ${agentLabel} (${displayName}) \u2014 ${a.status}`;
|
|
21817
|
+
lines.push(header2);
|
|
21818
|
+
if (a.name) lines.push(` ID: ${shortId}`);
|
|
21819
|
+
if (a.description) lines.push(` ${a.description}`);
|
|
21820
|
+
if (a.task) lines.push(` Task: ${a.task.slice(0, 200)}${a.task.length > 200 ? "\u2026" : ""}`);
|
|
21821
|
+
if (a.tokenInput > 0 || a.tokenOutput > 0) {
|
|
21822
|
+
const inK = (a.tokenInput / 1e3).toFixed(1);
|
|
21823
|
+
const outK = (a.tokenOutput / 1e3).toFixed(1);
|
|
21824
|
+
lines.push(` Tokens: ${inK}k in / ${outK}k out`);
|
|
21825
|
+
}
|
|
21826
|
+
lines.push("");
|
|
21774
21827
|
}
|
|
21775
|
-
|
|
21776
|
-
lines.
|
|
21828
|
+
lines.push(`Total: ${agents2.length} agent(s) (${runningCount} running, ${queuedCount} queued)`);
|
|
21829
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
21777
21830
|
if (typeof channel.sendKeyboard === "function") {
|
|
21778
|
-
const
|
|
21779
|
-
|
|
21780
|
-
|
|
21781
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
{ label: "\u2795 Add", data: "ollama:add_prompt" }
|
|
21786
|
-
]
|
|
21787
|
-
];
|
|
21788
|
-
if (servers.length <= 4) {
|
|
21789
|
-
const removeRow = servers.map((s) => ({
|
|
21790
|
-
label: `\u{1F5D1} ${s.name}`,
|
|
21791
|
-
data: `ollama:remove_confirm:${s.name}`,
|
|
21792
|
-
style: "danger"
|
|
21793
|
-
}));
|
|
21794
|
-
buttons.push(removeRow);
|
|
21831
|
+
const agentButtons = [];
|
|
21832
|
+
for (const a of agents2) {
|
|
21833
|
+
const shortId = a.id.slice(0, 8);
|
|
21834
|
+
const taskLabel = a.task ? a.task.slice(0, 20) : a.status;
|
|
21835
|
+
agentButtons.push([
|
|
21836
|
+
{ label: `\u{1F6D1} Stop ${shortId}: ${taskLabel}`, data: `agents:stop:${shortId}`, style: "danger" }
|
|
21837
|
+
]);
|
|
21795
21838
|
}
|
|
21796
|
-
|
|
21797
|
-
|
|
21798
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
21839
|
+
agentButtons.push([{ label: "\u{1F4CB} View Task Board", data: "agents:tasks" }]);
|
|
21840
|
+
await channel.sendKeyboard(chatId, "Agent actions:", agentButtons);
|
|
21799
21841
|
}
|
|
21800
21842
|
}
|
|
21801
|
-
async function
|
|
21802
|
-
const
|
|
21803
|
-
const
|
|
21804
|
-
if (
|
|
21805
|
-
await channel.sendText(chatId,
|
|
21843
|
+
async function handleTasksCommand(chatId, commandArgs, msg, channel) {
|
|
21844
|
+
const db3 = getDb();
|
|
21845
|
+
const orch = getActiveOrchestration(db3, chatId);
|
|
21846
|
+
if (!orch) {
|
|
21847
|
+
await channel.sendText(chatId, "<b>Task Board</b>\n\nNo active orchestration. Start a task to create one.", { parseMode: "html" });
|
|
21806
21848
|
return;
|
|
21807
21849
|
}
|
|
21808
|
-
const
|
|
21809
|
-
|
|
21810
|
-
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21814
|
-
|
|
21815
|
-
|
|
21850
|
+
const tasks = listTasksByOrchestration(db3, orch.id);
|
|
21851
|
+
const byStatus = {
|
|
21852
|
+
pending: [],
|
|
21853
|
+
in_progress: [],
|
|
21854
|
+
completed: [],
|
|
21855
|
+
failed: [],
|
|
21856
|
+
abandoned: []
|
|
21857
|
+
};
|
|
21858
|
+
for (const t of tasks) {
|
|
21859
|
+
if (byStatus[t.status]) byStatus[t.status].push(t);
|
|
21816
21860
|
}
|
|
21817
|
-
const
|
|
21818
|
-
const
|
|
21819
|
-
|
|
21820
|
-
"",
|
|
21821
|
-
|
|
21861
|
+
const lines = ["<b>Task Board</b>", ""];
|
|
21862
|
+
const sections = [
|
|
21863
|
+
{ label: "Pending", emoji: "\u{1F4CB}", status: "pending" },
|
|
21864
|
+
{ label: "In Progress", emoji: "\u{1F504}", status: "in_progress" },
|
|
21865
|
+
{ label: "Completed", emoji: "\u2705", status: "completed" },
|
|
21866
|
+
{ label: "Failed", emoji: "\u274C", status: "failed" },
|
|
21867
|
+
{ label: "Abandoned", emoji: "\u{1F6AB}", status: "abandoned" }
|
|
21822
21868
|
];
|
|
21823
|
-
for (const
|
|
21824
|
-
|
|
21825
|
-
|
|
21826
|
-
|
|
21827
|
-
|
|
21828
|
-
|
|
21829
|
-
|
|
21830
|
-
let models;
|
|
21831
|
-
if (serverName) {
|
|
21832
|
-
const server = OllamaStore.getServer(serverName);
|
|
21833
|
-
if (!server) {
|
|
21834
|
-
await channel.sendText(chatId, `Server "${serverName}" not found.`, { parseMode: "plain" });
|
|
21835
|
-
return;
|
|
21869
|
+
for (const { label: label2, emoji, status } of sections) {
|
|
21870
|
+
const list = byStatus[status];
|
|
21871
|
+
if (list.length === 0) continue;
|
|
21872
|
+
lines.push(`${emoji} ${label2}:`);
|
|
21873
|
+
for (const t of list) {
|
|
21874
|
+
const assignee = t.assignee ? ` (\u2192 ${t.assignee.slice(0, 8)})` : "";
|
|
21875
|
+
lines.push(` #${t.id}: ${t.subject}${assignee}`);
|
|
21836
21876
|
}
|
|
21837
|
-
|
|
21838
|
-
} else {
|
|
21839
|
-
models = OllamaStore.getAvailableModels();
|
|
21877
|
+
lines.push("");
|
|
21840
21878
|
}
|
|
21841
|
-
if (
|
|
21842
|
-
|
|
21843
|
-
return;
|
|
21879
|
+
if (lines.length === 2) {
|
|
21880
|
+
lines.push("No tasks yet.");
|
|
21844
21881
|
}
|
|
21845
|
-
|
|
21846
|
-
|
|
21847
|
-
|
|
21848
|
-
|
|
21849
|
-
|
|
21850
|
-
|
|
21851
|
-
|
|
21852
|
-
|
|
21853
|
-
|
|
21854
|
-
|
|
21882
|
+
await channel.sendText(chatId, lines.join("\n").trimEnd(), { parseMode: "html" });
|
|
21883
|
+
if (typeof channel.sendKeyboard === "function" && tasks.length > 0) {
|
|
21884
|
+
const taskButtons = [];
|
|
21885
|
+
const viewable = tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
|
|
21886
|
+
for (const t of viewable.slice(0, 10)) {
|
|
21887
|
+
const statusIcon = t.status === "in_progress" ? "\u{1F504}" : "\u{1F4CB}";
|
|
21888
|
+
taskButtons.push([
|
|
21889
|
+
{ label: `${statusIcon} #${t.id}: ${t.subject.slice(0, 30)}`, data: `tasks:view:${t.id}`, style: "primary" }
|
|
21890
|
+
]);
|
|
21891
|
+
}
|
|
21892
|
+
if (taskButtons.length > 0) {
|
|
21893
|
+
await channel.sendKeyboard(chatId, "View task details:", taskButtons);
|
|
21894
|
+
}
|
|
21855
21895
|
}
|
|
21856
|
-
lines.push("", `${models.length} model${models.length !== 1 ? "s" : ""} total`);
|
|
21857
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
21858
21896
|
}
|
|
21859
|
-
async function
|
|
21860
|
-
|
|
21861
|
-
|
|
21862
|
-
const
|
|
21863
|
-
|
|
21864
|
-
|
|
21865
|
-
await channel.sendText(chatId, "No models found. Check server connectivity.", { parseMode: "plain" });
|
|
21897
|
+
async function handleStopagentCommand(chatId, commandArgs, msg, channel) {
|
|
21898
|
+
if (!commandArgs) {
|
|
21899
|
+
const db4 = getDb();
|
|
21900
|
+
const agents3 = listActiveAgents(db4);
|
|
21901
|
+
if (agents3.length === 0) {
|
|
21902
|
+
await channel.sendText(chatId, "No active agents to stop.", { parseMode: "plain" });
|
|
21866
21903
|
return;
|
|
21867
21904
|
}
|
|
21868
|
-
|
|
21869
|
-
|
|
21870
|
-
const
|
|
21871
|
-
|
|
21905
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21906
|
+
const STATUS_EMOJI_STOP = { running: "\u{1F7E2}", queued: "\u{1F7E1}", starting: "\u{1F535}", idle: "\u26AA" };
|
|
21907
|
+
const agentLines = agents3.map((a) => {
|
|
21908
|
+
const emoji = STATUS_EMOJI_STOP[a.status] ?? "\u26AA";
|
|
21909
|
+
const shortId = a.id.slice(0, 8);
|
|
21910
|
+
const taskLabel = a.task ? a.task.slice(0, 40) : "no task";
|
|
21911
|
+
const runtimeMs = Date.now() - (/* @__PURE__ */ new Date(a.createdAt + "Z")).getTime();
|
|
21912
|
+
const mins = Math.floor(runtimeMs / 6e4);
|
|
21913
|
+
const runtime = mins < 1 ? "<1m" : `${mins}m`;
|
|
21914
|
+
return `${emoji} ${shortId} \u2014 "${taskLabel}" (${a.status}, ${runtime})`;
|
|
21915
|
+
});
|
|
21916
|
+
const buttons = agents3.map((a) => {
|
|
21917
|
+
const shortId = a.id.slice(0, 8);
|
|
21918
|
+
const taskLabel = a.task ? a.task.slice(0, 20) : a.status;
|
|
21919
|
+
return [{ label: `${shortId}: ${taskLabel}`, data: `stopagent:pick:${shortId}`, style: "danger" }];
|
|
21920
|
+
});
|
|
21921
|
+
buttons.push([
|
|
21922
|
+
{ label: "\u{1F6D1} Stop All", data: "stopagent:all", style: "danger" },
|
|
21923
|
+
{ label: "Cancel", data: "stopagent:cancel" }
|
|
21924
|
+
]);
|
|
21925
|
+
await channel.sendKeyboard(chatId, `Stop which agent?
|
|
21926
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
21927
|
+
|
|
21928
|
+
${agentLines.join("\n")}`, buttons);
|
|
21929
|
+
} else {
|
|
21930
|
+
await channel.sendText(chatId, "Usage: /stopagent <id>\nUse first 8 chars of agent ID or full ID.", { parseMode: "plain" });
|
|
21872
21931
|
}
|
|
21873
|
-
|
|
21874
|
-
}
|
|
21875
|
-
|
|
21932
|
+
return;
|
|
21933
|
+
}
|
|
21934
|
+
const db3 = getDb();
|
|
21935
|
+
const agents2 = listActiveAgents(db3);
|
|
21936
|
+
const id = commandArgs.trim();
|
|
21937
|
+
const match = agents2.find((a) => a.id === id || a.id.startsWith(id));
|
|
21938
|
+
if (!match) {
|
|
21939
|
+
await channel.sendText(chatId, `No active agent found matching "${id}". Use /agents to list.`, { parseMode: "plain" });
|
|
21940
|
+
return;
|
|
21876
21941
|
}
|
|
21942
|
+
const ok = cancelAgent(match.id);
|
|
21943
|
+
await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", { parseMode: "plain" });
|
|
21877
21944
|
}
|
|
21878
|
-
async function
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
|
|
21882
|
-
|
|
21883
|
-
|
|
21884
|
-
|
|
21885
|
-
|
|
21945
|
+
async function handleMcpCommand(chatId, commandArgs, msg, channel) {
|
|
21946
|
+
const db3 = getDb();
|
|
21947
|
+
const lines = ["<b>\u{1F50C} MCP Servers</b>"];
|
|
21948
|
+
let totalCount = 0;
|
|
21949
|
+
let connectedCount = 0;
|
|
21950
|
+
const centralMcps = listRegisteredMcps(db3);
|
|
21951
|
+
if (centralMcps.length > 0) {
|
|
21952
|
+
lines.push("", "\u2501\u2501 <b>CC-Claw</b> \u2501\u2501");
|
|
21953
|
+
for (const m of centralMcps) {
|
|
21954
|
+
totalCount++;
|
|
21955
|
+
connectedCount++;
|
|
21956
|
+
const autoTag = m.enabledByDefault ? " \u{1F4CC}" : "";
|
|
21957
|
+
const desc = m.description ? ` <i>${m.description}</i>` : "";
|
|
21958
|
+
lines.push(` \u2705 <b>${m.name}</b>${autoTag}${desc}`);
|
|
21886
21959
|
}
|
|
21887
|
-
|
|
21888
|
-
|
|
21889
|
-
|
|
21890
|
-
|
|
21960
|
+
}
|
|
21961
|
+
if (process.env.DASHBOARD_ENABLED === "1") {
|
|
21962
|
+
totalCount++;
|
|
21963
|
+
connectedCount++;
|
|
21964
|
+
lines.push("", "\u2501\u2501 <b>Built-in</b> \u2501\u2501");
|
|
21965
|
+
lines.push(` \u2705 <b>cc-claw</b> <i>Agent orchestrator (spawn, tasks, inbox)</i>`);
|
|
21966
|
+
}
|
|
21967
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
21968
|
+
const { homedir: homedirImport } = await import("os");
|
|
21969
|
+
const discoveryCwd = homedirImport();
|
|
21970
|
+
const runnerResults = await Promise.allSettled(
|
|
21971
|
+
getAllRunners().map((runner) => {
|
|
21972
|
+
const listCmd = runner.getMcpListCommand();
|
|
21973
|
+
if (!listCmd.length) return Promise.resolve({ runner, output: "" });
|
|
21974
|
+
const exe = runner.getExecutablePath();
|
|
21975
|
+
return new Promise((resolve) => {
|
|
21976
|
+
execFile5(exe, listCmd.slice(1), {
|
|
21977
|
+
encoding: "utf-8",
|
|
21978
|
+
timeout: 3e4,
|
|
21979
|
+
cwd: discoveryCwd,
|
|
21980
|
+
env: runner.getEnv()
|
|
21981
|
+
}, (_err, stdout, stderr) => {
|
|
21982
|
+
const combined = ((stdout ?? "") + "\n" + (stderr ?? "")).trim();
|
|
21983
|
+
resolve({ runner, output: combined });
|
|
21984
|
+
});
|
|
21985
|
+
});
|
|
21986
|
+
})
|
|
21987
|
+
);
|
|
21988
|
+
for (const result of runnerResults) {
|
|
21989
|
+
if (result.status !== "fulfilled") continue;
|
|
21990
|
+
const { runner, output: output2 } = result.value;
|
|
21991
|
+
if (!runner.getMcpListCommand().length) continue;
|
|
21992
|
+
const parsed = parseMcpListOutput(output2);
|
|
21993
|
+
lines.push("", `\u2501\u2501 <b>${runner.displayName}</b> \u2501\u2501`);
|
|
21994
|
+
if (parsed.length === 0) {
|
|
21995
|
+
lines.push(" <i>No servers configured</i>");
|
|
21996
|
+
continue;
|
|
21997
|
+
}
|
|
21998
|
+
for (const srv of parsed) {
|
|
21999
|
+
totalCount++;
|
|
22000
|
+
if (srv.connected) connectedCount++;
|
|
22001
|
+
const icon = srv.connected ? "\u2705" : "\u274C";
|
|
22002
|
+
lines.push(` ${icon} <b>${srv.name}</b>`);
|
|
21891
22003
|
}
|
|
21892
|
-
const online = results.filter((s) => s.status === "online").length;
|
|
21893
|
-
lines.push("", `${online}/${results.length} online`);
|
|
21894
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
21895
|
-
} catch (err) {
|
|
21896
|
-
await channel.sendText(chatId, `Health check failed: ${err instanceof Error ? err.message : String(err)}`, { parseMode: "plain" });
|
|
21897
|
-
}
|
|
21898
|
-
}
|
|
21899
|
-
async function sendRemoveConfirm(chatId, channel, name) {
|
|
21900
|
-
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
21901
|
-
const server = OllamaStore.getServer(name);
|
|
21902
|
-
if (!server) {
|
|
21903
|
-
await channel.sendText(chatId, `Server "${name}" not found.`, { parseMode: "plain" });
|
|
21904
|
-
return;
|
|
21905
22004
|
}
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
`Remove server <b>${name}</b>?`,
|
|
21909
|
-
"",
|
|
21910
|
-
`Host: ${server.host}:${server.port}`,
|
|
21911
|
-
`Status: ${server.status}`,
|
|
21912
|
-
`Models: ${modelCount}`,
|
|
21913
|
-
"",
|
|
21914
|
-
"This will also remove all cached model data."
|
|
21915
|
-
].join("\n");
|
|
21916
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21917
|
-
await channel.sendKeyboard(chatId, text, [
|
|
21918
|
-
[
|
|
21919
|
-
{ label: "\u2705 Confirm", data: `ollama:remove:${name}`, style: "danger" },
|
|
21920
|
-
{ label: "\u274C Cancel", data: "ollama:dashboard" }
|
|
21921
|
-
]
|
|
21922
|
-
]);
|
|
22005
|
+
if (totalCount === 0) {
|
|
22006
|
+
lines.push("", "<i>No MCP servers found.</i>");
|
|
21923
22007
|
} else {
|
|
21924
|
-
|
|
22008
|
+
lines.splice(1, 0, `<i>${connectedCount}/${totalCount} connected</i>`);
|
|
21925
22009
|
}
|
|
22010
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
|
|
21926
22011
|
}
|
|
21927
|
-
async function
|
|
21928
|
-
|
|
21929
|
-
|
|
21930
|
-
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
|
|
21940
|
-
|
|
21941
|
-
|
|
22012
|
+
async function handleCronCommand(chatId, commandArgs, msg, channel) {
|
|
22013
|
+
if (!commandArgs) {
|
|
22014
|
+
await sendJobsBoard(chatId, channel, 1);
|
|
22015
|
+
return;
|
|
22016
|
+
}
|
|
22017
|
+
const cronParts = commandArgs.split(/\s+/);
|
|
22018
|
+
const cronSub = cronParts[0].toLowerCase();
|
|
22019
|
+
const cronSubArgs = cronParts.slice(1).join(" ");
|
|
22020
|
+
switch (cronSub) {
|
|
22021
|
+
case "cancel": {
|
|
22022
|
+
if (!cronSubArgs) {
|
|
22023
|
+
await sendJobPicker(chatId, channel, "cancel");
|
|
22024
|
+
return;
|
|
22025
|
+
}
|
|
22026
|
+
const id = parseInt(cronSubArgs, 10);
|
|
22027
|
+
const ok = cancelJob(id);
|
|
22028
|
+
await channel.sendText(chatId, ok ? `Job #${id} cancelled.` : `Job #${id} not found.`, { parseMode: "plain" });
|
|
22029
|
+
break;
|
|
21942
22030
|
}
|
|
21943
|
-
case "
|
|
21944
|
-
|
|
21945
|
-
|
|
21946
|
-
|
|
21947
|
-
|
|
21948
|
-
|
|
22031
|
+
case "pause": {
|
|
22032
|
+
if (!cronSubArgs) {
|
|
22033
|
+
await sendJobPicker(chatId, channel, "pause");
|
|
22034
|
+
return;
|
|
22035
|
+
}
|
|
22036
|
+
const pauseId = parseInt(cronSubArgs, 10);
|
|
22037
|
+
const paused = pauseJob(pauseId);
|
|
22038
|
+
await channel.sendText(chatId, paused ? `Job #${pauseId} paused.` : `Job #${pauseId} not found.`, { parseMode: "plain" });
|
|
22039
|
+
break;
|
|
22040
|
+
}
|
|
22041
|
+
case "resume": {
|
|
22042
|
+
if (!cronSubArgs) {
|
|
22043
|
+
await sendJobPicker(chatId, channel, "resume");
|
|
22044
|
+
return;
|
|
22045
|
+
}
|
|
22046
|
+
const resumeId = parseInt(cronSubArgs, 10);
|
|
22047
|
+
const resumed = resumeJob(resumeId);
|
|
22048
|
+
await channel.sendText(chatId, resumed ? `Job #${resumeId} resumed.` : `Job #${resumeId} not found.`, { parseMode: "plain" });
|
|
22049
|
+
break;
|
|
22050
|
+
}
|
|
22051
|
+
case "run": {
|
|
22052
|
+
if (!cronSubArgs) {
|
|
22053
|
+
await sendJobPicker(chatId, channel, "run");
|
|
22054
|
+
return;
|
|
22055
|
+
}
|
|
22056
|
+
const runId = parseInt(cronSubArgs, 10);
|
|
22057
|
+
await channel.sendText(chatId, `Triggering job #${runId}...`, { parseMode: "plain" });
|
|
22058
|
+
const runResult = await triggerJob(runId);
|
|
22059
|
+
await channel.sendText(chatId, runResult, { parseMode: "plain" });
|
|
22060
|
+
break;
|
|
22061
|
+
}
|
|
22062
|
+
case "runs": {
|
|
22063
|
+
const runsId = cronSubArgs ? parseInt(cronSubArgs, 10) : void 0;
|
|
22064
|
+
if (runsId) {
|
|
22065
|
+
await sendJobRunsView(chatId, runsId, channel, 1);
|
|
21949
22066
|
} else {
|
|
21950
|
-
|
|
22067
|
+
const cronRuns2 = getJobRuns(void 0, 10);
|
|
22068
|
+
if (cronRuns2.length === 0) {
|
|
22069
|
+
await channel.sendText(chatId, "No run history.", { parseMode: "plain" });
|
|
22070
|
+
return;
|
|
22071
|
+
}
|
|
22072
|
+
const runLines = cronRuns2.map((r) => {
|
|
22073
|
+
const dur = r.durationMs ? ` (${(r.durationMs / 1e3).toFixed(1)}s)` : "";
|
|
22074
|
+
return `#${r.jobId} [${r.status}] ${formatLocalDateTime(r.startedAt)}${dur}`;
|
|
22075
|
+
});
|
|
22076
|
+
await channel.sendText(chatId, runLines.join("\n\n"), { parseMode: "plain" });
|
|
21951
22077
|
}
|
|
21952
|
-
|
|
22078
|
+
break;
|
|
22079
|
+
}
|
|
22080
|
+
case "edit": {
|
|
22081
|
+
if (!cronSubArgs) {
|
|
22082
|
+
await channel.sendText(chatId, "Usage: /cron edit <id>", { parseMode: "plain" });
|
|
22083
|
+
return;
|
|
22084
|
+
}
|
|
22085
|
+
const editId = parseInt(cronSubArgs, 10);
|
|
22086
|
+
await startEditWizard(chatId, editId, channel);
|
|
22087
|
+
break;
|
|
22088
|
+
}
|
|
22089
|
+
case "health": {
|
|
22090
|
+
const report = getHealthReport();
|
|
22091
|
+
await channel.sendText(chatId, formatHealthReport(report), { parseMode: "plain" });
|
|
22092
|
+
break;
|
|
21953
22093
|
}
|
|
21954
|
-
case "add_prompt":
|
|
21955
|
-
await channel.sendText(chatId, [
|
|
21956
|
-
"To add a server, send:",
|
|
21957
|
-
"",
|
|
21958
|
-
" /ollama add <name> <host> [port]",
|
|
21959
|
-
"",
|
|
21960
|
-
"Examples:",
|
|
21961
|
-
" /ollama add local 192.168.1.100",
|
|
21962
|
-
" /ollama add mac-studio 10.0.0.5 11434",
|
|
21963
|
-
" /ollama add cloud api.ollama.example.com 443"
|
|
21964
|
-
].join("\n"), { parseMode: "plain" });
|
|
21965
|
-
return;
|
|
21966
|
-
case "help":
|
|
21967
|
-
await channel.sendText(chatId, [
|
|
21968
|
-
"\u{1F999} Ollama Commands:",
|
|
21969
|
-
"",
|
|
21970
|
-
"/ollama \u2014 Server dashboard",
|
|
21971
|
-
"/ollama add <name> <host> [port] \u2014 Add server",
|
|
21972
|
-
"/ollama remove <name> \u2014 Remove server",
|
|
21973
|
-
"/ollama models [server] \u2014 List models",
|
|
21974
|
-
"/ollama discover [server] \u2014 Discover models",
|
|
21975
|
-
"/ollama health \u2014 Ping all servers"
|
|
21976
|
-
].join("\n"), { parseMode: "plain" });
|
|
21977
|
-
return;
|
|
21978
22094
|
default:
|
|
21979
|
-
await
|
|
22095
|
+
await startWizard(chatId, commandArgs, channel);
|
|
21980
22096
|
}
|
|
21981
22097
|
}
|
|
21982
|
-
var
|
|
21983
|
-
"src/router/
|
|
22098
|
+
var init_command_handlers = __esm({
|
|
22099
|
+
"src/router/command-handlers.ts"() {
|
|
21984
22100
|
"use strict";
|
|
22101
|
+
init_format();
|
|
22102
|
+
init_log();
|
|
22103
|
+
init_format_time();
|
|
22104
|
+
init_version();
|
|
22105
|
+
init_image_gen();
|
|
22106
|
+
init_stt();
|
|
22107
|
+
init_agent();
|
|
22108
|
+
init_classify();
|
|
22109
|
+
init_install();
|
|
22110
|
+
init_profile();
|
|
22111
|
+
init_heartbeat2();
|
|
22112
|
+
init_discover();
|
|
22113
|
+
init_store5();
|
|
22114
|
+
init_summarize();
|
|
22115
|
+
init_session_log();
|
|
22116
|
+
init_backends();
|
|
22117
|
+
init_cron();
|
|
22118
|
+
init_wizard();
|
|
22119
|
+
init_health2();
|
|
22120
|
+
init_store();
|
|
22121
|
+
init_orchestrator();
|
|
22122
|
+
init_registry();
|
|
22123
|
+
init_registry2();
|
|
22124
|
+
init_types();
|
|
22125
|
+
init_store3();
|
|
22126
|
+
init_helpers();
|
|
22127
|
+
init_shell();
|
|
22128
|
+
init_ui();
|
|
22129
|
+
init_state();
|
|
22130
|
+
init_evolve2();
|
|
22131
|
+
init_optimize();
|
|
21985
22132
|
}
|
|
21986
22133
|
});
|
|
21987
22134
|
|
|
@@ -22525,7 +22672,7 @@ ${plan.originalMessage}`;
|
|
|
22525
22672
|
await sendJobsBoard(chatId, channel, 1);
|
|
22526
22673
|
} else if (rest.startsWith("view:")) {
|
|
22527
22674
|
const id = parseInt(rest.slice(5), 10);
|
|
22528
|
-
await sendJobDetail(chatId, id, channel);
|
|
22675
|
+
await sendJobDetail(chatId, id, channel, messageId);
|
|
22529
22676
|
} else if (rest.startsWith("run:")) {
|
|
22530
22677
|
const id = parseInt(rest.slice(4), 10);
|
|
22531
22678
|
await channel.sendText(chatId, `Triggering job #${id}...`, { parseMode: "plain" });
|
|
@@ -22541,7 +22688,7 @@ ${plan.originalMessage}`;
|
|
|
22541
22688
|
const id = parseInt(rest.slice(6), 10);
|
|
22542
22689
|
const paused = pauseJob(id);
|
|
22543
22690
|
if (paused) {
|
|
22544
|
-
await sendJobDetail(chatId, id, channel);
|
|
22691
|
+
await sendJobDetail(chatId, id, channel, messageId);
|
|
22545
22692
|
} else {
|
|
22546
22693
|
await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
|
|
22547
22694
|
}
|
|
@@ -22549,7 +22696,7 @@ ${plan.originalMessage}`;
|
|
|
22549
22696
|
const id = parseInt(rest.slice(7), 10);
|
|
22550
22697
|
const resumed = resumeJob(id);
|
|
22551
22698
|
if (resumed) {
|
|
22552
|
-
await sendJobDetail(chatId, id, channel);
|
|
22699
|
+
await sendJobDetail(chatId, id, channel, messageId);
|
|
22553
22700
|
} else {
|
|
22554
22701
|
await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
|
|
22555
22702
|
}
|
|
@@ -22588,17 +22735,19 @@ This cannot be undone.`,
|
|
|
22588
22735
|
const jobId = parseInt(parts[0], 10);
|
|
22589
22736
|
const page = isPaginationCallback(data, `job:runs:${jobId}:`);
|
|
22590
22737
|
if (page !== null) {
|
|
22591
|
-
await sendJobRunsView(chatId, jobId, channel, page);
|
|
22738
|
+
await sendJobRunsView(chatId, jobId, channel, page, messageId);
|
|
22592
22739
|
} else {
|
|
22593
|
-
await sendJobRunsView(chatId, jobId, channel, 1);
|
|
22740
|
+
await sendJobRunsView(chatId, jobId, channel, 1, messageId);
|
|
22594
22741
|
}
|
|
22595
22742
|
} else if (rest.startsWith("edit:")) {
|
|
22596
22743
|
const id = parseInt(rest.slice(5), 10);
|
|
22597
22744
|
await startEditWizard(chatId, id, channel);
|
|
22745
|
+
} else if (rest === "back") {
|
|
22746
|
+
await sendJobsBoard(chatId, channel, 1, messageId);
|
|
22598
22747
|
} else {
|
|
22599
22748
|
const page = isPaginationCallback(data, "job:");
|
|
22600
22749
|
if (page !== null) {
|
|
22601
|
-
await sendJobsBoard(chatId, channel, page);
|
|
22750
|
+
await sendJobsBoard(chatId, channel, page, messageId);
|
|
22602
22751
|
}
|
|
22603
22752
|
}
|
|
22604
22753
|
return;
|
|
@@ -22991,7 +23140,7 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
22991
23140
|
const rest = data.slice(7);
|
|
22992
23141
|
const page = isPaginationCallback(data, "forget:");
|
|
22993
23142
|
if (page !== null) {
|
|
22994
|
-
await sendForgetPicker(chatId, channel, page);
|
|
23143
|
+
await sendForgetPicker(chatId, channel, page, messageId);
|
|
22995
23144
|
} else if (rest.startsWith("pick:")) {
|
|
22996
23145
|
const id = parseInt(rest.slice(5), 10);
|
|
22997
23146
|
const memory2 = getMemoryById(id);
|
|
@@ -23032,7 +23181,7 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
23032
23181
|
const rest = data.slice(4);
|
|
23033
23182
|
const page = isPaginationCallback(data, "mem:");
|
|
23034
23183
|
if (page !== null) {
|
|
23035
|
-
await sendMemoryPage(chatId, channel, page);
|
|
23184
|
+
await sendMemoryPage(chatId, channel, page, messageId);
|
|
23036
23185
|
} else if (rest.startsWith("view:")) {
|
|
23037
23186
|
const id = parseInt(rest.slice(5), 10);
|
|
23038
23187
|
const memory2 = getMemoryById(id);
|
|
@@ -23093,7 +23242,7 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
23093
23242
|
const id = parseInt(rest.slice(5), 10);
|
|
23094
23243
|
await channel.sendText(chatId, `Type: /memory edit ${id} <new content>`, { parseMode: "plain" });
|
|
23095
23244
|
} else if (rest === "back") {
|
|
23096
|
-
await sendMemoryPage(chatId, channel, 1);
|
|
23245
|
+
await sendMemoryPage(chatId, channel, 1, messageId);
|
|
23097
23246
|
} else if (rest === "showall") {
|
|
23098
23247
|
const memories = listMemories();
|
|
23099
23248
|
const lines = memories.map(
|
|
@@ -23172,7 +23321,7 @@ Example: /limits ${bid} daily 500000`, { parseMode: "plain" });
|
|
|
23172
23321
|
} else if (data.startsWith("skills:page:")) {
|
|
23173
23322
|
const page = parseInt(data.slice(12), 10);
|
|
23174
23323
|
const skills2 = await discoverAllSkills();
|
|
23175
|
-
await sendSkillsPage(chatId, channel, skills2, page);
|
|
23324
|
+
await sendSkillsPage(chatId, channel, skills2, page, messageId);
|
|
23176
23325
|
} else if (data.startsWith("skill:")) {
|
|
23177
23326
|
const parts = data.slice(6).split(":");
|
|
23178
23327
|
let skillName;
|
|
@@ -23347,6 +23496,10 @@ async function handleText(msg, channel) {
|
|
|
23347
23496
|
await handleWizardText(chatId, text, channel);
|
|
23348
23497
|
return;
|
|
23349
23498
|
}
|
|
23499
|
+
if (hasOllamaWizard(chatId)) {
|
|
23500
|
+
await handleOllamaWizardText(chatId, text, channel);
|
|
23501
|
+
return;
|
|
23502
|
+
}
|
|
23350
23503
|
const rememberMatch = text.match(/^remember\s+(?:that\s+)?(.+)/i);
|
|
23351
23504
|
if (rememberMatch) {
|
|
23352
23505
|
const content = rememberMatch[1];
|
|
@@ -23807,6 +23960,7 @@ var init_router2 = __esm({
|
|
|
23807
23960
|
init_store5();
|
|
23808
23961
|
init_backends();
|
|
23809
23962
|
init_wizard();
|
|
23963
|
+
init_ollama3();
|
|
23810
23964
|
init_classify2();
|
|
23811
23965
|
init_session_log2();
|
|
23812
23966
|
init_live_status();
|
|
@@ -25283,6 +25437,46 @@ var init_telegram2 = __esm({
|
|
|
25283
25437
|
}
|
|
25284
25438
|
}
|
|
25285
25439
|
}
|
|
25440
|
+
async editKeyboard(chatId, messageId, text, buttons) {
|
|
25441
|
+
const keyboard = new InlineKeyboard();
|
|
25442
|
+
for (const row of buttons) {
|
|
25443
|
+
for (const btn of row) {
|
|
25444
|
+
keyboard.text(btn.label, btn.data);
|
|
25445
|
+
if (btn.style === "success") keyboard.success();
|
|
25446
|
+
else if (btn.style === "danger") keyboard.danger();
|
|
25447
|
+
else if (btn.style === "primary") keyboard.primary();
|
|
25448
|
+
}
|
|
25449
|
+
keyboard.row();
|
|
25450
|
+
}
|
|
25451
|
+
const formatted = sanitizeForTelegram(formatForTelegram(text));
|
|
25452
|
+
try {
|
|
25453
|
+
await withRetry(
|
|
25454
|
+
"editKeyboard:html",
|
|
25455
|
+
() => this.bot.api.editMessageText(numericChatId(chatId), parseInt(messageId), formatted, {
|
|
25456
|
+
parse_mode: "HTML",
|
|
25457
|
+
reply_markup: keyboard
|
|
25458
|
+
})
|
|
25459
|
+
);
|
|
25460
|
+
return true;
|
|
25461
|
+
} catch (err) {
|
|
25462
|
+
if (isRateLimitError(err)) return false;
|
|
25463
|
+
try {
|
|
25464
|
+
await withRetry(
|
|
25465
|
+
"editKeyboard:plain",
|
|
25466
|
+
() => this.bot.api.editMessageText(
|
|
25467
|
+
numericChatId(chatId),
|
|
25468
|
+
parseInt(messageId),
|
|
25469
|
+
formatted.replace(/<[^>]+>/g, ""),
|
|
25470
|
+
{ reply_markup: keyboard }
|
|
25471
|
+
)
|
|
25472
|
+
);
|
|
25473
|
+
return true;
|
|
25474
|
+
} catch (err2) {
|
|
25475
|
+
warn("[telegram] editKeyboard failed:", err2 instanceof Error ? err2.message : err2);
|
|
25476
|
+
return false;
|
|
25477
|
+
}
|
|
25478
|
+
}
|
|
25479
|
+
}
|
|
25286
25480
|
/** Register a handler for inline keyboard callback queries */
|
|
25287
25481
|
onCallback(handler) {
|
|
25288
25482
|
this.callbackHandlers.push(handler);
|
|
@@ -27338,9 +27532,10 @@ var init_status = __esm({
|
|
|
27338
27532
|
// src/cli/commands/doctor.ts
|
|
27339
27533
|
var doctor_exports = {};
|
|
27340
27534
|
__export(doctor_exports, {
|
|
27341
|
-
doctorCommand: () => doctorCommand
|
|
27535
|
+
doctorCommand: () => doctorCommand,
|
|
27536
|
+
doctorErrors: () => doctorErrors
|
|
27342
27537
|
});
|
|
27343
|
-
import { existsSync as existsSync30, statSync as statSync10, accessSync, constants } from "fs";
|
|
27538
|
+
import { existsSync as existsSync30, statSync as statSync10, accessSync, readFileSync as readFileSync20, constants } from "fs";
|
|
27344
27539
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
27345
27540
|
async function doctorCommand(globalOpts, localOpts) {
|
|
27346
27541
|
const checks = [];
|
|
@@ -27456,8 +27651,8 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
27456
27651
|
}
|
|
27457
27652
|
if (existsSync30(ERROR_LOG_PATH)) {
|
|
27458
27653
|
try {
|
|
27459
|
-
const { readFileSync:
|
|
27460
|
-
const logContent =
|
|
27654
|
+
const { readFileSync: readFileSync28 } = await import("fs");
|
|
27655
|
+
const logContent = readFileSync28(ERROR_LOG_PATH, "utf-8");
|
|
27461
27656
|
const recentLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
27462
27657
|
const last24h = Date.now() - 864e5;
|
|
27463
27658
|
const recentErrors = recentLines.filter((line) => {
|
|
@@ -27469,29 +27664,38 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
27469
27664
|
let rate429 = 0;
|
|
27470
27665
|
let contentSilence = 0;
|
|
27471
27666
|
let spawnTimeout = 0;
|
|
27472
|
-
|
|
27667
|
+
const otherLines = [];
|
|
27473
27668
|
for (const line of recentErrors) {
|
|
27474
27669
|
if (/429|rate.?limit/i.test(line)) rate429++;
|
|
27475
27670
|
else if (/content silence/i.test(line)) contentSilence++;
|
|
27476
27671
|
else if (/spawn timeout|timeout after \d+s/i.test(line)) spawnTimeout++;
|
|
27477
|
-
else
|
|
27672
|
+
else otherLines.push(line);
|
|
27478
27673
|
}
|
|
27479
|
-
const
|
|
27674
|
+
const viewFix = "cc-claw logs --error to view details";
|
|
27675
|
+
const clearFix = "cc-claw doctor --fix to clear stale errors";
|
|
27480
27676
|
if (rate429 > 10) {
|
|
27481
|
-
checks.push({ name: "Telegram rate limits", status: "error", message: `${rate429} rate-limit (429) errors in last 24h \u2014 message delivery blocked`, fix:
|
|
27677
|
+
checks.push({ name: "Telegram rate limits", status: "error", message: `${rate429} rate-limit (429) errors in last 24h \u2014 message delivery blocked`, fix: `${viewFix}, ${clearFix}` });
|
|
27482
27678
|
} else if (rate429 > 0) {
|
|
27483
|
-
checks.push({ name: "Telegram rate limits", status: "warning", message: `${rate429} rate-limit (429) errors in last 24h`, fix:
|
|
27679
|
+
checks.push({ name: "Telegram rate limits", status: "warning", message: `${rate429} rate-limit (429) errors in last 24h`, fix: `${viewFix}, ${clearFix}` });
|
|
27484
27680
|
}
|
|
27485
27681
|
if (contentSilence > 0) {
|
|
27486
|
-
checks.push({ name: "Content silence", status: "warning", message: `${contentSilence} agent silence timeout(s) in last 24h \u2014 API went unresponsive`, fix:
|
|
27682
|
+
checks.push({ name: "Content silence", status: "warning", message: `${contentSilence} agent silence timeout(s) in last 24h \u2014 API went unresponsive`, fix: `${viewFix}, ${clearFix}` });
|
|
27487
27683
|
}
|
|
27488
27684
|
if (spawnTimeout > 0) {
|
|
27489
|
-
checks.push({ name: "Spawn timeouts", status: "warning", message: `${spawnTimeout} backend timeout(s) in last 24h`, fix:
|
|
27490
|
-
}
|
|
27491
|
-
if (
|
|
27492
|
-
|
|
27685
|
+
checks.push({ name: "Spawn timeouts", status: "warning", message: `${spawnTimeout} backend timeout(s) in last 24h`, fix: `${viewFix}, ${clearFix}` });
|
|
27686
|
+
}
|
|
27687
|
+
if (otherLines.length > 0) {
|
|
27688
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
27689
|
+
for (const line of otherLines) {
|
|
27690
|
+
const body = line.replace(/^\[\d{4}-\d{2}-\d{2}\s+[\d:+-]+\]\s*/, "").slice(0, 120);
|
|
27691
|
+
const key = body.slice(0, 80);
|
|
27692
|
+
summaries.set(key, (summaries.get(key) ?? 0) + 1);
|
|
27693
|
+
}
|
|
27694
|
+
const topErrors = [...summaries.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([msg, count]) => count > 1 ? `${msg}\u2026 (\xD7${count})` : `${msg}\u2026`).join("\n ");
|
|
27695
|
+
checks.push({ name: "Other errors", status: "warning", message: `${otherLines.length} error(s) in last 24h:
|
|
27696
|
+
${topErrors}`, fix: `${viewFix}, ${clearFix}` });
|
|
27493
27697
|
}
|
|
27494
|
-
if (rate429 === 0 && contentSilence === 0 && spawnTimeout === 0 &&
|
|
27698
|
+
if (rate429 === 0 && contentSilence === 0 && spawnTimeout === 0 && otherLines.length === 0) {
|
|
27495
27699
|
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
27496
27700
|
}
|
|
27497
27701
|
} else {
|
|
@@ -27631,6 +27835,86 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
27631
27835
|
});
|
|
27632
27836
|
process.exit(errors > 0 ? 1 : warnings > 0 ? 2 : 0);
|
|
27633
27837
|
}
|
|
27838
|
+
function getRecentErrors() {
|
|
27839
|
+
if (!existsSync30(ERROR_LOG_PATH)) return null;
|
|
27840
|
+
const logContent = readFileSync20(ERROR_LOG_PATH, "utf-8");
|
|
27841
|
+
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
27842
|
+
const last24h = Date.now() - 864e5;
|
|
27843
|
+
const lines = allLines.filter((line) => {
|
|
27844
|
+
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:+-]+)/);
|
|
27845
|
+
if (match) return (/* @__PURE__ */ new Date(`${match[1]}T${match[2]}`)).getTime() > last24h;
|
|
27846
|
+
return false;
|
|
27847
|
+
});
|
|
27848
|
+
if (lines.length === 0) return null;
|
|
27849
|
+
const classified = { rate429: [], contentSilence: [], spawnTimeout: [], other: [] };
|
|
27850
|
+
for (const line of lines) {
|
|
27851
|
+
if (/429|rate.?limit/i.test(line)) classified.rate429.push(line);
|
|
27852
|
+
else if (/content silence/i.test(line)) classified.contentSilence.push(line);
|
|
27853
|
+
else if (/spawn timeout|timeout after \d+s/i.test(line)) classified.spawnTimeout.push(line);
|
|
27854
|
+
else classified.other.push(line);
|
|
27855
|
+
}
|
|
27856
|
+
return { lines, classified };
|
|
27857
|
+
}
|
|
27858
|
+
function stripTimestamp(line) {
|
|
27859
|
+
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:+-]+)\]\s*(.*)/);
|
|
27860
|
+
if (match) return { time: match[2].replace(/[-+]\d{4}$/, ""), body: match[3] };
|
|
27861
|
+
return { time: "", body: line };
|
|
27862
|
+
}
|
|
27863
|
+
async function doctorErrors(globalOpts) {
|
|
27864
|
+
const result = getRecentErrors();
|
|
27865
|
+
if (!result) {
|
|
27866
|
+
output({ errors: [] }, () => `
|
|
27867
|
+
${success("No errors in last 24h.")}
|
|
27868
|
+
`);
|
|
27869
|
+
return;
|
|
27870
|
+
}
|
|
27871
|
+
const { classified } = result;
|
|
27872
|
+
const total = classified.rate429.length + classified.contentSilence.length + classified.spawnTimeout.length + classified.other.length;
|
|
27873
|
+
output({ total, classified }, () => {
|
|
27874
|
+
const lines = [
|
|
27875
|
+
"",
|
|
27876
|
+
box("Recent Errors (last 24h)"),
|
|
27877
|
+
""
|
|
27878
|
+
];
|
|
27879
|
+
const renderCategory = (label2, items, maxShow) => {
|
|
27880
|
+
if (items.length === 0) return;
|
|
27881
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
27882
|
+
for (const line of items) {
|
|
27883
|
+
const { time, body } = stripTimestamp(line);
|
|
27884
|
+
const key = body.slice(0, 120);
|
|
27885
|
+
const existing = deduped.get(key);
|
|
27886
|
+
if (existing) {
|
|
27887
|
+
existing.count++;
|
|
27888
|
+
existing.lastTime = time;
|
|
27889
|
+
} else {
|
|
27890
|
+
deduped.set(key, { count: 1, lastTime: time });
|
|
27891
|
+
}
|
|
27892
|
+
}
|
|
27893
|
+
lines.push(` ${warnMark()} ${label2} (${items.length})`);
|
|
27894
|
+
const entries = [...deduped.entries()].sort((a, b) => b[1].count - a[1].count).slice(0, maxShow);
|
|
27895
|
+
for (const [msg, { count, lastTime }] of entries) {
|
|
27896
|
+
const timeStr = lastTime ? `[${lastTime}] ` : "";
|
|
27897
|
+
const countStr = count > 1 ? muted(` (\xD7${count})`) : "";
|
|
27898
|
+
lines.push(` ${timeStr}${msg.slice(0, 100)}${countStr}`);
|
|
27899
|
+
}
|
|
27900
|
+
if (deduped.size > maxShow) {
|
|
27901
|
+
lines.push(muted(` \u2026 and ${deduped.size - maxShow} more unique error(s)`));
|
|
27902
|
+
}
|
|
27903
|
+
lines.push("");
|
|
27904
|
+
};
|
|
27905
|
+
renderCategory("Telegram rate limits", classified.rate429, 5);
|
|
27906
|
+
renderCategory("Content silence timeouts", classified.contentSilence, 5);
|
|
27907
|
+
renderCategory("Spawn timeouts", classified.spawnTimeout, 5);
|
|
27908
|
+
renderCategory("Other errors", classified.other, 10);
|
|
27909
|
+
if (total === 0) {
|
|
27910
|
+
lines.push(` ${success("No errors in last 24h.")}`);
|
|
27911
|
+
} else {
|
|
27912
|
+
lines.push(muted(` ${total} total error(s). Run cc-claw doctor --fix to clear.`));
|
|
27913
|
+
}
|
|
27914
|
+
lines.push("");
|
|
27915
|
+
return lines.join("\n");
|
|
27916
|
+
});
|
|
27917
|
+
}
|
|
27634
27918
|
function formatUptime3(seconds) {
|
|
27635
27919
|
seconds = Math.floor(seconds);
|
|
27636
27920
|
if (seconds < 60) return `${seconds}s`;
|
|
@@ -27652,7 +27936,7 @@ var logs_exports = {};
|
|
|
27652
27936
|
__export(logs_exports, {
|
|
27653
27937
|
logsCommand: () => logsCommand
|
|
27654
27938
|
});
|
|
27655
|
-
import { existsSync as existsSync31, readFileSync as
|
|
27939
|
+
import { existsSync as existsSync31, readFileSync as readFileSync21, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
27656
27940
|
async function logsCommand(opts) {
|
|
27657
27941
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
27658
27942
|
if (!existsSync31(logFile)) {
|
|
@@ -27660,7 +27944,7 @@ async function logsCommand(opts) {
|
|
|
27660
27944
|
process.exit(1);
|
|
27661
27945
|
}
|
|
27662
27946
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
27663
|
-
const content =
|
|
27947
|
+
const content = readFileSync21(logFile, "utf-8");
|
|
27664
27948
|
const allLines = content.split("\n");
|
|
27665
27949
|
const tailLines = allLines.slice(-maxLines);
|
|
27666
27950
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -27670,7 +27954,7 @@ async function logsCommand(opts) {
|
|
|
27670
27954
|
let lastLength = content.length;
|
|
27671
27955
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
27672
27956
|
try {
|
|
27673
|
-
const newContent =
|
|
27957
|
+
const newContent = readFileSync21(logFile, "utf-8");
|
|
27674
27958
|
if (newContent.length > lastLength) {
|
|
27675
27959
|
const newPart = newContent.slice(lastLength);
|
|
27676
27960
|
process.stdout.write(newPart);
|
|
@@ -27702,7 +27986,7 @@ __export(session_logs_exports, {
|
|
|
27702
27986
|
sessionLogsList: () => sessionLogsList,
|
|
27703
27987
|
sessionLogsTail: () => sessionLogsTail
|
|
27704
27988
|
});
|
|
27705
|
-
import { readFileSync as
|
|
27989
|
+
import { readFileSync as readFileSync22, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
27706
27990
|
async function sessionLogsList(opts) {
|
|
27707
27991
|
const logs = listSessionLogs();
|
|
27708
27992
|
if (logs.length === 0) {
|
|
@@ -27759,12 +28043,12 @@ async function sessionLogsTail(opts) {
|
|
|
27759
28043
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
27760
28044
|
let lastLength = 0;
|
|
27761
28045
|
try {
|
|
27762
|
-
lastLength =
|
|
28046
|
+
lastLength = readFileSync22(targetPath, "utf-8").length;
|
|
27763
28047
|
} catch {
|
|
27764
28048
|
}
|
|
27765
28049
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
27766
28050
|
try {
|
|
27767
|
-
const content =
|
|
28051
|
+
const content = readFileSync22(targetPath, "utf-8");
|
|
27768
28052
|
if (content.length > lastLength) {
|
|
27769
28053
|
process.stdout.write(content.slice(lastLength));
|
|
27770
28054
|
lastLength = content.length;
|
|
@@ -27806,7 +28090,7 @@ __export(gemini_exports, {
|
|
|
27806
28090
|
geminiReorder: () => geminiReorder,
|
|
27807
28091
|
geminiRotation: () => geminiRotation
|
|
27808
28092
|
});
|
|
27809
|
-
import { existsSync as existsSync33, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as
|
|
28093
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync23, chmodSync } from "fs";
|
|
27810
28094
|
import { join as join31 } from "path";
|
|
27811
28095
|
import { createInterface as createInterface8 } from "readline";
|
|
27812
28096
|
function requireDb() {
|
|
@@ -27837,7 +28121,7 @@ function resolveOAuthEmail(configHome) {
|
|
|
27837
28121
|
try {
|
|
27838
28122
|
const accountsPath = join31(configHome, ".gemini", "google_accounts.json");
|
|
27839
28123
|
if (!existsSync33(accountsPath)) return null;
|
|
27840
|
-
const accounts = JSON.parse(
|
|
28124
|
+
const accounts = JSON.parse(readFileSync23(accountsPath, "utf-8"));
|
|
27841
28125
|
return accounts.active || null;
|
|
27842
28126
|
} catch {
|
|
27843
28127
|
return null;
|
|
@@ -28071,7 +28355,7 @@ __export(backend_cmd_factory_exports, {
|
|
|
28071
28355
|
makeReorder: () => makeReorder,
|
|
28072
28356
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
28073
28357
|
});
|
|
28074
|
-
import { existsSync as existsSync34, mkdirSync as mkdirSync15, readFileSync as
|
|
28358
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync15, readFileSync as readFileSync24 } from "fs";
|
|
28075
28359
|
import { join as join32 } from "path";
|
|
28076
28360
|
import { createInterface as createInterface9 } from "readline";
|
|
28077
28361
|
function requireDb2() {
|
|
@@ -28343,7 +28627,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
28343
28627
|
const claudeJsonNested = join32(slotDir, ".claude", ".claude.json");
|
|
28344
28628
|
if (existsSync34(claudeJson)) {
|
|
28345
28629
|
try {
|
|
28346
|
-
const data = JSON.parse(
|
|
28630
|
+
const data = JSON.parse(readFileSync24(claudeJson, "utf-8"));
|
|
28347
28631
|
return Boolean(data.oauthAccount);
|
|
28348
28632
|
} catch {
|
|
28349
28633
|
return false;
|
|
@@ -28351,7 +28635,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
28351
28635
|
}
|
|
28352
28636
|
if (existsSync34(claudeJsonNested)) {
|
|
28353
28637
|
try {
|
|
28354
|
-
const data = JSON.parse(
|
|
28638
|
+
const data = JSON.parse(readFileSync24(claudeJsonNested, "utf-8"));
|
|
28355
28639
|
return Boolean(data.oauthAccount);
|
|
28356
28640
|
} catch {
|
|
28357
28641
|
return false;
|
|
@@ -28374,7 +28658,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
28374
28658
|
try {
|
|
28375
28659
|
const claudeJson = join32(slotDir, ".claude.json");
|
|
28376
28660
|
if (existsSync34(claudeJson)) {
|
|
28377
|
-
const data = JSON.parse(
|
|
28661
|
+
const data = JSON.parse(readFileSync24(claudeJson, "utf-8"));
|
|
28378
28662
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
28379
28663
|
}
|
|
28380
28664
|
} catch {
|
|
@@ -28393,7 +28677,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
28393
28677
|
},
|
|
28394
28678
|
extractLabel: (slotDir) => {
|
|
28395
28679
|
try {
|
|
28396
|
-
const authData = JSON.parse(
|
|
28680
|
+
const authData = JSON.parse(readFileSync24(join32(slotDir, "auth.json"), "utf-8"));
|
|
28397
28681
|
if (authData.email) return authData.email;
|
|
28398
28682
|
if (authData.account_name) return authData.account_name;
|
|
28399
28683
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -29638,7 +29922,7 @@ __export(config_exports2, {
|
|
|
29638
29922
|
configList: () => configList,
|
|
29639
29923
|
configSet: () => configSet
|
|
29640
29924
|
});
|
|
29641
|
-
import { existsSync as existsSync43, readFileSync as
|
|
29925
|
+
import { existsSync as existsSync43, readFileSync as readFileSync25 } from "fs";
|
|
29642
29926
|
async function configList(globalOpts) {
|
|
29643
29927
|
if (!existsSync43(DB_PATH)) {
|
|
29644
29928
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
@@ -29724,7 +30008,7 @@ async function configEnv(_globalOpts) {
|
|
|
29724
30008
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
29725
30009
|
process.exit(1);
|
|
29726
30010
|
}
|
|
29727
|
-
const content =
|
|
30011
|
+
const content = readFileSync25(ENV_PATH, "utf-8");
|
|
29728
30012
|
const entries = {};
|
|
29729
30013
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
29730
30014
|
for (const line of content.split("\n")) {
|
|
@@ -30473,11 +30757,11 @@ __export(chat_exports2, {
|
|
|
30473
30757
|
chatSend: () => chatSend
|
|
30474
30758
|
});
|
|
30475
30759
|
import { request as httpRequest2 } from "http";
|
|
30476
|
-
import { readFileSync as
|
|
30760
|
+
import { readFileSync as readFileSync26, existsSync as existsSync53 } from "fs";
|
|
30477
30761
|
function getToken2() {
|
|
30478
30762
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
30479
30763
|
try {
|
|
30480
|
-
if (existsSync53(TOKEN_PATH2)) return
|
|
30764
|
+
if (existsSync53(TOKEN_PATH2)) return readFileSync26(TOKEN_PATH2, "utf-8").trim();
|
|
30481
30765
|
} catch {
|
|
30482
30766
|
}
|
|
30483
30767
|
return null;
|
|
@@ -31423,7 +31707,7 @@ var init_optimize2 = __esm({
|
|
|
31423
31707
|
|
|
31424
31708
|
// src/setup.ts
|
|
31425
31709
|
var setup_exports = {};
|
|
31426
|
-
import { existsSync as existsSync55, writeFileSync as writeFileSync12, readFileSync as
|
|
31710
|
+
import { existsSync as existsSync55, writeFileSync as writeFileSync12, readFileSync as readFileSync27, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
31427
31711
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
31428
31712
|
import { createInterface as createInterface11 } from "readline";
|
|
31429
31713
|
import { join as join34 } from "path";
|
|
@@ -31508,7 +31792,7 @@ async function setup() {
|
|
|
31508
31792
|
if (envSource) {
|
|
31509
31793
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
31510
31794
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
31511
|
-
const existing =
|
|
31795
|
+
const existing = readFileSync27(envSource, "utf-8");
|
|
31512
31796
|
for (const line of existing.split("\n")) {
|
|
31513
31797
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
31514
31798
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
@@ -31849,9 +32133,14 @@ program.command("status").description("Comprehensive system status").option("--d
|
|
|
31849
32133
|
const { statusCommand: statusCommand2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
31850
32134
|
await statusCommand2(program.opts(), opts);
|
|
31851
32135
|
});
|
|
31852
|
-
program.command("doctor").description("Diagnose common issues and suggest fixes").option("--fix", "Auto-apply safe fixes").action(async (opts) => {
|
|
31853
|
-
|
|
31854
|
-
|
|
32136
|
+
program.command("doctor").description("Diagnose common issues and suggest fixes").option("--fix", "Auto-apply safe fixes").argument("[subcommand]", "Sub-command: errors \u2014 show recent errors classified").action(async (subcommand, opts) => {
|
|
32137
|
+
if (subcommand === "errors") {
|
|
32138
|
+
const { doctorErrors: doctorErrors2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
|
|
32139
|
+
await doctorErrors2(program.opts());
|
|
32140
|
+
} else {
|
|
32141
|
+
const { doctorCommand: doctorCommand2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
|
|
32142
|
+
await doctorCommand2(program.opts(), opts);
|
|
32143
|
+
}
|
|
31855
32144
|
});
|
|
31856
32145
|
program.command("logs").description("Tail daemon logs").option("-f, --follow", "Follow mode (like tail -f)").option("--error", "Show error log instead of stdout").option("--lines <n>", "Number of lines", "100").action(async (opts) => {
|
|
31857
32146
|
const { logsCommand: logsCommand2 } = await Promise.resolve().then(() => (init_logs(), logs_exports));
|