cc-claw 0.2.4 → 0.2.6
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/README.md +10 -0
- package/dist/cli.js +145 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,16 @@ Send text, voice, photos, documents, or videos. Switch backends with `/claude`,
|
|
|
43
43
|
npm install -g cc-claw
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
## Upgrade
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install -g cc-claw@latest # Update to latest version
|
|
50
|
+
cc-claw --version # Verify
|
|
51
|
+
cc-claw service restart # Restart to pick up changes
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
No config changes or migrations needed.
|
|
55
|
+
|
|
46
56
|
## Setup
|
|
47
57
|
|
|
48
58
|
```bash
|
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.6" : (() => {
|
|
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"
|
|
@@ -2437,7 +2436,9 @@ var init_gemini = __esm({
|
|
|
2437
2436
|
"-p",
|
|
2438
2437
|
opts.prompt,
|
|
2439
2438
|
"-o",
|
|
2440
|
-
"stream-json"
|
|
2439
|
+
"stream-json",
|
|
2440
|
+
"--allowed-mcp-server-names"
|
|
2441
|
+
// no values = skip all user MCP servers (avoids crash/hang from broken servers)
|
|
2441
2442
|
];
|
|
2442
2443
|
if (opts.sessionId) args.push("--resume", opts.sessionId);
|
|
2443
2444
|
if (opts.model) args.push("-m", opts.model);
|
|
@@ -2712,8 +2713,7 @@ function normalizeFtsScores(items) {
|
|
|
2712
2713
|
const ranks = items.map((it) => Math.abs(it._ftsRank ?? 0));
|
|
2713
2714
|
const maxRank = Math.max(...ranks, 1e-3);
|
|
2714
2715
|
for (let i = 0; i < items.length; i++) {
|
|
2715
|
-
|
|
2716
|
-
scores.set(id, ranks[i] / maxRank);
|
|
2716
|
+
scores.set(items[i].id, ranks[i] / maxRank);
|
|
2717
2717
|
}
|
|
2718
2718
|
return scores;
|
|
2719
2719
|
}
|
|
@@ -2929,13 +2929,13 @@ function syncNativeCliFiles() {
|
|
|
2929
2929
|
} catch {
|
|
2930
2930
|
}
|
|
2931
2931
|
try {
|
|
2932
|
-
const
|
|
2933
|
-
const
|
|
2934
|
-
if (
|
|
2932
|
+
const soulMtime = existsSync4(SOUL_PATH) ? statSync(SOUL_PATH).mtimeMs : 0;
|
|
2933
|
+
const userMtime = existsSync4(USER_PATH) ? statSync(USER_PATH).mtimeMs : 0;
|
|
2934
|
+
if (soulMtime === lastSoulMtime && userMtime === lastUserMtime) {
|
|
2935
2935
|
return;
|
|
2936
2936
|
}
|
|
2937
|
-
lastSoulMtime =
|
|
2938
|
-
lastUserMtime =
|
|
2937
|
+
lastSoulMtime = soulMtime;
|
|
2938
|
+
lastUserMtime = userMtime;
|
|
2939
2939
|
} catch {
|
|
2940
2940
|
}
|
|
2941
2941
|
const nativeContent = [
|
|
@@ -3267,13 +3267,14 @@ ${transcript}`;
|
|
|
3267
3267
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3268
3268
|
...config2.cwd ? { cwd: config2.cwd } : {}
|
|
3269
3269
|
});
|
|
3270
|
+
proc.stderr?.resume();
|
|
3271
|
+
const rl2 = createInterface({ input: proc.stdout });
|
|
3270
3272
|
const timeout = setTimeout(() => {
|
|
3271
3273
|
warn(`[summarize] Timeout after ${SUMMARIZE_TIMEOUT_MS / 1e3}s for chat ${chatId} \u2014 killing process`);
|
|
3274
|
+
rl2.close();
|
|
3272
3275
|
proc.kill("SIGTERM");
|
|
3273
3276
|
setTimeout(() => proc.kill("SIGKILL"), 2e3);
|
|
3274
3277
|
}, SUMMARIZE_TIMEOUT_MS);
|
|
3275
|
-
proc.stderr?.resume();
|
|
3276
|
-
const rl2 = createInterface({ input: proc.stdout });
|
|
3277
3278
|
rl2.on("line", (line) => {
|
|
3278
3279
|
if (!line.trim()) return;
|
|
3279
3280
|
let msg;
|
|
@@ -3295,9 +3296,9 @@ ${transcript}`;
|
|
|
3295
3296
|
if (ev.type === "result") {
|
|
3296
3297
|
resultText = ev.resultText || accumulatedText;
|
|
3297
3298
|
if (ev.usage) {
|
|
3298
|
-
inputTokens
|
|
3299
|
-
outputTokens
|
|
3300
|
-
cacheReadTokens
|
|
3299
|
+
inputTokens += ev.usage.input;
|
|
3300
|
+
outputTokens += ev.usage.output;
|
|
3301
|
+
cacheReadTokens += ev.usage.cacheRead;
|
|
3301
3302
|
}
|
|
3302
3303
|
if (adapter.shouldKillOnResult()) {
|
|
3303
3304
|
rl2.close();
|
|
@@ -3482,8 +3483,9 @@ import { spawn as spawn2 } from "child_process";
|
|
|
3482
3483
|
import { createInterface as createInterface2 } from "readline";
|
|
3483
3484
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3484
3485
|
function stripFrontmatter(text) {
|
|
3485
|
-
const match = text.match(/^---\s*\n[\s\S]*?\n---\s*\n?/);
|
|
3486
|
-
|
|
3486
|
+
const match = text.match(/^---\s*\n[\s\S]*?\n---\s*\n?/m);
|
|
3487
|
+
if (!match || match.index !== 0) return text;
|
|
3488
|
+
return text.slice(match[0].length);
|
|
3487
3489
|
}
|
|
3488
3490
|
function buildAgentPrompt(opts, runnerSkillPath) {
|
|
3489
3491
|
const parts = [];
|
|
@@ -3782,8 +3784,8 @@ __export(loader_exports, {
|
|
|
3782
3784
|
import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
3783
3785
|
import { join as join6 } from "path";
|
|
3784
3786
|
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() };
|
|
3787
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/m);
|
|
3788
|
+
if (!match || match.index !== 0) return { meta: {}, body: content.trim() };
|
|
3787
3789
|
const yamlBlock = match[1];
|
|
3788
3790
|
const body = match[2].trim();
|
|
3789
3791
|
const meta = {};
|
|
@@ -4283,6 +4285,7 @@ function startNextQueued(chatId) {
|
|
|
4283
4285
|
maxRuntimeMs: agent.maxRuntimeMs,
|
|
4284
4286
|
mcps: JSON.parse(agent.mcpsAdded)
|
|
4285
4287
|
}).catch((err) => {
|
|
4288
|
+
updateAgentStatus(db3, next.id, "failed");
|
|
4286
4289
|
error(`[orchestrator] Failed to start queued agent ${next.id}:`, err);
|
|
4287
4290
|
});
|
|
4288
4291
|
}
|
|
@@ -5210,7 +5213,6 @@ var init_server = __esm({
|
|
|
5210
5213
|
var agent_exports = {};
|
|
5211
5214
|
__export(agent_exports, {
|
|
5212
5215
|
askAgent: () => askAgent,
|
|
5213
|
-
askClaude: () => askClaude,
|
|
5214
5216
|
isAgentActive: () => isAgentActive,
|
|
5215
5217
|
isChatBusy: () => isChatBusy,
|
|
5216
5218
|
stopAgent: () => stopAgent
|
|
@@ -5310,7 +5312,8 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5310
5312
|
if (onToolAction && ev.toolName) {
|
|
5311
5313
|
const toolInput = ev.toolInput ?? {};
|
|
5312
5314
|
if (ev.toolId) pendingTools.set(ev.toolId, { name: ev.toolName, input: toolInput });
|
|
5313
|
-
onToolAction(ev.toolName, toolInput, void 0).catch(() => {
|
|
5315
|
+
onToolAction(ev.toolName, toolInput, void 0).catch((err) => {
|
|
5316
|
+
error("[agent] tool action error:", err);
|
|
5314
5317
|
});
|
|
5315
5318
|
}
|
|
5316
5319
|
break;
|
|
@@ -5319,10 +5322,12 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5319
5322
|
const pending = ev.toolId ? pendingTools.get(ev.toolId) : void 0;
|
|
5320
5323
|
if (pending) {
|
|
5321
5324
|
pendingTools.delete(ev.toolId);
|
|
5322
|
-
onToolAction(pending.name, pending.input, ev.toolOutput ?? "").catch(() => {
|
|
5325
|
+
onToolAction(pending.name, pending.input, ev.toolOutput ?? "").catch((err) => {
|
|
5326
|
+
error("[agent] tool action error:", err);
|
|
5323
5327
|
});
|
|
5324
5328
|
} else if (ev.toolName) {
|
|
5325
|
-
onToolAction(ev.toolName, {}, ev.toolOutput ?? "").catch(() => {
|
|
5329
|
+
onToolAction(ev.toolName, {}, ev.toolOutput ?? "").catch((err) => {
|
|
5330
|
+
error("[agent] tool action error:", err);
|
|
5326
5331
|
});
|
|
5327
5332
|
}
|
|
5328
5333
|
}
|
|
@@ -5344,7 +5349,10 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5344
5349
|
cacheRead = ev.usage.cacheRead;
|
|
5345
5350
|
}
|
|
5346
5351
|
if (adapter.shouldKillOnResult()) {
|
|
5347
|
-
|
|
5352
|
+
try {
|
|
5353
|
+
rl2.close();
|
|
5354
|
+
} catch {
|
|
5355
|
+
}
|
|
5348
5356
|
proc.kill("SIGTERM");
|
|
5349
5357
|
}
|
|
5350
5358
|
break;
|
|
@@ -5370,7 +5378,7 @@ function spawnQuery(adapter, config2, model2, cancelState, onStream, onToolActio
|
|
|
5370
5378
|
}
|
|
5371
5379
|
if (code && code !== 0 && !cancelState.cancelled && !resultText) {
|
|
5372
5380
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
5373
|
-
reject(new Error(`CLI exited with code ${code}${stderr ? `: ${stderr.slice(0,
|
|
5381
|
+
reject(new Error(`CLI exited with code ${code}${stderr ? `: ${stderr.slice(0, 500)}` : ""}`));
|
|
5374
5382
|
return;
|
|
5375
5383
|
}
|
|
5376
5384
|
resolve({ resultText, sessionId, input, output: output2, cacheRead, sawToolEvents, sawResultEvent });
|
|
@@ -5466,7 +5474,7 @@ function injectMcpConfig(adapterId, args, mcpConfigPath) {
|
|
|
5466
5474
|
if (!flag) return args;
|
|
5467
5475
|
return [...args, ...flag, mcpConfigPath];
|
|
5468
5476
|
}
|
|
5469
|
-
var __filename2, __dirname2, activeChats, chatLocks, SPAWN_TIMEOUT_MS, MCP_CONFIG_FLAG
|
|
5477
|
+
var __filename2, __dirname2, activeChats, chatLocks, SPAWN_TIMEOUT_MS, MCP_CONFIG_FLAG;
|
|
5470
5478
|
var init_agent = __esm({
|
|
5471
5479
|
"src/agent.ts"() {
|
|
5472
5480
|
"use strict";
|
|
@@ -5487,7 +5495,6 @@ var init_agent = __esm({
|
|
|
5487
5495
|
MCP_CONFIG_FLAG = {
|
|
5488
5496
|
claude: ["--mcp-config"]
|
|
5489
5497
|
};
|
|
5490
|
-
askClaude = askAgent;
|
|
5491
5498
|
}
|
|
5492
5499
|
});
|
|
5493
5500
|
|
|
@@ -5917,6 +5924,11 @@ async function executeJob(job) {
|
|
|
5917
5924
|
return;
|
|
5918
5925
|
}
|
|
5919
5926
|
runningJobs.add(job.id);
|
|
5927
|
+
const timer = activeTimers.get(job.id);
|
|
5928
|
+
if (timer instanceof Cron) {
|
|
5929
|
+
const nextRun = timer.nextRun();
|
|
5930
|
+
if (nextRun) updateJobNextRun(job.id, nextRun.toISOString());
|
|
5931
|
+
}
|
|
5920
5932
|
const t0 = Date.now();
|
|
5921
5933
|
const resolvedModel = resolveJobModel(job);
|
|
5922
5934
|
const runId = insertJobRun(job.id, resolvedModel);
|
|
@@ -5942,11 +5954,6 @@ async function executeJob(job) {
|
|
|
5942
5954
|
addUsage(job.chatId, response.usage.input, response.usage.output, response.usage.cacheRead, resolvedModel);
|
|
5943
5955
|
}
|
|
5944
5956
|
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
5957
|
} catch (err) {
|
|
5951
5958
|
const durationMs = Date.now() - t0;
|
|
5952
5959
|
const errorClass = classifyError(err);
|
|
@@ -6082,9 +6089,9 @@ function writeEnvWrapper(server) {
|
|
|
6082
6089
|
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
6083
6090
|
const lines = ["#!/bin/sh"];
|
|
6084
6091
|
for (const [k, v] of Object.entries(server.env ?? {})) {
|
|
6085
|
-
lines.push(`export ${k}=
|
|
6092
|
+
lines.push(`export ${k}='${v.replace(/'/g, "'\\''")}'`);
|
|
6086
6093
|
}
|
|
6087
|
-
lines.push(`exec ${server.command} ${(server.args ?? []).map((a) => `
|
|
6094
|
+
lines.push(`exec ${server.command} ${(server.args ?? []).map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ")}`);
|
|
6088
6095
|
const path = join8(dir, `wrapper-${server.name}.sh`);
|
|
6089
6096
|
writeFileSync4(path, lines.join("\n") + "\n", { mode: 448 });
|
|
6090
6097
|
return path;
|
|
@@ -6254,12 +6261,7 @@ function templateReplace(args, vars) {
|
|
|
6254
6261
|
}
|
|
6255
6262
|
function configToRunner(config2) {
|
|
6256
6263
|
const exePath = resolveExecutable(config2);
|
|
6257
|
-
|
|
6258
|
-
if (config2.outputParsing.preset) {
|
|
6259
|
-
parseLine = buildGenericParser(config2);
|
|
6260
|
-
} else {
|
|
6261
|
-
parseLine = buildGenericParser(config2);
|
|
6262
|
-
}
|
|
6264
|
+
const parseLine = buildGenericParser(config2);
|
|
6263
6265
|
return {
|
|
6264
6266
|
id: config2.id,
|
|
6265
6267
|
displayName: config2.displayName,
|
|
@@ -6355,6 +6357,12 @@ function registerConfigRunners() {
|
|
|
6355
6357
|
}
|
|
6356
6358
|
function watchRunnerConfigs(onChange) {
|
|
6357
6359
|
if (!existsSync9(RUNNERS_PATH)) return;
|
|
6360
|
+
for (const prev of watchedFiles) {
|
|
6361
|
+
if (!existsSync9(prev)) {
|
|
6362
|
+
unwatchFile(prev);
|
|
6363
|
+
watchedFiles.delete(prev);
|
|
6364
|
+
}
|
|
6365
|
+
}
|
|
6358
6366
|
const files = readdirSync4(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
6359
6367
|
for (const file of files) {
|
|
6360
6368
|
const fullPath = join9(RUNNERS_PATH, file);
|
|
@@ -6521,7 +6529,10 @@ function formatForTelegram(markdown) {
|
|
|
6521
6529
|
html = html.replace(/^#{1,6}\s+(.+)$/gm, "<b>$1</b>");
|
|
6522
6530
|
html = html.replace(/^[-*_]{3,}$/gm, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
6523
6531
|
html = html.replace(/^[\s]*[-*]\s+/gm, "\u2022 ");
|
|
6524
|
-
html = html.replace(/\x00CODE(\d+)\x00/g, (
|
|
6532
|
+
html = html.replace(/\x00CODE(\d+)\x00/g, (match, idx) => {
|
|
6533
|
+
const block = codeBlocks[parseInt(idx, 10)];
|
|
6534
|
+
return block !== void 0 ? block : match;
|
|
6535
|
+
});
|
|
6525
6536
|
return html.trim();
|
|
6526
6537
|
}
|
|
6527
6538
|
function escapeOutsideCode(text) {
|
|
@@ -6561,6 +6572,10 @@ var init_telegram = __esm({
|
|
|
6561
6572
|
|
|
6562
6573
|
// src/channels/telegram.ts
|
|
6563
6574
|
import { Bot, InlineKeyboard, InputFile } from "grammy";
|
|
6575
|
+
function numericChatId(chatId) {
|
|
6576
|
+
const raw = chatId.includes(":") ? chatId.split(":").pop() : chatId;
|
|
6577
|
+
return parseInt(raw);
|
|
6578
|
+
}
|
|
6564
6579
|
var TelegramChannel;
|
|
6565
6580
|
var init_telegram2 = __esm({
|
|
6566
6581
|
"src/channels/telegram.ts"() {
|
|
@@ -6710,13 +6725,13 @@ var init_telegram2 = __esm({
|
|
|
6710
6725
|
await this.bot.stop();
|
|
6711
6726
|
}
|
|
6712
6727
|
async sendTyping(chatId) {
|
|
6713
|
-
await this.bot.api.sendChatAction(
|
|
6728
|
+
await this.bot.api.sendChatAction(numericChatId(chatId), "typing");
|
|
6714
6729
|
}
|
|
6715
6730
|
async sendText(chatId, text, parseMode) {
|
|
6716
6731
|
if (parseMode === "plain") {
|
|
6717
6732
|
const plainChunks = splitMessage(text);
|
|
6718
6733
|
for (const chunk of plainChunks) {
|
|
6719
|
-
await this.bot.api.sendMessage(
|
|
6734
|
+
await this.bot.api.sendMessage(numericChatId(chatId), chunk);
|
|
6720
6735
|
}
|
|
6721
6736
|
return;
|
|
6722
6737
|
}
|
|
@@ -6724,12 +6739,12 @@ var init_telegram2 = __esm({
|
|
|
6724
6739
|
const chunks = splitMessage(formatted);
|
|
6725
6740
|
for (const chunk of chunks) {
|
|
6726
6741
|
try {
|
|
6727
|
-
await this.bot.api.sendMessage(
|
|
6742
|
+
await this.bot.api.sendMessage(numericChatId(chatId), chunk, {
|
|
6728
6743
|
parse_mode: "HTML"
|
|
6729
6744
|
});
|
|
6730
6745
|
} catch {
|
|
6731
6746
|
await this.bot.api.sendMessage(
|
|
6732
|
-
|
|
6747
|
+
numericChatId(chatId),
|
|
6733
6748
|
chunk.replace(/<[^>]+>/g, "")
|
|
6734
6749
|
);
|
|
6735
6750
|
}
|
|
@@ -6737,13 +6752,13 @@ var init_telegram2 = __esm({
|
|
|
6737
6752
|
}
|
|
6738
6753
|
async sendVoice(chatId, audioBuffer, fileName) {
|
|
6739
6754
|
await this.bot.api.sendVoice(
|
|
6740
|
-
|
|
6755
|
+
numericChatId(chatId),
|
|
6741
6756
|
new InputFile(audioBuffer, fileName ?? "response.ogg")
|
|
6742
6757
|
);
|
|
6743
6758
|
}
|
|
6744
6759
|
async sendFile(chatId, buffer, fileName) {
|
|
6745
6760
|
await this.bot.api.sendDocument(
|
|
6746
|
-
|
|
6761
|
+
numericChatId(chatId),
|
|
6747
6762
|
new InputFile(buffer, fileName)
|
|
6748
6763
|
);
|
|
6749
6764
|
}
|
|
@@ -6755,7 +6770,7 @@ var init_telegram2 = __esm({
|
|
|
6755
6770
|
}
|
|
6756
6771
|
async sendTextReturningId(chatId, text, parseMode) {
|
|
6757
6772
|
try {
|
|
6758
|
-
const msg = await this.bot.api.sendMessage(
|
|
6773
|
+
const msg = await this.bot.api.sendMessage(numericChatId(chatId), text);
|
|
6759
6774
|
return msg.message_id.toString();
|
|
6760
6775
|
} catch {
|
|
6761
6776
|
return void 0;
|
|
@@ -6764,14 +6779,14 @@ var init_telegram2 = __esm({
|
|
|
6764
6779
|
async editText(chatId, messageId, text, parseMode) {
|
|
6765
6780
|
const formatted = parseMode === "html" ? text : formatForTelegram(text);
|
|
6766
6781
|
try {
|
|
6767
|
-
await this.bot.api.editMessageText(
|
|
6782
|
+
await this.bot.api.editMessageText(numericChatId(chatId), parseInt(messageId), formatted, {
|
|
6768
6783
|
parse_mode: "HTML"
|
|
6769
6784
|
});
|
|
6770
6785
|
return true;
|
|
6771
6786
|
} catch {
|
|
6772
6787
|
try {
|
|
6773
6788
|
await this.bot.api.editMessageText(
|
|
6774
|
-
|
|
6789
|
+
numericChatId(chatId),
|
|
6775
6790
|
parseInt(messageId),
|
|
6776
6791
|
formatted.replace(/<[^>]+>/g, "")
|
|
6777
6792
|
);
|
|
@@ -6793,13 +6808,13 @@ var init_telegram2 = __esm({
|
|
|
6793
6808
|
}
|
|
6794
6809
|
keyboard.row();
|
|
6795
6810
|
}
|
|
6796
|
-
await this.bot.api.sendMessage(
|
|
6811
|
+
await this.bot.api.sendMessage(numericChatId(chatId), text, {
|
|
6797
6812
|
reply_markup: keyboard
|
|
6798
6813
|
});
|
|
6799
6814
|
}
|
|
6800
6815
|
async reactToMessage(chatId, messageId, emoji) {
|
|
6801
6816
|
try {
|
|
6802
|
-
await this.bot.api.setMessageReaction(
|
|
6817
|
+
await this.bot.api.setMessageReaction(numericChatId(chatId), parseInt(messageId), [
|
|
6803
6818
|
{ type: "emoji", emoji }
|
|
6804
6819
|
]);
|
|
6805
6820
|
} catch {
|
|
@@ -7093,6 +7108,9 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
7093
7108
|
stdio: "pipe",
|
|
7094
7109
|
timeout: 3e4
|
|
7095
7110
|
});
|
|
7111
|
+
if (!existsSync10(join11(tmpDir, ".git"))) {
|
|
7112
|
+
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
7113
|
+
}
|
|
7096
7114
|
const searchRoot = subPath ? join11(tmpDir, subPath) : tmpDir;
|
|
7097
7115
|
const skillDir = await findSkillDir(searchRoot);
|
|
7098
7116
|
if (!skillDir) {
|
|
@@ -7165,7 +7183,12 @@ async function findSkillDir(root) {
|
|
|
7165
7183
|
const entries = await readdir2(root, { withFileTypes: true });
|
|
7166
7184
|
for (const entry of entries) {
|
|
7167
7185
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
7168
|
-
|
|
7186
|
+
let subEntries;
|
|
7187
|
+
try {
|
|
7188
|
+
subEntries = await readdir2(join11(root, entry.name), { withFileTypes: true });
|
|
7189
|
+
} catch {
|
|
7190
|
+
continue;
|
|
7191
|
+
}
|
|
7169
7192
|
for (const sub of subEntries) {
|
|
7170
7193
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
7171
7194
|
for (const c of candidates) {
|
|
@@ -7371,8 +7394,7 @@ function stopAllHeartbeats() {
|
|
|
7371
7394
|
}
|
|
7372
7395
|
function startAllHeartbeats() {
|
|
7373
7396
|
try {
|
|
7374
|
-
const
|
|
7375
|
-
const db3 = getDb2();
|
|
7397
|
+
const db3 = getDb();
|
|
7376
7398
|
const rows = db3.prepare(
|
|
7377
7399
|
"SELECT chat_id FROM chat_heartbeat WHERE enabled = 1"
|
|
7378
7400
|
).all();
|
|
@@ -7610,9 +7632,11 @@ async function macOsTts(text) {
|
|
|
7610
7632
|
await execFileAsync2("say", ["-o", tmpAiff, sanitized]);
|
|
7611
7633
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpAiff, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
7612
7634
|
const oggBuffer = await readFile4(tmpOgg);
|
|
7613
|
-
unlink(tmpAiff).catch(() => {
|
|
7635
|
+
unlink(tmpAiff).catch((err) => {
|
|
7636
|
+
error("[tts] cleanup failed:", err);
|
|
7614
7637
|
});
|
|
7615
|
-
unlink(tmpOgg).catch(() => {
|
|
7638
|
+
unlink(tmpOgg).catch((err) => {
|
|
7639
|
+
error("[tts] cleanup failed:", err);
|
|
7616
7640
|
});
|
|
7617
7641
|
return oggBuffer;
|
|
7618
7642
|
}
|
|
@@ -7743,8 +7767,7 @@ async function startWizard(chatId, input, channel) {
|
|
|
7743
7767
|
step: "schedule",
|
|
7744
7768
|
task: parsed?.task || input,
|
|
7745
7769
|
scheduleType: parsed?.scheduleType,
|
|
7746
|
-
cron: parsed?.cron
|
|
7747
|
-
everyMs: parsed?.everyMs
|
|
7770
|
+
cron: parsed?.cron
|
|
7748
7771
|
};
|
|
7749
7772
|
if (parsed?.cron) {
|
|
7750
7773
|
pending.step = "timezone";
|
|
@@ -10548,10 +10571,18 @@ async function main() {
|
|
|
10548
10571
|
bootstrapBuiltinMcps(getDb());
|
|
10549
10572
|
const SUMMARIZE_TIMEOUT_MS = 3e4;
|
|
10550
10573
|
try {
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10574
|
+
let timer;
|
|
10575
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
10576
|
+
timer = setTimeout(() => reject(new Error("timeout")), SUMMARIZE_TIMEOUT_MS);
|
|
10577
|
+
});
|
|
10578
|
+
try {
|
|
10579
|
+
await Promise.race([
|
|
10580
|
+
summarizeAllPending(),
|
|
10581
|
+
timeoutPromise
|
|
10582
|
+
]);
|
|
10583
|
+
} finally {
|
|
10584
|
+
clearTimeout(timer);
|
|
10585
|
+
}
|
|
10555
10586
|
} catch {
|
|
10556
10587
|
log("[cc-claw] Session summarization skipped (timeout or backend unavailable)");
|
|
10557
10588
|
}
|
|
@@ -10782,7 +10813,7 @@ function uninstallMacOS() {
|
|
|
10782
10813
|
}
|
|
10783
10814
|
function statusMacOS() {
|
|
10784
10815
|
try {
|
|
10785
|
-
const out = execSync5("launchctl list | grep cc-claw", { encoding: "utf-8" }).trim();
|
|
10816
|
+
const out = execSync5("launchctl list | grep cc-claw", { shell: "/bin/sh", encoding: "utf-8" }).trim();
|
|
10786
10817
|
if (out) {
|
|
10787
10818
|
const parts = out.split(/\s+/);
|
|
10788
10819
|
const pid = parts[0];
|
|
@@ -11025,7 +11056,8 @@ async function restartService() {
|
|
|
11025
11056
|
const os = platform2();
|
|
11026
11057
|
try {
|
|
11027
11058
|
if (os === "darwin") {
|
|
11028
|
-
execSync6("
|
|
11059
|
+
const uid = process.getuid?.() ?? execSync6("id -u", { encoding: "utf-8" }).trim();
|
|
11060
|
+
execSync6(`launchctl kickstart -k gui/${uid}/com.cc-claw`);
|
|
11029
11061
|
console.log(`
|
|
11030
11062
|
${success("Daemon restarted.")}
|
|
11031
11063
|
`);
|
|
@@ -11324,7 +11356,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
11324
11356
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
11325
11357
|
const readDb = openDatabaseReadOnly2();
|
|
11326
11358
|
const journal = readDb.prepare("PRAGMA journal_mode").get();
|
|
11327
|
-
if (journal
|
|
11359
|
+
if (!journal) {
|
|
11360
|
+
checks.push({ name: "WAL mode", status: "warning", message: "could not read journal_mode" });
|
|
11361
|
+
} else if (journal.journal_mode === "wal") {
|
|
11328
11362
|
checks.push({ name: "WAL mode", status: "ok", message: "enabled" });
|
|
11329
11363
|
} else {
|
|
11330
11364
|
checks.push({ name: "WAL mode", status: "warning", message: `${journal.journal_mode} (expected wal)` });
|
|
@@ -12128,8 +12162,8 @@ async function agentsSpawn(globalOpts, opts) {
|
|
|
12128
12162
|
role: opts.role
|
|
12129
12163
|
});
|
|
12130
12164
|
if (res.ok) {
|
|
12165
|
+
const { success: s } = await Promise.resolve().then(() => (init_format(), format_exports));
|
|
12131
12166
|
output(res.data, (d) => {
|
|
12132
|
-
const { success: s } = (init_format(), __toCommonJS(format_exports));
|
|
12133
12167
|
return `
|
|
12134
12168
|
${s(`Agent spawned: ${d.agentId?.slice(0, 8) ?? "?"} (${opts.runner})`)}
|
|
12135
12169
|
`;
|
|
@@ -12393,13 +12427,21 @@ async function usageTokens(globalOpts) {
|
|
|
12393
12427
|
const { getAllAdapters: getAllAdapters2, getAllBackendIds: getAllBackendIds2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
12394
12428
|
const readDb = openDatabaseReadOnly2();
|
|
12395
12429
|
const backendIds = getAllBackendIds2();
|
|
12430
|
+
const adapters2 = getAllAdapters2();
|
|
12396
12431
|
const cutoff24h = new Date(Date.now() - 864e5).toISOString();
|
|
12397
12432
|
const data = backendIds.map((bid) => {
|
|
12398
|
-
const
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12433
|
+
const adapter = adapters2.find((a) => a.id === bid);
|
|
12434
|
+
const modelIds = adapter ? Object.keys(adapter.pricing) : [];
|
|
12435
|
+
let row;
|
|
12436
|
+
if (modelIds.length > 0) {
|
|
12437
|
+
const placeholders = modelIds.map(() => "?").join(",");
|
|
12438
|
+
row = readDb.prepare(`
|
|
12439
|
+
SELECT COALESCE(SUM(input_tokens),0) as input_tokens, COALESCE(SUM(output_tokens),0) as output_tokens, COUNT(*) as request_count
|
|
12440
|
+
FROM usage_log WHERE model IN (${placeholders}) AND created_at > ?
|
|
12441
|
+
`).get(...modelIds, cutoff24h);
|
|
12442
|
+
} else {
|
|
12443
|
+
row = { input_tokens: 0, output_tokens: 0, request_count: 0 };
|
|
12444
|
+
}
|
|
12403
12445
|
return { backend: bid, displayName: adapter?.displayName ?? bid, ...row };
|
|
12404
12446
|
});
|
|
12405
12447
|
readDb.close();
|
|
@@ -12496,6 +12538,7 @@ async function configList(globalOpts) {
|
|
|
12496
12538
|
const config2 = {};
|
|
12497
12539
|
for (const key of RUNTIME_KEYS) {
|
|
12498
12540
|
const { table, col } = KEY_TABLE_MAP[key];
|
|
12541
|
+
if (!ALLOWED_TABLES.has(table) || !ALLOWED_COLS.has(col)) continue;
|
|
12499
12542
|
try {
|
|
12500
12543
|
const row = readDb.prepare(`SELECT ${col} FROM ${table} WHERE chat_id = ?`).get(chatId);
|
|
12501
12544
|
config2[key] = row ? row[col] : null;
|
|
@@ -12527,6 +12570,10 @@ async function configGet(globalOpts, key) {
|
|
|
12527
12570
|
const readDb = openDatabaseReadOnly2();
|
|
12528
12571
|
const chatId = resolveChatId(globalOpts);
|
|
12529
12572
|
const { table, col } = KEY_TABLE_MAP[key];
|
|
12573
|
+
if (!ALLOWED_TABLES.has(table) || !ALLOWED_COLS.has(col)) {
|
|
12574
|
+
outputError("INVALID_KEY", `Invalid config mapping for "${key}".`);
|
|
12575
|
+
process.exit(1);
|
|
12576
|
+
}
|
|
12530
12577
|
let value = null;
|
|
12531
12578
|
try {
|
|
12532
12579
|
const row = readDb.prepare(`SELECT ${col} FROM ${table} WHERE chat_id = ?`).get(chatId);
|
|
@@ -12585,7 +12632,7 @@ async function configEnv(_globalOpts) {
|
|
|
12585
12632
|
return lines.join("\n");
|
|
12586
12633
|
});
|
|
12587
12634
|
}
|
|
12588
|
-
var RUNTIME_KEYS, KEY_TABLE_MAP;
|
|
12635
|
+
var RUNTIME_KEYS, KEY_TABLE_MAP, ALLOWED_TABLES, ALLOWED_COLS;
|
|
12589
12636
|
var init_config = __esm({
|
|
12590
12637
|
"src/cli/commands/config.ts"() {
|
|
12591
12638
|
"use strict";
|
|
@@ -12603,6 +12650,8 @@ var init_config = __esm({
|
|
|
12603
12650
|
cwd: { table: "chat_cwd", col: "cwd" },
|
|
12604
12651
|
voice: { table: "chat_voice", col: "enabled" }
|
|
12605
12652
|
};
|
|
12653
|
+
ALLOWED_TABLES = new Set(Object.values(KEY_TABLE_MAP).map((v) => v.table));
|
|
12654
|
+
ALLOWED_COLS = new Set(Object.values(KEY_TABLE_MAP).map((v) => v.col));
|
|
12606
12655
|
}
|
|
12607
12656
|
});
|
|
12608
12657
|
|
|
@@ -12676,11 +12725,14 @@ __export(permissions_exports, {
|
|
|
12676
12725
|
verboseSet: () => verboseSet
|
|
12677
12726
|
});
|
|
12678
12727
|
import { existsSync as existsSync30 } from "fs";
|
|
12679
|
-
|
|
12728
|
+
function ensureDb2() {
|
|
12680
12729
|
if (!existsSync30(DB_PATH)) {
|
|
12681
12730
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12682
12731
|
process.exit(1);
|
|
12683
12732
|
}
|
|
12733
|
+
}
|
|
12734
|
+
async function permissionsGet(globalOpts) {
|
|
12735
|
+
ensureDb2();
|
|
12684
12736
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
12685
12737
|
const readDb = openDatabaseReadOnly2();
|
|
12686
12738
|
const chatId = resolveChatId(globalOpts);
|
|
@@ -12712,10 +12764,7 @@ async function permissionsSet(globalOpts, mode) {
|
|
|
12712
12764
|
}
|
|
12713
12765
|
}
|
|
12714
12766
|
async function toolsList(globalOpts) {
|
|
12715
|
-
|
|
12716
|
-
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12717
|
-
process.exit(1);
|
|
12718
|
-
}
|
|
12767
|
+
ensureDb2();
|
|
12719
12768
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
12720
12769
|
const readDb = openDatabaseReadOnly2();
|
|
12721
12770
|
const chatId = resolveChatId(globalOpts);
|
|
@@ -12776,10 +12825,7 @@ async function toggleTool2(globalOpts, name, enabled) {
|
|
|
12776
12825
|
}
|
|
12777
12826
|
}
|
|
12778
12827
|
async function verboseGet(globalOpts) {
|
|
12779
|
-
|
|
12780
|
-
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12781
|
-
process.exit(1);
|
|
12782
|
-
}
|
|
12828
|
+
ensureDb2();
|
|
12783
12829
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports2));
|
|
12784
12830
|
const readDb = openDatabaseReadOnly2();
|
|
12785
12831
|
const chatId = resolveChatId(globalOpts);
|
|
@@ -13682,7 +13728,7 @@ async function requiredInput(prompt, existing) {
|
|
|
13682
13728
|
}
|
|
13683
13729
|
async function validateBotToken(token) {
|
|
13684
13730
|
try {
|
|
13685
|
-
const res = await fetch(`https://api.telegram.org/bot${token}/getMe
|
|
13731
|
+
const res = await fetch(`https://api.telegram.org/bot${token}/getMe`, { signal: AbortSignal.timeout(1e4) });
|
|
13686
13732
|
const data = await res.json();
|
|
13687
13733
|
if (data.ok && data.result?.username) {
|
|
13688
13734
|
return { valid: true, botName: data.result.username };
|
package/package.json
CHANGED