cc-claw 0.20.7 → 0.20.9
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 +985 -468
- 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.20.
|
|
36
|
+
VERSION = true ? "0.20.9" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -1625,6 +1625,30 @@ function initSchema(db3) {
|
|
|
1625
1625
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1626
1626
|
);
|
|
1627
1627
|
`);
|
|
1628
|
+
try {
|
|
1629
|
+
db3.exec("ALTER TABLE chat_heartbeat ADD COLUMN backend TEXT");
|
|
1630
|
+
} catch {
|
|
1631
|
+
}
|
|
1632
|
+
try {
|
|
1633
|
+
db3.exec("ALTER TABLE chat_heartbeat ADD COLUMN model TEXT");
|
|
1634
|
+
} catch {
|
|
1635
|
+
}
|
|
1636
|
+
try {
|
|
1637
|
+
db3.exec("ALTER TABLE chat_heartbeat ADD COLUMN fallbacks TEXT");
|
|
1638
|
+
} catch {
|
|
1639
|
+
}
|
|
1640
|
+
try {
|
|
1641
|
+
db3.exec("ALTER TABLE chat_heartbeat ADD COLUMN target TEXT");
|
|
1642
|
+
} catch {
|
|
1643
|
+
}
|
|
1644
|
+
try {
|
|
1645
|
+
db3.exec("ALTER TABLE chat_heartbeat ADD COLUMN channel TEXT NOT NULL DEFAULT 'telegram'");
|
|
1646
|
+
} catch {
|
|
1647
|
+
}
|
|
1648
|
+
try {
|
|
1649
|
+
db3.exec("ALTER TABLE chat_heartbeat ADD COLUMN thinking TEXT");
|
|
1650
|
+
} catch {
|
|
1651
|
+
}
|
|
1628
1652
|
db3.exec(`
|
|
1629
1653
|
CREATE TABLE IF NOT EXISTS cwd_bookmarks (
|
|
1630
1654
|
chatId TEXT NOT NULL,
|
|
@@ -3440,36 +3464,89 @@ function getHeartbeatConfig(chatId) {
|
|
|
3440
3464
|
return getDb().prepare(
|
|
3441
3465
|
`SELECT chat_id as chatId, enabled, interval_ms as intervalMs,
|
|
3442
3466
|
active_start as activeStart, active_end as activeEnd,
|
|
3443
|
-
last_beat_at as lastBeatAt, next_beat_at as nextBeatAt
|
|
3467
|
+
last_beat_at as lastBeatAt, next_beat_at as nextBeatAt,
|
|
3468
|
+
backend, model, fallbacks, target, channel, thinking
|
|
3444
3469
|
FROM chat_heartbeat WHERE chat_id = ?`
|
|
3445
3470
|
).get(chatId);
|
|
3446
3471
|
}
|
|
3447
3472
|
function setHeartbeatConfig(chatId, config2) {
|
|
3448
3473
|
getDb().prepare(`
|
|
3449
|
-
INSERT INTO chat_heartbeat (chat_id
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
config2.intervalMs
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
config2.
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3474
|
+
INSERT OR IGNORE INTO chat_heartbeat (chat_id) VALUES (?)
|
|
3475
|
+
`).run(chatId);
|
|
3476
|
+
const updates = [];
|
|
3477
|
+
const values = [];
|
|
3478
|
+
if (config2.enabled !== void 0) {
|
|
3479
|
+
updates.push("enabled = ?");
|
|
3480
|
+
values.push(config2.enabled ? 1 : 0);
|
|
3481
|
+
}
|
|
3482
|
+
if (config2.intervalMs !== void 0) {
|
|
3483
|
+
updates.push("interval_ms = ?");
|
|
3484
|
+
values.push(config2.intervalMs);
|
|
3485
|
+
}
|
|
3486
|
+
if (config2.activeStart !== void 0) {
|
|
3487
|
+
updates.push("active_start = ?");
|
|
3488
|
+
values.push(config2.activeStart);
|
|
3489
|
+
}
|
|
3490
|
+
if (config2.activeEnd !== void 0) {
|
|
3491
|
+
updates.push("active_end = ?");
|
|
3492
|
+
values.push(config2.activeEnd);
|
|
3493
|
+
}
|
|
3494
|
+
if ("backend" in config2) {
|
|
3495
|
+
updates.push("backend = ?");
|
|
3496
|
+
values.push(config2.backend ?? null);
|
|
3497
|
+
}
|
|
3498
|
+
if ("model" in config2) {
|
|
3499
|
+
updates.push("model = ?");
|
|
3500
|
+
values.push(config2.model ?? null);
|
|
3501
|
+
}
|
|
3502
|
+
if ("fallbacks" in config2) {
|
|
3503
|
+
updates.push("fallbacks = ?");
|
|
3504
|
+
values.push(config2.fallbacks ?? null);
|
|
3505
|
+
}
|
|
3506
|
+
if ("target" in config2) {
|
|
3507
|
+
updates.push("target = ?");
|
|
3508
|
+
values.push(config2.target ?? null);
|
|
3509
|
+
}
|
|
3510
|
+
if (config2.channel !== void 0) {
|
|
3511
|
+
updates.push("channel = ?");
|
|
3512
|
+
values.push(config2.channel);
|
|
3513
|
+
}
|
|
3514
|
+
if ("thinking" in config2) {
|
|
3515
|
+
updates.push("thinking = ?");
|
|
3516
|
+
values.push(config2.thinking ?? null);
|
|
3517
|
+
}
|
|
3518
|
+
if (updates.length > 0) {
|
|
3519
|
+
values.push(chatId);
|
|
3520
|
+
getDb().prepare(
|
|
3521
|
+
`UPDATE chat_heartbeat SET ${updates.join(", ")} WHERE chat_id = ?`
|
|
3522
|
+
).run(...values);
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
function updateHeartbeatField(chatId, field, value) {
|
|
3526
|
+
const allowedFields = ["backend", "model", "fallbacks", "target", "channel", "thinking", "enabled", "interval_ms", "active_start", "active_end"];
|
|
3527
|
+
if (!allowedFields.includes(field)) throw new Error(`Invalid heartbeat field: ${field}`);
|
|
3528
|
+
getDb().prepare(`
|
|
3529
|
+
INSERT OR IGNORE INTO chat_heartbeat (chat_id) VALUES (?)
|
|
3530
|
+
`).run(chatId);
|
|
3531
|
+
getDb().prepare(
|
|
3532
|
+
`UPDATE chat_heartbeat SET ${field} = ? WHERE chat_id = ?`
|
|
3533
|
+
).run(value, chatId);
|
|
3467
3534
|
}
|
|
3468
3535
|
function updateHeartbeatTimestamps(chatId, lastBeat, nextBeat) {
|
|
3469
3536
|
getDb().prepare(
|
|
3470
3537
|
"UPDATE chat_heartbeat SET last_beat_at = ?, next_beat_at = ? WHERE chat_id = ?"
|
|
3471
3538
|
).run(lastBeat, nextBeat, chatId);
|
|
3472
3539
|
}
|
|
3540
|
+
function parseHeartbeatFallbacks(fallbacksJson) {
|
|
3541
|
+
if (!fallbacksJson) return [];
|
|
3542
|
+
try {
|
|
3543
|
+
const parsed = JSON.parse(fallbacksJson);
|
|
3544
|
+
if (!Array.isArray(parsed)) return [];
|
|
3545
|
+
return parsed.filter((f) => f && typeof f.backend === "string");
|
|
3546
|
+
} catch {
|
|
3547
|
+
return [];
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3473
3550
|
function getActiveWatches(chatId) {
|
|
3474
3551
|
return getDb().prepare(
|
|
3475
3552
|
`SELECT id, chat_id as chatId, description, expires_at as expiresAt, created_at as createdAt
|
|
@@ -3940,6 +4017,7 @@ __export(store_exports5, {
|
|
|
3940
4017
|
markSlotExhausted: () => markSlotExhausted,
|
|
3941
4018
|
markSlotSuccess: () => markSlotSuccess,
|
|
3942
4019
|
openDatabaseReadOnly: () => openDatabaseReadOnly,
|
|
4020
|
+
parseHeartbeatFallbacks: () => parseHeartbeatFallbacks,
|
|
3943
4021
|
pinChatBackendSlot: () => pinChatBackendSlot,
|
|
3944
4022
|
pinChatGeminiSlot: () => pinChatGeminiSlot,
|
|
3945
4023
|
pruneJobRuns: () => pruneJobRuns,
|
|
@@ -3997,6 +4075,7 @@ __export(store_exports5, {
|
|
|
3997
4075
|
toggleShowThinkingUi: () => toggleShowThinkingUi,
|
|
3998
4076
|
toggleTool: () => toggleTool,
|
|
3999
4077
|
touchBookmark: () => touchBookmark,
|
|
4078
|
+
updateHeartbeatField: () => updateHeartbeatField,
|
|
4000
4079
|
updateHeartbeatTimestamps: () => updateHeartbeatTimestamps,
|
|
4001
4080
|
updateJob: () => updateJob,
|
|
4002
4081
|
updateJobEnabled: () => updateJobEnabled,
|
|
@@ -6120,8 +6199,8 @@ async function healthCheck(serverName) {
|
|
|
6120
6199
|
}
|
|
6121
6200
|
if (needsCatalogRefresh) {
|
|
6122
6201
|
try {
|
|
6123
|
-
const { getAdapter:
|
|
6124
|
-
const adapter =
|
|
6202
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
6203
|
+
const adapter = getAdapter5("ollama");
|
|
6125
6204
|
if ("refreshModelCatalog" in adapter) {
|
|
6126
6205
|
adapter.refreshModelCatalog();
|
|
6127
6206
|
}
|
|
@@ -6867,21 +6946,33 @@ You are an expert user and operator of the CC-Claw architecture. Because you are
|
|
|
6867
6946
|
- **Dry Run & Webhooks**: Jobs aren't just for Telegram messages. Using \`--delivery dry_run\`, you can test jobs internally (output is logged to the database but not spammed to chat). You can also route AI output directly to external APIs using \`--delivery webhook\`.
|
|
6868
6947
|
- **Auditing**: Every cron execution and its output is permanently logged in the database's \`message_log\` for verifiable auditing.
|
|
6869
6948
|
|
|
6870
|
-
## 5.
|
|
6949
|
+
## 5. Heartbeat & Watches (Proactive Monitoring)
|
|
6950
|
+
- **Heartbeat**: A periodic awareness cycle that checks system health and user-defined watches. Configured via \`/heartbeat\`.
|
|
6951
|
+
- **Active Hours**: Heartbeat only fires within configured active hours (default 08:00-22:00).
|
|
6952
|
+
- **Smart Suppression**: When nothing needs attention, the AI responds with \`HEARTBEAT_OK\` and no message is delivered to the user.
|
|
6953
|
+
- **Delivery Parity**: Like cron jobs, heartbeat supports custom backend, model, fallback chain, thinking level, and delivery target.
|
|
6954
|
+
- **Watches**: AI-executable awareness tasks added via \`/watch\`. These are instructions the heartbeat AI will check using its available tools.
|
|
6955
|
+
- \`/watch add Check my email for urgent messages\` \u2014 adds a persistent watch
|
|
6956
|
+
- \`/watch add Monitor Ollama server health for 24h\` \u2014 adds a time-limited watch
|
|
6957
|
+
- \`/watch list\` \u2014 shows all active watches
|
|
6958
|
+
- \`/watch remove <id>\` \u2014 removes a watch
|
|
6959
|
+
- **\u26A0\uFE0F HEARTBEAT MANAGEMENT**: Heartbeat and watch configuration MUST be done exclusively through \`/heartbeat\` and \`/watch\` slash commands. NEVER modify the \`chat_heartbeat\` or \`heartbeat_watches\` database tables directly.
|
|
6960
|
+
|
|
6961
|
+
## 6. System Controls & Modes
|
|
6871
6962
|
- **Permission Modes**: Command execution is gated. \`safe\` mode requires user approval for mutations, \`yolo\` auto-runs everything without asking, and \`plan\` is strictly read-only for drafting proposals.
|
|
6872
6963
|
- **Global Settings**: The user controls your behavior system-wide using commands like \`/response_style\` (concise, normal, detailed) to enforce verbosity constraints, and \`/voice_config\` to select Text-to-Speech voices from premium providers like ElevenLabs, Grok (xAI), or local macOS.
|
|
6873
6964
|
- **Telegram UX**: Most slash commands use interactive inline keyboards with colored buttons. \`/menu\` (or \`/m\`) is the home screen. \`/usage\` unifies cost, limits, and usage tracking. Inline mode (\`@botname query\`) searches memories from any chat.
|
|
6874
6965
|
|
|
6875
|
-
##
|
|
6966
|
+
## 7. Integrations & MCP
|
|
6876
6967
|
- **Skills**: You can load specific workflows from the \`~/.cc-claw/workspace/skills/\` directory.
|
|
6877
6968
|
- **Files**: You can send physical files to the user across Telegram by simply writing \`[SEND_FILE:/absolute/path/to/file]\` in your response.
|
|
6878
6969
|
- **MCP Ecosystem**: You are deeply natively integrated with Model Context Protocol (MCP) servers (like Perplexity, NotebookLM, Context7) granting you immense external reach.
|
|
6879
6970
|
|
|
6880
|
-
##
|
|
6971
|
+
## 8. CLI Reference
|
|
6881
6972
|
- Run \\\`cc-claw --ai\\\` for a comprehensive command reference covering all CLI and Telegram commands
|
|
6882
6973
|
- Use \\\`cc-claw --ai --install\\\` to install the skill to all backend skill directories
|
|
6883
6974
|
|
|
6884
|
-
##
|
|
6975
|
+
## 9. CRITICAL RULES \u2014 Memory & State
|
|
6885
6976
|
- NEVER use native CLI memory tools (Gemini's save_memory, Claude's memory, etc.) \u2014 they are backend-local
|
|
6886
6977
|
- NEVER modify the SQLite database directly (no INSERT/UPDATE/DELETE SQL)
|
|
6887
6978
|
- ALWAYS use CC-Claw slash commands (/remember, /evolve, etc.) or the cc-claw CLI
|
|
@@ -9489,8 +9580,8 @@ async function spawnSubAgent(chatId, opts) {
|
|
|
9489
9580
|
if (!runner) throw new Error(`Unknown runner: ${opts.runner}`);
|
|
9490
9581
|
if (opts.model) {
|
|
9491
9582
|
try {
|
|
9492
|
-
const { getAdapter:
|
|
9493
|
-
const adapter =
|
|
9583
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
9584
|
+
const adapter = getAdapter5(opts.runner);
|
|
9494
9585
|
if (adapter.availableModels && !adapter.availableModels[opts.model]) {
|
|
9495
9586
|
const validModels = Object.keys(adapter.availableModels).join(", ");
|
|
9496
9587
|
throw new Error(`Unknown model "${opts.model}" for ${opts.runner}. Available: ${validModels}`);
|
|
@@ -9871,8 +9962,8 @@ async function handleAgentComplete(agentId, chatId, resultText, usage2, mcpsAdde
|
|
|
9871
9962
|
const runner = getRunner(agent.runnerId);
|
|
9872
9963
|
if (runner) {
|
|
9873
9964
|
try {
|
|
9874
|
-
const { getAdapter:
|
|
9875
|
-
const adapter =
|
|
9965
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
9966
|
+
const adapter = getAdapter5(agent.runnerId);
|
|
9876
9967
|
const model2 = agent.model ?? adapter.defaultModel;
|
|
9877
9968
|
const pricing = adapter.pricing[model2];
|
|
9878
9969
|
if (pricing) {
|
|
@@ -10621,7 +10712,7 @@ var init_chat = __esm({
|
|
|
10621
10712
|
}
|
|
10622
10713
|
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
10623
10714
|
const { getMode: getMode3, getCwd: getCwd3, getModel: getModel3, addUsage: addUsage3, getBackend: getBackend3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
10624
|
-
const { getAdapterForChat:
|
|
10715
|
+
const { getAdapterForChat: getAdapterForChat3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
10625
10716
|
const chatId = body.chatId;
|
|
10626
10717
|
const backend2 = body.backend;
|
|
10627
10718
|
const PERM_LEVEL = { plan: 0, safe: 1, yolo: 2 };
|
|
@@ -10631,7 +10722,7 @@ var init_chat = __esm({
|
|
|
10631
10722
|
const cwd = body.cwd ?? getCwd3(chatId);
|
|
10632
10723
|
const model2 = body.model ?? getModel3(chatId) ?? (() => {
|
|
10633
10724
|
try {
|
|
10634
|
-
return
|
|
10725
|
+
return getAdapterForChat3(chatId).defaultModel;
|
|
10635
10726
|
} catch {
|
|
10636
10727
|
return void 0;
|
|
10637
10728
|
}
|
|
@@ -15654,8 +15745,8 @@ async function handleAdd(chatId, channel, name, host, port) {
|
|
|
15654
15745
|
}
|
|
15655
15746
|
const models = await OllamaService.discoverModels(name);
|
|
15656
15747
|
try {
|
|
15657
|
-
const { getAdapter:
|
|
15658
|
-
const adapter =
|
|
15748
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
15749
|
+
const adapter = getAdapter5("ollama");
|
|
15659
15750
|
if ("refreshModelCatalog" in adapter) {
|
|
15660
15751
|
adapter.refreshModelCatalog();
|
|
15661
15752
|
}
|
|
@@ -15708,8 +15799,8 @@ async function sendDiscover(chatId, channel, serverName) {
|
|
|
15708
15799
|
const { OllamaService } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
15709
15800
|
const models = await OllamaService.discoverModels(serverName);
|
|
15710
15801
|
try {
|
|
15711
|
-
const { getAdapter:
|
|
15712
|
-
const adapter =
|
|
15802
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
15803
|
+
const adapter = getAdapter5("ollama");
|
|
15713
15804
|
if ("refreshModelCatalog" in adapter) {
|
|
15714
15805
|
adapter.refreshModelCatalog();
|
|
15715
15806
|
}
|
|
@@ -17878,8 +17969,197 @@ var init_install = __esm({
|
|
|
17878
17969
|
}
|
|
17879
17970
|
});
|
|
17880
17971
|
|
|
17972
|
+
// src/health/checks.ts
|
|
17973
|
+
import { existsSync as existsSync18, statSync as statSync7, readFileSync as readFileSync9 } from "fs";
|
|
17974
|
+
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
17975
|
+
function getRecentErrors() {
|
|
17976
|
+
if (!existsSync18(ERROR_LOG_PATH)) return null;
|
|
17977
|
+
const logContent = readFileSync9(ERROR_LOG_PATH, "utf-8");
|
|
17978
|
+
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
17979
|
+
const last24h = Date.now() - 864e5;
|
|
17980
|
+
const lines = allLines.filter((line) => {
|
|
17981
|
+
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:+-]+)/);
|
|
17982
|
+
if (match) return (/* @__PURE__ */ new Date(`${match[1]}T${match[2]}`)).getTime() > last24h;
|
|
17983
|
+
return false;
|
|
17984
|
+
});
|
|
17985
|
+
if (lines.length === 0) return null;
|
|
17986
|
+
const classified = { rate429: [], contentSilence: [], spawnTimeout: [], other: [], total: 0 };
|
|
17987
|
+
for (const line of lines) {
|
|
17988
|
+
if (/429|rate.?limit/i.test(line)) classified.rate429.push(line);
|
|
17989
|
+
else if (/content silence/i.test(line)) classified.contentSilence.push(line);
|
|
17990
|
+
else if (/spawn timeout|timeout after \d+s/i.test(line)) classified.spawnTimeout.push(line);
|
|
17991
|
+
else classified.other.push(line);
|
|
17992
|
+
}
|
|
17993
|
+
classified.total = lines.length;
|
|
17994
|
+
return classified;
|
|
17995
|
+
}
|
|
17996
|
+
function checkDatabase() {
|
|
17997
|
+
const checks = [];
|
|
17998
|
+
if (existsSync18(DB_PATH)) {
|
|
17999
|
+
const size = statSync7(DB_PATH).size;
|
|
18000
|
+
checks.push({ name: "Database", status: "ok", message: `${(size / 1024).toFixed(0)}KB` });
|
|
18001
|
+
} else {
|
|
18002
|
+
checks.push({ name: "Database", status: "error", message: "Not found", fix: "cc-claw setup" });
|
|
18003
|
+
}
|
|
18004
|
+
return checks;
|
|
18005
|
+
}
|
|
18006
|
+
function checkDiskSpace() {
|
|
18007
|
+
try {
|
|
18008
|
+
const dfOutput = execSync4("df -k " + DATA_PATH, { encoding: "utf-8" });
|
|
18009
|
+
const lines = dfOutput.trim().split("\n");
|
|
18010
|
+
if (lines.length >= 2) {
|
|
18011
|
+
const parts = lines[1].split(/\s+/);
|
|
18012
|
+
const availKB = parseInt(parts[3], 10);
|
|
18013
|
+
if (availKB < 1e5) {
|
|
18014
|
+
return { name: "Disk space", status: "warning", message: `${(availKB / 1024).toFixed(0)}MB available` };
|
|
18015
|
+
}
|
|
18016
|
+
return { name: "Disk space", status: "ok", message: `${(availKB / 1024 / 1024).toFixed(1)}GB available` };
|
|
18017
|
+
}
|
|
18018
|
+
} catch {
|
|
18019
|
+
}
|
|
18020
|
+
return null;
|
|
18021
|
+
}
|
|
18022
|
+
function checkErrorLog() {
|
|
18023
|
+
const checks = [];
|
|
18024
|
+
const errors = getRecentErrors();
|
|
18025
|
+
if (!errors) {
|
|
18026
|
+
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
18027
|
+
return checks;
|
|
18028
|
+
}
|
|
18029
|
+
if (errors.rate429.length > 10) {
|
|
18030
|
+
checks.push({ name: "Rate limits", status: "error", message: `${errors.rate429.length} rate-limit (429) errors in last 24h` });
|
|
18031
|
+
} else if (errors.rate429.length > 0) {
|
|
18032
|
+
checks.push({ name: "Rate limits", status: "warning", message: `${errors.rate429.length} rate-limit errors in last 24h` });
|
|
18033
|
+
}
|
|
18034
|
+
if (errors.contentSilence.length > 0) {
|
|
18035
|
+
checks.push({ name: "Content silence", status: "warning", message: `${errors.contentSilence.length} silence timeout(s) in last 24h` });
|
|
18036
|
+
}
|
|
18037
|
+
if (errors.spawnTimeout.length > 0) {
|
|
18038
|
+
checks.push({ name: "Spawn timeouts", status: "warning", message: `${errors.spawnTimeout.length} backend timeout(s) in last 24h` });
|
|
18039
|
+
}
|
|
18040
|
+
if (errors.other.length > 0) {
|
|
18041
|
+
checks.push({ name: "Other errors", status: "warning", message: `${errors.other.length} error(s) in last 24h` });
|
|
18042
|
+
}
|
|
18043
|
+
if (checks.length === 0) {
|
|
18044
|
+
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
18045
|
+
}
|
|
18046
|
+
return checks;
|
|
18047
|
+
}
|
|
18048
|
+
function checkBackendCLIs() {
|
|
18049
|
+
const checks = [];
|
|
18050
|
+
const CLI_BINARIES = { claude: "claude", gemini: "gemini", codex: "codex", cursor: "agent" };
|
|
18051
|
+
let installed = 0;
|
|
18052
|
+
for (const [label2, binary] of Object.entries(CLI_BINARIES)) {
|
|
18053
|
+
try {
|
|
18054
|
+
const path = execFileSync2("which", [binary], { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
18055
|
+
if (path) {
|
|
18056
|
+
checks.push({ name: `${label2} CLI`, status: "ok", message: path });
|
|
18057
|
+
installed++;
|
|
18058
|
+
}
|
|
18059
|
+
} catch {
|
|
18060
|
+
}
|
|
18061
|
+
}
|
|
18062
|
+
if (installed === 0) {
|
|
18063
|
+
checks.push({ name: "Backend CLIs", status: "error", message: "No backend CLIs found" });
|
|
18064
|
+
}
|
|
18065
|
+
return checks;
|
|
18066
|
+
}
|
|
18067
|
+
function checkOllamaServers() {
|
|
18068
|
+
try {
|
|
18069
|
+
const { OllamaStore } = (init_ollama(), __toCommonJS(ollama_exports));
|
|
18070
|
+
const servers = OllamaStore.listServers();
|
|
18071
|
+
if (servers.length > 0) {
|
|
18072
|
+
const online = servers.filter((s) => s.status === "online");
|
|
18073
|
+
const models = OllamaStore.getAvailableModels();
|
|
18074
|
+
if (online.length === servers.length) {
|
|
18075
|
+
return { name: "Ollama", status: "ok", message: `${online.length} server(s) online, ${models.length} model(s)` };
|
|
18076
|
+
} else if (online.length > 0) {
|
|
18077
|
+
return { name: "Ollama", status: "warning", message: `${online.length}/${servers.length} server(s) online` };
|
|
18078
|
+
}
|
|
18079
|
+
return { name: "Ollama", status: "warning", message: `${servers.length} server(s) configured, all offline`, fix: "ollama serve" };
|
|
18080
|
+
}
|
|
18081
|
+
} catch {
|
|
18082
|
+
}
|
|
18083
|
+
return null;
|
|
18084
|
+
}
|
|
18085
|
+
function checkBackendLimitsAll() {
|
|
18086
|
+
const checks = [];
|
|
18087
|
+
try {
|
|
18088
|
+
const { getAllBackendIds: getAllBackendIds3 } = (init_backends(), __toCommonJS(backends_exports));
|
|
18089
|
+
const { checkBackendLimits: checkBackendLimits2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
18090
|
+
for (const backend2 of getAllBackendIds3()) {
|
|
18091
|
+
const limitMsg = checkBackendLimits2(backend2);
|
|
18092
|
+
if (limitMsg) {
|
|
18093
|
+
checks.push({ name: `${backend2} limits`, status: "warning", message: limitMsg });
|
|
18094
|
+
}
|
|
18095
|
+
}
|
|
18096
|
+
} catch {
|
|
18097
|
+
}
|
|
18098
|
+
return checks;
|
|
18099
|
+
}
|
|
18100
|
+
function checkSchedulerHealth() {
|
|
18101
|
+
const checks = [];
|
|
18102
|
+
try {
|
|
18103
|
+
const { getHealthReport: getHealthReport2 } = (init_health2(), __toCommonJS(health_exports2));
|
|
18104
|
+
const report = getHealthReport2();
|
|
18105
|
+
if (report.failingJobs.length > 0) {
|
|
18106
|
+
for (const j of report.failingJobs) {
|
|
18107
|
+
checks.push({
|
|
18108
|
+
name: `Job #${j.id}`,
|
|
18109
|
+
status: report.failingJobs.length > 2 ? "error" : "warning",
|
|
18110
|
+
message: `"${j.description.slice(0, 40)}": ${j.failures} consecutive failures`
|
|
18111
|
+
});
|
|
18112
|
+
}
|
|
18113
|
+
}
|
|
18114
|
+
checks.push({
|
|
18115
|
+
name: "Scheduler",
|
|
18116
|
+
status: report.status === "healthy" ? "ok" : "warning",
|
|
18117
|
+
message: `${report.activeJobs} jobs active, ${report.failingJobs.length} failing`
|
|
18118
|
+
});
|
|
18119
|
+
} catch {
|
|
18120
|
+
}
|
|
18121
|
+
return checks;
|
|
18122
|
+
}
|
|
18123
|
+
function runAllHealthChecks() {
|
|
18124
|
+
const checks = [];
|
|
18125
|
+
checks.push(...checkDatabase());
|
|
18126
|
+
const disk = checkDiskSpace();
|
|
18127
|
+
if (disk) checks.push(disk);
|
|
18128
|
+
checks.push(...checkErrorLog());
|
|
18129
|
+
checks.push(...checkBackendLimitsAll());
|
|
18130
|
+
checks.push(...checkSchedulerHealth());
|
|
18131
|
+
const ollama2 = checkOllamaServers();
|
|
18132
|
+
if (ollama2) checks.push(ollama2);
|
|
18133
|
+
const errors = checks.filter((c) => c.status === "error").length;
|
|
18134
|
+
const warnings = checks.filter((c) => c.status === "warning").length;
|
|
18135
|
+
let statusLine;
|
|
18136
|
+
if (errors === 0 && warnings === 0) {
|
|
18137
|
+
statusLine = "\u2705 All systems OK";
|
|
18138
|
+
} else {
|
|
18139
|
+
const parts = [];
|
|
18140
|
+
if (errors > 0) parts.push(`${errors} error(s)`);
|
|
18141
|
+
if (warnings > 0) parts.push(`${warnings} warning(s)`);
|
|
18142
|
+
statusLine = `\u26A0\uFE0F ${parts.join(", ")}`;
|
|
18143
|
+
}
|
|
18144
|
+
return { checks, errors, warnings, statusLine };
|
|
18145
|
+
}
|
|
18146
|
+
function formatHealthForPrompt(summary) {
|
|
18147
|
+
const lines = [];
|
|
18148
|
+
for (const check of summary.checks) {
|
|
18149
|
+
const icon = check.status === "ok" ? "\u2705" : check.status === "warning" ? "\u26A0\uFE0F" : "\u274C";
|
|
18150
|
+
lines.push(`${icon} ${check.name}: ${check.message}`);
|
|
18151
|
+
}
|
|
18152
|
+
return lines.join("\n");
|
|
18153
|
+
}
|
|
18154
|
+
var init_checks = __esm({
|
|
18155
|
+
"src/health/checks.ts"() {
|
|
18156
|
+
"use strict";
|
|
18157
|
+
init_paths();
|
|
18158
|
+
}
|
|
18159
|
+
});
|
|
18160
|
+
|
|
17881
18161
|
// src/bootstrap/heartbeat.ts
|
|
17882
|
-
import { readFileSync as
|
|
18162
|
+
import { readFileSync as readFileSync10, existsSync as existsSync19 } from "fs";
|
|
17883
18163
|
import { join as join19 } from "path";
|
|
17884
18164
|
function initHeartbeat(channelReg) {
|
|
17885
18165
|
registry2 = channelReg;
|
|
@@ -17921,6 +18201,14 @@ function stopAllHeartbeats() {
|
|
|
17921
18201
|
function startAllHeartbeats() {
|
|
17922
18202
|
try {
|
|
17923
18203
|
const db3 = getDb();
|
|
18204
|
+
const allowedIds = (process.env.ALLOWED_CHAT_ID ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
18205
|
+
for (const chatId of allowedIds) {
|
|
18206
|
+
const existing = getHeartbeatConfig(chatId);
|
|
18207
|
+
if (!existing) {
|
|
18208
|
+
setHeartbeatConfig(chatId, { enabled: true });
|
|
18209
|
+
log(`[heartbeat] Enabled by default for chat ${chatId}`);
|
|
18210
|
+
}
|
|
18211
|
+
}
|
|
17924
18212
|
const rows = db3.prepare(
|
|
17925
18213
|
"SELECT chat_id FROM chat_heartbeat WHERE enabled = 1"
|
|
17926
18214
|
).all();
|
|
@@ -17933,6 +18221,11 @@ function startAllHeartbeats() {
|
|
|
17933
18221
|
} catch {
|
|
17934
18222
|
}
|
|
17935
18223
|
}
|
|
18224
|
+
async function runHeartbeatNow(chatId) {
|
|
18225
|
+
const config2 = getHeartbeatConfig(chatId);
|
|
18226
|
+
if (!config2) return;
|
|
18227
|
+
await runHeartbeat(chatId, config2);
|
|
18228
|
+
}
|
|
17936
18229
|
async function runHeartbeat(chatId, config2) {
|
|
17937
18230
|
if (!isWithinActiveHours(config2.activeStart, config2.activeEnd)) {
|
|
17938
18231
|
log(`[heartbeat] Skipping for ${chatId}: outside active hours (${config2.activeStart}-${config2.activeEnd})`);
|
|
@@ -17940,15 +18233,17 @@ async function runHeartbeat(chatId, config2) {
|
|
|
17940
18233
|
}
|
|
17941
18234
|
cleanExpiredWatches();
|
|
17942
18235
|
const prompt = assembleHeartbeatPrompt(chatId);
|
|
18236
|
+
const resolvedBackend = resolveBackendId(chatId, config2);
|
|
18237
|
+
const resolvedModel = config2.model ?? void 0;
|
|
17943
18238
|
try {
|
|
17944
|
-
const response = await askAgent(chatId, prompt, {
|
|
18239
|
+
const response = await askAgent(chatId, prompt, {
|
|
18240
|
+
bootstrapTier: "heartbeat",
|
|
18241
|
+
backend: resolvedBackend,
|
|
18242
|
+
model: resolvedModel,
|
|
18243
|
+
timeoutMs: 12e4
|
|
18244
|
+
});
|
|
17945
18245
|
if (response.usage) {
|
|
17946
|
-
|
|
17947
|
-
try {
|
|
17948
|
-
heartbeatModel = getModel(chatId) ?? getAdapterForChat(chatId).defaultModel;
|
|
17949
|
-
} catch {
|
|
17950
|
-
heartbeatModel = getModel(chatId) ?? "unknown";
|
|
17951
|
-
}
|
|
18246
|
+
const heartbeatModel = resolvedModel ?? getModel(chatId) ?? resolvedBackend;
|
|
17952
18247
|
addUsage(chatId, response.usage.input, response.usage.output, response.usage.cacheRead, heartbeatModel, void 0, response.usage.contextSize);
|
|
17953
18248
|
}
|
|
17954
18249
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -17959,14 +18254,62 @@ async function runHeartbeat(chatId, config2) {
|
|
|
17959
18254
|
log(`[heartbeat] ${chatId}: nothing to report`);
|
|
17960
18255
|
return;
|
|
17961
18256
|
}
|
|
17962
|
-
|
|
17963
|
-
if (channel) {
|
|
17964
|
-
await channel.sendText(chatId, response.text);
|
|
17965
|
-
}
|
|
18257
|
+
await deliverHeartbeatMessage(chatId, config2, response.text);
|
|
17966
18258
|
} catch (err) {
|
|
17967
18259
|
error(`[heartbeat] Error for ${chatId}: ${errorMessage(err)}`);
|
|
18260
|
+
const fallbacks = parseHeartbeatFallbacks(config2.fallbacks);
|
|
18261
|
+
if (fallbacks.length > 0) {
|
|
18262
|
+
for (const fb of fallbacks) {
|
|
18263
|
+
try {
|
|
18264
|
+
log(`[heartbeat] Trying fallback: ${fb.backend}${fb.model ? `:${fb.model}` : ""}`);
|
|
18265
|
+
const fbResponse = await askAgent(chatId, prompt, {
|
|
18266
|
+
bootstrapTier: "heartbeat",
|
|
18267
|
+
backend: fb.backend,
|
|
18268
|
+
model: fb.model,
|
|
18269
|
+
timeoutMs: 12e4
|
|
18270
|
+
});
|
|
18271
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18272
|
+
const next = new Date(Date.now() + config2.intervalMs).toISOString();
|
|
18273
|
+
updateHeartbeatTimestamps(chatId, now, next);
|
|
18274
|
+
const trimmed = fbResponse.text.trim();
|
|
18275
|
+
if (trimmed === HEARTBEAT_OK || trimmed.startsWith(HEARTBEAT_OK)) {
|
|
18276
|
+
log(`[heartbeat] ${chatId}: nothing to report (via fallback ${fb.backend})`);
|
|
18277
|
+
return;
|
|
18278
|
+
}
|
|
18279
|
+
await deliverHeartbeatMessage(chatId, config2, fbResponse.text);
|
|
18280
|
+
return;
|
|
18281
|
+
} catch (fbErr) {
|
|
18282
|
+
error(`[heartbeat] Fallback ${fb.backend} failed: ${errorMessage(fbErr)}`);
|
|
18283
|
+
}
|
|
18284
|
+
}
|
|
18285
|
+
}
|
|
18286
|
+
}
|
|
18287
|
+
}
|
|
18288
|
+
async function deliverHeartbeatMessage(chatId, config2, text) {
|
|
18289
|
+
const channelName = config2.channel ?? "telegram";
|
|
18290
|
+
const channel = registry2?.get(channelName);
|
|
18291
|
+
if (!channel) return;
|
|
18292
|
+
const targetChatId = config2.target ?? chatId;
|
|
18293
|
+
if (channelName === "telegram") {
|
|
18294
|
+
const topicMatch = targetChatId.match(/^(.+):topic:(\d+)$/);
|
|
18295
|
+
if (topicMatch) {
|
|
18296
|
+
await channel.sendText(topicMatch[1], text, { threadId: parseInt(topicMatch[2], 10) });
|
|
18297
|
+
} else {
|
|
18298
|
+
await channel.sendText(targetChatId, text);
|
|
18299
|
+
}
|
|
18300
|
+
} else {
|
|
18301
|
+
await channel.sendText(targetChatId, text);
|
|
17968
18302
|
}
|
|
17969
18303
|
}
|
|
18304
|
+
function resolveBackendId(chatId, config2) {
|
|
18305
|
+
if (config2.backend) {
|
|
18306
|
+
const available = getAvailableBackendIds();
|
|
18307
|
+
if (available.includes(config2.backend)) {
|
|
18308
|
+
return config2.backend;
|
|
18309
|
+
}
|
|
18310
|
+
}
|
|
18311
|
+
return void 0;
|
|
18312
|
+
}
|
|
17970
18313
|
function isWithinActiveHours(start, end) {
|
|
17971
18314
|
try {
|
|
17972
18315
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -17985,55 +18328,25 @@ function isWithinActiveHours(start, end) {
|
|
|
17985
18328
|
}
|
|
17986
18329
|
function assembleHeartbeatPrompt(chatId) {
|
|
17987
18330
|
const sections = [];
|
|
17988
|
-
sections.push("You are running a periodic heartbeat check. Review the following and
|
|
17989
|
-
const
|
|
17990
|
-
const
|
|
17991
|
-
|
|
17992
|
-
|
|
17993
|
-
for (const j of report.failingJobs) {
|
|
17994
|
-
healthLines.push(` - Job #${j.id} "${j.description}": ${j.failures} consecutive failures`);
|
|
17995
|
-
}
|
|
17996
|
-
}
|
|
17997
|
-
const recentRuns = getJobRuns(void 0, 20);
|
|
17998
|
-
const failedSinceLastBeat = recentRuns.filter((r) => r.status === "failed");
|
|
17999
|
-
if (failedSinceLastBeat.length > 0) {
|
|
18000
|
-
healthLines.push(`${failedSinceLastBeat.length} job run(s) failed recently.`);
|
|
18001
|
-
}
|
|
18002
|
-
for (const backend2 of getAllBackendIds()) {
|
|
18003
|
-
const limitMsg = checkBackendLimits(backend2);
|
|
18004
|
-
if (limitMsg) healthLines.push(limitMsg);
|
|
18005
|
-
}
|
|
18006
|
-
try {
|
|
18007
|
-
const { OllamaService } = (init_ollama(), __toCommonJS(ollama_exports));
|
|
18008
|
-
const servers = OllamaService.listServers();
|
|
18009
|
-
if (servers.length > 0) {
|
|
18010
|
-
const offline = servers.filter((s) => s.status === "offline");
|
|
18011
|
-
if (offline.length > 0) {
|
|
18012
|
-
healthLines.push(`Ollama: ${offline.length}/${servers.length} server(s) offline: ${offline.map((s) => s.name).join(", ")}`);
|
|
18013
|
-
}
|
|
18014
|
-
}
|
|
18015
|
-
} catch {
|
|
18016
|
-
}
|
|
18017
|
-
if (healthLines.length > 0) {
|
|
18018
|
-
sections.push(`[System health]
|
|
18019
|
-
${healthLines.join("\n")}`);
|
|
18020
|
-
} else {
|
|
18021
|
-
sections.push("[System health]\nAll systems normal. No failures or limit warnings.");
|
|
18022
|
-
}
|
|
18331
|
+
sections.push("You are running a periodic heartbeat check. Review the following system health data and active watches. If nothing needs the user's attention, respond with exactly HEARTBEAT_OK and nothing else. Only alert on genuine issues \u2014 do NOT report that everything is fine.");
|
|
18332
|
+
const healthSummary = runAllHealthChecks();
|
|
18333
|
+
const healthText = formatHealthForPrompt(healthSummary);
|
|
18334
|
+
sections.push(`[System Health Report]
|
|
18335
|
+
${healthText}`);
|
|
18023
18336
|
const watches = getActiveWatches(chatId);
|
|
18024
18337
|
if (watches.length > 0) {
|
|
18025
|
-
const watchLines = watches.map((w) => {
|
|
18338
|
+
const watchLines = watches.map((w, i) => {
|
|
18026
18339
|
const expiry = w.expiresAt ? ` (expires: ${w.expiresAt})` : "";
|
|
18027
|
-
return
|
|
18340
|
+
return `${i + 1}. ${w.description}${expiry}`;
|
|
18028
18341
|
});
|
|
18029
|
-
sections.push(`[Active
|
|
18342
|
+
sections.push(`[Active Watches \u2014 execute these checks using your tools]
|
|
18030
18343
|
${watchLines.join("\n")}`);
|
|
18031
18344
|
}
|
|
18032
|
-
if (
|
|
18345
|
+
if (existsSync19(HEARTBEAT_MD_PATH)) {
|
|
18033
18346
|
try {
|
|
18034
|
-
const custom =
|
|
18347
|
+
const custom = readFileSync10(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
18035
18348
|
if (custom) {
|
|
18036
|
-
sections.push(`[Custom checks]
|
|
18349
|
+
sections.push(`[Custom checks from HEARTBEAT.md]
|
|
18037
18350
|
${custom}`);
|
|
18038
18351
|
}
|
|
18039
18352
|
} catch {
|
|
@@ -18054,6 +18367,16 @@ function formatHeartbeatStatus(chatId) {
|
|
|
18054
18367
|
`Last beat: ${config2.lastBeatAt ?? "never"}`,
|
|
18055
18368
|
`Next beat: ${config2.nextBeatAt ?? "N/A"}`
|
|
18056
18369
|
];
|
|
18370
|
+
if (config2.backend || config2.model) {
|
|
18371
|
+
lines.push(`Backend: ${config2.backend ?? "default"} | Model: ${config2.model ?? "default"}`);
|
|
18372
|
+
}
|
|
18373
|
+
if (config2.target) {
|
|
18374
|
+
lines.push(`Delivery: ${config2.target}`);
|
|
18375
|
+
}
|
|
18376
|
+
const fallbacks = parseHeartbeatFallbacks(config2.fallbacks);
|
|
18377
|
+
if (fallbacks.length > 0) {
|
|
18378
|
+
lines.push(`Fallbacks: ${fallbacks.map((f) => `${f.backend}${f.model ? `:${f.model}` : ""}`).join(", ")}`);
|
|
18379
|
+
}
|
|
18057
18380
|
if (watches.length > 0) {
|
|
18058
18381
|
lines.push("", `Active watches (${watches.length}):`);
|
|
18059
18382
|
for (const w of watches) {
|
|
@@ -18075,7 +18398,7 @@ var init_heartbeat2 = __esm({
|
|
|
18075
18398
|
init_agent();
|
|
18076
18399
|
init_store5();
|
|
18077
18400
|
init_backends();
|
|
18078
|
-
|
|
18401
|
+
init_checks();
|
|
18079
18402
|
init_log();
|
|
18080
18403
|
HEARTBEAT_MD_PATH = join19(WORKSPACE_PATH, "HEARTBEAT.md");
|
|
18081
18404
|
HEARTBEAT_OK = "HEARTBEAT_OK";
|
|
@@ -18731,13 +19054,18 @@ async function sendMemoryPage(chatId, channel, page, messageId) {
|
|
|
18731
19054
|
buttons.push(footerRow);
|
|
18732
19055
|
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
18733
19056
|
}
|
|
18734
|
-
async function sendHeartbeatKeyboard(chatId, channel) {
|
|
19057
|
+
async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
18735
19058
|
const config2 = getHeartbeatConfig(chatId);
|
|
18736
19059
|
const enabled = config2?.enabled === 1;
|
|
18737
19060
|
const intervalMs = config2?.intervalMs ?? 18e5;
|
|
18738
19061
|
const intervalMin = intervalMs / 6e4;
|
|
18739
19062
|
const activeStart = config2?.activeStart ?? "08:00";
|
|
18740
19063
|
const activeEnd = config2?.activeEnd ?? "22:00";
|
|
19064
|
+
const watches = getActiveWatches(chatId);
|
|
19065
|
+
const fallbacks = parseHeartbeatFallbacks(config2?.fallbacks ?? null);
|
|
19066
|
+
const backendDisplay = config2?.backend ? capitalize(config2.backend) : "Default";
|
|
19067
|
+
const modelDisplay = config2?.model ?? "default";
|
|
19068
|
+
const thinkingDisplay = config2?.thinking ?? "off";
|
|
18741
19069
|
const lines = [
|
|
18742
19070
|
buildSectionHeader("Heartbeat"),
|
|
18743
19071
|
"CC-Claw periodically wakes up to check on",
|
|
@@ -18746,22 +19074,87 @@ async function sendHeartbeatKeyboard(chatId, channel) {
|
|
|
18746
19074
|
"",
|
|
18747
19075
|
`Status: ${enabled ? "ON" : "OFF"}`,
|
|
18748
19076
|
`Interval: every ${intervalMin} min`,
|
|
18749
|
-
`Active hours: ${activeStart}-${activeEnd}
|
|
19077
|
+
`Active hours: ${activeStart}-${activeEnd}`,
|
|
19078
|
+
`Backend: ${backendDisplay} | Model: ${modelDisplay}`
|
|
18750
19079
|
];
|
|
19080
|
+
if (fallbacks.length > 0) {
|
|
19081
|
+
lines.push(`Fallbacks: ${fallbacks.map((f) => `${capitalize(f.backend)}${f.model ? `:${f.model}` : ""}`).join(", ")}`);
|
|
19082
|
+
}
|
|
19083
|
+
if (config2?.target) {
|
|
19084
|
+
lines.push(`Target: ${config2.target}`);
|
|
19085
|
+
}
|
|
19086
|
+
if (watches.length > 0) {
|
|
19087
|
+
lines.push(`Active watches: ${watches.length}`);
|
|
19088
|
+
}
|
|
19089
|
+
const lastBeat = config2?.lastBeatAt ?? "never";
|
|
19090
|
+
lines.push(`Last beat: ${lastBeat}`);
|
|
19091
|
+
const buttons = [];
|
|
19092
|
+
buttons.push([
|
|
19093
|
+
{ label: `${enabled ? "\u2713 " : ""}On`, data: "hb:on", ...enabled ? { style: "success" } : {} },
|
|
19094
|
+
{ label: `${!enabled ? "\u2713 " : ""}Off`, data: "hb:off", ...!enabled ? { style: "danger" } : {} },
|
|
19095
|
+
{ label: "\u25B6 Run Now", data: "hb:run", style: "primary" }
|
|
19096
|
+
]);
|
|
18751
19097
|
const presets = [15, 30, 60, 120];
|
|
18752
|
-
|
|
19098
|
+
buttons.push(presets.map((m) => ({
|
|
18753
19099
|
label: `${m === intervalMin ? "\u2713 " : ""}${m} min`,
|
|
18754
19100
|
data: `hb:interval:${m}`,
|
|
18755
19101
|
...m === intervalMin ? { style: "primary" } : {}
|
|
19102
|
+
})));
|
|
19103
|
+
const available = getAvailableBackendIds();
|
|
19104
|
+
buttons.push([{ label: "\u2699\uFE0F Main Engine", data: "hb:noop" }]);
|
|
19105
|
+
const backendRow = available.map((bid) => ({
|
|
19106
|
+
label: `${config2?.backend === bid ? "\u2713 " : ""}${capitalize(bid)}`,
|
|
19107
|
+
data: `hb:backend:${bid}`,
|
|
19108
|
+
...config2?.backend === bid ? { style: "primary" } : {}
|
|
18756
19109
|
}));
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
|
|
18763
|
-
|
|
18764
|
-
|
|
19110
|
+
if (config2?.backend) {
|
|
19111
|
+
backendRow.push({ label: "\u2715", data: "hb:backend:default", style: "danger" });
|
|
19112
|
+
}
|
|
19113
|
+
buttons.push(backendRow);
|
|
19114
|
+
const targetBackend = config2?.backend ?? getBackend(chatId) ?? "claude";
|
|
19115
|
+
try {
|
|
19116
|
+
const adapter = getAdapter(targetBackend);
|
|
19117
|
+
const models = Object.entries(adapter.availableModels);
|
|
19118
|
+
if (models.length > 0) {
|
|
19119
|
+
const modelRow = models.slice(0, 4).map(([key]) => ({
|
|
19120
|
+
label: `${config2?.model === key ? "\u2713 " : ""}${shortModelName(key)}`,
|
|
19121
|
+
data: `hb:model:${key}`,
|
|
19122
|
+
...config2?.model === key ? { style: "primary" } : {}
|
|
19123
|
+
}));
|
|
19124
|
+
if (config2?.model) {
|
|
19125
|
+
modelRow.push({ label: "\u2715", data: "hb:model:default", style: "danger" });
|
|
19126
|
+
}
|
|
19127
|
+
buttons.push(modelRow);
|
|
19128
|
+
}
|
|
19129
|
+
} catch {
|
|
19130
|
+
}
|
|
19131
|
+
const fbCandidates = available.filter((bid) => !fallbacks.some((f) => f.backend === bid) && bid !== config2?.backend);
|
|
19132
|
+
if (fallbacks.length > 0 || fbCandidates.length > 0) {
|
|
19133
|
+
buttons.push([{ label: "\u{1F504} Fallback Chain", data: "hb:noop" }]);
|
|
19134
|
+
}
|
|
19135
|
+
if (fallbacks.length > 0) {
|
|
19136
|
+
buttons.push([
|
|
19137
|
+
{ label: fallbacks.map((f) => capitalize(f.backend)).join(" \u2192 "), data: "hb:noop" },
|
|
19138
|
+
{ label: "Clear All", data: "hb:fb:clear", style: "danger" }
|
|
19139
|
+
]);
|
|
19140
|
+
}
|
|
19141
|
+
if (fbCandidates.length > 0) {
|
|
19142
|
+
buttons.push(fbCandidates.slice(0, 4).map((bid) => ({
|
|
19143
|
+
label: `+ ${capitalize(bid)}`,
|
|
19144
|
+
data: `hb:fb:add:${bid}`
|
|
19145
|
+
})));
|
|
19146
|
+
}
|
|
19147
|
+
const optionsRow = [
|
|
19148
|
+
{ label: `\u{1F4AD} Thinking: ${thinkingDisplay}`, data: "hb:thinking" }
|
|
19149
|
+
];
|
|
19150
|
+
if (watches.length > 0) {
|
|
19151
|
+
optionsRow.push({ label: `\u{1F441} Watches (${watches.length})`, data: "hb:watches" });
|
|
19152
|
+
} else {
|
|
19153
|
+
optionsRow.push({ label: "+ Add Watch", data: "hb:addwatch" });
|
|
19154
|
+
}
|
|
19155
|
+
buttons.push(optionsRow);
|
|
19156
|
+
buttons.push([{ label: `\u23F0 ${activeStart}\u2013${activeEnd} (use /heartbeat hours)`, data: "hb:noop" }]);
|
|
19157
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
18765
19158
|
}
|
|
18766
19159
|
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
18767
19160
|
const memories = listMemories();
|
|
@@ -19403,13 +19796,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
19403
19796
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
19404
19797
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
19405
19798
|
if (current === "frozen") {
|
|
19406
|
-
const { readFileSync:
|
|
19799
|
+
const { readFileSync: readFileSync29, existsSync: existsSync57 } = await import("fs");
|
|
19407
19800
|
const { join: join36 } = await import("path");
|
|
19408
19801
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
19409
19802
|
const soulPath = join36(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
19410
19803
|
const userPath = join36(CC_CLAW_HOME3, "identity/USER.md");
|
|
19411
|
-
const soul =
|
|
19412
|
-
const user =
|
|
19804
|
+
const soul = existsSync57(soulPath) ? readFileSync29(soulPath, "utf-8") : "";
|
|
19805
|
+
const user = existsSync57(userPath) ? readFileSync29(userPath, "utf-8") : "";
|
|
19413
19806
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
19414
19807
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
19415
19808
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -19486,18 +19879,18 @@ var init_evolve2 = __esm({
|
|
|
19486
19879
|
});
|
|
19487
19880
|
|
|
19488
19881
|
// src/optimizer/identity-audit.ts
|
|
19489
|
-
import { readFileSync as
|
|
19882
|
+
import { readFileSync as readFileSync11, existsSync as existsSync20, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
19490
19883
|
import { join as join21 } from "path";
|
|
19491
19884
|
function readIdentityFile2(filename) {
|
|
19492
19885
|
try {
|
|
19493
|
-
return
|
|
19886
|
+
return readFileSync11(join21(IDENTITY_PATH, filename), "utf-8");
|
|
19494
19887
|
} catch {
|
|
19495
19888
|
return "";
|
|
19496
19889
|
}
|
|
19497
19890
|
}
|
|
19498
19891
|
function getMtime(filepath) {
|
|
19499
19892
|
try {
|
|
19500
|
-
return
|
|
19893
|
+
return statSync8(filepath).mtime.toISOString();
|
|
19501
19894
|
} catch {
|
|
19502
19895
|
return "unknown";
|
|
19503
19896
|
}
|
|
@@ -19506,7 +19899,7 @@ function findBackupFiles() {
|
|
|
19506
19899
|
const backups = [];
|
|
19507
19900
|
const dirs = [IDENTITY_PATH];
|
|
19508
19901
|
const contextDir = join21(IDENTITY_PATH, "..", "workspace", "context");
|
|
19509
|
-
if (
|
|
19902
|
+
if (existsSync20(contextDir)) dirs.push(contextDir);
|
|
19510
19903
|
for (const dir of dirs) {
|
|
19511
19904
|
try {
|
|
19512
19905
|
for (const entry of readdirSync10(dir)) {
|
|
@@ -19643,7 +20036,7 @@ var init_identity_audit = __esm({
|
|
|
19643
20036
|
});
|
|
19644
20037
|
|
|
19645
20038
|
// src/optimizer/skill-audit.ts
|
|
19646
|
-
import { readFileSync as
|
|
20039
|
+
import { readFileSync as readFileSync12, existsSync as existsSync21 } from "fs";
|
|
19647
20040
|
import { join as join22, basename as basename3 } from "path";
|
|
19648
20041
|
function parseFrontmatter3(content) {
|
|
19649
20042
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -19684,7 +20077,7 @@ function detectDependentSkills(content) {
|
|
|
19684
20077
|
return Array.from(deps);
|
|
19685
20078
|
}
|
|
19686
20079
|
function computeSkillStats(skillPath) {
|
|
19687
|
-
const content =
|
|
20080
|
+
const content = readFileSync12(skillPath, "utf-8");
|
|
19688
20081
|
const lines = content.split("\n");
|
|
19689
20082
|
return {
|
|
19690
20083
|
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join22(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
@@ -19711,9 +20104,9 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
19711
20104
|
join22(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
19712
20105
|
];
|
|
19713
20106
|
for (const candidate of candidates) {
|
|
19714
|
-
if (
|
|
20107
|
+
if (existsSync21(candidate)) {
|
|
19715
20108
|
try {
|
|
19716
|
-
const content =
|
|
20109
|
+
const content = readFileSync12(candidate, "utf-8");
|
|
19717
20110
|
results.push({
|
|
19718
20111
|
name,
|
|
19719
20112
|
content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
|
|
@@ -19828,7 +20221,7 @@ __export(analyze_exports2, {
|
|
|
19828
20221
|
});
|
|
19829
20222
|
import { spawn as spawn7 } from "child_process";
|
|
19830
20223
|
import { createInterface as createInterface7 } from "readline";
|
|
19831
|
-
import { readFileSync as
|
|
20224
|
+
import { readFileSync as readFileSync13, existsSync as existsSync22, readdirSync as readdirSync12 } from "fs";
|
|
19832
20225
|
import { join as join23 } from "path";
|
|
19833
20226
|
import { homedir as homedir7 } from "os";
|
|
19834
20227
|
function parseOptimizeOutput(raw, validAreas) {
|
|
@@ -19959,7 +20352,7 @@ function getModelDisplayInfo(chatId) {
|
|
|
19959
20352
|
}
|
|
19960
20353
|
function readIdentityFile3(filename) {
|
|
19961
20354
|
try {
|
|
19962
|
-
return
|
|
20355
|
+
return readFileSync13(join23(IDENTITY_PATH, filename), "utf-8");
|
|
19963
20356
|
} catch {
|
|
19964
20357
|
return "";
|
|
19965
20358
|
}
|
|
@@ -19967,12 +20360,12 @@ function readIdentityFile3(filename) {
|
|
|
19967
20360
|
function loadContextFiles2() {
|
|
19968
20361
|
const contextDir = join23(homedir7(), ".cc-claw", "workspace", "context");
|
|
19969
20362
|
const results = [];
|
|
19970
|
-
if (!
|
|
20363
|
+
if (!existsSync22(contextDir)) return results;
|
|
19971
20364
|
try {
|
|
19972
20365
|
for (const entry of readdirSync12(contextDir)) {
|
|
19973
20366
|
if (!entry.endsWith(".md")) continue;
|
|
19974
20367
|
try {
|
|
19975
|
-
const content =
|
|
20368
|
+
const content = readFileSync13(join23(contextDir, entry), "utf-8");
|
|
19976
20369
|
results.push({ name: entry, content });
|
|
19977
20370
|
} catch {
|
|
19978
20371
|
}
|
|
@@ -20024,7 +20417,7 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20024
20417
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
20025
20418
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
20026
20419
|
const ccClawSkillsDir = join23(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20027
|
-
const skillContent =
|
|
20420
|
+
const skillContent = readFileSync13(skillPath, "utf-8");
|
|
20028
20421
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
20029
20422
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
20030
20423
|
const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
|
|
@@ -20040,14 +20433,14 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20040
20433
|
function listCcClawSkills() {
|
|
20041
20434
|
const skillsDir = join23(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20042
20435
|
const entries = [];
|
|
20043
|
-
if (!
|
|
20436
|
+
if (!existsSync22(skillsDir)) return entries;
|
|
20044
20437
|
try {
|
|
20045
20438
|
for (const dir of readdirSync12(skillsDir)) {
|
|
20046
20439
|
const skillFile = join23(skillsDir, dir, "SKILL.md");
|
|
20047
|
-
if (!
|
|
20440
|
+
if (!existsSync22(skillFile)) continue;
|
|
20048
20441
|
let description = "skill";
|
|
20049
20442
|
try {
|
|
20050
|
-
const content =
|
|
20443
|
+
const content = readFileSync13(skillFile, "utf-8");
|
|
20051
20444
|
const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
|
|
20052
20445
|
if (descMatch) description = descMatch[1].trim().slice(0, 60);
|
|
20053
20446
|
} catch {
|
|
@@ -20367,7 +20760,7 @@ var init_ui2 = __esm({
|
|
|
20367
20760
|
});
|
|
20368
20761
|
|
|
20369
20762
|
// src/router/optimize.ts
|
|
20370
|
-
import { readFileSync as
|
|
20763
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync23, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
20371
20764
|
import { join as join24, dirname as dirname4 } from "path";
|
|
20372
20765
|
import { homedir as homedir8 } from "os";
|
|
20373
20766
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
@@ -20638,13 +21031,13 @@ async function applyFinding(chatId, channel, index) {
|
|
|
20638
21031
|
await showFinding(chatId, channel, index + 1);
|
|
20639
21032
|
return;
|
|
20640
21033
|
}
|
|
20641
|
-
if (!
|
|
21034
|
+
if (!existsSync23(targetPath)) {
|
|
20642
21035
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
20643
21036
|
session2.skipped.push(index);
|
|
20644
21037
|
await showFinding(chatId, channel, index + 1);
|
|
20645
21038
|
return;
|
|
20646
21039
|
}
|
|
20647
|
-
const original =
|
|
21040
|
+
const original = readFileSync14(targetPath, "utf-8");
|
|
20648
21041
|
const backupPath = targetPath + `.bak.${Date.now()}`;
|
|
20649
21042
|
writeFileSync7(backupPath, original, "utf-8");
|
|
20650
21043
|
pruneBackups2(targetPath);
|
|
@@ -22082,6 +22475,92 @@ async function handleHeartbeatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22082
22475
|
await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
|
|
22083
22476
|
}
|
|
22084
22477
|
}
|
|
22478
|
+
async function handleWatchCommand(chatId, commandArgs, msg, channel) {
|
|
22479
|
+
if (!commandArgs) {
|
|
22480
|
+
const watches = getActiveWatches(chatId);
|
|
22481
|
+
if (watches.length === 0) {
|
|
22482
|
+
await channel.sendText(chatId, [
|
|
22483
|
+
"No active watches.",
|
|
22484
|
+
"",
|
|
22485
|
+
"Watches are awareness tasks that the heartbeat",
|
|
22486
|
+
"AI checks periodically using its tools.",
|
|
22487
|
+
"",
|
|
22488
|
+
"Usage:",
|
|
22489
|
+
" /watch add Check my email for urgent messages",
|
|
22490
|
+
" /watch add Monitor Ollama server health",
|
|
22491
|
+
" /watch add Check calendar for upcoming meetings",
|
|
22492
|
+
" /watch list",
|
|
22493
|
+
" /watch remove <id>"
|
|
22494
|
+
].join("\n"), { parseMode: "plain" });
|
|
22495
|
+
} else {
|
|
22496
|
+
const lines = ["Active watches:", ""];
|
|
22497
|
+
for (const w of watches) {
|
|
22498
|
+
const expiry = w.expiresAt ? ` (until ${formatLocalDateTime(w.expiresAt)})` : "";
|
|
22499
|
+
lines.push(`#${w.id}: ${w.description}${expiry}`);
|
|
22500
|
+
}
|
|
22501
|
+
lines.push("", "Use /watch remove <id> to remove a watch.");
|
|
22502
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
22503
|
+
}
|
|
22504
|
+
return;
|
|
22505
|
+
}
|
|
22506
|
+
const parts = commandArgs.trim().split(/\s+/);
|
|
22507
|
+
const action = parts[0].toLowerCase();
|
|
22508
|
+
const rest = parts.slice(1).join(" ");
|
|
22509
|
+
switch (action) {
|
|
22510
|
+
case "add": {
|
|
22511
|
+
if (!rest) {
|
|
22512
|
+
await channel.sendText(chatId, "Usage: /watch add <description>\n\nExamples:\n /watch add Check my email for urgent messages\n /watch add Monitor if Ollama is running\n /watch add Check calendar for meetings in the next hour", { parseMode: "plain" });
|
|
22513
|
+
return;
|
|
22514
|
+
}
|
|
22515
|
+
let expiresAt;
|
|
22516
|
+
const durationMatch = rest.match(/\s+for\s+(\d+)([hdw])$/i);
|
|
22517
|
+
let description = rest;
|
|
22518
|
+
if (durationMatch) {
|
|
22519
|
+
description = rest.slice(0, durationMatch.index).trim();
|
|
22520
|
+
const amount = parseInt(durationMatch[1], 10);
|
|
22521
|
+
const unit = durationMatch[2].toLowerCase();
|
|
22522
|
+
const ms = unit === "h" ? amount * 36e5 : unit === "d" ? amount * 864e5 : amount * 6048e5;
|
|
22523
|
+
expiresAt = new Date(Date.now() + ms).toISOString();
|
|
22524
|
+
}
|
|
22525
|
+
const id = addHeartbeatWatch(chatId, description, expiresAt);
|
|
22526
|
+
const expiryMsg = expiresAt ? ` (expires: ${formatLocalDateTime(expiresAt)})` : "";
|
|
22527
|
+
await channel.sendText(chatId, `\u2705 Watch #${id} added${expiryMsg}:
|
|
22528
|
+
${description}`, { parseMode: "plain" });
|
|
22529
|
+
return;
|
|
22530
|
+
}
|
|
22531
|
+
case "remove":
|
|
22532
|
+
case "delete": {
|
|
22533
|
+
const id = parseInt(rest, 10);
|
|
22534
|
+
if (isNaN(id)) {
|
|
22535
|
+
await channel.sendText(chatId, "Usage: /watch remove <id>", { parseMode: "plain" });
|
|
22536
|
+
return;
|
|
22537
|
+
}
|
|
22538
|
+
const removed = removeHeartbeatWatch(id);
|
|
22539
|
+
await channel.sendText(chatId, removed ? `\u2705 Watch #${id} removed.` : `Watch #${id} not found.`, { parseMode: "plain" });
|
|
22540
|
+
return;
|
|
22541
|
+
}
|
|
22542
|
+
case "list": {
|
|
22543
|
+
const watches = getActiveWatches(chatId);
|
|
22544
|
+
if (watches.length === 0) {
|
|
22545
|
+
await channel.sendText(chatId, "No active watches.", { parseMode: "plain" });
|
|
22546
|
+
} else {
|
|
22547
|
+
const lines = ["Active watches:", ""];
|
|
22548
|
+
for (const w of watches) {
|
|
22549
|
+
const expiry = w.expiresAt ? ` (until ${formatLocalDateTime(w.expiresAt)})` : "";
|
|
22550
|
+
lines.push(`#${w.id}: ${w.description}${expiry}`);
|
|
22551
|
+
}
|
|
22552
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
22553
|
+
}
|
|
22554
|
+
return;
|
|
22555
|
+
}
|
|
22556
|
+
default:
|
|
22557
|
+
const watchDesc = commandArgs.trim();
|
|
22558
|
+
const watchId = addHeartbeatWatch(chatId, watchDesc);
|
|
22559
|
+
await channel.sendText(chatId, `\u2705 Watch #${watchId} added:
|
|
22560
|
+
${watchDesc}`, { parseMode: "plain" });
|
|
22561
|
+
return;
|
|
22562
|
+
}
|
|
22563
|
+
}
|
|
22085
22564
|
async function handleAgentsCommand(chatId, commandArgs, msg, channel) {
|
|
22086
22565
|
if (commandArgs?.startsWith("mode")) {
|
|
22087
22566
|
const modeArg = commandArgs.slice(5).trim().toLowerCase();
|
|
@@ -22644,6 +23123,9 @@ async function handleCommand(msg, channel) {
|
|
|
22644
23123
|
case "heartbeat":
|
|
22645
23124
|
await handleHeartbeatCommand(chatId, commandArgs, msg, channel);
|
|
22646
23125
|
break;
|
|
23126
|
+
case "watch":
|
|
23127
|
+
await handleWatchCommand(chatId, commandArgs, msg, channel);
|
|
23128
|
+
break;
|
|
22647
23129
|
case "agents":
|
|
22648
23130
|
await handleAgentsCommand(chatId, commandArgs, msg, channel);
|
|
22649
23131
|
break;
|
|
@@ -23542,8 +24024,8 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
23542
24024
|
if (action === "toggle") {
|
|
23543
24025
|
const backend2 = parts[2];
|
|
23544
24026
|
const model2 = parts[3];
|
|
23545
|
-
const { getAdapter:
|
|
23546
|
-
const toggleAdapter =
|
|
24027
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24028
|
+
const toggleAdapter = getAdapter5(backend2);
|
|
23547
24029
|
const label2 = toggleAdapter.availableModels[model2]?.label ?? model2;
|
|
23548
24030
|
const { toggleParticipant: toggleParticipant2, buildSelectKeyboard: buildSelectKeyboard2, hasPendingCouncil: hasPendingCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
|
|
23549
24031
|
if (!hasPendingCouncil2(chatId)) {
|
|
@@ -23793,11 +24275,12 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
23793
24275
|
if (rest === "on") {
|
|
23794
24276
|
setHeartbeatConfig(chatId, { enabled: true });
|
|
23795
24277
|
startHeartbeatForChat(chatId);
|
|
23796
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
24278
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
23797
24279
|
} else if (rest === "off") {
|
|
23798
24280
|
setHeartbeatConfig(chatId, { enabled: false });
|
|
23799
24281
|
stopHeartbeatForChat(chatId);
|
|
23800
|
-
await
|
|
24282
|
+
await channel.sendText(chatId, "\u26A0\uFE0F Heartbeat disabled. You won't receive proactive health alerts or watch notifications.\nRe-enable with /heartbeat on.", { parseMode: "plain" });
|
|
24283
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
23801
24284
|
} else if (rest.startsWith("interval:")) {
|
|
23802
24285
|
const min = parseInt(rest.slice(9), 10);
|
|
23803
24286
|
if (isNaN(min) || min < 1) return;
|
|
@@ -23806,7 +24289,55 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
23806
24289
|
stopHeartbeatForChat(chatId);
|
|
23807
24290
|
const hbConf = getHeartbeatConfig(chatId);
|
|
23808
24291
|
if (hbConf?.enabled) startHeartbeatForChat(chatId);
|
|
23809
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
24292
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24293
|
+
} else if (rest.startsWith("backend:")) {
|
|
24294
|
+
const bid = rest.slice(8);
|
|
24295
|
+
if (bid === "default") {
|
|
24296
|
+
updateHeartbeatField(chatId, "backend", null);
|
|
24297
|
+
updateHeartbeatField(chatId, "model", null);
|
|
24298
|
+
} else {
|
|
24299
|
+
updateHeartbeatField(chatId, "backend", bid);
|
|
24300
|
+
updateHeartbeatField(chatId, "model", null);
|
|
24301
|
+
}
|
|
24302
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24303
|
+
} else if (rest.startsWith("model:")) {
|
|
24304
|
+
const model2 = rest.slice(6);
|
|
24305
|
+
if (model2 === "default") {
|
|
24306
|
+
updateHeartbeatField(chatId, "model", null);
|
|
24307
|
+
} else {
|
|
24308
|
+
updateHeartbeatField(chatId, "model", model2);
|
|
24309
|
+
}
|
|
24310
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24311
|
+
} else if (rest === "thinking") {
|
|
24312
|
+
const config2 = getHeartbeatConfig(chatId);
|
|
24313
|
+
const current = config2?.thinking ?? "off";
|
|
24314
|
+
const cycle = ["off", "low", "medium", "high"];
|
|
24315
|
+
const next = cycle[(cycle.indexOf(current) + 1) % cycle.length];
|
|
24316
|
+
updateHeartbeatField(chatId, "thinking", next === "off" ? null : next);
|
|
24317
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24318
|
+
} else if (rest === "run") {
|
|
24319
|
+
await channel.sendText(chatId, "\u23F3 Running heartbeat check...", { parseMode: "plain" });
|
|
24320
|
+
try {
|
|
24321
|
+
await runHeartbeatNow(chatId);
|
|
24322
|
+
await channel.sendText(chatId, "\u2705 Heartbeat check complete.", { parseMode: "plain" });
|
|
24323
|
+
} catch (err) {
|
|
24324
|
+
await channel.sendText(chatId, `\u274C Heartbeat failed: ${err instanceof Error ? err.message : String(err)}`, { parseMode: "plain" });
|
|
24325
|
+
}
|
|
24326
|
+
} else if (rest === "watches" || rest === "addwatch") {
|
|
24327
|
+
await channel.sendText(chatId, "Use /watch to manage awareness tasks.\n\nExamples:\n /watch add Check my email for urgent messages\n /watch add Check if Ollama is running\n /watch list\n /watch remove 1", { parseMode: "plain" });
|
|
24328
|
+
} else if (rest.startsWith("fb:add:")) {
|
|
24329
|
+
const bid = rest.slice(7);
|
|
24330
|
+
const config2 = getHeartbeatConfig(chatId);
|
|
24331
|
+
const current = parseHeartbeatFallbacks(config2?.fallbacks ?? null);
|
|
24332
|
+
if (!current.some((f) => f.backend === bid)) {
|
|
24333
|
+
current.push({ backend: bid });
|
|
24334
|
+
updateHeartbeatField(chatId, "fallbacks", JSON.stringify(current));
|
|
24335
|
+
}
|
|
24336
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24337
|
+
} else if (rest === "fb:clear") {
|
|
24338
|
+
updateHeartbeatField(chatId, "fallbacks", null);
|
|
24339
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24340
|
+
} else if (rest === "noop") {
|
|
23810
24341
|
}
|
|
23811
24342
|
return;
|
|
23812
24343
|
} else if (data.startsWith("newchat:")) {
|
|
@@ -25636,18 +26167,18 @@ var init_wrap_backend = __esm({
|
|
|
25636
26167
|
});
|
|
25637
26168
|
|
|
25638
26169
|
// src/agents/runners/config-loader.ts
|
|
25639
|
-
import { readFileSync as
|
|
26170
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
25640
26171
|
import { join as join27 } from "path";
|
|
25641
|
-
import { execFileSync as
|
|
26172
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
25642
26173
|
function resolveExecutable2(config2) {
|
|
25643
|
-
if (
|
|
26174
|
+
if (existsSync24(config2.executable)) return config2.executable;
|
|
25644
26175
|
try {
|
|
25645
|
-
return
|
|
26176
|
+
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
25646
26177
|
} catch {
|
|
25647
26178
|
}
|
|
25648
26179
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
25649
26180
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
25650
|
-
if (
|
|
26181
|
+
if (existsSync24(resolved)) return resolved;
|
|
25651
26182
|
}
|
|
25652
26183
|
return config2.executable;
|
|
25653
26184
|
}
|
|
@@ -25778,7 +26309,7 @@ function configToRunner(config2) {
|
|
|
25778
26309
|
}
|
|
25779
26310
|
function loadRunnerConfig(filePath) {
|
|
25780
26311
|
try {
|
|
25781
|
-
const content =
|
|
26312
|
+
const content = readFileSync15(filePath, "utf-8");
|
|
25782
26313
|
return JSON.parse(content);
|
|
25783
26314
|
} catch (err) {
|
|
25784
26315
|
warn(`[runners] Failed to load config ${filePath}: ${err}`);
|
|
@@ -25786,7 +26317,7 @@ function loadRunnerConfig(filePath) {
|
|
|
25786
26317
|
}
|
|
25787
26318
|
}
|
|
25788
26319
|
function loadAllRunnerConfigs() {
|
|
25789
|
-
if (!
|
|
26320
|
+
if (!existsSync24(RUNNERS_PATH)) {
|
|
25790
26321
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
25791
26322
|
return [];
|
|
25792
26323
|
}
|
|
@@ -25814,9 +26345,9 @@ function registerConfigRunners() {
|
|
|
25814
26345
|
return count;
|
|
25815
26346
|
}
|
|
25816
26347
|
function watchRunnerConfigs(onChange) {
|
|
25817
|
-
if (!
|
|
26348
|
+
if (!existsSync24(RUNNERS_PATH)) return;
|
|
25818
26349
|
for (const prev of watchedFiles) {
|
|
25819
|
-
if (!
|
|
26350
|
+
if (!existsSync24(prev)) {
|
|
25820
26351
|
unwatchFile(prev);
|
|
25821
26352
|
watchedFiles.delete(prev);
|
|
25822
26353
|
}
|
|
@@ -26348,6 +26879,7 @@ var init_telegram2 = __esm({
|
|
|
26348
26879
|
{ command: "model_signature", description: "Toggle model+thinking signature on responses" },
|
|
26349
26880
|
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
26350
26881
|
{ command: "heartbeat", description: "Configure proactive heartbeat" },
|
|
26882
|
+
{ command: "watch", description: "Manage heartbeat awareness tasks" },
|
|
26351
26883
|
{ command: "chats", description: "Manage multi-chat aliases" },
|
|
26352
26884
|
{ command: "intent", description: "Test intent classifier on a message" },
|
|
26353
26885
|
{ command: "evolve", description: "Self-learning & evolution controls" },
|
|
@@ -26911,19 +27443,19 @@ var init_telegram2 = __esm({
|
|
|
26911
27443
|
});
|
|
26912
27444
|
|
|
26913
27445
|
// src/skills/bootstrap.ts
|
|
26914
|
-
import { existsSync as
|
|
27446
|
+
import { existsSync as existsSync25 } from "fs";
|
|
26915
27447
|
import { readdir as readdir5, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
|
|
26916
27448
|
import { join as join28, dirname as dirname5 } from "path";
|
|
26917
27449
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
26918
27450
|
async function copyAgentManifestSkills() {
|
|
26919
|
-
if (!
|
|
27451
|
+
if (!existsSync25(PKG_SKILLS)) return;
|
|
26920
27452
|
try {
|
|
26921
27453
|
const entries = await readdir5(PKG_SKILLS, { withFileTypes: true });
|
|
26922
27454
|
for (const entry of entries) {
|
|
26923
27455
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
26924
27456
|
const src = join28(PKG_SKILLS, entry.name);
|
|
26925
27457
|
const dest = join28(SKILLS_PATH, entry.name);
|
|
26926
|
-
if (
|
|
27458
|
+
if (existsSync25(dest)) continue;
|
|
26927
27459
|
await copyFile(src, dest);
|
|
26928
27460
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
26929
27461
|
}
|
|
@@ -26934,7 +27466,7 @@ async function copyAgentManifestSkills() {
|
|
|
26934
27466
|
async function bootstrapSkills() {
|
|
26935
27467
|
await copyAgentManifestSkills();
|
|
26936
27468
|
const usmDir = join28(SKILLS_PATH, USM_DIR_NAME);
|
|
26937
|
-
if (
|
|
27469
|
+
if (existsSync25(usmDir)) return;
|
|
26938
27470
|
try {
|
|
26939
27471
|
const entries = await readdir5(SKILLS_PATH);
|
|
26940
27472
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
@@ -26958,7 +27490,7 @@ async function bootstrapSkills() {
|
|
|
26958
27490
|
}
|
|
26959
27491
|
async function patchUsmForCcClaw(usmDir) {
|
|
26960
27492
|
const skillPath = join28(usmDir, "SKILL.md");
|
|
26961
|
-
if (!
|
|
27493
|
+
if (!existsSync25(skillPath)) return;
|
|
26962
27494
|
try {
|
|
26963
27495
|
let content = await readFile8(skillPath, "utf-8");
|
|
26964
27496
|
let patched = false;
|
|
@@ -27226,13 +27758,13 @@ __export(ai_skill_exports, {
|
|
|
27226
27758
|
generateAiSkill: () => generateAiSkill,
|
|
27227
27759
|
installAiSkill: () => installAiSkill
|
|
27228
27760
|
});
|
|
27229
|
-
import { existsSync as
|
|
27761
|
+
import { existsSync as existsSync26, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
27230
27762
|
import { join as join29 } from "path";
|
|
27231
27763
|
import { homedir as homedir9 } from "os";
|
|
27232
27764
|
function generateAiSkill() {
|
|
27233
27765
|
const version = VERSION;
|
|
27234
27766
|
let systemState = "";
|
|
27235
|
-
if (
|
|
27767
|
+
if (existsSync26(DB_PATH)) {
|
|
27236
27768
|
try {
|
|
27237
27769
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
27238
27770
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -27513,7 +28045,7 @@ cc-claw voice set off # Disable voice responses
|
|
|
27513
28045
|
# Requires GEMINI_API_KEY in ~/.cc-claw/.env
|
|
27514
28046
|
\`\`\`
|
|
27515
28047
|
|
|
27516
|
-
### Heartbeat
|
|
28048
|
+
### Heartbeat & Watches (Proactive Monitoring)
|
|
27517
28049
|
\`\`\`bash
|
|
27518
28050
|
cc-claw heartbeat get --json # Show heartbeat config
|
|
27519
28051
|
cc-claw heartbeat set on # Enable proactive awareness
|
|
@@ -27522,6 +28054,20 @@ cc-claw heartbeat set interval 30m # Set check interval
|
|
|
27522
28054
|
cc-claw heartbeat set hours 9-22 # Set active hours
|
|
27523
28055
|
\`\`\`
|
|
27524
28056
|
|
|
28057
|
+
**Heartbeat** runs a periodic awareness cycle that checks system health and user-defined watches.
|
|
28058
|
+
It supports delivery parity: custom backend, model, fallback chain, thinking level, and delivery target \u2014 all configured via the interactive \`/heartbeat\` keyboard in Telegram.
|
|
28059
|
+
When nothing needs attention, the AI responds with \`HEARTBEAT_OK\` and no message is delivered.
|
|
28060
|
+
|
|
28061
|
+
**Watches** are AI-executable awareness tasks checked during each heartbeat cycle:
|
|
28062
|
+
\`\`\`
|
|
28063
|
+
/watch add Check my email for urgent messages # Persistent watch
|
|
28064
|
+
/watch add Monitor Ollama health for 24h # Time-limited watch
|
|
28065
|
+
/watch list # List active watches
|
|
28066
|
+
/watch remove <id> # Remove a watch
|
|
28067
|
+
\`\`\`
|
|
28068
|
+
|
|
28069
|
+
**\u26A0\uFE0F CRITICAL:** Heartbeat and watch configuration MUST be done via \`/heartbeat\` and \`/watch\` commands. NEVER modify \`chat_heartbeat\` or \`heartbeat_watches\` database tables directly.
|
|
28070
|
+
|
|
27525
28071
|
### Summarizer
|
|
27526
28072
|
\`\`\`bash
|
|
27527
28073
|
cc-claw summarizer get --json # Current summarizer config
|
|
@@ -27692,7 +28238,7 @@ var index_exports = {};
|
|
|
27692
28238
|
__export(index_exports, {
|
|
27693
28239
|
main: () => main
|
|
27694
28240
|
});
|
|
27695
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
28241
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync27, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync17 } from "fs";
|
|
27696
28242
|
import { join as join30 } from "path";
|
|
27697
28243
|
import dotenv from "dotenv";
|
|
27698
28244
|
function migrateLayout() {
|
|
@@ -27706,7 +28252,7 @@ function migrateLayout() {
|
|
|
27706
28252
|
[join30(CC_CLAW_HOME, "cc-claw.error.log.1"), join30(LOGS_PATH, "cc-claw.error.log.1")]
|
|
27707
28253
|
];
|
|
27708
28254
|
for (const [from, to] of moves) {
|
|
27709
|
-
if (
|
|
28255
|
+
if (existsSync27(from) && !existsSync27(to)) {
|
|
27710
28256
|
try {
|
|
27711
28257
|
renameSync2(from, to);
|
|
27712
28258
|
} catch {
|
|
@@ -27717,7 +28263,7 @@ function migrateLayout() {
|
|
|
27717
28263
|
function rotateLogs() {
|
|
27718
28264
|
for (const file of [LOG_PATH, ERROR_LOG_PATH]) {
|
|
27719
28265
|
try {
|
|
27720
|
-
const { size } =
|
|
28266
|
+
const { size } = statSync9(file);
|
|
27721
28267
|
if (size > LOG_MAX_BYTES) {
|
|
27722
28268
|
const archivePath = `${file}.1`;
|
|
27723
28269
|
try {
|
|
@@ -27735,7 +28281,7 @@ async function main() {
|
|
|
27735
28281
|
let version = "unknown";
|
|
27736
28282
|
try {
|
|
27737
28283
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
27738
|
-
version = JSON.parse(
|
|
28284
|
+
version = JSON.parse(readFileSync17(pkgPath, "utf-8")).version;
|
|
27739
28285
|
} catch {
|
|
27740
28286
|
}
|
|
27741
28287
|
log(`[cc-claw] Starting v${version}`);
|
|
@@ -27749,11 +28295,11 @@ async function main() {
|
|
|
27749
28295
|
} catch {
|
|
27750
28296
|
}
|
|
27751
28297
|
try {
|
|
27752
|
-
const { getAdapter:
|
|
28298
|
+
const { getAdapter: getAdapter5, probeBackendAvailability: probeBackendAvailability2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
27753
28299
|
await probeBackendAvailability2();
|
|
27754
|
-
const claude =
|
|
28300
|
+
const claude = getAdapter5("claude");
|
|
27755
28301
|
if ("getAuthMode" in claude) claude.getAuthMode();
|
|
27756
|
-
const cursor =
|
|
28302
|
+
const cursor = getAdapter5("cursor");
|
|
27757
28303
|
if ("probeTier" in cursor) cursor.probeTier();
|
|
27758
28304
|
} catch {
|
|
27759
28305
|
}
|
|
@@ -27773,8 +28319,8 @@ async function main() {
|
|
|
27773
28319
|
}
|
|
27774
28320
|
if (modelCount > 0) {
|
|
27775
28321
|
try {
|
|
27776
|
-
const { getAdapter:
|
|
27777
|
-
const adapter =
|
|
28322
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
28323
|
+
const adapter = getAdapter5("ollama");
|
|
27778
28324
|
if ("refreshModelCatalog" in adapter) {
|
|
27779
28325
|
adapter.refreshModelCatalog();
|
|
27780
28326
|
}
|
|
@@ -27855,10 +28401,10 @@ async function main() {
|
|
|
27855
28401
|
});
|
|
27856
28402
|
log("[cc-claw] Agent orchestrator initialized");
|
|
27857
28403
|
try {
|
|
27858
|
-
const { execSync:
|
|
27859
|
-
const codexPath =
|
|
28404
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
28405
|
+
const codexPath = execSync7("which codex", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
27860
28406
|
if (codexPath) {
|
|
27861
|
-
const features =
|
|
28407
|
+
const features = execSync7("codex features list", { encoding: "utf-8", timeout: 1e4 });
|
|
27862
28408
|
if (/multi_agent\s+\S+\s+false/.test(features)) {
|
|
27863
28409
|
warn("[cc-claw] Codex multi_agent feature is disabled \u2014 native sub-agent detection will not work. Run: codex features enable multi_agent");
|
|
27864
28410
|
}
|
|
@@ -27986,10 +28532,10 @@ var init_index = __esm({
|
|
|
27986
28532
|
init_health3();
|
|
27987
28533
|
init_image_gen();
|
|
27988
28534
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
27989
|
-
if (!
|
|
28535
|
+
if (!existsSync27(dir)) mkdirSync12(dir, { recursive: true });
|
|
27990
28536
|
}
|
|
27991
28537
|
migrateLayout();
|
|
27992
|
-
if (
|
|
28538
|
+
if (existsSync27(ENV_PATH)) {
|
|
27993
28539
|
dotenv.config({ path: ENV_PATH });
|
|
27994
28540
|
} else {
|
|
27995
28541
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -28010,12 +28556,12 @@ __export(api_client_exports, {
|
|
|
28010
28556
|
apiPost: () => apiPost,
|
|
28011
28557
|
isDaemonRunning: () => isDaemonRunning
|
|
28012
28558
|
});
|
|
28013
|
-
import { readFileSync as
|
|
28559
|
+
import { readFileSync as readFileSync18, existsSync as existsSync28 } from "fs";
|
|
28014
28560
|
import { request as httpRequest, Agent } from "http";
|
|
28015
28561
|
function getToken() {
|
|
28016
28562
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
28017
28563
|
try {
|
|
28018
|
-
if (
|
|
28564
|
+
if (existsSync28(TOKEN_PATH)) return readFileSync18(TOKEN_PATH, "utf-8").trim();
|
|
28019
28565
|
} catch {
|
|
28020
28566
|
}
|
|
28021
28567
|
return null;
|
|
@@ -28114,8 +28660,8 @@ __export(service_exports2, {
|
|
|
28114
28660
|
serviceStatus: () => serviceStatus,
|
|
28115
28661
|
uninstallService: () => uninstallService
|
|
28116
28662
|
});
|
|
28117
|
-
import { existsSync as
|
|
28118
|
-
import { execFileSync as
|
|
28663
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
28664
|
+
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
28119
28665
|
import { homedir as homedir10, platform } from "os";
|
|
28120
28666
|
import { join as join31, dirname as dirname6 } from "path";
|
|
28121
28667
|
function xmlEscape(s) {
|
|
@@ -28123,10 +28669,10 @@ function xmlEscape(s) {
|
|
|
28123
28669
|
}
|
|
28124
28670
|
function resolveExecutable3(name) {
|
|
28125
28671
|
try {
|
|
28126
|
-
return
|
|
28672
|
+
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
28127
28673
|
} catch {
|
|
28128
28674
|
const fallback = process.argv[1];
|
|
28129
|
-
if (fallback &&
|
|
28675
|
+
if (fallback && existsSync29(fallback)) return fallback;
|
|
28130
28676
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
28131
28677
|
}
|
|
28132
28678
|
}
|
|
@@ -28141,7 +28687,7 @@ function getPathDirs() {
|
|
|
28141
28687
|
"/bin"
|
|
28142
28688
|
]);
|
|
28143
28689
|
try {
|
|
28144
|
-
const prefix =
|
|
28690
|
+
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
28145
28691
|
if (prefix) dirs.add(join31(prefix, "bin"));
|
|
28146
28692
|
} catch {
|
|
28147
28693
|
}
|
|
@@ -28201,26 +28747,26 @@ function generatePlist() {
|
|
|
28201
28747
|
}
|
|
28202
28748
|
function installMacOS() {
|
|
28203
28749
|
const agentsDir = dirname6(PLIST_PATH);
|
|
28204
|
-
if (!
|
|
28205
|
-
if (!
|
|
28206
|
-
if (
|
|
28750
|
+
if (!existsSync29(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
28751
|
+
if (!existsSync29(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
28752
|
+
if (existsSync29(PLIST_PATH)) {
|
|
28207
28753
|
try {
|
|
28208
|
-
|
|
28754
|
+
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
28209
28755
|
} catch {
|
|
28210
28756
|
}
|
|
28211
28757
|
}
|
|
28212
28758
|
writeFileSync9(PLIST_PATH, generatePlist());
|
|
28213
28759
|
console.log(` Installed: ${PLIST_PATH}`);
|
|
28214
|
-
|
|
28760
|
+
execFileSync4("launchctl", ["load", PLIST_PATH]);
|
|
28215
28761
|
console.log(" Service loaded and starting.");
|
|
28216
28762
|
}
|
|
28217
28763
|
function uninstallMacOS() {
|
|
28218
|
-
if (!
|
|
28764
|
+
if (!existsSync29(PLIST_PATH)) {
|
|
28219
28765
|
console.log(" No service found to uninstall.");
|
|
28220
28766
|
return;
|
|
28221
28767
|
}
|
|
28222
28768
|
try {
|
|
28223
|
-
|
|
28769
|
+
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
28224
28770
|
} catch {
|
|
28225
28771
|
}
|
|
28226
28772
|
unlinkSync8(PLIST_PATH);
|
|
@@ -28246,7 +28792,7 @@ async function getUptimeFromDaemon() {
|
|
|
28246
28792
|
}
|
|
28247
28793
|
function statusMacOS() {
|
|
28248
28794
|
try {
|
|
28249
|
-
const out =
|
|
28795
|
+
const out = execFileSync4("launchctl", ["list"], { encoding: "utf-8" });
|
|
28250
28796
|
const line = out.split("\n").find((l) => l.includes("cc-claw"));
|
|
28251
28797
|
if (line) {
|
|
28252
28798
|
const parts = line.trim().split(/\s+/);
|
|
@@ -28290,42 +28836,42 @@ WantedBy=default.target
|
|
|
28290
28836
|
`;
|
|
28291
28837
|
}
|
|
28292
28838
|
function installLinux() {
|
|
28293
|
-
if (!
|
|
28294
|
-
if (!
|
|
28839
|
+
if (!existsSync29(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
28840
|
+
if (!existsSync29(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
28295
28841
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
28296
28842
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
28297
|
-
|
|
28298
|
-
|
|
28299
|
-
|
|
28843
|
+
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
28844
|
+
execFileSync4("systemctl", ["--user", "enable", "cc-claw"]);
|
|
28845
|
+
execFileSync4("systemctl", ["--user", "start", "cc-claw"]);
|
|
28300
28846
|
console.log(" Service enabled and started.");
|
|
28301
28847
|
}
|
|
28302
28848
|
function uninstallLinux() {
|
|
28303
|
-
if (!
|
|
28849
|
+
if (!existsSync29(UNIT_PATH)) {
|
|
28304
28850
|
console.log(" No service found to uninstall.");
|
|
28305
28851
|
return;
|
|
28306
28852
|
}
|
|
28307
28853
|
try {
|
|
28308
|
-
|
|
28854
|
+
execFileSync4("systemctl", ["--user", "stop", "cc-claw"]);
|
|
28309
28855
|
} catch {
|
|
28310
28856
|
}
|
|
28311
28857
|
try {
|
|
28312
|
-
|
|
28858
|
+
execFileSync4("systemctl", ["--user", "disable", "cc-claw"]);
|
|
28313
28859
|
} catch {
|
|
28314
28860
|
}
|
|
28315
28861
|
unlinkSync8(UNIT_PATH);
|
|
28316
|
-
|
|
28862
|
+
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
28317
28863
|
console.log(" Service uninstalled.");
|
|
28318
28864
|
}
|
|
28319
28865
|
function statusLinux() {
|
|
28320
28866
|
try {
|
|
28321
|
-
const out =
|
|
28867
|
+
const out = execSync5("systemctl --user is-active cc-claw", { encoding: "utf-8" }).trim();
|
|
28322
28868
|
console.log(` Service is ${out}.`);
|
|
28323
28869
|
} catch {
|
|
28324
28870
|
console.log(" Not running or not installed.");
|
|
28325
28871
|
}
|
|
28326
28872
|
}
|
|
28327
28873
|
function installService() {
|
|
28328
|
-
if (!
|
|
28874
|
+
if (!existsSync29(join31(CC_CLAW_HOME, ".env"))) {
|
|
28329
28875
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
28330
28876
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
28331
28877
|
process.exitCode = 1;
|
|
@@ -28466,18 +29012,18 @@ __export(daemon_exports, {
|
|
|
28466
29012
|
restartService: () => restartService,
|
|
28467
29013
|
stopService: () => stopService
|
|
28468
29014
|
});
|
|
28469
|
-
import { execSync as
|
|
29015
|
+
import { execSync as execSync6 } from "child_process";
|
|
28470
29016
|
import { platform as platform2 } from "os";
|
|
28471
29017
|
async function stopService() {
|
|
28472
29018
|
const os2 = platform2();
|
|
28473
29019
|
try {
|
|
28474
29020
|
if (os2 === "darwin") {
|
|
28475
|
-
|
|
29021
|
+
execSync6("launchctl stop com.cc-claw");
|
|
28476
29022
|
console.log(`
|
|
28477
29023
|
${success("Daemon stopped.")}
|
|
28478
29024
|
`);
|
|
28479
29025
|
} else if (os2 === "linux") {
|
|
28480
|
-
|
|
29026
|
+
execSync6("systemctl --user stop cc-claw");
|
|
28481
29027
|
console.log(`
|
|
28482
29028
|
${success("Daemon stopped.")}
|
|
28483
29029
|
`);
|
|
@@ -28495,13 +29041,13 @@ async function restartService() {
|
|
|
28495
29041
|
const os2 = platform2();
|
|
28496
29042
|
try {
|
|
28497
29043
|
if (os2 === "darwin") {
|
|
28498
|
-
const uid = process.getuid?.() ??
|
|
28499
|
-
|
|
29044
|
+
const uid = process.getuid?.() ?? execSync6("id -u", { encoding: "utf-8" }).trim();
|
|
29045
|
+
execSync6(`launchctl kickstart -k gui/${uid}/com.cc-claw`);
|
|
28500
29046
|
console.log(`
|
|
28501
29047
|
${success("Daemon restarted.")}
|
|
28502
29048
|
`);
|
|
28503
29049
|
} else if (os2 === "linux") {
|
|
28504
|
-
|
|
29050
|
+
execSync6("systemctl --user restart cc-claw");
|
|
28505
29051
|
console.log(`
|
|
28506
29052
|
${success("Daemon restarted.")}
|
|
28507
29053
|
`);
|
|
@@ -28523,13 +29069,13 @@ var init_daemon = __esm({
|
|
|
28523
29069
|
});
|
|
28524
29070
|
|
|
28525
29071
|
// src/cli/resolve-chat.ts
|
|
28526
|
-
import { readFileSync as
|
|
29072
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
28527
29073
|
function resolveChatId2(globalOpts) {
|
|
28528
29074
|
const explicit = globalOpts.chat;
|
|
28529
29075
|
if (explicit) return explicit;
|
|
28530
29076
|
if (_cachedDefault) return _cachedDefault;
|
|
28531
29077
|
try {
|
|
28532
|
-
const content =
|
|
29078
|
+
const content = readFileSync20(ENV_PATH, "utf-8");
|
|
28533
29079
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
28534
29080
|
if (match) {
|
|
28535
29081
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -28553,13 +29099,13 @@ var status_exports = {};
|
|
|
28553
29099
|
__export(status_exports, {
|
|
28554
29100
|
statusCommand: () => statusCommand
|
|
28555
29101
|
});
|
|
28556
|
-
import { existsSync as
|
|
29102
|
+
import { existsSync as existsSync30, statSync as statSync10 } from "fs";
|
|
28557
29103
|
async function statusCommand(globalOpts, localOpts) {
|
|
28558
29104
|
try {
|
|
28559
29105
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
28560
29106
|
const readDb = openDatabaseReadOnly2();
|
|
28561
29107
|
const chatId = resolveChatId2(globalOpts);
|
|
28562
|
-
const { getAdapterForChat:
|
|
29108
|
+
const { getAdapterForChat: getAdapterForChat3, getAdapter: getAdapter5, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
28563
29109
|
let backend2 = null;
|
|
28564
29110
|
let modelName = "not set";
|
|
28565
29111
|
let contextMax = 2e5;
|
|
@@ -28567,7 +29113,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
28567
29113
|
const backendRow = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
28568
29114
|
if (backendRow) {
|
|
28569
29115
|
try {
|
|
28570
|
-
const a =
|
|
29116
|
+
const a = getAdapter5(backendRow.backend);
|
|
28571
29117
|
backend2 = { id: a.id, displayName: a.displayName };
|
|
28572
29118
|
} catch {
|
|
28573
29119
|
backend2 = { id: backendRow.backend, displayName: backendRow.backend };
|
|
@@ -28580,7 +29126,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
28580
29126
|
if (modelRow) modelName = modelRow.model;
|
|
28581
29127
|
else if (backend2) {
|
|
28582
29128
|
try {
|
|
28583
|
-
modelName =
|
|
29129
|
+
modelName = getAdapter5(backend2.id).defaultModel;
|
|
28584
29130
|
} catch {
|
|
28585
29131
|
}
|
|
28586
29132
|
}
|
|
@@ -28593,7 +29139,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
28593
29139
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
28594
29140
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
28595
29141
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
28596
|
-
const dbStat =
|
|
29142
|
+
const dbStat = existsSync30(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
28597
29143
|
let daemonRunning = false;
|
|
28598
29144
|
let daemonInfo = {};
|
|
28599
29145
|
try {
|
|
@@ -28705,13 +29251,13 @@ __export(doctor_exports, {
|
|
|
28705
29251
|
doctorCommand: () => doctorCommand,
|
|
28706
29252
|
doctorErrors: () => doctorErrors
|
|
28707
29253
|
});
|
|
28708
|
-
import { existsSync as
|
|
28709
|
-
import { execFileSync as
|
|
29254
|
+
import { existsSync as existsSync31, accessSync, constants } from "fs";
|
|
29255
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
28710
29256
|
async function doctorCommand(globalOpts, localOpts) {
|
|
28711
29257
|
const checks = [];
|
|
28712
|
-
|
|
28713
|
-
|
|
28714
|
-
|
|
29258
|
+
const dbChecks = checkDatabase();
|
|
29259
|
+
checks.push(...dbChecks);
|
|
29260
|
+
if (existsSync31(DB_PATH)) {
|
|
28715
29261
|
try {
|
|
28716
29262
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
28717
29263
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -28736,27 +29282,13 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28736
29282
|
} catch (err) {
|
|
28737
29283
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
28738
29284
|
}
|
|
28739
|
-
} else {
|
|
28740
|
-
checks.push({ name: "Database", status: "error", message: `Not found at ${DB_PATH}`, fix: "cc-claw setup" });
|
|
28741
29285
|
}
|
|
28742
|
-
if (
|
|
29286
|
+
if (existsSync31(ENV_PATH)) {
|
|
28743
29287
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
28744
29288
|
} else {
|
|
28745
29289
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
28746
29290
|
}
|
|
28747
|
-
|
|
28748
|
-
let installedBackends = 0;
|
|
28749
|
-
for (const [label2, binary] of Object.entries(CLI_BINARIES)) {
|
|
28750
|
-
try {
|
|
28751
|
-
const path = execFileSync4("which", [binary], { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
28752
|
-
checks.push({ name: `${label2} CLI`, status: "ok", message: path });
|
|
28753
|
-
installedBackends++;
|
|
28754
|
-
} catch {
|
|
28755
|
-
}
|
|
28756
|
-
}
|
|
28757
|
-
if (installedBackends === 0) {
|
|
28758
|
-
checks.push({ name: "Backend CLIs", status: "error", message: "No backend CLIs found. Install at least one (claude, gemini, codex, or cursor agent)." });
|
|
28759
|
-
}
|
|
29291
|
+
checks.push(...checkBackendCLIs());
|
|
28760
29292
|
try {
|
|
28761
29293
|
const { isDaemonRunning: isDaemonRunning2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
28762
29294
|
const running = await isDaemonRunning2();
|
|
@@ -28787,14 +29319,14 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28787
29319
|
checks.push({ name: "Daemon", status: "warning", message: "could not probe" });
|
|
28788
29320
|
}
|
|
28789
29321
|
try {
|
|
28790
|
-
const latest =
|
|
29322
|
+
const latest = execFileSync5("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
28791
29323
|
if (latest && latest !== VERSION) {
|
|
28792
29324
|
checks.push({ name: "Update available", status: "warning", message: `v${latest} available (current: v${VERSION})`, fix: "npm install -g cc-claw@latest" });
|
|
28793
29325
|
}
|
|
28794
29326
|
} catch {
|
|
28795
29327
|
}
|
|
28796
29328
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
28797
|
-
if (
|
|
29329
|
+
if (existsSync31(tokenPath)) {
|
|
28798
29330
|
try {
|
|
28799
29331
|
accessSync(tokenPath, constants.R_OK);
|
|
28800
29332
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -28804,125 +29336,28 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28804
29336
|
} else {
|
|
28805
29337
|
checks.push({ name: "API token", status: "warning", message: "no token file (daemon not started?)", fix: "cc-claw service start" });
|
|
28806
29338
|
}
|
|
28807
|
-
|
|
28808
|
-
|
|
28809
|
-
|
|
28810
|
-
|
|
28811
|
-
|
|
28812
|
-
const parts = lines[1].split(/\s+/);
|
|
28813
|
-
const availKB = parseInt(parts[3], 10);
|
|
28814
|
-
if (availKB < 1e5) {
|
|
28815
|
-
checks.push({ name: "Disk space", status: "warning", message: `${(availKB / 1024).toFixed(0)}MB available` });
|
|
28816
|
-
} else {
|
|
28817
|
-
checks.push({ name: "Disk space", status: "ok", message: `${(availKB / 1024 / 1024).toFixed(1)}GB available` });
|
|
28818
|
-
}
|
|
28819
|
-
}
|
|
28820
|
-
} catch {
|
|
28821
|
-
}
|
|
28822
|
-
if (existsSync30(ERROR_LOG_PATH)) {
|
|
28823
|
-
try {
|
|
28824
|
-
const { readFileSync: readFileSync28 } = await import("fs");
|
|
28825
|
-
const logContent = readFileSync28(ERROR_LOG_PATH, "utf-8");
|
|
28826
|
-
const recentLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
28827
|
-
const last24h = Date.now() - 864e5;
|
|
28828
|
-
const recentErrors = recentLines.filter((line) => {
|
|
28829
|
-
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:]+)/);
|
|
28830
|
-
if (match) return (/* @__PURE__ */ new Date(`${match[1]}T${match[2]}`)).getTime() > last24h;
|
|
28831
|
-
return false;
|
|
28832
|
-
});
|
|
28833
|
-
if (recentErrors.length > 0) {
|
|
28834
|
-
let rate429 = 0;
|
|
28835
|
-
let contentSilence = 0;
|
|
28836
|
-
let spawnTimeout = 0;
|
|
28837
|
-
const otherLines = [];
|
|
28838
|
-
for (const line of recentErrors) {
|
|
28839
|
-
if (/429|rate.?limit/i.test(line)) rate429++;
|
|
28840
|
-
else if (/content silence/i.test(line)) contentSilence++;
|
|
28841
|
-
else if (/spawn timeout|timeout after \d+s/i.test(line)) spawnTimeout++;
|
|
28842
|
-
else otherLines.push(line);
|
|
28843
|
-
}
|
|
28844
|
-
const viewFix = "cc-claw logs --error to view details";
|
|
28845
|
-
const clearFix = "cc-claw doctor --fix to clear stale errors";
|
|
28846
|
-
if (rate429 > 10) {
|
|
28847
|
-
checks.push({ name: "Telegram rate limits", status: "error", message: `${rate429} rate-limit (429) errors in last 24h \u2014 message delivery blocked`, fix: `${viewFix}, ${clearFix}` });
|
|
28848
|
-
} else if (rate429 > 0) {
|
|
28849
|
-
checks.push({ name: "Telegram rate limits", status: "warning", message: `${rate429} rate-limit (429) errors in last 24h`, fix: `${viewFix}, ${clearFix}` });
|
|
28850
|
-
}
|
|
28851
|
-
if (contentSilence > 0) {
|
|
28852
|
-
checks.push({ name: "Content silence", status: "warning", message: `${contentSilence} agent silence timeout(s) in last 24h \u2014 API went unresponsive`, fix: `${viewFix}, ${clearFix}` });
|
|
28853
|
-
}
|
|
28854
|
-
if (spawnTimeout > 0) {
|
|
28855
|
-
checks.push({ name: "Spawn timeouts", status: "warning", message: `${spawnTimeout} backend timeout(s) in last 24h`, fix: `${viewFix}, ${clearFix}` });
|
|
28856
|
-
}
|
|
28857
|
-
if (otherLines.length > 0) {
|
|
28858
|
-
const summaries = /* @__PURE__ */ new Map();
|
|
28859
|
-
for (const line of otherLines) {
|
|
28860
|
-
const body = line.replace(/^\[\d{4}-\d{2}-\d{2}\s+[\d:+-]+\]\s*/, "").slice(0, 120);
|
|
28861
|
-
const key = body.slice(0, 80);
|
|
28862
|
-
summaries.set(key, (summaries.get(key) ?? 0) + 1);
|
|
28863
|
-
}
|
|
28864
|
-
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 ");
|
|
28865
|
-
checks.push({ name: "Other errors", status: "warning", message: `${otherLines.length} error(s) in last 24h:
|
|
28866
|
-
${topErrors}`, fix: `${viewFix}, ${clearFix}` });
|
|
28867
|
-
}
|
|
28868
|
-
if (rate429 === 0 && contentSilence === 0 && spawnTimeout === 0 && otherLines.length === 0) {
|
|
28869
|
-
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
28870
|
-
}
|
|
28871
|
-
} else {
|
|
28872
|
-
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
28873
|
-
}
|
|
28874
|
-
} catch {
|
|
28875
|
-
checks.push({ name: "Error log", status: "ok", message: "no errors logged" });
|
|
28876
|
-
}
|
|
28877
|
-
} else {
|
|
28878
|
-
checks.push({ name: "Error log", status: "ok", message: "no error log file" });
|
|
28879
|
-
}
|
|
28880
|
-
try {
|
|
28881
|
-
const { OllamaStore } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
28882
|
-
const servers = OllamaStore.listServers();
|
|
28883
|
-
if (servers.length > 0) {
|
|
28884
|
-
const online = servers.filter((s) => s.status === "online");
|
|
28885
|
-
const models = OllamaStore.getAvailableModels();
|
|
28886
|
-
if (online.length === servers.length) {
|
|
28887
|
-
checks.push({
|
|
28888
|
-
name: "Ollama",
|
|
28889
|
-
status: "ok",
|
|
28890
|
-
message: `${online.length} server(s) online, ${models.length} model(s)`
|
|
28891
|
-
});
|
|
28892
|
-
} else if (online.length > 0) {
|
|
28893
|
-
checks.push({
|
|
28894
|
-
name: "Ollama",
|
|
28895
|
-
status: "warning",
|
|
28896
|
-
message: `${online.length}/${servers.length} server(s) online, ${models.length} model(s)`
|
|
28897
|
-
});
|
|
28898
|
-
} else {
|
|
28899
|
-
checks.push({
|
|
28900
|
-
name: "Ollama",
|
|
28901
|
-
status: "warning",
|
|
28902
|
-
message: `${servers.length} server(s) configured, all offline`,
|
|
28903
|
-
fix: "Check Ollama is running: ollama serve"
|
|
28904
|
-
});
|
|
28905
|
-
}
|
|
28906
|
-
}
|
|
28907
|
-
} catch {
|
|
28908
|
-
}
|
|
29339
|
+
const disk = checkDiskSpace();
|
|
29340
|
+
if (disk) checks.push(disk);
|
|
29341
|
+
checks.push(...checkErrorLog());
|
|
29342
|
+
const ollama2 = checkOllamaServers();
|
|
29343
|
+
if (ollama2) checks.push(ollama2);
|
|
28909
29344
|
if (localOpts.fix) {
|
|
28910
29345
|
const fixable = checks.filter((c) => c.status !== "ok" && c.fix);
|
|
28911
29346
|
for (const check of fixable) {
|
|
28912
29347
|
if (check.name === "Daemon" && check.fix?.includes("service start")) {
|
|
28913
29348
|
try {
|
|
28914
|
-
const { execSync:
|
|
29349
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
28915
29350
|
const os2 = (await import("os")).platform();
|
|
28916
29351
|
if (os2 === "darwin") {
|
|
28917
29352
|
try {
|
|
28918
|
-
|
|
29353
|
+
execSync7("launchctl start com.cc-claw");
|
|
28919
29354
|
check.status = "ok";
|
|
28920
29355
|
check.message = "restarted";
|
|
28921
29356
|
} catch {
|
|
28922
29357
|
}
|
|
28923
29358
|
} else if (os2 === "linux") {
|
|
28924
29359
|
try {
|
|
28925
|
-
|
|
29360
|
+
execSync7("systemctl --user start cc-claw");
|
|
28926
29361
|
check.status = "ok";
|
|
28927
29362
|
check.message = "restarted";
|
|
28928
29363
|
} catch {
|
|
@@ -28955,9 +29390,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28955
29390
|
}
|
|
28956
29391
|
}
|
|
28957
29392
|
const errorChecks = checks.filter(
|
|
28958
|
-
(c) => ["
|
|
29393
|
+
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
28959
29394
|
);
|
|
28960
|
-
if (errorChecks.length > 0 &&
|
|
29395
|
+
if (errorChecks.length > 0 && existsSync31(ERROR_LOG_PATH)) {
|
|
28961
29396
|
try {
|
|
28962
29397
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
28963
29398
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -28970,9 +29405,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28970
29405
|
}
|
|
28971
29406
|
}
|
|
28972
29407
|
}
|
|
28973
|
-
const
|
|
28974
|
-
const
|
|
28975
|
-
const report = { checks, errors, warnings };
|
|
29408
|
+
const errCount = checks.filter((c) => c.status === "error").length;
|
|
29409
|
+
const warnCount = checks.filter((c) => c.status === "warning").length;
|
|
29410
|
+
const report = { checks, errors: errCount, warnings: warnCount };
|
|
28976
29411
|
output(report, (d) => {
|
|
28977
29412
|
const r = d;
|
|
28978
29413
|
const lines = [
|
|
@@ -29003,27 +29438,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29003
29438
|
lines.push("");
|
|
29004
29439
|
return lines.join("\n");
|
|
29005
29440
|
});
|
|
29006
|
-
process.exit(
|
|
29007
|
-
}
|
|
29008
|
-
function getRecentErrors() {
|
|
29009
|
-
if (!existsSync30(ERROR_LOG_PATH)) return null;
|
|
29010
|
-
const logContent = readFileSync20(ERROR_LOG_PATH, "utf-8");
|
|
29011
|
-
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
29012
|
-
const last24h = Date.now() - 864e5;
|
|
29013
|
-
const lines = allLines.filter((line) => {
|
|
29014
|
-
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:+-]+)/);
|
|
29015
|
-
if (match) return (/* @__PURE__ */ new Date(`${match[1]}T${match[2]}`)).getTime() > last24h;
|
|
29016
|
-
return false;
|
|
29017
|
-
});
|
|
29018
|
-
if (lines.length === 0) return null;
|
|
29019
|
-
const classified = { rate429: [], contentSilence: [], spawnTimeout: [], other: [] };
|
|
29020
|
-
for (const line of lines) {
|
|
29021
|
-
if (/429|rate.?limit/i.test(line)) classified.rate429.push(line);
|
|
29022
|
-
else if (/content silence/i.test(line)) classified.contentSilence.push(line);
|
|
29023
|
-
else if (/spawn timeout|timeout after \d+s/i.test(line)) classified.spawnTimeout.push(line);
|
|
29024
|
-
else classified.other.push(line);
|
|
29025
|
-
}
|
|
29026
|
-
return { lines, classified };
|
|
29441
|
+
process.exit(errCount > 0 ? 1 : warnCount > 0 ? 2 : 0);
|
|
29027
29442
|
}
|
|
29028
29443
|
function stripTimestamp(line) {
|
|
29029
29444
|
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:+-]+)\]\s*(.*)/);
|
|
@@ -29038,9 +29453,8 @@ async function doctorErrors(globalOpts) {
|
|
|
29038
29453
|
`);
|
|
29039
29454
|
return;
|
|
29040
29455
|
}
|
|
29041
|
-
const {
|
|
29042
|
-
|
|
29043
|
-
output({ total, classified }, () => {
|
|
29456
|
+
const { rate429, contentSilence, spawnTimeout, other, total } = result;
|
|
29457
|
+
output({ total, classified: result }, () => {
|
|
29044
29458
|
const lines = [
|
|
29045
29459
|
"",
|
|
29046
29460
|
box("Recent Errors (last 24h)"),
|
|
@@ -29072,10 +29486,10 @@ async function doctorErrors(globalOpts) {
|
|
|
29072
29486
|
}
|
|
29073
29487
|
lines.push("");
|
|
29074
29488
|
};
|
|
29075
|
-
renderCategory("Telegram rate limits",
|
|
29076
|
-
renderCategory("Content silence timeouts",
|
|
29077
|
-
renderCategory("Spawn timeouts",
|
|
29078
|
-
renderCategory("Other errors",
|
|
29489
|
+
renderCategory("Telegram rate limits", rate429, 5);
|
|
29490
|
+
renderCategory("Content silence timeouts", contentSilence, 5);
|
|
29491
|
+
renderCategory("Spawn timeouts", spawnTimeout, 5);
|
|
29492
|
+
renderCategory("Other errors", other, 10);
|
|
29079
29493
|
if (total === 0) {
|
|
29080
29494
|
lines.push(` ${success("No errors in last 24h.")}`);
|
|
29081
29495
|
} else {
|
|
@@ -29098,6 +29512,7 @@ var init_doctor = __esm({
|
|
|
29098
29512
|
init_format2();
|
|
29099
29513
|
init_paths();
|
|
29100
29514
|
init_version();
|
|
29515
|
+
init_checks();
|
|
29101
29516
|
}
|
|
29102
29517
|
});
|
|
29103
29518
|
|
|
@@ -29106,15 +29521,15 @@ var logs_exports = {};
|
|
|
29106
29521
|
__export(logs_exports, {
|
|
29107
29522
|
logsCommand: () => logsCommand
|
|
29108
29523
|
});
|
|
29109
|
-
import { existsSync as
|
|
29524
|
+
import { existsSync as existsSync32, readFileSync as readFileSync22, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
29110
29525
|
async function logsCommand(opts) {
|
|
29111
29526
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
29112
|
-
if (!
|
|
29527
|
+
if (!existsSync32(logFile)) {
|
|
29113
29528
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
29114
29529
|
process.exit(1);
|
|
29115
29530
|
}
|
|
29116
29531
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
29117
|
-
const content =
|
|
29532
|
+
const content = readFileSync22(logFile, "utf-8");
|
|
29118
29533
|
const allLines = content.split("\n");
|
|
29119
29534
|
const tailLines = allLines.slice(-maxLines);
|
|
29120
29535
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -29124,7 +29539,7 @@ async function logsCommand(opts) {
|
|
|
29124
29539
|
let lastLength = content.length;
|
|
29125
29540
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
29126
29541
|
try {
|
|
29127
|
-
const newContent =
|
|
29542
|
+
const newContent = readFileSync22(logFile, "utf-8");
|
|
29128
29543
|
if (newContent.length > lastLength) {
|
|
29129
29544
|
const newPart = newContent.slice(lastLength);
|
|
29130
29545
|
process.stdout.write(newPart);
|
|
@@ -29156,7 +29571,7 @@ __export(session_logs_exports, {
|
|
|
29156
29571
|
sessionLogsList: () => sessionLogsList,
|
|
29157
29572
|
sessionLogsTail: () => sessionLogsTail
|
|
29158
29573
|
});
|
|
29159
|
-
import { readFileSync as
|
|
29574
|
+
import { readFileSync as readFileSync23, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
29160
29575
|
async function sessionLogsList(opts) {
|
|
29161
29576
|
const logs = listSessionLogs();
|
|
29162
29577
|
if (logs.length === 0) {
|
|
@@ -29213,12 +29628,12 @@ async function sessionLogsTail(opts) {
|
|
|
29213
29628
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
29214
29629
|
let lastLength = 0;
|
|
29215
29630
|
try {
|
|
29216
|
-
lastLength =
|
|
29631
|
+
lastLength = readFileSync23(targetPath, "utf-8").length;
|
|
29217
29632
|
} catch {
|
|
29218
29633
|
}
|
|
29219
29634
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
29220
29635
|
try {
|
|
29221
|
-
const content =
|
|
29636
|
+
const content = readFileSync23(targetPath, "utf-8");
|
|
29222
29637
|
if (content.length > lastLength) {
|
|
29223
29638
|
process.stdout.write(content.slice(lastLength));
|
|
29224
29639
|
lastLength = content.length;
|
|
@@ -29262,11 +29677,11 @@ __export(gemini_exports, {
|
|
|
29262
29677
|
geminiReorder: () => geminiReorder,
|
|
29263
29678
|
geminiRotation: () => geminiRotation
|
|
29264
29679
|
});
|
|
29265
|
-
import { existsSync as
|
|
29680
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync24, chmodSync } from "fs";
|
|
29266
29681
|
import { join as join32 } from "path";
|
|
29267
29682
|
import { createInterface as createInterface8 } from "readline";
|
|
29268
29683
|
function requireDb() {
|
|
29269
|
-
if (!
|
|
29684
|
+
if (!existsSync34(DB_PATH)) {
|
|
29270
29685
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
29271
29686
|
process.exit(1);
|
|
29272
29687
|
}
|
|
@@ -29292,8 +29707,8 @@ function resolveOAuthEmail(configHome) {
|
|
|
29292
29707
|
if (!configHome) return null;
|
|
29293
29708
|
try {
|
|
29294
29709
|
const accountsPath = join32(configHome, ".gemini", "google_accounts.json");
|
|
29295
|
-
if (!
|
|
29296
|
-
const accounts = JSON.parse(
|
|
29710
|
+
if (!existsSync34(accountsPath)) return null;
|
|
29711
|
+
const accounts = JSON.parse(readFileSync24(accountsPath, "utf-8"));
|
|
29297
29712
|
return accounts.active || null;
|
|
29298
29713
|
} catch {
|
|
29299
29714
|
return null;
|
|
@@ -29376,7 +29791,7 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
29376
29791
|
async function geminiAddAccount(globalOpts, opts) {
|
|
29377
29792
|
await requireWriteDb();
|
|
29378
29793
|
const slotsDir = join32(CC_CLAW_HOME, "gemini-slots");
|
|
29379
|
-
if (!
|
|
29794
|
+
if (!existsSync34(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
29380
29795
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
29381
29796
|
const tempId = Date.now();
|
|
29382
29797
|
const slotDir = join32(slotsDir, `slot-${tempId}`);
|
|
@@ -29390,9 +29805,9 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
29390
29805
|
console.log(" Sign in with the Google account you want for this slot.");
|
|
29391
29806
|
console.log(" After sign-in, type /quit to return here.");
|
|
29392
29807
|
console.log("");
|
|
29393
|
-
const { execSync:
|
|
29808
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29394
29809
|
try {
|
|
29395
|
-
|
|
29810
|
+
execSync7(`GEMINI_CLI_HOME=${slotDir} gemini`, {
|
|
29396
29811
|
stdio: "inherit",
|
|
29397
29812
|
env: { ...process.env, GEMINI_CLI_HOME: slotDir, GEMINI_API_KEY: void 0, GOOGLE_API_KEY: void 0 },
|
|
29398
29813
|
cwd: slotDir
|
|
@@ -29400,7 +29815,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
29400
29815
|
} catch {
|
|
29401
29816
|
}
|
|
29402
29817
|
const oauthPath = join32(slotDir, ".gemini", "oauth_creds.json");
|
|
29403
|
-
if (!
|
|
29818
|
+
if (!existsSync34(oauthPath)) {
|
|
29404
29819
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
29405
29820
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
29406
29821
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -29520,7 +29935,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29520
29935
|
return;
|
|
29521
29936
|
}
|
|
29522
29937
|
const settingsPath = join32(slot.configHome, ".gemini", "settings.json");
|
|
29523
|
-
if (!
|
|
29938
|
+
if (!existsSync34(settingsPath)) {
|
|
29524
29939
|
mkdirSync14(join32(slot.configHome, ".gemini"), { recursive: true });
|
|
29525
29940
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
29526
29941
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
@@ -29531,9 +29946,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29531
29946
|
console.log(" Sign in with the same Google account when prompted.");
|
|
29532
29947
|
console.log(" After sign-in, type /quit to return here.");
|
|
29533
29948
|
console.log("");
|
|
29534
|
-
const { execSync:
|
|
29949
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29535
29950
|
try {
|
|
29536
|
-
|
|
29951
|
+
execSync7(`gemini`, {
|
|
29537
29952
|
stdio: "inherit",
|
|
29538
29953
|
env: {
|
|
29539
29954
|
...process.env,
|
|
@@ -29546,7 +29961,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29546
29961
|
} catch {
|
|
29547
29962
|
}
|
|
29548
29963
|
const oauthPath = join32(slot.configHome, ".gemini", "oauth_creds.json");
|
|
29549
|
-
if (!
|
|
29964
|
+
if (!existsSync34(oauthPath)) {
|
|
29550
29965
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
29551
29966
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
29552
29967
|
`);
|
|
@@ -29556,7 +29971,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29556
29971
|
setGeminiSlotEnabled2(slotId, true);
|
|
29557
29972
|
let accountEmail = slot.label;
|
|
29558
29973
|
try {
|
|
29559
|
-
const accounts = JSON.parse(
|
|
29974
|
+
const accounts = JSON.parse(readFileSync24(join32(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
29560
29975
|
if (accounts.active) accountEmail = accounts.active;
|
|
29561
29976
|
} catch {
|
|
29562
29977
|
}
|
|
@@ -29615,11 +30030,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
29615
30030
|
makeReorder: () => makeReorder,
|
|
29616
30031
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
29617
30032
|
});
|
|
29618
|
-
import { existsSync as
|
|
30033
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync15, readFileSync as readFileSync25 } from "fs";
|
|
29619
30034
|
import { join as join33 } from "path";
|
|
29620
30035
|
import { createInterface as createInterface9 } from "readline";
|
|
29621
30036
|
function requireDb2() {
|
|
29622
|
-
if (!
|
|
30037
|
+
if (!existsSync35(DB_PATH)) {
|
|
29623
30038
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
29624
30039
|
process.exit(1);
|
|
29625
30040
|
}
|
|
@@ -29709,7 +30124,7 @@ function makeAddAccount(backend2, displayName) {
|
|
|
29709
30124
|
}
|
|
29710
30125
|
await requireWriteDb2();
|
|
29711
30126
|
const slotsDir = join33(CC_CLAW_HOME, config2.slotsSubdir);
|
|
29712
|
-
if (!
|
|
30127
|
+
if (!existsSync35(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
29713
30128
|
const tempId = Date.now();
|
|
29714
30129
|
const slotDir = join33(slotsDir, `slot-${tempId}`);
|
|
29715
30130
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
@@ -29719,14 +30134,14 @@ function makeAddAccount(backend2, displayName) {
|
|
|
29719
30134
|
console.log(` Sign in with the account you want for this slot.`);
|
|
29720
30135
|
console.log(` After sign-in completes, return here.`);
|
|
29721
30136
|
console.log("");
|
|
29722
|
-
const { execSync:
|
|
30137
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29723
30138
|
const loginEnv = {
|
|
29724
30139
|
...process.env,
|
|
29725
30140
|
[config2.envKey]: config2.envValue(slotDir),
|
|
29726
30141
|
...config2.envOverrides
|
|
29727
30142
|
};
|
|
29728
30143
|
try {
|
|
29729
|
-
|
|
30144
|
+
execSync7(config2.loginCommand.join(" "), {
|
|
29730
30145
|
stdio: "inherit",
|
|
29731
30146
|
env: loginEnv,
|
|
29732
30147
|
cwd: slotDir
|
|
@@ -29871,14 +30286,14 @@ function makeRelogin(backend2, displayName) {
|
|
|
29871
30286
|
console.log(` Re-authenticating ${displayName} slot "${slot.label || `#${slot.id}`}"...`);
|
|
29872
30287
|
console.log(` Sign in with the same account when the browser opens.`);
|
|
29873
30288
|
console.log("");
|
|
29874
|
-
const { execSync:
|
|
30289
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29875
30290
|
const loginEnv = {
|
|
29876
30291
|
...process.env,
|
|
29877
30292
|
[config2.envKey]: config2.envValue(slot.configHome),
|
|
29878
30293
|
...config2.envOverrides
|
|
29879
30294
|
};
|
|
29880
30295
|
try {
|
|
29881
|
-
|
|
30296
|
+
execSync7(config2.loginCommand.join(" "), {
|
|
29882
30297
|
stdio: "inherit",
|
|
29883
30298
|
env: loginEnv,
|
|
29884
30299
|
cwd: slot.configHome
|
|
@@ -29967,17 +30382,17 @@ var init_backend_cmd_factory = __esm({
|
|
|
29967
30382
|
verifyCredentials: (slotDir) => {
|
|
29968
30383
|
const claudeJson = join33(slotDir, ".claude.json");
|
|
29969
30384
|
const claudeJsonNested = join33(slotDir, ".claude", ".claude.json");
|
|
29970
|
-
if (
|
|
30385
|
+
if (existsSync35(claudeJson)) {
|
|
29971
30386
|
try {
|
|
29972
|
-
const data = JSON.parse(
|
|
30387
|
+
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
29973
30388
|
return Boolean(data.oauthAccount);
|
|
29974
30389
|
} catch {
|
|
29975
30390
|
return false;
|
|
29976
30391
|
}
|
|
29977
30392
|
}
|
|
29978
|
-
if (
|
|
30393
|
+
if (existsSync35(claudeJsonNested)) {
|
|
29979
30394
|
try {
|
|
29980
|
-
const data = JSON.parse(
|
|
30395
|
+
const data = JSON.parse(readFileSync25(claudeJsonNested, "utf-8"));
|
|
29981
30396
|
return Boolean(data.oauthAccount);
|
|
29982
30397
|
} catch {
|
|
29983
30398
|
return false;
|
|
@@ -29987,8 +30402,8 @@ var init_backend_cmd_factory = __esm({
|
|
|
29987
30402
|
},
|
|
29988
30403
|
extractLabel: (slotDir) => {
|
|
29989
30404
|
try {
|
|
29990
|
-
const { execSync:
|
|
29991
|
-
const out =
|
|
30405
|
+
const { execSync: execSync7 } = __require("child_process");
|
|
30406
|
+
const out = execSync7("claude auth status", {
|
|
29992
30407
|
encoding: "utf-8",
|
|
29993
30408
|
env: { ...process.env, HOME: slotDir, ANTHROPIC_API_KEY: void 0 },
|
|
29994
30409
|
timeout: 1e4
|
|
@@ -29999,8 +30414,8 @@ var init_backend_cmd_factory = __esm({
|
|
|
29999
30414
|
}
|
|
30000
30415
|
try {
|
|
30001
30416
|
const claudeJson = join33(slotDir, ".claude.json");
|
|
30002
|
-
if (
|
|
30003
|
-
const data = JSON.parse(
|
|
30417
|
+
if (existsSync35(claudeJson)) {
|
|
30418
|
+
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
30004
30419
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
30005
30420
|
}
|
|
30006
30421
|
} catch {
|
|
@@ -30015,11 +30430,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
30015
30430
|
envValue: (slotDir) => slotDir,
|
|
30016
30431
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
30017
30432
|
verifyCredentials: (slotDir) => {
|
|
30018
|
-
return
|
|
30433
|
+
return existsSync35(join33(slotDir, "auth.json"));
|
|
30019
30434
|
},
|
|
30020
30435
|
extractLabel: (slotDir) => {
|
|
30021
30436
|
try {
|
|
30022
|
-
const authData = JSON.parse(
|
|
30437
|
+
const authData = JSON.parse(readFileSync25(join33(slotDir, "auth.json"), "utf-8"));
|
|
30023
30438
|
if (authData.email) return authData.email;
|
|
30024
30439
|
if (authData.account_name) return authData.account_name;
|
|
30025
30440
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -30043,9 +30458,9 @@ __export(ollama_exports3, {
|
|
|
30043
30458
|
ollamaRemove: () => ollamaRemove,
|
|
30044
30459
|
ollamaTest: () => ollamaTest
|
|
30045
30460
|
});
|
|
30046
|
-
import { existsSync as
|
|
30461
|
+
import { existsSync as existsSync36 } from "fs";
|
|
30047
30462
|
function requireDb3() {
|
|
30048
|
-
if (!
|
|
30463
|
+
if (!existsSync36(DB_PATH)) {
|
|
30049
30464
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30050
30465
|
process.exit(1);
|
|
30051
30466
|
}
|
|
@@ -30304,12 +30719,12 @@ __export(backend_exports, {
|
|
|
30304
30719
|
backendList: () => backendList,
|
|
30305
30720
|
backendSet: () => backendSet
|
|
30306
30721
|
});
|
|
30307
|
-
import { existsSync as
|
|
30722
|
+
import { existsSync as existsSync37 } from "fs";
|
|
30308
30723
|
async function backendList(globalOpts) {
|
|
30309
30724
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
30310
30725
|
const chatId = resolveChatId2(globalOpts);
|
|
30311
30726
|
let activeBackend = null;
|
|
30312
|
-
if (
|
|
30727
|
+
if (existsSync37(DB_PATH)) {
|
|
30313
30728
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30314
30729
|
const readDb = openDatabaseReadOnly2();
|
|
30315
30730
|
try {
|
|
@@ -30340,7 +30755,7 @@ async function backendList(globalOpts) {
|
|
|
30340
30755
|
}
|
|
30341
30756
|
async function backendGet(globalOpts) {
|
|
30342
30757
|
const chatId = resolveChatId2(globalOpts);
|
|
30343
|
-
if (!
|
|
30758
|
+
if (!existsSync37(DB_PATH)) {
|
|
30344
30759
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30345
30760
|
process.exit(1);
|
|
30346
30761
|
}
|
|
@@ -30384,13 +30799,13 @@ __export(model_exports, {
|
|
|
30384
30799
|
modelList: () => modelList,
|
|
30385
30800
|
modelSet: () => modelSet
|
|
30386
30801
|
});
|
|
30387
|
-
import { existsSync as
|
|
30802
|
+
import { existsSync as existsSync38 } from "fs";
|
|
30388
30803
|
async function modelList(globalOpts) {
|
|
30389
30804
|
const chatId = resolveChatId2(globalOpts);
|
|
30390
30805
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30391
|
-
const { getAdapter:
|
|
30806
|
+
const { getAdapter: getAdapter5, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
30392
30807
|
let backendId = "claude";
|
|
30393
|
-
if (
|
|
30808
|
+
if (existsSync38(DB_PATH)) {
|
|
30394
30809
|
const readDb = openDatabaseReadOnly2();
|
|
30395
30810
|
try {
|
|
30396
30811
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -30400,7 +30815,7 @@ async function modelList(globalOpts) {
|
|
|
30400
30815
|
readDb.close();
|
|
30401
30816
|
}
|
|
30402
30817
|
try {
|
|
30403
|
-
const adapter =
|
|
30818
|
+
const adapter = getAdapter5(backendId);
|
|
30404
30819
|
const models = Object.entries(adapter.availableModels).map(([id, info]) => ({
|
|
30405
30820
|
id,
|
|
30406
30821
|
label: info.label,
|
|
@@ -30423,7 +30838,7 @@ async function modelList(globalOpts) {
|
|
|
30423
30838
|
}
|
|
30424
30839
|
async function modelGet(globalOpts) {
|
|
30425
30840
|
const chatId = resolveChatId2(globalOpts);
|
|
30426
|
-
if (!
|
|
30841
|
+
if (!existsSync38(DB_PATH)) {
|
|
30427
30842
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30428
30843
|
process.exit(1);
|
|
30429
30844
|
}
|
|
@@ -30467,9 +30882,9 @@ __export(memory_exports2, {
|
|
|
30467
30882
|
memoryList: () => memoryList,
|
|
30468
30883
|
memorySearch: () => memorySearch
|
|
30469
30884
|
});
|
|
30470
|
-
import { existsSync as
|
|
30885
|
+
import { existsSync as existsSync39 } from "fs";
|
|
30471
30886
|
async function memoryList(globalOpts) {
|
|
30472
|
-
if (!
|
|
30887
|
+
if (!existsSync39(DB_PATH)) {
|
|
30473
30888
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30474
30889
|
process.exit(1);
|
|
30475
30890
|
}
|
|
@@ -30493,7 +30908,7 @@ async function memoryList(globalOpts) {
|
|
|
30493
30908
|
});
|
|
30494
30909
|
}
|
|
30495
30910
|
async function memorySearch(globalOpts, query) {
|
|
30496
|
-
if (!
|
|
30911
|
+
if (!existsSync39(DB_PATH)) {
|
|
30497
30912
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30498
30913
|
process.exit(1);
|
|
30499
30914
|
}
|
|
@@ -30515,7 +30930,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
30515
30930
|
});
|
|
30516
30931
|
}
|
|
30517
30932
|
async function memoryHistory(globalOpts, opts) {
|
|
30518
|
-
if (!
|
|
30933
|
+
if (!existsSync39(DB_PATH)) {
|
|
30519
30934
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30520
30935
|
process.exit(1);
|
|
30521
30936
|
}
|
|
@@ -30563,7 +30978,7 @@ __export(cron_exports2, {
|
|
|
30563
30978
|
cronList: () => cronList,
|
|
30564
30979
|
cronRuns: () => cronRuns
|
|
30565
30980
|
});
|
|
30566
|
-
import { existsSync as
|
|
30981
|
+
import { existsSync as existsSync40 } from "fs";
|
|
30567
30982
|
function parseFallbacks(raw) {
|
|
30568
30983
|
return raw.slice(0, 3).map((f) => {
|
|
30569
30984
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -30584,7 +30999,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
30584
30999
|
return val;
|
|
30585
31000
|
}
|
|
30586
31001
|
async function cronList(globalOpts) {
|
|
30587
|
-
if (!
|
|
31002
|
+
if (!existsSync40(DB_PATH)) {
|
|
30588
31003
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30589
31004
|
process.exit(1);
|
|
30590
31005
|
}
|
|
@@ -30622,7 +31037,7 @@ async function cronList(globalOpts) {
|
|
|
30622
31037
|
});
|
|
30623
31038
|
}
|
|
30624
31039
|
async function cronHealth(globalOpts) {
|
|
30625
|
-
if (!
|
|
31040
|
+
if (!existsSync40(DB_PATH)) {
|
|
30626
31041
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30627
31042
|
process.exit(1);
|
|
30628
31043
|
}
|
|
@@ -30783,7 +31198,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
30783
31198
|
}
|
|
30784
31199
|
}
|
|
30785
31200
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
30786
|
-
if (!
|
|
31201
|
+
if (!existsSync40(DB_PATH)) {
|
|
30787
31202
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30788
31203
|
process.exit(1);
|
|
30789
31204
|
}
|
|
@@ -30830,9 +31245,9 @@ __export(agents_exports, {
|
|
|
30830
31245
|
runnersList: () => runnersList,
|
|
30831
31246
|
tasksList: () => tasksList
|
|
30832
31247
|
});
|
|
30833
|
-
import { existsSync as
|
|
31248
|
+
import { existsSync as existsSync41 } from "fs";
|
|
30834
31249
|
async function agentsList(globalOpts) {
|
|
30835
|
-
if (!
|
|
31250
|
+
if (!existsSync41(DB_PATH)) {
|
|
30836
31251
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30837
31252
|
process.exit(1);
|
|
30838
31253
|
}
|
|
@@ -30863,7 +31278,7 @@ async function agentsList(globalOpts) {
|
|
|
30863
31278
|
});
|
|
30864
31279
|
}
|
|
30865
31280
|
async function tasksList(globalOpts) {
|
|
30866
|
-
if (!
|
|
31281
|
+
if (!existsSync41(DB_PATH)) {
|
|
30867
31282
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30868
31283
|
process.exit(1);
|
|
30869
31284
|
}
|
|
@@ -30991,10 +31406,10 @@ __export(db_exports, {
|
|
|
30991
31406
|
dbPath: () => dbPath,
|
|
30992
31407
|
dbStats: () => dbStats
|
|
30993
31408
|
});
|
|
30994
|
-
import { existsSync as
|
|
31409
|
+
import { existsSync as existsSync42, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
30995
31410
|
import { dirname as dirname7 } from "path";
|
|
30996
31411
|
async function dbStats(globalOpts) {
|
|
30997
|
-
if (!
|
|
31412
|
+
if (!existsSync42(DB_PATH)) {
|
|
30998
31413
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
30999
31414
|
process.exit(1);
|
|
31000
31415
|
}
|
|
@@ -31002,7 +31417,7 @@ async function dbStats(globalOpts) {
|
|
|
31002
31417
|
const readDb = openDatabaseReadOnly2();
|
|
31003
31418
|
const mainSize = statSync11(DB_PATH).size;
|
|
31004
31419
|
const walPath = DB_PATH + "-wal";
|
|
31005
|
-
const walSize =
|
|
31420
|
+
const walSize = existsSync42(walPath) ? statSync11(walPath).size : 0;
|
|
31006
31421
|
const tableNames = readDb.prepare(
|
|
31007
31422
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
31008
31423
|
).all();
|
|
@@ -31036,7 +31451,7 @@ async function dbPath(globalOpts) {
|
|
|
31036
31451
|
output({ path: DB_PATH }, (d) => d.path);
|
|
31037
31452
|
}
|
|
31038
31453
|
async function dbBackup(globalOpts, destPath) {
|
|
31039
|
-
if (!
|
|
31454
|
+
if (!existsSync42(DB_PATH)) {
|
|
31040
31455
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
31041
31456
|
process.exit(1);
|
|
31042
31457
|
}
|
|
@@ -31045,7 +31460,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
31045
31460
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
31046
31461
|
copyFileSync3(DB_PATH, dest);
|
|
31047
31462
|
const walPath = DB_PATH + "-wal";
|
|
31048
|
-
if (
|
|
31463
|
+
if (existsSync42(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
31049
31464
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
31050
31465
|
const b = d;
|
|
31051
31466
|
return `
|
|
@@ -31074,9 +31489,9 @@ __export(usage_exports, {
|
|
|
31074
31489
|
usageCost: () => usageCost,
|
|
31075
31490
|
usageTokens: () => usageTokens
|
|
31076
31491
|
});
|
|
31077
|
-
import { existsSync as
|
|
31492
|
+
import { existsSync as existsSync43 } from "fs";
|
|
31078
31493
|
function ensureDb() {
|
|
31079
|
-
if (!
|
|
31494
|
+
if (!existsSync43(DB_PATH)) {
|
|
31080
31495
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31081
31496
|
process.exit(1);
|
|
31082
31497
|
}
|
|
@@ -31266,9 +31681,9 @@ __export(config_exports2, {
|
|
|
31266
31681
|
configList: () => configList,
|
|
31267
31682
|
configSet: () => configSet
|
|
31268
31683
|
});
|
|
31269
|
-
import { existsSync as
|
|
31684
|
+
import { existsSync as existsSync44, readFileSync as readFileSync26 } from "fs";
|
|
31270
31685
|
async function configList(globalOpts) {
|
|
31271
|
-
if (!
|
|
31686
|
+
if (!existsSync44(DB_PATH)) {
|
|
31272
31687
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31273
31688
|
process.exit(1);
|
|
31274
31689
|
}
|
|
@@ -31302,7 +31717,7 @@ async function configGet(globalOpts, key) {
|
|
|
31302
31717
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
31303
31718
|
process.exit(1);
|
|
31304
31719
|
}
|
|
31305
|
-
if (!
|
|
31720
|
+
if (!existsSync44(DB_PATH)) {
|
|
31306
31721
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31307
31722
|
process.exit(1);
|
|
31308
31723
|
}
|
|
@@ -31348,11 +31763,11 @@ async function configSet(globalOpts, key, value) {
|
|
|
31348
31763
|
}
|
|
31349
31764
|
}
|
|
31350
31765
|
async function configEnv(_globalOpts) {
|
|
31351
|
-
if (!
|
|
31766
|
+
if (!existsSync44(ENV_PATH)) {
|
|
31352
31767
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
31353
31768
|
process.exit(1);
|
|
31354
31769
|
}
|
|
31355
|
-
const content =
|
|
31770
|
+
const content = readFileSync26(ENV_PATH, "utf-8");
|
|
31356
31771
|
const entries = {};
|
|
31357
31772
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
31358
31773
|
for (const line of content.split("\n")) {
|
|
@@ -31402,9 +31817,9 @@ __export(session_exports, {
|
|
|
31402
31817
|
sessionGet: () => sessionGet,
|
|
31403
31818
|
sessionNew: () => sessionNew
|
|
31404
31819
|
});
|
|
31405
|
-
import { existsSync as
|
|
31820
|
+
import { existsSync as existsSync45 } from "fs";
|
|
31406
31821
|
async function sessionGet(globalOpts) {
|
|
31407
|
-
if (!
|
|
31822
|
+
if (!existsSync45(DB_PATH)) {
|
|
31408
31823
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31409
31824
|
process.exit(1);
|
|
31410
31825
|
}
|
|
@@ -31465,9 +31880,9 @@ __export(permissions_exports, {
|
|
|
31465
31880
|
verboseGet: () => verboseGet,
|
|
31466
31881
|
verboseSet: () => verboseSet
|
|
31467
31882
|
});
|
|
31468
|
-
import { existsSync as
|
|
31883
|
+
import { existsSync as existsSync46 } from "fs";
|
|
31469
31884
|
function ensureDb2() {
|
|
31470
|
-
if (!
|
|
31885
|
+
if (!existsSync46(DB_PATH)) {
|
|
31471
31886
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31472
31887
|
process.exit(1);
|
|
31473
31888
|
}
|
|
@@ -31614,9 +32029,9 @@ __export(cwd_exports, {
|
|
|
31614
32029
|
cwdGet: () => cwdGet,
|
|
31615
32030
|
cwdSet: () => cwdSet
|
|
31616
32031
|
});
|
|
31617
|
-
import { existsSync as
|
|
32032
|
+
import { existsSync as existsSync47 } from "fs";
|
|
31618
32033
|
async function cwdGet(globalOpts) {
|
|
31619
|
-
if (!
|
|
32034
|
+
if (!existsSync47(DB_PATH)) {
|
|
31620
32035
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31621
32036
|
process.exit(1);
|
|
31622
32037
|
}
|
|
@@ -31678,9 +32093,9 @@ __export(voice_exports, {
|
|
|
31678
32093
|
voiceGet: () => voiceGet,
|
|
31679
32094
|
voiceSet: () => voiceSet
|
|
31680
32095
|
});
|
|
31681
|
-
import { existsSync as
|
|
32096
|
+
import { existsSync as existsSync48 } from "fs";
|
|
31682
32097
|
async function voiceGet(globalOpts) {
|
|
31683
|
-
if (!
|
|
32098
|
+
if (!existsSync48(DB_PATH)) {
|
|
31684
32099
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31685
32100
|
process.exit(1);
|
|
31686
32101
|
}
|
|
@@ -31729,9 +32144,9 @@ __export(heartbeat_exports, {
|
|
|
31729
32144
|
heartbeatGet: () => heartbeatGet,
|
|
31730
32145
|
heartbeatSet: () => heartbeatSet
|
|
31731
32146
|
});
|
|
31732
|
-
import { existsSync as
|
|
32147
|
+
import { existsSync as existsSync49 } from "fs";
|
|
31733
32148
|
async function heartbeatGet(globalOpts) {
|
|
31734
|
-
if (!
|
|
32149
|
+
if (!existsSync49(DB_PATH)) {
|
|
31735
32150
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31736
32151
|
process.exit(1);
|
|
31737
32152
|
}
|
|
@@ -31746,8 +32161,25 @@ async function heartbeatGet(globalOpts) {
|
|
|
31746
32161
|
intervalMinutes: row.interval_ms / 6e4,
|
|
31747
32162
|
activeStart: row.active_start,
|
|
31748
32163
|
activeEnd: row.active_end,
|
|
31749
|
-
lastBeatAt: row.last_beat_at
|
|
31750
|
-
|
|
32164
|
+
lastBeatAt: row.last_beat_at,
|
|
32165
|
+
backend: row.backend ?? null,
|
|
32166
|
+
model: row.model ?? null,
|
|
32167
|
+
fallbacks: row.fallbacks ?? null,
|
|
32168
|
+
thinking: row.thinking ?? null,
|
|
32169
|
+
target: row.target ?? null
|
|
32170
|
+
} : {
|
|
32171
|
+
enabled: false,
|
|
32172
|
+
intervalMs: 18e5,
|
|
32173
|
+
intervalMinutes: 30,
|
|
32174
|
+
activeStart: "08:00",
|
|
32175
|
+
activeEnd: "22:00",
|
|
32176
|
+
lastBeatAt: null,
|
|
32177
|
+
backend: null,
|
|
32178
|
+
model: null,
|
|
32179
|
+
fallbacks: null,
|
|
32180
|
+
thinking: null,
|
|
32181
|
+
target: null
|
|
32182
|
+
};
|
|
31751
32183
|
output(data, (d) => {
|
|
31752
32184
|
const h = d;
|
|
31753
32185
|
const lines = [
|
|
@@ -31757,9 +32189,30 @@ async function heartbeatGet(globalOpts) {
|
|
|
31757
32189
|
kvLine("Status", h.enabled ? success("on") : muted("off")),
|
|
31758
32190
|
kvLine("Interval", `${h.intervalMinutes}m`),
|
|
31759
32191
|
kvLine("Active hours", `${h.activeStart} - ${h.activeEnd}`),
|
|
31760
|
-
kvLine("
|
|
31761
|
-
""
|
|
32192
|
+
kvLine("Backend", h.backend ?? muted("default")),
|
|
32193
|
+
kvLine("Model", h.model ?? muted("default")),
|
|
32194
|
+
kvLine("Thinking", h.thinking ?? muted("off"))
|
|
31762
32195
|
];
|
|
32196
|
+
if (h.fallbacks) {
|
|
32197
|
+
try {
|
|
32198
|
+
const parsed = JSON.parse(h.fallbacks);
|
|
32199
|
+
const chain = parsed.map((f) => `${f.backend}${f.model ? `:${f.model}` : ""}`).join(" \u2192 ");
|
|
32200
|
+
lines.push(kvLine("Fallbacks", chain));
|
|
32201
|
+
} catch {
|
|
32202
|
+
lines.push(kvLine("Fallbacks", h.fallbacks));
|
|
32203
|
+
}
|
|
32204
|
+
} else {
|
|
32205
|
+
lines.push(kvLine("Fallbacks", muted("none")));
|
|
32206
|
+
}
|
|
32207
|
+
if (h.target) {
|
|
32208
|
+
lines.push(kvLine("Target", h.target));
|
|
32209
|
+
}
|
|
32210
|
+
lines.push(kvLine("Last beat", h.lastBeatAt ?? muted("never")));
|
|
32211
|
+
if (!h.enabled) {
|
|
32212
|
+
lines.push("");
|
|
32213
|
+
lines.push(` ${warning("\u26A0 Heartbeat is disabled \u2014 you won't receive proactive alerts.")}`);
|
|
32214
|
+
}
|
|
32215
|
+
lines.push("");
|
|
31763
32216
|
return lines.join("\n");
|
|
31764
32217
|
});
|
|
31765
32218
|
}
|
|
@@ -31781,7 +32234,7 @@ async function heartbeatSet(globalOpts, subcommand, value) {
|
|
|
31781
32234
|
body.enabled = false;
|
|
31782
32235
|
successMsg = "Heartbeat disabled.";
|
|
31783
32236
|
break;
|
|
31784
|
-
case "interval":
|
|
32237
|
+
case "interval": {
|
|
31785
32238
|
if (!value) {
|
|
31786
32239
|
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set interval <value>");
|
|
31787
32240
|
process.exit(1);
|
|
@@ -31797,7 +32250,8 @@ async function heartbeatSet(globalOpts, subcommand, value) {
|
|
|
31797
32250
|
body.intervalMs = ms;
|
|
31798
32251
|
successMsg = `Heartbeat interval: ${ms / 6e4}m`;
|
|
31799
32252
|
break;
|
|
31800
|
-
|
|
32253
|
+
}
|
|
32254
|
+
case "hours": {
|
|
31801
32255
|
if (!value) {
|
|
31802
32256
|
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set hours <start>-<end>");
|
|
31803
32257
|
process.exit(1);
|
|
@@ -31811,8 +32265,71 @@ async function heartbeatSet(globalOpts, subcommand, value) {
|
|
|
31811
32265
|
body.activeEnd = `${hourMatch[2].padStart(2, "0")}:00`;
|
|
31812
32266
|
successMsg = `Active hours: ${body.activeStart} - ${body.activeEnd}`;
|
|
31813
32267
|
break;
|
|
32268
|
+
}
|
|
32269
|
+
case "backend": {
|
|
32270
|
+
if (!value) {
|
|
32271
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set backend <name|default>");
|
|
32272
|
+
process.exit(1);
|
|
32273
|
+
}
|
|
32274
|
+
if (value === "default") {
|
|
32275
|
+
body.backend = null;
|
|
32276
|
+
body.model = null;
|
|
32277
|
+
successMsg = "Heartbeat backend: default (chat-level)";
|
|
32278
|
+
} else {
|
|
32279
|
+
body.backend = value;
|
|
32280
|
+
body.model = null;
|
|
32281
|
+
successMsg = `Heartbeat backend: ${value}`;
|
|
32282
|
+
}
|
|
32283
|
+
break;
|
|
32284
|
+
}
|
|
32285
|
+
case "model": {
|
|
32286
|
+
if (!value) {
|
|
32287
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set model <name|default>");
|
|
32288
|
+
process.exit(1);
|
|
32289
|
+
}
|
|
32290
|
+
if (value === "default") {
|
|
32291
|
+
body.model = null;
|
|
32292
|
+
successMsg = "Heartbeat model: default";
|
|
32293
|
+
} else {
|
|
32294
|
+
body.model = value;
|
|
32295
|
+
successMsg = `Heartbeat model: ${value}`;
|
|
32296
|
+
}
|
|
32297
|
+
break;
|
|
32298
|
+
}
|
|
32299
|
+
case "thinking": {
|
|
32300
|
+
if (!value) {
|
|
32301
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set thinking <off|low|medium|high>");
|
|
32302
|
+
process.exit(1);
|
|
32303
|
+
}
|
|
32304
|
+
const valid = ["off", "low", "medium", "high"];
|
|
32305
|
+
if (!valid.includes(value)) {
|
|
32306
|
+
outputError("INVALID_THINKING", `Valid values: ${valid.join(", ")}`);
|
|
32307
|
+
process.exit(1);
|
|
32308
|
+
}
|
|
32309
|
+
body.thinking = value === "off" ? null : value;
|
|
32310
|
+
successMsg = `Heartbeat thinking: ${value}`;
|
|
32311
|
+
break;
|
|
32312
|
+
}
|
|
32313
|
+
case "fallbacks": {
|
|
32314
|
+
if (!value) {
|
|
32315
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set fallbacks <backend1,backend2|clear>");
|
|
32316
|
+
process.exit(1);
|
|
32317
|
+
}
|
|
32318
|
+
if (value === "clear" || value === "none") {
|
|
32319
|
+
body.fallbacks = null;
|
|
32320
|
+
successMsg = "Heartbeat fallbacks: cleared";
|
|
32321
|
+
} else {
|
|
32322
|
+
const chain = value.split(",").map((s) => {
|
|
32323
|
+
const [backend2, model2] = s.trim().split(":");
|
|
32324
|
+
return model2 ? { backend: backend2, model: model2 } : { backend: backend2 };
|
|
32325
|
+
});
|
|
32326
|
+
body.fallbacks = JSON.stringify(chain);
|
|
32327
|
+
successMsg = `Heartbeat fallbacks: ${chain.map((f) => f.backend).join(" \u2192 ")}`;
|
|
32328
|
+
}
|
|
32329
|
+
break;
|
|
32330
|
+
}
|
|
31814
32331
|
default:
|
|
31815
|
-
outputError("UNKNOWN_SUBCOMMAND", `Unknown: "${subcommand}". Use: on, off, interval, hours`);
|
|
32332
|
+
outputError("UNKNOWN_SUBCOMMAND", `Unknown: "${subcommand}". Use: on, off, interval, hours, backend, model, thinking, fallbacks`);
|
|
31816
32333
|
process.exit(1);
|
|
31817
32334
|
}
|
|
31818
32335
|
const res = await apiPost2("/api/heartbeat/set", body);
|
|
@@ -31840,9 +32357,9 @@ __export(summarizer_exports, {
|
|
|
31840
32357
|
summarizerGet: () => summarizerGet,
|
|
31841
32358
|
summarizerSet: () => summarizerSet
|
|
31842
32359
|
});
|
|
31843
|
-
import { existsSync as
|
|
32360
|
+
import { existsSync as existsSync50 } from "fs";
|
|
31844
32361
|
async function summarizerGet(globalOpts) {
|
|
31845
|
-
if (!
|
|
32362
|
+
if (!existsSync50(DB_PATH)) {
|
|
31846
32363
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31847
32364
|
process.exit(1);
|
|
31848
32365
|
}
|
|
@@ -31886,9 +32403,9 @@ __export(thinking_exports, {
|
|
|
31886
32403
|
thinkingGet: () => thinkingGet,
|
|
31887
32404
|
thinkingSet: () => thinkingSet
|
|
31888
32405
|
});
|
|
31889
|
-
import { existsSync as
|
|
32406
|
+
import { existsSync as existsSync51 } from "fs";
|
|
31890
32407
|
async function thinkingGet(globalOpts) {
|
|
31891
|
-
if (!
|
|
32408
|
+
if (!existsSync51(DB_PATH)) {
|
|
31892
32409
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31893
32410
|
process.exit(1);
|
|
31894
32411
|
}
|
|
@@ -31932,9 +32449,9 @@ __export(chats_exports, {
|
|
|
31932
32449
|
chatsList: () => chatsList,
|
|
31933
32450
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
31934
32451
|
});
|
|
31935
|
-
import { existsSync as
|
|
32452
|
+
import { existsSync as existsSync52 } from "fs";
|
|
31936
32453
|
async function chatsList(_globalOpts) {
|
|
31937
|
-
if (!
|
|
32454
|
+
if (!existsSync52(DB_PATH)) {
|
|
31938
32455
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31939
32456
|
process.exit(1);
|
|
31940
32457
|
}
|
|
@@ -32062,9 +32579,9 @@ var mcps_exports2 = {};
|
|
|
32062
32579
|
__export(mcps_exports2, {
|
|
32063
32580
|
mcpsList: () => mcpsList
|
|
32064
32581
|
});
|
|
32065
|
-
import { existsSync as
|
|
32582
|
+
import { existsSync as existsSync53 } from "fs";
|
|
32066
32583
|
async function mcpsList(_globalOpts) {
|
|
32067
|
-
if (!
|
|
32584
|
+
if (!existsSync53(DB_PATH)) {
|
|
32068
32585
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32069
32586
|
process.exit(1);
|
|
32070
32587
|
}
|
|
@@ -32101,11 +32618,11 @@ __export(chat_exports2, {
|
|
|
32101
32618
|
chatSend: () => chatSend
|
|
32102
32619
|
});
|
|
32103
32620
|
import { request as httpRequest2 } from "http";
|
|
32104
|
-
import { readFileSync as
|
|
32621
|
+
import { readFileSync as readFileSync27, existsSync as existsSync54 } from "fs";
|
|
32105
32622
|
function getToken2() {
|
|
32106
32623
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
32107
32624
|
try {
|
|
32108
|
-
if (
|
|
32625
|
+
if (existsSync54(TOKEN_PATH2)) return readFileSync27(TOKEN_PATH2, "utf-8").trim();
|
|
32109
32626
|
} catch {
|
|
32110
32627
|
}
|
|
32111
32628
|
return null;
|
|
@@ -32785,9 +33302,9 @@ __export(evolve_exports2, {
|
|
|
32785
33302
|
evolveStatus: () => evolveStatus,
|
|
32786
33303
|
evolveUndo: () => evolveUndo
|
|
32787
33304
|
});
|
|
32788
|
-
import { existsSync as
|
|
33305
|
+
import { existsSync as existsSync55 } from "fs";
|
|
32789
33306
|
function ensureDb3() {
|
|
32790
|
-
if (!
|
|
33307
|
+
if (!existsSync55(DB_PATH)) {
|
|
32791
33308
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32792
33309
|
process.exit(1);
|
|
32793
33310
|
}
|
|
@@ -33261,8 +33778,8 @@ var init_optimize2 = __esm({
|
|
|
33261
33778
|
|
|
33262
33779
|
// src/setup.ts
|
|
33263
33780
|
var setup_exports = {};
|
|
33264
|
-
import { existsSync as
|
|
33265
|
-
import { execFileSync as
|
|
33781
|
+
import { existsSync as existsSync56, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
33782
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
33266
33783
|
import { createInterface as createInterface11 } from "readline";
|
|
33267
33784
|
import { join as join35 } from "path";
|
|
33268
33785
|
function divider2() {
|
|
@@ -33326,7 +33843,7 @@ async function setup() {
|
|
|
33326
33843
|
let foundAnyBackend = false;
|
|
33327
33844
|
for (const bk of backends) {
|
|
33328
33845
|
try {
|
|
33329
|
-
const path =
|
|
33846
|
+
const path = execFileSync6("which", [bk.cmd], { encoding: "utf-8" }).trim();
|
|
33330
33847
|
console.log(green(` ${bk.name} CLI found: ${path}`));
|
|
33331
33848
|
foundAnyBackend = true;
|
|
33332
33849
|
} catch {
|
|
@@ -33339,21 +33856,21 @@ async function setup() {
|
|
|
33339
33856
|
}
|
|
33340
33857
|
console.log("");
|
|
33341
33858
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
33342
|
-
if (!
|
|
33859
|
+
if (!existsSync56(dir)) mkdirSync18(dir, { recursive: true });
|
|
33343
33860
|
}
|
|
33344
33861
|
const env = {};
|
|
33345
|
-
const envSource =
|
|
33862
|
+
const envSource = existsSync56(ENV_PATH) ? ENV_PATH : existsSync56(".env") ? ".env" : null;
|
|
33346
33863
|
if (envSource) {
|
|
33347
33864
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
33348
33865
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
33349
|
-
const existing =
|
|
33866
|
+
const existing = readFileSync28(envSource, "utf-8");
|
|
33350
33867
|
for (const line of existing.split("\n")) {
|
|
33351
33868
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
33352
33869
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
33353
33870
|
}
|
|
33354
33871
|
}
|
|
33355
33872
|
const cwdDb = join35(process.cwd(), "cc-claw.db");
|
|
33356
|
-
if (
|
|
33873
|
+
if (existsSync56(cwdDb) && !existsSync56(DB_PATH)) {
|
|
33357
33874
|
const { size } = statSync12(cwdDb);
|
|
33358
33875
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
33359
33876
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
@@ -34073,7 +34590,7 @@ heartbeat.command("get").description("Show heartbeat status and config").action(
|
|
|
34073
34590
|
const { heartbeatGet: heartbeatGet2 } = await Promise.resolve().then(() => (init_heartbeat3(), heartbeat_exports));
|
|
34074
34591
|
await heartbeatGet2(program.opts());
|
|
34075
34592
|
});
|
|
34076
|
-
heartbeat.command("set <subcommand> [value]").description("Configure heartbeat (on/off/interval
|
|
34593
|
+
heartbeat.command("set <subcommand> [value]").description("Configure heartbeat (on/off/interval/hours/backend/model/thinking/fallbacks)").action(async (subcommand, value) => {
|
|
34077
34594
|
const { heartbeatSet: heartbeatSet2 } = await Promise.resolve().then(() => (init_heartbeat3(), heartbeat_exports));
|
|
34078
34595
|
await heartbeatSet2(program.opts(), subcommand, value);
|
|
34079
34596
|
});
|
|
@@ -34246,8 +34763,8 @@ async function run(argv = process.argv) {
|
|
|
34246
34763
|
if (argv.includes("--version") || argv.includes("-V")) {
|
|
34247
34764
|
console.log(VERSION);
|
|
34248
34765
|
try {
|
|
34249
|
-
const { execFileSync:
|
|
34250
|
-
const latest =
|
|
34766
|
+
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
34767
|
+
const latest = execFileSync7("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
34251
34768
|
if (latest && latest !== VERSION) {
|
|
34252
34769
|
console.log(`
|
|
34253
34770
|
Update available: v${latest} (current: v${VERSION})`);
|