cc-claw 0.2.4 → 0.2.5
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 +139 -96
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ var VERSION;
|
|
|
48
48
|
var init_version = __esm({
|
|
49
49
|
"src/version.ts"() {
|
|
50
50
|
"use strict";
|
|
51
|
-
VERSION = true ? "0.2.
|
|
51
|
+
VERSION = true ? "0.2.5" : (() => {
|
|
52
52
|
try {
|
|
53
53
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
54
54
|
} catch {
|
|
@@ -345,7 +345,7 @@ function claimTask(db3, taskId, agentId) {
|
|
|
345
345
|
if (blockedBy.length > 0) {
|
|
346
346
|
const placeholders = blockedBy.map(() => "?").join(",");
|
|
347
347
|
const incomplete = db3.prepare(
|
|
348
|
-
`SELECT COUNT(*) as count FROM agent_tasks WHERE id IN (${placeholders}) AND status NOT IN ('completed')`
|
|
348
|
+
`SELECT COUNT(*) as count FROM agent_tasks WHERE id IN (${placeholders}) AND status NOT IN ('completed', 'failed', 'abandoned')`
|
|
349
349
|
).get(...blockedBy);
|
|
350
350
|
if (incomplete.count > 0) return false;
|
|
351
351
|
}
|
|
@@ -518,19 +518,18 @@ function cleanupActivity(db3) {
|
|
|
518
518
|
if (aged.changes > 0) {
|
|
519
519
|
log(`[activity] Pruned ${aged.changes} events older than ${RETENTION_DAYS} days`);
|
|
520
520
|
}
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
521
|
+
const excess = db3.prepare(`
|
|
522
|
+
DELETE FROM activity_log
|
|
523
|
+
WHERE id NOT IN (
|
|
524
|
+
SELECT id FROM (
|
|
525
|
+
SELECT id, ROW_NUMBER() OVER (PARTITION BY chatId ORDER BY createdAt DESC) AS rn
|
|
526
|
+
FROM activity_log
|
|
527
|
+
) ranked
|
|
528
|
+
WHERE rn <= ?
|
|
529
|
+
)
|
|
530
|
+
`).run(MAX_ROWS_PER_CHAT);
|
|
531
|
+
if (excess.changes > 0) {
|
|
532
|
+
log(`[activity] Capped activity log: pruned ${excess.changes} excess rows`);
|
|
534
533
|
}
|
|
535
534
|
}
|
|
536
535
|
var RETENTION_DAYS, MAX_ROWS_PER_CHAT;
|
|
@@ -1361,7 +1360,7 @@ function searchMemories(queryText, limit = 3) {
|
|
|
1361
1360
|
for (const mem of ftsResults) {
|
|
1362
1361
|
db.prepare(`
|
|
1363
1362
|
UPDATE memories
|
|
1364
|
-
SET salience = MIN(salience + 0.1
|
|
1363
|
+
SET salience = MAX(0, MIN(1.0, salience + 0.1)),
|
|
1365
1364
|
access_count = access_count + 1,
|
|
1366
1365
|
last_accessed = datetime('now')
|
|
1367
1366
|
WHERE id = ?
|
|
@@ -1640,7 +1639,7 @@ function searchSessionSummaries(queryText, limit = 3) {
|
|
|
1640
1639
|
for (const s of results) {
|
|
1641
1640
|
db.prepare(`
|
|
1642
1641
|
UPDATE session_summaries
|
|
1643
|
-
SET salience = MIN(salience + 0.1
|
|
1642
|
+
SET salience = MAX(0, MIN(1.0, salience + 0.1))
|
|
1644
1643
|
WHERE id = ?
|
|
1645
1644
|
`).run(s.id);
|
|
1646
1645
|
}
|
|
@@ -2233,7 +2232,7 @@ var init_claude = __esm({
|
|
|
2233
2232
|
}
|
|
2234
2233
|
try {
|
|
2235
2234
|
this._resolvedPath = execSync("which claude", { encoding: "utf-8" }).trim();
|
|
2236
|
-
} catch {
|
|
2235
|
+
} catch (_err) {
|
|
2237
2236
|
const candidates = [
|
|
2238
2237
|
"/opt/homebrew/bin/claude",
|
|
2239
2238
|
"/usr/local/bin/claude"
|
|
@@ -2712,8 +2711,7 @@ function normalizeFtsScores(items) {
|
|
|
2712
2711
|
const ranks = items.map((it) => Math.abs(it._ftsRank ?? 0));
|
|
2713
2712
|
const maxRank = Math.max(...ranks, 1e-3);
|
|
2714
2713
|
for (let i = 0; i < items.length; i++) {
|
|
2715
|
-
|
|
2716
|
-
scores.set(id, ranks[i] / maxRank);
|
|
2714
|
+
scores.set(items[i].id, ranks[i] / maxRank);
|
|
2717
2715
|
}
|
|
2718
2716
|
return scores;
|
|
2719
2717
|
}
|
|
@@ -2929,13 +2927,13 @@ function syncNativeCliFiles() {
|
|
|
2929
2927
|
} catch {
|
|
2930
2928
|
}
|
|
2931
2929
|
try {
|
|
2932
|
-
const
|
|
2933
|
-
const
|
|
2934
|
-
if (
|
|
2930
|
+
const soulMtime = existsSync4(SOUL_PATH) ? statSync(SOUL_PATH).mtimeMs : 0;
|
|
2931
|
+
const userMtime = existsSync4(USER_PATH) ? statSync(USER_PATH).mtimeMs : 0;
|
|
2932
|
+
if (soulMtime === lastSoulMtime && userMtime === lastUserMtime) {
|
|
2935
2933
|
return;
|
|
2936
2934
|
}
|
|
2937
|
-
lastSoulMtime =
|
|
2938
|
-
lastUserMtime =
|
|
2935
|
+
lastSoulMtime = soulMtime;
|
|
2936
|
+
lastUserMtime = userMtime;
|
|
2939
2937
|
} catch {
|
|
2940
2938
|
}
|
|
2941
2939
|
const nativeContent = [
|
|
@@ -3267,13 +3265,14 @@ ${transcript}`;
|
|
|
3267
3265
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3268
3266
|
...config2.cwd ? { cwd: config2.cwd } : {}
|
|
3269
3267
|
});
|
|
3268
|
+
proc.stderr?.resume();
|
|
3269
|
+
const rl2 = createInterface({ input: proc.stdout });
|
|
3270
3270
|
const timeout = setTimeout(() => {
|
|
3271
3271
|
warn(`[summarize] Timeout after ${SUMMARIZE_TIMEOUT_MS / 1e3}s for chat ${chatId} \u2014 killing process`);
|
|
3272
|
+
rl2.close();
|
|
3272
3273
|
proc.kill("SIGTERM");
|
|
3273
3274
|
setTimeout(() => proc.kill("SIGKILL"), 2e3);
|
|
3274
3275
|
}, SUMMARIZE_TIMEOUT_MS);
|
|
3275
|
-
proc.stderr?.resume();
|
|
3276
|
-
const rl2 = createInterface({ input: proc.stdout });
|
|
3277
3276
|
rl2.on("line", (line) => {
|
|
3278
3277
|
if (!line.trim()) return;
|
|
3279
3278
|
let msg;
|
|
@@ -3295,9 +3294,9 @@ ${transcript}`;
|
|
|
3295
3294
|
if (ev.type === "result") {
|
|
3296
3295
|
resultText = ev.resultText || accumulatedText;
|
|
3297
3296
|
if (ev.usage) {
|
|
3298
|
-
inputTokens
|
|
3299
|
-
outputTokens
|
|
3300
|
-
cacheReadTokens
|
|
3297
|
+
inputTokens += ev.usage.input;
|
|
3298
|
+
outputTokens += ev.usage.output;
|
|
3299
|
+
cacheReadTokens += ev.usage.cacheRead;
|
|
3301
3300
|
}
|
|
3302
3301
|
if (adapter.shouldKillOnResult()) {
|
|
3303
3302
|
rl2.close();
|
|
@@ -3482,8 +3481,9 @@ import { spawn as spawn2 } from "child_process";
|
|
|
3482
3481
|
import { createInterface as createInterface2 } from "readline";
|
|
3483
3482
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3484
3483
|
function stripFrontmatter(text) {
|
|
3485
|
-
const match = text.match(/^---\s*\n[\s\S]*?\n---\s*\n?/);
|
|
3486
|
-
|
|
3484
|
+
const match = text.match(/^---\s*\n[\s\S]*?\n---\s*\n?/m);
|
|
3485
|
+
if (!match || match.index !== 0) return text;
|
|
3486
|
+
return text.slice(match[0].length);
|
|
3487
3487
|
}
|
|
3488
3488
|
function buildAgentPrompt(opts, runnerSkillPath) {
|
|
3489
3489
|
const parts = [];
|
|
@@ -3782,8 +3782,8 @@ __export(loader_exports, {
|
|
|
3782
3782
|
import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
3783
3783
|
import { join as join6 } from "path";
|
|
3784
3784
|
function parseFrontmatter(content) {
|
|
3785
|
-
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
3786
|
-
if (!match) return { meta: {}, body: content.trim() };
|
|
3785
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/m);
|
|
3786
|
+
if (!match || match.index !== 0) return { meta: {}, body: content.trim() };
|
|
3787
3787
|
const yamlBlock = match[1];
|
|
3788
3788
|
const body = match[2].trim();
|
|
3789
3789
|
const meta = {};
|
|
@@ -4283,6 +4283,7 @@ function startNextQueued(chatId) {
|
|
|
4283
4283
|
maxRuntimeMs: agent.maxRuntimeMs,
|
|
4284
4284
|
mcps: JSON.parse(agent.mcpsAdded)
|
|
4285
4285
|
}).catch((err) => {
|
|
4286
|
+
updateAgentStatus(db3, next.id, "failed");
|
|
4286
4287
|
error(`[orchestrator] Failed to start queued agent ${next.id}:`, err);
|
|
4287
4288
|
});
|
|
4288
4289
|
}
|
|
@@ -5210,7 +5211,6 @@ var init_server = __esm({
|
|
|
5210
5211
|
var agent_exports = {};
|
|
5211
5212
|
__export(agent_exports, {
|
|
5212
5213
|
askAgent: () => askAgent,
|
|
5213
|
-
askClaude: () => askClaude,
|
|
5214
5214
|
isAgentActive: () => isAgentActive,
|
|
5215
5215
|
isChatBusy: () => isChatBusy,
|
|
5216
5216
|
stopAgent: () => stopAgent
|
|
@@ -5310,7 +5310,8 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5310
5310
|
if (onToolAction && ev.toolName) {
|
|
5311
5311
|
const toolInput = ev.toolInput ?? {};
|
|
5312
5312
|
if (ev.toolId) pendingTools.set(ev.toolId, { name: ev.toolName, input: toolInput });
|
|
5313
|
-
onToolAction(ev.toolName, toolInput, void 0).catch(() => {
|
|
5313
|
+
onToolAction(ev.toolName, toolInput, void 0).catch((err) => {
|
|
5314
|
+
error("[agent] tool action error:", err);
|
|
5314
5315
|
});
|
|
5315
5316
|
}
|
|
5316
5317
|
break;
|
|
@@ -5319,10 +5320,12 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5319
5320
|
const pending = ev.toolId ? pendingTools.get(ev.toolId) : void 0;
|
|
5320
5321
|
if (pending) {
|
|
5321
5322
|
pendingTools.delete(ev.toolId);
|
|
5322
|
-
onToolAction(pending.name, pending.input, ev.toolOutput ?? "").catch(() => {
|
|
5323
|
+
onToolAction(pending.name, pending.input, ev.toolOutput ?? "").catch((err) => {
|
|
5324
|
+
error("[agent] tool action error:", err);
|
|
5323
5325
|
});
|
|
5324
5326
|
} else if (ev.toolName) {
|
|
5325
|
-
onToolAction(ev.toolName, {}, ev.toolOutput ?? "").catch(() => {
|
|
5327
|
+
onToolAction(ev.toolName, {}, ev.toolOutput ?? "").catch((err) => {
|
|
5328
|
+
error("[agent] tool action error:", err);
|
|
5326
5329
|
});
|
|
5327
5330
|
}
|
|
5328
5331
|
}
|
|
@@ -5344,7 +5347,10 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5344
5347
|
cacheRead = ev.usage.cacheRead;
|
|
5345
5348
|
}
|
|
5346
5349
|
if (adapter.shouldKillOnResult()) {
|
|
5347
|
-
|
|
5350
|
+
try {
|
|
5351
|
+
rl2.close();
|
|
5352
|
+
} catch {
|
|
5353
|
+
}
|
|
5348
5354
|
proc.kill("SIGTERM");
|
|
5349
5355
|
}
|
|
5350
5356
|
break;
|
|
@@ -5466,7 +5472,7 @@ function injectMcpConfig(adapterId, args, mcpConfigPath) {
|
|
|
5466
5472
|
if (!flag) return args;
|
|
5467
5473
|
return [...args, ...flag, mcpConfigPath];
|
|
5468
5474
|
}
|
|
5469
|
-
var __filename2, __dirname2, activeChats, chatLocks, SPAWN_TIMEOUT_MS, MCP_CONFIG_FLAG
|
|
5475
|
+
var __filename2, __dirname2, activeChats, chatLocks, SPAWN_TIMEOUT_MS, MCP_CONFIG_FLAG;
|
|
5470
5476
|
var init_agent = __esm({
|
|
5471
5477
|
"src/agent.ts"() {
|
|
5472
5478
|
"use strict";
|
|
@@ -5487,7 +5493,6 @@ var init_agent = __esm({
|
|
|
5487
5493
|
MCP_CONFIG_FLAG = {
|
|
5488
5494
|
claude: ["--mcp-config"]
|
|
5489
5495
|
};
|
|
5490
|
-
askClaude = askAgent;
|
|
5491
5496
|
}
|
|
5492
5497
|
});
|
|
5493
5498
|
|
|
@@ -5917,6 +5922,11 @@ async function executeJob(job) {
|
|
|
5917
5922
|
return;
|
|
5918
5923
|
}
|
|
5919
5924
|
runningJobs.add(job.id);
|
|
5925
|
+
const timer = activeTimers.get(job.id);
|
|
5926
|
+
if (timer instanceof Cron) {
|
|
5927
|
+
const nextRun = timer.nextRun();
|
|
5928
|
+
if (nextRun) updateJobNextRun(job.id, nextRun.toISOString());
|
|
5929
|
+
}
|
|
5920
5930
|
const t0 = Date.now();
|
|
5921
5931
|
const resolvedModel = resolveJobModel(job);
|
|
5922
5932
|
const runId = insertJobRun(job.id, resolvedModel);
|
|
@@ -5942,11 +5952,6 @@ async function executeJob(job) {
|
|
|
5942
5952
|
addUsage(job.chatId, response.usage.input, response.usage.output, response.usage.cacheRead, resolvedModel);
|
|
5943
5953
|
}
|
|
5944
5954
|
await deliverJobOutput(job, response.text);
|
|
5945
|
-
const timer = activeTimers.get(job.id);
|
|
5946
|
-
if (timer instanceof Cron) {
|
|
5947
|
-
const nextRun = timer.nextRun();
|
|
5948
|
-
if (nextRun) updateJobNextRun(job.id, nextRun.toISOString());
|
|
5949
|
-
}
|
|
5950
5955
|
} catch (err) {
|
|
5951
5956
|
const durationMs = Date.now() - t0;
|
|
5952
5957
|
const errorClass = classifyError(err);
|
|
@@ -6082,9 +6087,9 @@ function writeEnvWrapper(server) {
|
|
|
6082
6087
|
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
6083
6088
|
const lines = ["#!/bin/sh"];
|
|
6084
6089
|
for (const [k, v] of Object.entries(server.env ?? {})) {
|
|
6085
|
-
lines.push(`export ${k}=
|
|
6090
|
+
lines.push(`export ${k}='${v.replace(/'/g, "'\\''")}'`);
|
|
6086
6091
|
}
|
|
6087
|
-
lines.push(`exec ${server.command} ${(server.args ?? []).map((a) => `
|
|
6092
|
+
lines.push(`exec ${server.command} ${(server.args ?? []).map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ")}`);
|
|
6088
6093
|
const path = join8(dir, `wrapper-${server.name}.sh`);
|
|
6089
6094
|
writeFileSync4(path, lines.join("\n") + "\n", { mode: 448 });
|
|
6090
6095
|
return path;
|
|
@@ -6254,12 +6259,7 @@ function templateReplace(args, vars) {
|
|
|
6254
6259
|
}
|
|
6255
6260
|
function configToRunner(config2) {
|
|
6256
6261
|
const exePath = resolveExecutable(config2);
|
|
6257
|
-
|
|
6258
|
-
if (config2.outputParsing.preset) {
|
|
6259
|
-
parseLine = buildGenericParser(config2);
|
|
6260
|
-
} else {
|
|
6261
|
-
parseLine = buildGenericParser(config2);
|
|
6262
|
-
}
|
|
6262
|
+
const parseLine = buildGenericParser(config2);
|
|
6263
6263
|
return {
|
|
6264
6264
|
id: config2.id,
|
|
6265
6265
|
displayName: config2.displayName,
|
|
@@ -6355,6 +6355,12 @@ function registerConfigRunners() {
|
|
|
6355
6355
|
}
|
|
6356
6356
|
function watchRunnerConfigs(onChange) {
|
|
6357
6357
|
if (!existsSync9(RUNNERS_PATH)) return;
|
|
6358
|
+
for (const prev of watchedFiles) {
|
|
6359
|
+
if (!existsSync9(prev)) {
|
|
6360
|
+
unwatchFile(prev);
|
|
6361
|
+
watchedFiles.delete(prev);
|
|
6362
|
+
}
|
|
6363
|
+
}
|
|
6358
6364
|
const files = readdirSync4(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
6359
6365
|
for (const file of files) {
|
|
6360
6366
|
const fullPath = join9(RUNNERS_PATH, file);
|
|
@@ -6521,7 +6527,10 @@ function formatForTelegram(markdown) {
|
|
|
6521
6527
|
html = html.replace(/^#{1,6}\s+(.+)$/gm, "<b>$1</b>");
|
|
6522
6528
|
html = html.replace(/^[-*_]{3,}$/gm, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
6523
6529
|
html = html.replace(/^[\s]*[-*]\s+/gm, "\u2022 ");
|
|
6524
|
-
html = html.replace(/\x00CODE(\d+)\x00/g, (
|
|
6530
|
+
html = html.replace(/\x00CODE(\d+)\x00/g, (match, idx) => {
|
|
6531
|
+
const block = codeBlocks[parseInt(idx, 10)];
|
|
6532
|
+
return block !== void 0 ? block : match;
|
|
6533
|
+
});
|
|
6525
6534
|
return html.trim();
|
|
6526
6535
|
}
|
|
6527
6536
|
function escapeOutsideCode(text) {
|
|
@@ -6561,6 +6570,10 @@ var init_telegram = __esm({
|
|
|
6561
6570
|
|
|
6562
6571
|
// src/channels/telegram.ts
|
|
6563
6572
|
import { Bot, InlineKeyboard, InputFile } from "grammy";
|
|
6573
|
+
function numericChatId(chatId) {
|
|
6574
|
+
const raw = chatId.includes(":") ? chatId.split(":").pop() : chatId;
|
|
6575
|
+
return parseInt(raw);
|
|
6576
|
+
}
|
|
6564
6577
|
var TelegramChannel;
|
|
6565
6578
|
var init_telegram2 = __esm({
|
|
6566
6579
|
"src/channels/telegram.ts"() {
|
|
@@ -6710,13 +6723,13 @@ var init_telegram2 = __esm({
|
|
|
6710
6723
|
await this.bot.stop();
|
|
6711
6724
|
}
|
|
6712
6725
|
async sendTyping(chatId) {
|
|
6713
|
-
await this.bot.api.sendChatAction(
|
|
6726
|
+
await this.bot.api.sendChatAction(numericChatId(chatId), "typing");
|
|
6714
6727
|
}
|
|
6715
6728
|
async sendText(chatId, text, parseMode) {
|
|
6716
6729
|
if (parseMode === "plain") {
|
|
6717
6730
|
const plainChunks = splitMessage(text);
|
|
6718
6731
|
for (const chunk of plainChunks) {
|
|
6719
|
-
await this.bot.api.sendMessage(
|
|
6732
|
+
await this.bot.api.sendMessage(numericChatId(chatId), chunk);
|
|
6720
6733
|
}
|
|
6721
6734
|
return;
|
|
6722
6735
|
}
|
|
@@ -6724,12 +6737,12 @@ var init_telegram2 = __esm({
|
|
|
6724
6737
|
const chunks = splitMessage(formatted);
|
|
6725
6738
|
for (const chunk of chunks) {
|
|
6726
6739
|
try {
|
|
6727
|
-
await this.bot.api.sendMessage(
|
|
6740
|
+
await this.bot.api.sendMessage(numericChatId(chatId), chunk, {
|
|
6728
6741
|
parse_mode: "HTML"
|
|
6729
6742
|
});
|
|
6730
6743
|
} catch {
|
|
6731
6744
|
await this.bot.api.sendMessage(
|
|
6732
|
-
|
|
6745
|
+
numericChatId(chatId),
|
|
6733
6746
|
chunk.replace(/<[^>]+>/g, "")
|
|
6734
6747
|
);
|
|
6735
6748
|
}
|
|
@@ -6737,13 +6750,13 @@ var init_telegram2 = __esm({
|
|
|
6737
6750
|
}
|
|
6738
6751
|
async sendVoice(chatId, audioBuffer, fileName) {
|
|
6739
6752
|
await this.bot.api.sendVoice(
|
|
6740
|
-
|
|
6753
|
+
numericChatId(chatId),
|
|
6741
6754
|
new InputFile(audioBuffer, fileName ?? "response.ogg")
|
|
6742
6755
|
);
|
|
6743
6756
|
}
|
|
6744
6757
|
async sendFile(chatId, buffer, fileName) {
|
|
6745
6758
|
await this.bot.api.sendDocument(
|
|
6746
|
-
|
|
6759
|
+
numericChatId(chatId),
|
|
6747
6760
|
new InputFile(buffer, fileName)
|
|
6748
6761
|
);
|
|
6749
6762
|
}
|
|
@@ -6755,7 +6768,7 @@ var init_telegram2 = __esm({
|
|
|
6755
6768
|
}
|
|
6756
6769
|
async sendTextReturningId(chatId, text, parseMode) {
|
|
6757
6770
|
try {
|
|
6758
|
-
const msg = await this.bot.api.sendMessage(
|
|
6771
|
+
const msg = await this.bot.api.sendMessage(numericChatId(chatId), text);
|
|
6759
6772
|
return msg.message_id.toString();
|
|
6760
6773
|
} catch {
|
|
6761
6774
|
return void 0;
|
|
@@ -6764,14 +6777,14 @@ var init_telegram2 = __esm({
|
|
|
6764
6777
|
async editText(chatId, messageId, text, parseMode) {
|
|
6765
6778
|
const formatted = parseMode === "html" ? text : formatForTelegram(text);
|
|
6766
6779
|
try {
|
|
6767
|
-
await this.bot.api.editMessageText(
|
|
6780
|
+
await this.bot.api.editMessageText(numericChatId(chatId), parseInt(messageId), formatted, {
|
|
6768
6781
|
parse_mode: "HTML"
|
|
6769
6782
|
});
|
|
6770
6783
|
return true;
|
|
6771
6784
|
} catch {
|
|
6772
6785
|
try {
|
|
6773
6786
|
await this.bot.api.editMessageText(
|
|
6774
|
-
|
|
6787
|
+
numericChatId(chatId),
|
|
6775
6788
|
parseInt(messageId),
|
|
6776
6789
|
formatted.replace(/<[^>]+>/g, "")
|
|
6777
6790
|
);
|
|
@@ -6793,13 +6806,13 @@ var init_telegram2 = __esm({
|
|
|
6793
6806
|
}
|
|
6794
6807
|
keyboard.row();
|
|
6795
6808
|
}
|
|
6796
|
-
await this.bot.api.sendMessage(
|
|
6809
|
+
await this.bot.api.sendMessage(numericChatId(chatId), text, {
|
|
6797
6810
|
reply_markup: keyboard
|
|
6798
6811
|
});
|
|
6799
6812
|
}
|
|
6800
6813
|
async reactToMessage(chatId, messageId, emoji) {
|
|
6801
6814
|
try {
|
|
6802
|
-
await this.bot.api.setMessageReaction(
|
|
6815
|
+
await this.bot.api.setMessageReaction(numericChatId(chatId), parseInt(messageId), [
|
|
6803
6816
|
{ type: "emoji", emoji }
|
|
6804
6817
|
]);
|
|
6805
6818
|
} catch {
|
|
@@ -7093,6 +7106,9 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
7093
7106
|
stdio: "pipe",
|
|
7094
7107
|
timeout: 3e4
|
|
7095
7108
|
});
|
|
7109
|
+
if (!existsSync10(join11(tmpDir, ".git"))) {
|
|
7110
|
+
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
7111
|
+
}
|
|
7096
7112
|
const searchRoot = subPath ? join11(tmpDir, subPath) : tmpDir;
|
|
7097
7113
|
const skillDir = await findSkillDir(searchRoot);
|
|
7098
7114
|
if (!skillDir) {
|
|
@@ -7165,7 +7181,12 @@ async function findSkillDir(root) {
|
|
|
7165
7181
|
const entries = await readdir2(root, { withFileTypes: true });
|
|
7166
7182
|
for (const entry of entries) {
|
|
7167
7183
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
7168
|
-
|
|
7184
|
+
let subEntries;
|
|
7185
|
+
try {
|
|
7186
|
+
subEntries = await readdir2(join11(root, entry.name), { withFileTypes: true });
|
|
7187
|
+
} catch {
|
|
7188
|
+
continue;
|
|
7189
|
+
}
|
|
7169
7190
|
for (const sub of subEntries) {
|
|
7170
7191
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
7171
7192
|
for (const c of candidates) {
|
|
@@ -7371,8 +7392,7 @@ function stopAllHeartbeats() {
|
|
|
7371
7392
|
}
|
|
7372
7393
|
function startAllHeartbeats() {
|
|
7373
7394
|
try {
|
|
7374
|
-
const
|
|
7375
|
-
const db3 = getDb2();
|
|
7395
|
+
const db3 = getDb();
|
|
7376
7396
|
const rows = db3.prepare(
|
|
7377
7397
|
"SELECT chat_id FROM chat_heartbeat WHERE enabled = 1"
|
|
7378
7398
|
).all();
|
|
@@ -7610,9 +7630,11 @@ async function macOsTts(text) {
|
|
|
7610
7630
|
await execFileAsync2("say", ["-o", tmpAiff, sanitized]);
|
|
7611
7631
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpAiff, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
7612
7632
|
const oggBuffer = await readFile4(tmpOgg);
|
|
7613
|
-
unlink(tmpAiff).catch(() => {
|
|
7633
|
+
unlink(tmpAiff).catch((err) => {
|
|
7634
|
+
error("[tts] cleanup failed:", err);
|
|
7614
7635
|
});
|
|
7615
|
-
unlink(tmpOgg).catch(() => {
|
|
7636
|
+
unlink(tmpOgg).catch((err) => {
|
|
7637
|
+
error("[tts] cleanup failed:", err);
|
|
7616
7638
|
});
|
|
7617
7639
|
return oggBuffer;
|
|
7618
7640
|
}
|
|
@@ -7743,8 +7765,7 @@ async function startWizard(chatId, input, channel) {
|
|
|
7743
7765
|
step: "schedule",
|
|
7744
7766
|
task: parsed?.task || input,
|
|
7745
7767
|
scheduleType: parsed?.scheduleType,
|
|
7746
|
-
cron: parsed?.cron
|
|
7747
|
-
everyMs: parsed?.everyMs
|
|
7768
|
+
cron: parsed?.cron
|
|
7748
7769
|
};
|
|
7749
7770
|
if (parsed?.cron) {
|
|
7750
7771
|
pending.step = "timezone";
|
|
@@ -10548,10 +10569,18 @@ async function main() {
|
|
|
10548
10569
|
bootstrapBuiltinMcps(getDb());
|
|
10549
10570
|
const SUMMARIZE_TIMEOUT_MS = 3e4;
|
|
10550
10571
|
try {
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10572
|
+
let timer;
|
|
10573
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
10574
|
+
timer = setTimeout(() => reject(new Error("timeout")), SUMMARIZE_TIMEOUT_MS);
|
|
10575
|
+
});
|
|
10576
|
+
try {
|
|
10577
|
+
await Promise.race([
|
|
10578
|
+
summarizeAllPending(),
|
|
10579
|
+
timeoutPromise
|
|
10580
|
+
]);
|
|
10581
|
+
} finally {
|
|
10582
|
+
clearTimeout(timer);
|
|
10583
|
+
}
|
|
10555
10584
|
} catch {
|
|
10556
10585
|
log("[cc-claw] Session summarization skipped (timeout or backend unavailable)");
|
|
10557
10586
|
}
|
|
@@ -10782,7 +10811,7 @@ function uninstallMacOS() {
|
|
|
10782
10811
|
}
|
|
10783
10812
|
function statusMacOS() {
|
|
10784
10813
|
try {
|
|
10785
|
-
const out = execSync5("launchctl list | grep cc-claw", { encoding: "utf-8" }).trim();
|
|
10814
|
+
const out = execSync5("launchctl list | grep cc-claw", { shell: "/bin/sh", encoding: "utf-8" }).trim();
|
|
10786
10815
|
if (out) {
|
|
10787
10816
|
const parts = out.split(/\s+/);
|
|
10788
10817
|
const pid = parts[0];
|
|
@@ -11324,7 +11353,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
11324
11353
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
11325
11354
|
const readDb = openDatabaseReadOnly2();
|
|
11326
11355
|
const journal = readDb.prepare("PRAGMA journal_mode").get();
|
|
11327
|
-
if (journal
|
|
11356
|
+
if (!journal) {
|
|
11357
|
+
checks.push({ name: "WAL mode", status: "warning", message: "could not read journal_mode" });
|
|
11358
|
+
} else if (journal.journal_mode === "wal") {
|
|
11328
11359
|
checks.push({ name: "WAL mode", status: "ok", message: "enabled" });
|
|
11329
11360
|
} else {
|
|
11330
11361
|
checks.push({ name: "WAL mode", status: "warning", message: `${journal.journal_mode} (expected wal)` });
|
|
@@ -12128,8 +12159,8 @@ async function agentsSpawn(globalOpts, opts) {
|
|
|
12128
12159
|
role: opts.role
|
|
12129
12160
|
});
|
|
12130
12161
|
if (res.ok) {
|
|
12162
|
+
const { success: s } = await Promise.resolve().then(() => (init_format(), format_exports));
|
|
12131
12163
|
output(res.data, (d) => {
|
|
12132
|
-
const { success: s } = (init_format(), __toCommonJS(format_exports));
|
|
12133
12164
|
return `
|
|
12134
12165
|
${s(`Agent spawned: ${d.agentId?.slice(0, 8) ?? "?"} (${opts.runner})`)}
|
|
12135
12166
|
`;
|
|
@@ -12393,13 +12424,21 @@ async function usageTokens(globalOpts) {
|
|
|
12393
12424
|
const { getAllAdapters: getAllAdapters2, getAllBackendIds: getAllBackendIds2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
12394
12425
|
const readDb = openDatabaseReadOnly2();
|
|
12395
12426
|
const backendIds = getAllBackendIds2();
|
|
12427
|
+
const adapters2 = getAllAdapters2();
|
|
12396
12428
|
const cutoff24h = new Date(Date.now() - 864e5).toISOString();
|
|
12397
12429
|
const data = backendIds.map((bid) => {
|
|
12398
|
-
const
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12430
|
+
const adapter = adapters2.find((a) => a.id === bid);
|
|
12431
|
+
const modelIds = adapter ? Object.keys(adapter.pricing) : [];
|
|
12432
|
+
let row;
|
|
12433
|
+
if (modelIds.length > 0) {
|
|
12434
|
+
const placeholders = modelIds.map(() => "?").join(",");
|
|
12435
|
+
row = readDb.prepare(`
|
|
12436
|
+
SELECT COALESCE(SUM(input_tokens),0) as input_tokens, COALESCE(SUM(output_tokens),0) as output_tokens, COUNT(*) as request_count
|
|
12437
|
+
FROM usage_log WHERE model IN (${placeholders}) AND created_at > ?
|
|
12438
|
+
`).get(...modelIds, cutoff24h);
|
|
12439
|
+
} else {
|
|
12440
|
+
row = { input_tokens: 0, output_tokens: 0, request_count: 0 };
|
|
12441
|
+
}
|
|
12403
12442
|
return { backend: bid, displayName: adapter?.displayName ?? bid, ...row };
|
|
12404
12443
|
});
|
|
12405
12444
|
readDb.close();
|
|
@@ -12496,6 +12535,7 @@ async function configList(globalOpts) {
|
|
|
12496
12535
|
const config2 = {};
|
|
12497
12536
|
for (const key of RUNTIME_KEYS) {
|
|
12498
12537
|
const { table, col } = KEY_TABLE_MAP[key];
|
|
12538
|
+
if (!ALLOWED_TABLES.has(table) || !ALLOWED_COLS.has(col)) continue;
|
|
12499
12539
|
try {
|
|
12500
12540
|
const row = readDb.prepare(`SELECT ${col} FROM ${table} WHERE chat_id = ?`).get(chatId);
|
|
12501
12541
|
config2[key] = row ? row[col] : null;
|
|
@@ -12527,6 +12567,10 @@ async function configGet(globalOpts, key) {
|
|
|
12527
12567
|
const readDb = openDatabaseReadOnly2();
|
|
12528
12568
|
const chatId = resolveChatId(globalOpts);
|
|
12529
12569
|
const { table, col } = KEY_TABLE_MAP[key];
|
|
12570
|
+
if (!ALLOWED_TABLES.has(table) || !ALLOWED_COLS.has(col)) {
|
|
12571
|
+
outputError("INVALID_KEY", `Invalid config mapping for "${key}".`);
|
|
12572
|
+
process.exit(1);
|
|
12573
|
+
}
|
|
12530
12574
|
let value = null;
|
|
12531
12575
|
try {
|
|
12532
12576
|
const row = readDb.prepare(`SELECT ${col} FROM ${table} WHERE chat_id = ?`).get(chatId);
|
|
@@ -12585,7 +12629,7 @@ async function configEnv(_globalOpts) {
|
|
|
12585
12629
|
return lines.join("\n");
|
|
12586
12630
|
});
|
|
12587
12631
|
}
|
|
12588
|
-
var RUNTIME_KEYS, KEY_TABLE_MAP;
|
|
12632
|
+
var RUNTIME_KEYS, KEY_TABLE_MAP, ALLOWED_TABLES, ALLOWED_COLS;
|
|
12589
12633
|
var init_config = __esm({
|
|
12590
12634
|
"src/cli/commands/config.ts"() {
|
|
12591
12635
|
"use strict";
|
|
@@ -12603,6 +12647,8 @@ var init_config = __esm({
|
|
|
12603
12647
|
cwd: { table: "chat_cwd", col: "cwd" },
|
|
12604
12648
|
voice: { table: "chat_voice", col: "enabled" }
|
|
12605
12649
|
};
|
|
12650
|
+
ALLOWED_TABLES = new Set(Object.values(KEY_TABLE_MAP).map((v) => v.table));
|
|
12651
|
+
ALLOWED_COLS = new Set(Object.values(KEY_TABLE_MAP).map((v) => v.col));
|
|
12606
12652
|
}
|
|
12607
12653
|
});
|
|
12608
12654
|
|
|
@@ -12676,11 +12722,14 @@ __export(permissions_exports, {
|
|
|
12676
12722
|
verboseSet: () => verboseSet
|
|
12677
12723
|
});
|
|
12678
12724
|
import { existsSync as existsSync30 } from "fs";
|
|
12679
|
-
|
|
12725
|
+
function ensureDb2() {
|
|
12680
12726
|
if (!existsSync30(DB_PATH)) {
|
|
12681
12727
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12682
12728
|
process.exit(1);
|
|
12683
12729
|
}
|
|
12730
|
+
}
|
|
12731
|
+
async function permissionsGet(globalOpts) {
|
|
12732
|
+
ensureDb2();
|
|
12684
12733
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
12685
12734
|
const readDb = openDatabaseReadOnly2();
|
|
12686
12735
|
const chatId = resolveChatId(globalOpts);
|
|
@@ -12712,10 +12761,7 @@ async function permissionsSet(globalOpts, mode) {
|
|
|
12712
12761
|
}
|
|
12713
12762
|
}
|
|
12714
12763
|
async function toolsList(globalOpts) {
|
|
12715
|
-
|
|
12716
|
-
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12717
|
-
process.exit(1);
|
|
12718
|
-
}
|
|
12764
|
+
ensureDb2();
|
|
12719
12765
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
12720
12766
|
const readDb = openDatabaseReadOnly2();
|
|
12721
12767
|
const chatId = resolveChatId(globalOpts);
|
|
@@ -12776,10 +12822,7 @@ async function toggleTool2(globalOpts, name, enabled) {
|
|
|
12776
12822
|
}
|
|
12777
12823
|
}
|
|
12778
12824
|
async function verboseGet(globalOpts) {
|
|
12779
|
-
|
|
12780
|
-
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12781
|
-
process.exit(1);
|
|
12782
|
-
}
|
|
12825
|
+
ensureDb2();
|
|
12783
12826
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
12784
12827
|
const readDb = openDatabaseReadOnly2();
|
|
12785
12828
|
const chatId = resolveChatId(globalOpts);
|
|
@@ -13682,7 +13725,7 @@ async function requiredInput(prompt, existing) {
|
|
|
13682
13725
|
}
|
|
13683
13726
|
async function validateBotToken(token) {
|
|
13684
13727
|
try {
|
|
13685
|
-
const res = await fetch(`https://api.telegram.org/bot${token}/getMe
|
|
13728
|
+
const res = await fetch(`https://api.telegram.org/bot${token}/getMe`, { signal: AbortSignal.timeout(1e4) });
|
|
13686
13729
|
const data = await res.json();
|
|
13687
13730
|
if (data.ok && data.result?.username) {
|
|
13688
13731
|
return { valid: true, botName: data.result.username };
|
package/package.json
CHANGED