cc-claw 0.20.7 → 0.20.8
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 +983 -467
- 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.8" : (() => {
|
|
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,86 @@ 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}`
|
|
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
|
+
const toggleRow = [
|
|
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" }
|
|
18750
19096
|
];
|
|
19097
|
+
buttons.push(toggleRow);
|
|
18751
19098
|
const presets = [15, 30, 60, 120];
|
|
18752
19099
|
const intervalRow = presets.map((m) => ({
|
|
18753
19100
|
label: `${m === intervalMin ? "\u2713 " : ""}${m} min`,
|
|
18754
19101
|
data: `hb:interval:${m}`,
|
|
18755
19102
|
...m === intervalMin ? { style: "primary" } : {}
|
|
18756
19103
|
}));
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
|
|
18760
|
-
|
|
18761
|
-
|
|
18762
|
-
|
|
18763
|
-
|
|
18764
|
-
|
|
19104
|
+
buttons.push(intervalRow);
|
|
19105
|
+
const available = getAvailableBackendIds();
|
|
19106
|
+
const backendRow = available.map((bid) => ({
|
|
19107
|
+
label: `${config2?.backend === bid ? "\u2713 " : ""}${capitalize(bid)}`,
|
|
19108
|
+
data: `hb:backend:${bid}`,
|
|
19109
|
+
...config2?.backend === bid ? { style: "primary" } : {}
|
|
19110
|
+
}));
|
|
19111
|
+
if (config2?.backend) {
|
|
19112
|
+
backendRow.push({ label: "Reset", data: "hb:backend:default" });
|
|
19113
|
+
}
|
|
19114
|
+
buttons.push(backendRow);
|
|
19115
|
+
const targetBackend = config2?.backend ?? getBackend(chatId) ?? "claude";
|
|
19116
|
+
try {
|
|
19117
|
+
const adapter = getAdapter(targetBackend);
|
|
19118
|
+
const models = Object.entries(adapter.availableModels);
|
|
19119
|
+
if (models.length > 0) {
|
|
19120
|
+
const modelRow = models.slice(0, 4).map(([key, info]) => ({
|
|
19121
|
+
label: `${config2?.model === key ? "\u2713 " : ""}${info.label}`,
|
|
19122
|
+
data: `hb:model:${key}`,
|
|
19123
|
+
...config2?.model === key ? { style: "primary" } : {}
|
|
19124
|
+
}));
|
|
19125
|
+
if (config2?.model) {
|
|
19126
|
+
modelRow.push({ label: "Reset", data: "hb:model:default" });
|
|
19127
|
+
}
|
|
19128
|
+
buttons.push(modelRow);
|
|
19129
|
+
}
|
|
19130
|
+
} catch {
|
|
19131
|
+
}
|
|
19132
|
+
if (fallbacks.length > 0) {
|
|
19133
|
+
const fbRow = [
|
|
19134
|
+
{ label: `\u{1F504} Fallbacks: ${fallbacks.map((f) => capitalize(f.backend)).join(" \u2192 ")}`, data: "hb:noop" },
|
|
19135
|
+
{ label: "Clear", data: "hb:fb:clear", style: "danger" }
|
|
19136
|
+
];
|
|
19137
|
+
buttons.push(fbRow);
|
|
19138
|
+
}
|
|
19139
|
+
const fbAddRow = available.filter((bid) => !fallbacks.some((f) => f.backend === bid) && bid !== config2?.backend).slice(0, 4).map((bid) => ({
|
|
19140
|
+
label: `+ ${capitalize(bid)}`,
|
|
19141
|
+
data: `hb:fb:add:${bid}`
|
|
19142
|
+
}));
|
|
19143
|
+
if (fbAddRow.length > 0) {
|
|
19144
|
+
buttons.push(fbAddRow);
|
|
19145
|
+
}
|
|
19146
|
+
const configRow = [
|
|
19147
|
+
{ label: `\u{1F4AD} Thinking: ${thinkingDisplay}`, data: "hb:thinking" }
|
|
19148
|
+
];
|
|
19149
|
+
if (watches.length > 0) {
|
|
19150
|
+
configRow.push({ label: `\u{1F441} Watches (${watches.length})`, data: "hb:watches" });
|
|
19151
|
+
} else {
|
|
19152
|
+
configRow.push({ label: "+ Add Watch", data: "hb:addwatch" });
|
|
19153
|
+
}
|
|
19154
|
+
buttons.push(configRow);
|
|
19155
|
+
buttons.push([{ label: `Active Hours: ${activeStart}-${activeEnd} (use /heartbeat hours)`, data: "hb:noop" }]);
|
|
19156
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
18765
19157
|
}
|
|
18766
19158
|
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
18767
19159
|
const memories = listMemories();
|
|
@@ -19403,13 +19795,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
19403
19795
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
19404
19796
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
19405
19797
|
if (current === "frozen") {
|
|
19406
|
-
const { readFileSync:
|
|
19798
|
+
const { readFileSync: readFileSync29, existsSync: existsSync57 } = await import("fs");
|
|
19407
19799
|
const { join: join36 } = await import("path");
|
|
19408
19800
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
19409
19801
|
const soulPath = join36(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
19410
19802
|
const userPath = join36(CC_CLAW_HOME3, "identity/USER.md");
|
|
19411
|
-
const soul =
|
|
19412
|
-
const user =
|
|
19803
|
+
const soul = existsSync57(soulPath) ? readFileSync29(soulPath, "utf-8") : "";
|
|
19804
|
+
const user = existsSync57(userPath) ? readFileSync29(userPath, "utf-8") : "";
|
|
19413
19805
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
19414
19806
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
19415
19807
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -19486,18 +19878,18 @@ var init_evolve2 = __esm({
|
|
|
19486
19878
|
});
|
|
19487
19879
|
|
|
19488
19880
|
// src/optimizer/identity-audit.ts
|
|
19489
|
-
import { readFileSync as
|
|
19881
|
+
import { readFileSync as readFileSync11, existsSync as existsSync20, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
19490
19882
|
import { join as join21 } from "path";
|
|
19491
19883
|
function readIdentityFile2(filename) {
|
|
19492
19884
|
try {
|
|
19493
|
-
return
|
|
19885
|
+
return readFileSync11(join21(IDENTITY_PATH, filename), "utf-8");
|
|
19494
19886
|
} catch {
|
|
19495
19887
|
return "";
|
|
19496
19888
|
}
|
|
19497
19889
|
}
|
|
19498
19890
|
function getMtime(filepath) {
|
|
19499
19891
|
try {
|
|
19500
|
-
return
|
|
19892
|
+
return statSync8(filepath).mtime.toISOString();
|
|
19501
19893
|
} catch {
|
|
19502
19894
|
return "unknown";
|
|
19503
19895
|
}
|
|
@@ -19506,7 +19898,7 @@ function findBackupFiles() {
|
|
|
19506
19898
|
const backups = [];
|
|
19507
19899
|
const dirs = [IDENTITY_PATH];
|
|
19508
19900
|
const contextDir = join21(IDENTITY_PATH, "..", "workspace", "context");
|
|
19509
|
-
if (
|
|
19901
|
+
if (existsSync20(contextDir)) dirs.push(contextDir);
|
|
19510
19902
|
for (const dir of dirs) {
|
|
19511
19903
|
try {
|
|
19512
19904
|
for (const entry of readdirSync10(dir)) {
|
|
@@ -19643,7 +20035,7 @@ var init_identity_audit = __esm({
|
|
|
19643
20035
|
});
|
|
19644
20036
|
|
|
19645
20037
|
// src/optimizer/skill-audit.ts
|
|
19646
|
-
import { readFileSync as
|
|
20038
|
+
import { readFileSync as readFileSync12, existsSync as existsSync21 } from "fs";
|
|
19647
20039
|
import { join as join22, basename as basename3 } from "path";
|
|
19648
20040
|
function parseFrontmatter3(content) {
|
|
19649
20041
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -19684,7 +20076,7 @@ function detectDependentSkills(content) {
|
|
|
19684
20076
|
return Array.from(deps);
|
|
19685
20077
|
}
|
|
19686
20078
|
function computeSkillStats(skillPath) {
|
|
19687
|
-
const content =
|
|
20079
|
+
const content = readFileSync12(skillPath, "utf-8");
|
|
19688
20080
|
const lines = content.split("\n");
|
|
19689
20081
|
return {
|
|
19690
20082
|
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join22(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
@@ -19711,9 +20103,9 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
19711
20103
|
join22(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
19712
20104
|
];
|
|
19713
20105
|
for (const candidate of candidates) {
|
|
19714
|
-
if (
|
|
20106
|
+
if (existsSync21(candidate)) {
|
|
19715
20107
|
try {
|
|
19716
|
-
const content =
|
|
20108
|
+
const content = readFileSync12(candidate, "utf-8");
|
|
19717
20109
|
results.push({
|
|
19718
20110
|
name,
|
|
19719
20111
|
content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
|
|
@@ -19828,7 +20220,7 @@ __export(analyze_exports2, {
|
|
|
19828
20220
|
});
|
|
19829
20221
|
import { spawn as spawn7 } from "child_process";
|
|
19830
20222
|
import { createInterface as createInterface7 } from "readline";
|
|
19831
|
-
import { readFileSync as
|
|
20223
|
+
import { readFileSync as readFileSync13, existsSync as existsSync22, readdirSync as readdirSync12 } from "fs";
|
|
19832
20224
|
import { join as join23 } from "path";
|
|
19833
20225
|
import { homedir as homedir7 } from "os";
|
|
19834
20226
|
function parseOptimizeOutput(raw, validAreas) {
|
|
@@ -19959,7 +20351,7 @@ function getModelDisplayInfo(chatId) {
|
|
|
19959
20351
|
}
|
|
19960
20352
|
function readIdentityFile3(filename) {
|
|
19961
20353
|
try {
|
|
19962
|
-
return
|
|
20354
|
+
return readFileSync13(join23(IDENTITY_PATH, filename), "utf-8");
|
|
19963
20355
|
} catch {
|
|
19964
20356
|
return "";
|
|
19965
20357
|
}
|
|
@@ -19967,12 +20359,12 @@ function readIdentityFile3(filename) {
|
|
|
19967
20359
|
function loadContextFiles2() {
|
|
19968
20360
|
const contextDir = join23(homedir7(), ".cc-claw", "workspace", "context");
|
|
19969
20361
|
const results = [];
|
|
19970
|
-
if (!
|
|
20362
|
+
if (!existsSync22(contextDir)) return results;
|
|
19971
20363
|
try {
|
|
19972
20364
|
for (const entry of readdirSync12(contextDir)) {
|
|
19973
20365
|
if (!entry.endsWith(".md")) continue;
|
|
19974
20366
|
try {
|
|
19975
|
-
const content =
|
|
20367
|
+
const content = readFileSync13(join23(contextDir, entry), "utf-8");
|
|
19976
20368
|
results.push({ name: entry, content });
|
|
19977
20369
|
} catch {
|
|
19978
20370
|
}
|
|
@@ -20024,7 +20416,7 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20024
20416
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
20025
20417
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
20026
20418
|
const ccClawSkillsDir = join23(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20027
|
-
const skillContent =
|
|
20419
|
+
const skillContent = readFileSync13(skillPath, "utf-8");
|
|
20028
20420
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
20029
20421
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
20030
20422
|
const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
|
|
@@ -20040,14 +20432,14 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20040
20432
|
function listCcClawSkills() {
|
|
20041
20433
|
const skillsDir = join23(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20042
20434
|
const entries = [];
|
|
20043
|
-
if (!
|
|
20435
|
+
if (!existsSync22(skillsDir)) return entries;
|
|
20044
20436
|
try {
|
|
20045
20437
|
for (const dir of readdirSync12(skillsDir)) {
|
|
20046
20438
|
const skillFile = join23(skillsDir, dir, "SKILL.md");
|
|
20047
|
-
if (!
|
|
20439
|
+
if (!existsSync22(skillFile)) continue;
|
|
20048
20440
|
let description = "skill";
|
|
20049
20441
|
try {
|
|
20050
|
-
const content =
|
|
20442
|
+
const content = readFileSync13(skillFile, "utf-8");
|
|
20051
20443
|
const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
|
|
20052
20444
|
if (descMatch) description = descMatch[1].trim().slice(0, 60);
|
|
20053
20445
|
} catch {
|
|
@@ -20367,7 +20759,7 @@ var init_ui2 = __esm({
|
|
|
20367
20759
|
});
|
|
20368
20760
|
|
|
20369
20761
|
// src/router/optimize.ts
|
|
20370
|
-
import { readFileSync as
|
|
20762
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync23, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
20371
20763
|
import { join as join24, dirname as dirname4 } from "path";
|
|
20372
20764
|
import { homedir as homedir8 } from "os";
|
|
20373
20765
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
@@ -20638,13 +21030,13 @@ async function applyFinding(chatId, channel, index) {
|
|
|
20638
21030
|
await showFinding(chatId, channel, index + 1);
|
|
20639
21031
|
return;
|
|
20640
21032
|
}
|
|
20641
|
-
if (!
|
|
21033
|
+
if (!existsSync23(targetPath)) {
|
|
20642
21034
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
20643
21035
|
session2.skipped.push(index);
|
|
20644
21036
|
await showFinding(chatId, channel, index + 1);
|
|
20645
21037
|
return;
|
|
20646
21038
|
}
|
|
20647
|
-
const original =
|
|
21039
|
+
const original = readFileSync14(targetPath, "utf-8");
|
|
20648
21040
|
const backupPath = targetPath + `.bak.${Date.now()}`;
|
|
20649
21041
|
writeFileSync7(backupPath, original, "utf-8");
|
|
20650
21042
|
pruneBackups2(targetPath);
|
|
@@ -22082,6 +22474,92 @@ async function handleHeartbeatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22082
22474
|
await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
|
|
22083
22475
|
}
|
|
22084
22476
|
}
|
|
22477
|
+
async function handleWatchCommand(chatId, commandArgs, msg, channel) {
|
|
22478
|
+
if (!commandArgs) {
|
|
22479
|
+
const watches = getActiveWatches(chatId);
|
|
22480
|
+
if (watches.length === 0) {
|
|
22481
|
+
await channel.sendText(chatId, [
|
|
22482
|
+
"No active watches.",
|
|
22483
|
+
"",
|
|
22484
|
+
"Watches are awareness tasks that the heartbeat",
|
|
22485
|
+
"AI checks periodically using its tools.",
|
|
22486
|
+
"",
|
|
22487
|
+
"Usage:",
|
|
22488
|
+
" /watch add Check my email for urgent messages",
|
|
22489
|
+
" /watch add Monitor Ollama server health",
|
|
22490
|
+
" /watch add Check calendar for upcoming meetings",
|
|
22491
|
+
" /watch list",
|
|
22492
|
+
" /watch remove <id>"
|
|
22493
|
+
].join("\n"), { parseMode: "plain" });
|
|
22494
|
+
} else {
|
|
22495
|
+
const lines = ["Active watches:", ""];
|
|
22496
|
+
for (const w of watches) {
|
|
22497
|
+
const expiry = w.expiresAt ? ` (until ${formatLocalDateTime(w.expiresAt)})` : "";
|
|
22498
|
+
lines.push(`#${w.id}: ${w.description}${expiry}`);
|
|
22499
|
+
}
|
|
22500
|
+
lines.push("", "Use /watch remove <id> to remove a watch.");
|
|
22501
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
22502
|
+
}
|
|
22503
|
+
return;
|
|
22504
|
+
}
|
|
22505
|
+
const parts = commandArgs.trim().split(/\s+/);
|
|
22506
|
+
const action = parts[0].toLowerCase();
|
|
22507
|
+
const rest = parts.slice(1).join(" ");
|
|
22508
|
+
switch (action) {
|
|
22509
|
+
case "add": {
|
|
22510
|
+
if (!rest) {
|
|
22511
|
+
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" });
|
|
22512
|
+
return;
|
|
22513
|
+
}
|
|
22514
|
+
let expiresAt;
|
|
22515
|
+
const durationMatch = rest.match(/\s+for\s+(\d+)([hdw])$/i);
|
|
22516
|
+
let description = rest;
|
|
22517
|
+
if (durationMatch) {
|
|
22518
|
+
description = rest.slice(0, durationMatch.index).trim();
|
|
22519
|
+
const amount = parseInt(durationMatch[1], 10);
|
|
22520
|
+
const unit = durationMatch[2].toLowerCase();
|
|
22521
|
+
const ms = unit === "h" ? amount * 36e5 : unit === "d" ? amount * 864e5 : amount * 6048e5;
|
|
22522
|
+
expiresAt = new Date(Date.now() + ms).toISOString();
|
|
22523
|
+
}
|
|
22524
|
+
const id = addHeartbeatWatch(chatId, description, expiresAt);
|
|
22525
|
+
const expiryMsg = expiresAt ? ` (expires: ${formatLocalDateTime(expiresAt)})` : "";
|
|
22526
|
+
await channel.sendText(chatId, `\u2705 Watch #${id} added${expiryMsg}:
|
|
22527
|
+
${description}`, { parseMode: "plain" });
|
|
22528
|
+
return;
|
|
22529
|
+
}
|
|
22530
|
+
case "remove":
|
|
22531
|
+
case "delete": {
|
|
22532
|
+
const id = parseInt(rest, 10);
|
|
22533
|
+
if (isNaN(id)) {
|
|
22534
|
+
await channel.sendText(chatId, "Usage: /watch remove <id>", { parseMode: "plain" });
|
|
22535
|
+
return;
|
|
22536
|
+
}
|
|
22537
|
+
const removed = removeHeartbeatWatch(id);
|
|
22538
|
+
await channel.sendText(chatId, removed ? `\u2705 Watch #${id} removed.` : `Watch #${id} not found.`, { parseMode: "plain" });
|
|
22539
|
+
return;
|
|
22540
|
+
}
|
|
22541
|
+
case "list": {
|
|
22542
|
+
const watches = getActiveWatches(chatId);
|
|
22543
|
+
if (watches.length === 0) {
|
|
22544
|
+
await channel.sendText(chatId, "No active watches.", { parseMode: "plain" });
|
|
22545
|
+
} else {
|
|
22546
|
+
const lines = ["Active watches:", ""];
|
|
22547
|
+
for (const w of watches) {
|
|
22548
|
+
const expiry = w.expiresAt ? ` (until ${formatLocalDateTime(w.expiresAt)})` : "";
|
|
22549
|
+
lines.push(`#${w.id}: ${w.description}${expiry}`);
|
|
22550
|
+
}
|
|
22551
|
+
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
22552
|
+
}
|
|
22553
|
+
return;
|
|
22554
|
+
}
|
|
22555
|
+
default:
|
|
22556
|
+
const watchDesc = commandArgs.trim();
|
|
22557
|
+
const watchId = addHeartbeatWatch(chatId, watchDesc);
|
|
22558
|
+
await channel.sendText(chatId, `\u2705 Watch #${watchId} added:
|
|
22559
|
+
${watchDesc}`, { parseMode: "plain" });
|
|
22560
|
+
return;
|
|
22561
|
+
}
|
|
22562
|
+
}
|
|
22085
22563
|
async function handleAgentsCommand(chatId, commandArgs, msg, channel) {
|
|
22086
22564
|
if (commandArgs?.startsWith("mode")) {
|
|
22087
22565
|
const modeArg = commandArgs.slice(5).trim().toLowerCase();
|
|
@@ -22644,6 +23122,9 @@ async function handleCommand(msg, channel) {
|
|
|
22644
23122
|
case "heartbeat":
|
|
22645
23123
|
await handleHeartbeatCommand(chatId, commandArgs, msg, channel);
|
|
22646
23124
|
break;
|
|
23125
|
+
case "watch":
|
|
23126
|
+
await handleWatchCommand(chatId, commandArgs, msg, channel);
|
|
23127
|
+
break;
|
|
22647
23128
|
case "agents":
|
|
22648
23129
|
await handleAgentsCommand(chatId, commandArgs, msg, channel);
|
|
22649
23130
|
break;
|
|
@@ -23542,8 +24023,8 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
23542
24023
|
if (action === "toggle") {
|
|
23543
24024
|
const backend2 = parts[2];
|
|
23544
24025
|
const model2 = parts[3];
|
|
23545
|
-
const { getAdapter:
|
|
23546
|
-
const toggleAdapter =
|
|
24026
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24027
|
+
const toggleAdapter = getAdapter5(backend2);
|
|
23547
24028
|
const label2 = toggleAdapter.availableModels[model2]?.label ?? model2;
|
|
23548
24029
|
const { toggleParticipant: toggleParticipant2, buildSelectKeyboard: buildSelectKeyboard2, hasPendingCouncil: hasPendingCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports));
|
|
23549
24030
|
if (!hasPendingCouncil2(chatId)) {
|
|
@@ -23793,11 +24274,12 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
23793
24274
|
if (rest === "on") {
|
|
23794
24275
|
setHeartbeatConfig(chatId, { enabled: true });
|
|
23795
24276
|
startHeartbeatForChat(chatId);
|
|
23796
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
24277
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
23797
24278
|
} else if (rest === "off") {
|
|
23798
24279
|
setHeartbeatConfig(chatId, { enabled: false });
|
|
23799
24280
|
stopHeartbeatForChat(chatId);
|
|
23800
|
-
await
|
|
24281
|
+
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" });
|
|
24282
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
23801
24283
|
} else if (rest.startsWith("interval:")) {
|
|
23802
24284
|
const min = parseInt(rest.slice(9), 10);
|
|
23803
24285
|
if (isNaN(min) || min < 1) return;
|
|
@@ -23806,7 +24288,55 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
23806
24288
|
stopHeartbeatForChat(chatId);
|
|
23807
24289
|
const hbConf = getHeartbeatConfig(chatId);
|
|
23808
24290
|
if (hbConf?.enabled) startHeartbeatForChat(chatId);
|
|
23809
|
-
await sendHeartbeatKeyboard(chatId, channel);
|
|
24291
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24292
|
+
} else if (rest.startsWith("backend:")) {
|
|
24293
|
+
const bid = rest.slice(8);
|
|
24294
|
+
if (bid === "default") {
|
|
24295
|
+
updateHeartbeatField(chatId, "backend", null);
|
|
24296
|
+
updateHeartbeatField(chatId, "model", null);
|
|
24297
|
+
} else {
|
|
24298
|
+
updateHeartbeatField(chatId, "backend", bid);
|
|
24299
|
+
updateHeartbeatField(chatId, "model", null);
|
|
24300
|
+
}
|
|
24301
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24302
|
+
} else if (rest.startsWith("model:")) {
|
|
24303
|
+
const model2 = rest.slice(6);
|
|
24304
|
+
if (model2 === "default") {
|
|
24305
|
+
updateHeartbeatField(chatId, "model", null);
|
|
24306
|
+
} else {
|
|
24307
|
+
updateHeartbeatField(chatId, "model", model2);
|
|
24308
|
+
}
|
|
24309
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24310
|
+
} else if (rest === "thinking") {
|
|
24311
|
+
const config2 = getHeartbeatConfig(chatId);
|
|
24312
|
+
const current = config2?.thinking ?? "off";
|
|
24313
|
+
const cycle = ["off", "low", "medium", "high"];
|
|
24314
|
+
const next = cycle[(cycle.indexOf(current) + 1) % cycle.length];
|
|
24315
|
+
updateHeartbeatField(chatId, "thinking", next === "off" ? null : next);
|
|
24316
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24317
|
+
} else if (rest === "run") {
|
|
24318
|
+
await channel.sendText(chatId, "\u23F3 Running heartbeat check...", { parseMode: "plain" });
|
|
24319
|
+
try {
|
|
24320
|
+
await runHeartbeatNow(chatId);
|
|
24321
|
+
await channel.sendText(chatId, "\u2705 Heartbeat check complete.", { parseMode: "plain" });
|
|
24322
|
+
} catch (err) {
|
|
24323
|
+
await channel.sendText(chatId, `\u274C Heartbeat failed: ${err instanceof Error ? err.message : String(err)}`, { parseMode: "plain" });
|
|
24324
|
+
}
|
|
24325
|
+
} else if (rest === "watches" || rest === "addwatch") {
|
|
24326
|
+
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" });
|
|
24327
|
+
} else if (rest.startsWith("fb:add:")) {
|
|
24328
|
+
const bid = rest.slice(7);
|
|
24329
|
+
const config2 = getHeartbeatConfig(chatId);
|
|
24330
|
+
const current = parseHeartbeatFallbacks(config2?.fallbacks ?? null);
|
|
24331
|
+
if (!current.some((f) => f.backend === bid)) {
|
|
24332
|
+
current.push({ backend: bid });
|
|
24333
|
+
updateHeartbeatField(chatId, "fallbacks", JSON.stringify(current));
|
|
24334
|
+
}
|
|
24335
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24336
|
+
} else if (rest === "fb:clear") {
|
|
24337
|
+
updateHeartbeatField(chatId, "fallbacks", null);
|
|
24338
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
24339
|
+
} else if (rest === "noop") {
|
|
23810
24340
|
}
|
|
23811
24341
|
return;
|
|
23812
24342
|
} else if (data.startsWith("newchat:")) {
|
|
@@ -25636,18 +26166,18 @@ var init_wrap_backend = __esm({
|
|
|
25636
26166
|
});
|
|
25637
26167
|
|
|
25638
26168
|
// src/agents/runners/config-loader.ts
|
|
25639
|
-
import { readFileSync as
|
|
26169
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
25640
26170
|
import { join as join27 } from "path";
|
|
25641
|
-
import { execFileSync as
|
|
26171
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
25642
26172
|
function resolveExecutable2(config2) {
|
|
25643
|
-
if (
|
|
26173
|
+
if (existsSync24(config2.executable)) return config2.executable;
|
|
25644
26174
|
try {
|
|
25645
|
-
return
|
|
26175
|
+
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
25646
26176
|
} catch {
|
|
25647
26177
|
}
|
|
25648
26178
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
25649
26179
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
25650
|
-
if (
|
|
26180
|
+
if (existsSync24(resolved)) return resolved;
|
|
25651
26181
|
}
|
|
25652
26182
|
return config2.executable;
|
|
25653
26183
|
}
|
|
@@ -25778,7 +26308,7 @@ function configToRunner(config2) {
|
|
|
25778
26308
|
}
|
|
25779
26309
|
function loadRunnerConfig(filePath) {
|
|
25780
26310
|
try {
|
|
25781
|
-
const content =
|
|
26311
|
+
const content = readFileSync15(filePath, "utf-8");
|
|
25782
26312
|
return JSON.parse(content);
|
|
25783
26313
|
} catch (err) {
|
|
25784
26314
|
warn(`[runners] Failed to load config ${filePath}: ${err}`);
|
|
@@ -25786,7 +26316,7 @@ function loadRunnerConfig(filePath) {
|
|
|
25786
26316
|
}
|
|
25787
26317
|
}
|
|
25788
26318
|
function loadAllRunnerConfigs() {
|
|
25789
|
-
if (!
|
|
26319
|
+
if (!existsSync24(RUNNERS_PATH)) {
|
|
25790
26320
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
25791
26321
|
return [];
|
|
25792
26322
|
}
|
|
@@ -25814,9 +26344,9 @@ function registerConfigRunners() {
|
|
|
25814
26344
|
return count;
|
|
25815
26345
|
}
|
|
25816
26346
|
function watchRunnerConfigs(onChange) {
|
|
25817
|
-
if (!
|
|
26347
|
+
if (!existsSync24(RUNNERS_PATH)) return;
|
|
25818
26348
|
for (const prev of watchedFiles) {
|
|
25819
|
-
if (!
|
|
26349
|
+
if (!existsSync24(prev)) {
|
|
25820
26350
|
unwatchFile(prev);
|
|
25821
26351
|
watchedFiles.delete(prev);
|
|
25822
26352
|
}
|
|
@@ -26348,6 +26878,7 @@ var init_telegram2 = __esm({
|
|
|
26348
26878
|
{ command: "model_signature", description: "Toggle model+thinking signature on responses" },
|
|
26349
26879
|
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
26350
26880
|
{ command: "heartbeat", description: "Configure proactive heartbeat" },
|
|
26881
|
+
{ command: "watch", description: "Manage heartbeat awareness tasks" },
|
|
26351
26882
|
{ command: "chats", description: "Manage multi-chat aliases" },
|
|
26352
26883
|
{ command: "intent", description: "Test intent classifier on a message" },
|
|
26353
26884
|
{ command: "evolve", description: "Self-learning & evolution controls" },
|
|
@@ -26911,19 +27442,19 @@ var init_telegram2 = __esm({
|
|
|
26911
27442
|
});
|
|
26912
27443
|
|
|
26913
27444
|
// src/skills/bootstrap.ts
|
|
26914
|
-
import { existsSync as
|
|
27445
|
+
import { existsSync as existsSync25 } from "fs";
|
|
26915
27446
|
import { readdir as readdir5, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
|
|
26916
27447
|
import { join as join28, dirname as dirname5 } from "path";
|
|
26917
27448
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
26918
27449
|
async function copyAgentManifestSkills() {
|
|
26919
|
-
if (!
|
|
27450
|
+
if (!existsSync25(PKG_SKILLS)) return;
|
|
26920
27451
|
try {
|
|
26921
27452
|
const entries = await readdir5(PKG_SKILLS, { withFileTypes: true });
|
|
26922
27453
|
for (const entry of entries) {
|
|
26923
27454
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
26924
27455
|
const src = join28(PKG_SKILLS, entry.name);
|
|
26925
27456
|
const dest = join28(SKILLS_PATH, entry.name);
|
|
26926
|
-
if (
|
|
27457
|
+
if (existsSync25(dest)) continue;
|
|
26927
27458
|
await copyFile(src, dest);
|
|
26928
27459
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
26929
27460
|
}
|
|
@@ -26934,7 +27465,7 @@ async function copyAgentManifestSkills() {
|
|
|
26934
27465
|
async function bootstrapSkills() {
|
|
26935
27466
|
await copyAgentManifestSkills();
|
|
26936
27467
|
const usmDir = join28(SKILLS_PATH, USM_DIR_NAME);
|
|
26937
|
-
if (
|
|
27468
|
+
if (existsSync25(usmDir)) return;
|
|
26938
27469
|
try {
|
|
26939
27470
|
const entries = await readdir5(SKILLS_PATH);
|
|
26940
27471
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
@@ -26958,7 +27489,7 @@ async function bootstrapSkills() {
|
|
|
26958
27489
|
}
|
|
26959
27490
|
async function patchUsmForCcClaw(usmDir) {
|
|
26960
27491
|
const skillPath = join28(usmDir, "SKILL.md");
|
|
26961
|
-
if (!
|
|
27492
|
+
if (!existsSync25(skillPath)) return;
|
|
26962
27493
|
try {
|
|
26963
27494
|
let content = await readFile8(skillPath, "utf-8");
|
|
26964
27495
|
let patched = false;
|
|
@@ -27226,13 +27757,13 @@ __export(ai_skill_exports, {
|
|
|
27226
27757
|
generateAiSkill: () => generateAiSkill,
|
|
27227
27758
|
installAiSkill: () => installAiSkill
|
|
27228
27759
|
});
|
|
27229
|
-
import { existsSync as
|
|
27760
|
+
import { existsSync as existsSync26, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
27230
27761
|
import { join as join29 } from "path";
|
|
27231
27762
|
import { homedir as homedir9 } from "os";
|
|
27232
27763
|
function generateAiSkill() {
|
|
27233
27764
|
const version = VERSION;
|
|
27234
27765
|
let systemState = "";
|
|
27235
|
-
if (
|
|
27766
|
+
if (existsSync26(DB_PATH)) {
|
|
27236
27767
|
try {
|
|
27237
27768
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
27238
27769
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -27513,7 +28044,7 @@ cc-claw voice set off # Disable voice responses
|
|
|
27513
28044
|
# Requires GEMINI_API_KEY in ~/.cc-claw/.env
|
|
27514
28045
|
\`\`\`
|
|
27515
28046
|
|
|
27516
|
-
### Heartbeat
|
|
28047
|
+
### Heartbeat & Watches (Proactive Monitoring)
|
|
27517
28048
|
\`\`\`bash
|
|
27518
28049
|
cc-claw heartbeat get --json # Show heartbeat config
|
|
27519
28050
|
cc-claw heartbeat set on # Enable proactive awareness
|
|
@@ -27522,6 +28053,20 @@ cc-claw heartbeat set interval 30m # Set check interval
|
|
|
27522
28053
|
cc-claw heartbeat set hours 9-22 # Set active hours
|
|
27523
28054
|
\`\`\`
|
|
27524
28055
|
|
|
28056
|
+
**Heartbeat** runs a periodic awareness cycle that checks system health and user-defined watches.
|
|
28057
|
+
It supports delivery parity: custom backend, model, fallback chain, thinking level, and delivery target \u2014 all configured via the interactive \`/heartbeat\` keyboard in Telegram.
|
|
28058
|
+
When nothing needs attention, the AI responds with \`HEARTBEAT_OK\` and no message is delivered.
|
|
28059
|
+
|
|
28060
|
+
**Watches** are AI-executable awareness tasks checked during each heartbeat cycle:
|
|
28061
|
+
\`\`\`
|
|
28062
|
+
/watch add Check my email for urgent messages # Persistent watch
|
|
28063
|
+
/watch add Monitor Ollama health for 24h # Time-limited watch
|
|
28064
|
+
/watch list # List active watches
|
|
28065
|
+
/watch remove <id> # Remove a watch
|
|
28066
|
+
\`\`\`
|
|
28067
|
+
|
|
28068
|
+
**\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.
|
|
28069
|
+
|
|
27525
28070
|
### Summarizer
|
|
27526
28071
|
\`\`\`bash
|
|
27527
28072
|
cc-claw summarizer get --json # Current summarizer config
|
|
@@ -27692,7 +28237,7 @@ var index_exports = {};
|
|
|
27692
28237
|
__export(index_exports, {
|
|
27693
28238
|
main: () => main
|
|
27694
28239
|
});
|
|
27695
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
28240
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync27, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync17 } from "fs";
|
|
27696
28241
|
import { join as join30 } from "path";
|
|
27697
28242
|
import dotenv from "dotenv";
|
|
27698
28243
|
function migrateLayout() {
|
|
@@ -27706,7 +28251,7 @@ function migrateLayout() {
|
|
|
27706
28251
|
[join30(CC_CLAW_HOME, "cc-claw.error.log.1"), join30(LOGS_PATH, "cc-claw.error.log.1")]
|
|
27707
28252
|
];
|
|
27708
28253
|
for (const [from, to] of moves) {
|
|
27709
|
-
if (
|
|
28254
|
+
if (existsSync27(from) && !existsSync27(to)) {
|
|
27710
28255
|
try {
|
|
27711
28256
|
renameSync2(from, to);
|
|
27712
28257
|
} catch {
|
|
@@ -27717,7 +28262,7 @@ function migrateLayout() {
|
|
|
27717
28262
|
function rotateLogs() {
|
|
27718
28263
|
for (const file of [LOG_PATH, ERROR_LOG_PATH]) {
|
|
27719
28264
|
try {
|
|
27720
|
-
const { size } =
|
|
28265
|
+
const { size } = statSync9(file);
|
|
27721
28266
|
if (size > LOG_MAX_BYTES) {
|
|
27722
28267
|
const archivePath = `${file}.1`;
|
|
27723
28268
|
try {
|
|
@@ -27735,7 +28280,7 @@ async function main() {
|
|
|
27735
28280
|
let version = "unknown";
|
|
27736
28281
|
try {
|
|
27737
28282
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
27738
|
-
version = JSON.parse(
|
|
28283
|
+
version = JSON.parse(readFileSync17(pkgPath, "utf-8")).version;
|
|
27739
28284
|
} catch {
|
|
27740
28285
|
}
|
|
27741
28286
|
log(`[cc-claw] Starting v${version}`);
|
|
@@ -27749,11 +28294,11 @@ async function main() {
|
|
|
27749
28294
|
} catch {
|
|
27750
28295
|
}
|
|
27751
28296
|
try {
|
|
27752
|
-
const { getAdapter:
|
|
28297
|
+
const { getAdapter: getAdapter5, probeBackendAvailability: probeBackendAvailability2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
27753
28298
|
await probeBackendAvailability2();
|
|
27754
|
-
const claude =
|
|
28299
|
+
const claude = getAdapter5("claude");
|
|
27755
28300
|
if ("getAuthMode" in claude) claude.getAuthMode();
|
|
27756
|
-
const cursor =
|
|
28301
|
+
const cursor = getAdapter5("cursor");
|
|
27757
28302
|
if ("probeTier" in cursor) cursor.probeTier();
|
|
27758
28303
|
} catch {
|
|
27759
28304
|
}
|
|
@@ -27773,8 +28318,8 @@ async function main() {
|
|
|
27773
28318
|
}
|
|
27774
28319
|
if (modelCount > 0) {
|
|
27775
28320
|
try {
|
|
27776
|
-
const { getAdapter:
|
|
27777
|
-
const adapter =
|
|
28321
|
+
const { getAdapter: getAdapter5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
28322
|
+
const adapter = getAdapter5("ollama");
|
|
27778
28323
|
if ("refreshModelCatalog" in adapter) {
|
|
27779
28324
|
adapter.refreshModelCatalog();
|
|
27780
28325
|
}
|
|
@@ -27855,10 +28400,10 @@ async function main() {
|
|
|
27855
28400
|
});
|
|
27856
28401
|
log("[cc-claw] Agent orchestrator initialized");
|
|
27857
28402
|
try {
|
|
27858
|
-
const { execSync:
|
|
27859
|
-
const codexPath =
|
|
28403
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
28404
|
+
const codexPath = execSync7("which codex", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
27860
28405
|
if (codexPath) {
|
|
27861
|
-
const features =
|
|
28406
|
+
const features = execSync7("codex features list", { encoding: "utf-8", timeout: 1e4 });
|
|
27862
28407
|
if (/multi_agent\s+\S+\s+false/.test(features)) {
|
|
27863
28408
|
warn("[cc-claw] Codex multi_agent feature is disabled \u2014 native sub-agent detection will not work. Run: codex features enable multi_agent");
|
|
27864
28409
|
}
|
|
@@ -27986,10 +28531,10 @@ var init_index = __esm({
|
|
|
27986
28531
|
init_health3();
|
|
27987
28532
|
init_image_gen();
|
|
27988
28533
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
27989
|
-
if (!
|
|
28534
|
+
if (!existsSync27(dir)) mkdirSync12(dir, { recursive: true });
|
|
27990
28535
|
}
|
|
27991
28536
|
migrateLayout();
|
|
27992
|
-
if (
|
|
28537
|
+
if (existsSync27(ENV_PATH)) {
|
|
27993
28538
|
dotenv.config({ path: ENV_PATH });
|
|
27994
28539
|
} else {
|
|
27995
28540
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -28010,12 +28555,12 @@ __export(api_client_exports, {
|
|
|
28010
28555
|
apiPost: () => apiPost,
|
|
28011
28556
|
isDaemonRunning: () => isDaemonRunning
|
|
28012
28557
|
});
|
|
28013
|
-
import { readFileSync as
|
|
28558
|
+
import { readFileSync as readFileSync18, existsSync as existsSync28 } from "fs";
|
|
28014
28559
|
import { request as httpRequest, Agent } from "http";
|
|
28015
28560
|
function getToken() {
|
|
28016
28561
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
28017
28562
|
try {
|
|
28018
|
-
if (
|
|
28563
|
+
if (existsSync28(TOKEN_PATH)) return readFileSync18(TOKEN_PATH, "utf-8").trim();
|
|
28019
28564
|
} catch {
|
|
28020
28565
|
}
|
|
28021
28566
|
return null;
|
|
@@ -28114,8 +28659,8 @@ __export(service_exports2, {
|
|
|
28114
28659
|
serviceStatus: () => serviceStatus,
|
|
28115
28660
|
uninstallService: () => uninstallService
|
|
28116
28661
|
});
|
|
28117
|
-
import { existsSync as
|
|
28118
|
-
import { execFileSync as
|
|
28662
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
28663
|
+
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
28119
28664
|
import { homedir as homedir10, platform } from "os";
|
|
28120
28665
|
import { join as join31, dirname as dirname6 } from "path";
|
|
28121
28666
|
function xmlEscape(s) {
|
|
@@ -28123,10 +28668,10 @@ function xmlEscape(s) {
|
|
|
28123
28668
|
}
|
|
28124
28669
|
function resolveExecutable3(name) {
|
|
28125
28670
|
try {
|
|
28126
|
-
return
|
|
28671
|
+
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
28127
28672
|
} catch {
|
|
28128
28673
|
const fallback = process.argv[1];
|
|
28129
|
-
if (fallback &&
|
|
28674
|
+
if (fallback && existsSync29(fallback)) return fallback;
|
|
28130
28675
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
28131
28676
|
}
|
|
28132
28677
|
}
|
|
@@ -28141,7 +28686,7 @@ function getPathDirs() {
|
|
|
28141
28686
|
"/bin"
|
|
28142
28687
|
]);
|
|
28143
28688
|
try {
|
|
28144
|
-
const prefix =
|
|
28689
|
+
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
28145
28690
|
if (prefix) dirs.add(join31(prefix, "bin"));
|
|
28146
28691
|
} catch {
|
|
28147
28692
|
}
|
|
@@ -28201,26 +28746,26 @@ function generatePlist() {
|
|
|
28201
28746
|
}
|
|
28202
28747
|
function installMacOS() {
|
|
28203
28748
|
const agentsDir = dirname6(PLIST_PATH);
|
|
28204
|
-
if (!
|
|
28205
|
-
if (!
|
|
28206
|
-
if (
|
|
28749
|
+
if (!existsSync29(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
28750
|
+
if (!existsSync29(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
28751
|
+
if (existsSync29(PLIST_PATH)) {
|
|
28207
28752
|
try {
|
|
28208
|
-
|
|
28753
|
+
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
28209
28754
|
} catch {
|
|
28210
28755
|
}
|
|
28211
28756
|
}
|
|
28212
28757
|
writeFileSync9(PLIST_PATH, generatePlist());
|
|
28213
28758
|
console.log(` Installed: ${PLIST_PATH}`);
|
|
28214
|
-
|
|
28759
|
+
execFileSync4("launchctl", ["load", PLIST_PATH]);
|
|
28215
28760
|
console.log(" Service loaded and starting.");
|
|
28216
28761
|
}
|
|
28217
28762
|
function uninstallMacOS() {
|
|
28218
|
-
if (!
|
|
28763
|
+
if (!existsSync29(PLIST_PATH)) {
|
|
28219
28764
|
console.log(" No service found to uninstall.");
|
|
28220
28765
|
return;
|
|
28221
28766
|
}
|
|
28222
28767
|
try {
|
|
28223
|
-
|
|
28768
|
+
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
28224
28769
|
} catch {
|
|
28225
28770
|
}
|
|
28226
28771
|
unlinkSync8(PLIST_PATH);
|
|
@@ -28246,7 +28791,7 @@ async function getUptimeFromDaemon() {
|
|
|
28246
28791
|
}
|
|
28247
28792
|
function statusMacOS() {
|
|
28248
28793
|
try {
|
|
28249
|
-
const out =
|
|
28794
|
+
const out = execFileSync4("launchctl", ["list"], { encoding: "utf-8" });
|
|
28250
28795
|
const line = out.split("\n").find((l) => l.includes("cc-claw"));
|
|
28251
28796
|
if (line) {
|
|
28252
28797
|
const parts = line.trim().split(/\s+/);
|
|
@@ -28290,42 +28835,42 @@ WantedBy=default.target
|
|
|
28290
28835
|
`;
|
|
28291
28836
|
}
|
|
28292
28837
|
function installLinux() {
|
|
28293
|
-
if (!
|
|
28294
|
-
if (!
|
|
28838
|
+
if (!existsSync29(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
28839
|
+
if (!existsSync29(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
28295
28840
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
28296
28841
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
28297
|
-
|
|
28298
|
-
|
|
28299
|
-
|
|
28842
|
+
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
28843
|
+
execFileSync4("systemctl", ["--user", "enable", "cc-claw"]);
|
|
28844
|
+
execFileSync4("systemctl", ["--user", "start", "cc-claw"]);
|
|
28300
28845
|
console.log(" Service enabled and started.");
|
|
28301
28846
|
}
|
|
28302
28847
|
function uninstallLinux() {
|
|
28303
|
-
if (!
|
|
28848
|
+
if (!existsSync29(UNIT_PATH)) {
|
|
28304
28849
|
console.log(" No service found to uninstall.");
|
|
28305
28850
|
return;
|
|
28306
28851
|
}
|
|
28307
28852
|
try {
|
|
28308
|
-
|
|
28853
|
+
execFileSync4("systemctl", ["--user", "stop", "cc-claw"]);
|
|
28309
28854
|
} catch {
|
|
28310
28855
|
}
|
|
28311
28856
|
try {
|
|
28312
|
-
|
|
28857
|
+
execFileSync4("systemctl", ["--user", "disable", "cc-claw"]);
|
|
28313
28858
|
} catch {
|
|
28314
28859
|
}
|
|
28315
28860
|
unlinkSync8(UNIT_PATH);
|
|
28316
|
-
|
|
28861
|
+
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
28317
28862
|
console.log(" Service uninstalled.");
|
|
28318
28863
|
}
|
|
28319
28864
|
function statusLinux() {
|
|
28320
28865
|
try {
|
|
28321
|
-
const out =
|
|
28866
|
+
const out = execSync5("systemctl --user is-active cc-claw", { encoding: "utf-8" }).trim();
|
|
28322
28867
|
console.log(` Service is ${out}.`);
|
|
28323
28868
|
} catch {
|
|
28324
28869
|
console.log(" Not running or not installed.");
|
|
28325
28870
|
}
|
|
28326
28871
|
}
|
|
28327
28872
|
function installService() {
|
|
28328
|
-
if (!
|
|
28873
|
+
if (!existsSync29(join31(CC_CLAW_HOME, ".env"))) {
|
|
28329
28874
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
28330
28875
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
28331
28876
|
process.exitCode = 1;
|
|
@@ -28466,18 +29011,18 @@ __export(daemon_exports, {
|
|
|
28466
29011
|
restartService: () => restartService,
|
|
28467
29012
|
stopService: () => stopService
|
|
28468
29013
|
});
|
|
28469
|
-
import { execSync as
|
|
29014
|
+
import { execSync as execSync6 } from "child_process";
|
|
28470
29015
|
import { platform as platform2 } from "os";
|
|
28471
29016
|
async function stopService() {
|
|
28472
29017
|
const os2 = platform2();
|
|
28473
29018
|
try {
|
|
28474
29019
|
if (os2 === "darwin") {
|
|
28475
|
-
|
|
29020
|
+
execSync6("launchctl stop com.cc-claw");
|
|
28476
29021
|
console.log(`
|
|
28477
29022
|
${success("Daemon stopped.")}
|
|
28478
29023
|
`);
|
|
28479
29024
|
} else if (os2 === "linux") {
|
|
28480
|
-
|
|
29025
|
+
execSync6("systemctl --user stop cc-claw");
|
|
28481
29026
|
console.log(`
|
|
28482
29027
|
${success("Daemon stopped.")}
|
|
28483
29028
|
`);
|
|
@@ -28495,13 +29040,13 @@ async function restartService() {
|
|
|
28495
29040
|
const os2 = platform2();
|
|
28496
29041
|
try {
|
|
28497
29042
|
if (os2 === "darwin") {
|
|
28498
|
-
const uid = process.getuid?.() ??
|
|
28499
|
-
|
|
29043
|
+
const uid = process.getuid?.() ?? execSync6("id -u", { encoding: "utf-8" }).trim();
|
|
29044
|
+
execSync6(`launchctl kickstart -k gui/${uid}/com.cc-claw`);
|
|
28500
29045
|
console.log(`
|
|
28501
29046
|
${success("Daemon restarted.")}
|
|
28502
29047
|
`);
|
|
28503
29048
|
} else if (os2 === "linux") {
|
|
28504
|
-
|
|
29049
|
+
execSync6("systemctl --user restart cc-claw");
|
|
28505
29050
|
console.log(`
|
|
28506
29051
|
${success("Daemon restarted.")}
|
|
28507
29052
|
`);
|
|
@@ -28523,13 +29068,13 @@ var init_daemon = __esm({
|
|
|
28523
29068
|
});
|
|
28524
29069
|
|
|
28525
29070
|
// src/cli/resolve-chat.ts
|
|
28526
|
-
import { readFileSync as
|
|
29071
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
28527
29072
|
function resolveChatId2(globalOpts) {
|
|
28528
29073
|
const explicit = globalOpts.chat;
|
|
28529
29074
|
if (explicit) return explicit;
|
|
28530
29075
|
if (_cachedDefault) return _cachedDefault;
|
|
28531
29076
|
try {
|
|
28532
|
-
const content =
|
|
29077
|
+
const content = readFileSync20(ENV_PATH, "utf-8");
|
|
28533
29078
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
28534
29079
|
if (match) {
|
|
28535
29080
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -28553,13 +29098,13 @@ var status_exports = {};
|
|
|
28553
29098
|
__export(status_exports, {
|
|
28554
29099
|
statusCommand: () => statusCommand
|
|
28555
29100
|
});
|
|
28556
|
-
import { existsSync as
|
|
29101
|
+
import { existsSync as existsSync30, statSync as statSync10 } from "fs";
|
|
28557
29102
|
async function statusCommand(globalOpts, localOpts) {
|
|
28558
29103
|
try {
|
|
28559
29104
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
28560
29105
|
const readDb = openDatabaseReadOnly2();
|
|
28561
29106
|
const chatId = resolveChatId2(globalOpts);
|
|
28562
|
-
const { getAdapterForChat:
|
|
29107
|
+
const { getAdapterForChat: getAdapterForChat3, getAdapter: getAdapter5, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
28563
29108
|
let backend2 = null;
|
|
28564
29109
|
let modelName = "not set";
|
|
28565
29110
|
let contextMax = 2e5;
|
|
@@ -28567,7 +29112,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
28567
29112
|
const backendRow = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
28568
29113
|
if (backendRow) {
|
|
28569
29114
|
try {
|
|
28570
|
-
const a =
|
|
29115
|
+
const a = getAdapter5(backendRow.backend);
|
|
28571
29116
|
backend2 = { id: a.id, displayName: a.displayName };
|
|
28572
29117
|
} catch {
|
|
28573
29118
|
backend2 = { id: backendRow.backend, displayName: backendRow.backend };
|
|
@@ -28580,7 +29125,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
28580
29125
|
if (modelRow) modelName = modelRow.model;
|
|
28581
29126
|
else if (backend2) {
|
|
28582
29127
|
try {
|
|
28583
|
-
modelName =
|
|
29128
|
+
modelName = getAdapter5(backend2.id).defaultModel;
|
|
28584
29129
|
} catch {
|
|
28585
29130
|
}
|
|
28586
29131
|
}
|
|
@@ -28593,7 +29138,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
28593
29138
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
28594
29139
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
28595
29140
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
28596
|
-
const dbStat =
|
|
29141
|
+
const dbStat = existsSync30(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
28597
29142
|
let daemonRunning = false;
|
|
28598
29143
|
let daemonInfo = {};
|
|
28599
29144
|
try {
|
|
@@ -28705,13 +29250,13 @@ __export(doctor_exports, {
|
|
|
28705
29250
|
doctorCommand: () => doctorCommand,
|
|
28706
29251
|
doctorErrors: () => doctorErrors
|
|
28707
29252
|
});
|
|
28708
|
-
import { existsSync as
|
|
28709
|
-
import { execFileSync as
|
|
29253
|
+
import { existsSync as existsSync31, accessSync, constants } from "fs";
|
|
29254
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
28710
29255
|
async function doctorCommand(globalOpts, localOpts) {
|
|
28711
29256
|
const checks = [];
|
|
28712
|
-
|
|
28713
|
-
|
|
28714
|
-
|
|
29257
|
+
const dbChecks = checkDatabase();
|
|
29258
|
+
checks.push(...dbChecks);
|
|
29259
|
+
if (existsSync31(DB_PATH)) {
|
|
28715
29260
|
try {
|
|
28716
29261
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
28717
29262
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -28736,27 +29281,13 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28736
29281
|
} catch (err) {
|
|
28737
29282
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
28738
29283
|
}
|
|
28739
|
-
} else {
|
|
28740
|
-
checks.push({ name: "Database", status: "error", message: `Not found at ${DB_PATH}`, fix: "cc-claw setup" });
|
|
28741
29284
|
}
|
|
28742
|
-
if (
|
|
29285
|
+
if (existsSync31(ENV_PATH)) {
|
|
28743
29286
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
28744
29287
|
} else {
|
|
28745
29288
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
28746
29289
|
}
|
|
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
|
-
}
|
|
29290
|
+
checks.push(...checkBackendCLIs());
|
|
28760
29291
|
try {
|
|
28761
29292
|
const { isDaemonRunning: isDaemonRunning2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
28762
29293
|
const running = await isDaemonRunning2();
|
|
@@ -28787,14 +29318,14 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28787
29318
|
checks.push({ name: "Daemon", status: "warning", message: "could not probe" });
|
|
28788
29319
|
}
|
|
28789
29320
|
try {
|
|
28790
|
-
const latest =
|
|
29321
|
+
const latest = execFileSync5("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
28791
29322
|
if (latest && latest !== VERSION) {
|
|
28792
29323
|
checks.push({ name: "Update available", status: "warning", message: `v${latest} available (current: v${VERSION})`, fix: "npm install -g cc-claw@latest" });
|
|
28793
29324
|
}
|
|
28794
29325
|
} catch {
|
|
28795
29326
|
}
|
|
28796
29327
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
28797
|
-
if (
|
|
29328
|
+
if (existsSync31(tokenPath)) {
|
|
28798
29329
|
try {
|
|
28799
29330
|
accessSync(tokenPath, constants.R_OK);
|
|
28800
29331
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -28804,125 +29335,28 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28804
29335
|
} else {
|
|
28805
29336
|
checks.push({ name: "API token", status: "warning", message: "no token file (daemon not started?)", fix: "cc-claw service start" });
|
|
28806
29337
|
}
|
|
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
|
-
}
|
|
29338
|
+
const disk = checkDiskSpace();
|
|
29339
|
+
if (disk) checks.push(disk);
|
|
29340
|
+
checks.push(...checkErrorLog());
|
|
29341
|
+
const ollama2 = checkOllamaServers();
|
|
29342
|
+
if (ollama2) checks.push(ollama2);
|
|
28909
29343
|
if (localOpts.fix) {
|
|
28910
29344
|
const fixable = checks.filter((c) => c.status !== "ok" && c.fix);
|
|
28911
29345
|
for (const check of fixable) {
|
|
28912
29346
|
if (check.name === "Daemon" && check.fix?.includes("service start")) {
|
|
28913
29347
|
try {
|
|
28914
|
-
const { execSync:
|
|
29348
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
28915
29349
|
const os2 = (await import("os")).platform();
|
|
28916
29350
|
if (os2 === "darwin") {
|
|
28917
29351
|
try {
|
|
28918
|
-
|
|
29352
|
+
execSync7("launchctl start com.cc-claw");
|
|
28919
29353
|
check.status = "ok";
|
|
28920
29354
|
check.message = "restarted";
|
|
28921
29355
|
} catch {
|
|
28922
29356
|
}
|
|
28923
29357
|
} else if (os2 === "linux") {
|
|
28924
29358
|
try {
|
|
28925
|
-
|
|
29359
|
+
execSync7("systemctl --user start cc-claw");
|
|
28926
29360
|
check.status = "ok";
|
|
28927
29361
|
check.message = "restarted";
|
|
28928
29362
|
} catch {
|
|
@@ -28955,9 +29389,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28955
29389
|
}
|
|
28956
29390
|
}
|
|
28957
29391
|
const errorChecks = checks.filter(
|
|
28958
|
-
(c) => ["
|
|
29392
|
+
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
28959
29393
|
);
|
|
28960
|
-
if (errorChecks.length > 0 &&
|
|
29394
|
+
if (errorChecks.length > 0 && existsSync31(ERROR_LOG_PATH)) {
|
|
28961
29395
|
try {
|
|
28962
29396
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
28963
29397
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -28970,9 +29404,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
28970
29404
|
}
|
|
28971
29405
|
}
|
|
28972
29406
|
}
|
|
28973
|
-
const
|
|
28974
|
-
const
|
|
28975
|
-
const report = { checks, errors, warnings };
|
|
29407
|
+
const errCount = checks.filter((c) => c.status === "error").length;
|
|
29408
|
+
const warnCount = checks.filter((c) => c.status === "warning").length;
|
|
29409
|
+
const report = { checks, errors: errCount, warnings: warnCount };
|
|
28976
29410
|
output(report, (d) => {
|
|
28977
29411
|
const r = d;
|
|
28978
29412
|
const lines = [
|
|
@@ -29003,27 +29437,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29003
29437
|
lines.push("");
|
|
29004
29438
|
return lines.join("\n");
|
|
29005
29439
|
});
|
|
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 };
|
|
29440
|
+
process.exit(errCount > 0 ? 1 : warnCount > 0 ? 2 : 0);
|
|
29027
29441
|
}
|
|
29028
29442
|
function stripTimestamp(line) {
|
|
29029
29443
|
const match = line.match(/^\[(\d{4}-\d{2}-\d{2})\s+([\d:+-]+)\]\s*(.*)/);
|
|
@@ -29038,9 +29452,8 @@ async function doctorErrors(globalOpts) {
|
|
|
29038
29452
|
`);
|
|
29039
29453
|
return;
|
|
29040
29454
|
}
|
|
29041
|
-
const {
|
|
29042
|
-
|
|
29043
|
-
output({ total, classified }, () => {
|
|
29455
|
+
const { rate429, contentSilence, spawnTimeout, other, total } = result;
|
|
29456
|
+
output({ total, classified: result }, () => {
|
|
29044
29457
|
const lines = [
|
|
29045
29458
|
"",
|
|
29046
29459
|
box("Recent Errors (last 24h)"),
|
|
@@ -29072,10 +29485,10 @@ async function doctorErrors(globalOpts) {
|
|
|
29072
29485
|
}
|
|
29073
29486
|
lines.push("");
|
|
29074
29487
|
};
|
|
29075
|
-
renderCategory("Telegram rate limits",
|
|
29076
|
-
renderCategory("Content silence timeouts",
|
|
29077
|
-
renderCategory("Spawn timeouts",
|
|
29078
|
-
renderCategory("Other errors",
|
|
29488
|
+
renderCategory("Telegram rate limits", rate429, 5);
|
|
29489
|
+
renderCategory("Content silence timeouts", contentSilence, 5);
|
|
29490
|
+
renderCategory("Spawn timeouts", spawnTimeout, 5);
|
|
29491
|
+
renderCategory("Other errors", other, 10);
|
|
29079
29492
|
if (total === 0) {
|
|
29080
29493
|
lines.push(` ${success("No errors in last 24h.")}`);
|
|
29081
29494
|
} else {
|
|
@@ -29098,6 +29511,7 @@ var init_doctor = __esm({
|
|
|
29098
29511
|
init_format2();
|
|
29099
29512
|
init_paths();
|
|
29100
29513
|
init_version();
|
|
29514
|
+
init_checks();
|
|
29101
29515
|
}
|
|
29102
29516
|
});
|
|
29103
29517
|
|
|
@@ -29106,15 +29520,15 @@ var logs_exports = {};
|
|
|
29106
29520
|
__export(logs_exports, {
|
|
29107
29521
|
logsCommand: () => logsCommand
|
|
29108
29522
|
});
|
|
29109
|
-
import { existsSync as
|
|
29523
|
+
import { existsSync as existsSync32, readFileSync as readFileSync22, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
29110
29524
|
async function logsCommand(opts) {
|
|
29111
29525
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
29112
|
-
if (!
|
|
29526
|
+
if (!existsSync32(logFile)) {
|
|
29113
29527
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
29114
29528
|
process.exit(1);
|
|
29115
29529
|
}
|
|
29116
29530
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
29117
|
-
const content =
|
|
29531
|
+
const content = readFileSync22(logFile, "utf-8");
|
|
29118
29532
|
const allLines = content.split("\n");
|
|
29119
29533
|
const tailLines = allLines.slice(-maxLines);
|
|
29120
29534
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -29124,7 +29538,7 @@ async function logsCommand(opts) {
|
|
|
29124
29538
|
let lastLength = content.length;
|
|
29125
29539
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
29126
29540
|
try {
|
|
29127
|
-
const newContent =
|
|
29541
|
+
const newContent = readFileSync22(logFile, "utf-8");
|
|
29128
29542
|
if (newContent.length > lastLength) {
|
|
29129
29543
|
const newPart = newContent.slice(lastLength);
|
|
29130
29544
|
process.stdout.write(newPart);
|
|
@@ -29156,7 +29570,7 @@ __export(session_logs_exports, {
|
|
|
29156
29570
|
sessionLogsList: () => sessionLogsList,
|
|
29157
29571
|
sessionLogsTail: () => sessionLogsTail
|
|
29158
29572
|
});
|
|
29159
|
-
import { readFileSync as
|
|
29573
|
+
import { readFileSync as readFileSync23, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
29160
29574
|
async function sessionLogsList(opts) {
|
|
29161
29575
|
const logs = listSessionLogs();
|
|
29162
29576
|
if (logs.length === 0) {
|
|
@@ -29213,12 +29627,12 @@ async function sessionLogsTail(opts) {
|
|
|
29213
29627
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
29214
29628
|
let lastLength = 0;
|
|
29215
29629
|
try {
|
|
29216
|
-
lastLength =
|
|
29630
|
+
lastLength = readFileSync23(targetPath, "utf-8").length;
|
|
29217
29631
|
} catch {
|
|
29218
29632
|
}
|
|
29219
29633
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
29220
29634
|
try {
|
|
29221
|
-
const content =
|
|
29635
|
+
const content = readFileSync23(targetPath, "utf-8");
|
|
29222
29636
|
if (content.length > lastLength) {
|
|
29223
29637
|
process.stdout.write(content.slice(lastLength));
|
|
29224
29638
|
lastLength = content.length;
|
|
@@ -29262,11 +29676,11 @@ __export(gemini_exports, {
|
|
|
29262
29676
|
geminiReorder: () => geminiReorder,
|
|
29263
29677
|
geminiRotation: () => geminiRotation
|
|
29264
29678
|
});
|
|
29265
|
-
import { existsSync as
|
|
29679
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync24, chmodSync } from "fs";
|
|
29266
29680
|
import { join as join32 } from "path";
|
|
29267
29681
|
import { createInterface as createInterface8 } from "readline";
|
|
29268
29682
|
function requireDb() {
|
|
29269
|
-
if (!
|
|
29683
|
+
if (!existsSync34(DB_PATH)) {
|
|
29270
29684
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
29271
29685
|
process.exit(1);
|
|
29272
29686
|
}
|
|
@@ -29292,8 +29706,8 @@ function resolveOAuthEmail(configHome) {
|
|
|
29292
29706
|
if (!configHome) return null;
|
|
29293
29707
|
try {
|
|
29294
29708
|
const accountsPath = join32(configHome, ".gemini", "google_accounts.json");
|
|
29295
|
-
if (!
|
|
29296
|
-
const accounts = JSON.parse(
|
|
29709
|
+
if (!existsSync34(accountsPath)) return null;
|
|
29710
|
+
const accounts = JSON.parse(readFileSync24(accountsPath, "utf-8"));
|
|
29297
29711
|
return accounts.active || null;
|
|
29298
29712
|
} catch {
|
|
29299
29713
|
return null;
|
|
@@ -29376,7 +29790,7 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
29376
29790
|
async function geminiAddAccount(globalOpts, opts) {
|
|
29377
29791
|
await requireWriteDb();
|
|
29378
29792
|
const slotsDir = join32(CC_CLAW_HOME, "gemini-slots");
|
|
29379
|
-
if (!
|
|
29793
|
+
if (!existsSync34(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
29380
29794
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
29381
29795
|
const tempId = Date.now();
|
|
29382
29796
|
const slotDir = join32(slotsDir, `slot-${tempId}`);
|
|
@@ -29390,9 +29804,9 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
29390
29804
|
console.log(" Sign in with the Google account you want for this slot.");
|
|
29391
29805
|
console.log(" After sign-in, type /quit to return here.");
|
|
29392
29806
|
console.log("");
|
|
29393
|
-
const { execSync:
|
|
29807
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29394
29808
|
try {
|
|
29395
|
-
|
|
29809
|
+
execSync7(`GEMINI_CLI_HOME=${slotDir} gemini`, {
|
|
29396
29810
|
stdio: "inherit",
|
|
29397
29811
|
env: { ...process.env, GEMINI_CLI_HOME: slotDir, GEMINI_API_KEY: void 0, GOOGLE_API_KEY: void 0 },
|
|
29398
29812
|
cwd: slotDir
|
|
@@ -29400,7 +29814,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
29400
29814
|
} catch {
|
|
29401
29815
|
}
|
|
29402
29816
|
const oauthPath = join32(slotDir, ".gemini", "oauth_creds.json");
|
|
29403
|
-
if (!
|
|
29817
|
+
if (!existsSync34(oauthPath)) {
|
|
29404
29818
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
29405
29819
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
29406
29820
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -29520,7 +29934,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29520
29934
|
return;
|
|
29521
29935
|
}
|
|
29522
29936
|
const settingsPath = join32(slot.configHome, ".gemini", "settings.json");
|
|
29523
|
-
if (!
|
|
29937
|
+
if (!existsSync34(settingsPath)) {
|
|
29524
29938
|
mkdirSync14(join32(slot.configHome, ".gemini"), { recursive: true });
|
|
29525
29939
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
29526
29940
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
@@ -29531,9 +29945,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29531
29945
|
console.log(" Sign in with the same Google account when prompted.");
|
|
29532
29946
|
console.log(" After sign-in, type /quit to return here.");
|
|
29533
29947
|
console.log("");
|
|
29534
|
-
const { execSync:
|
|
29948
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29535
29949
|
try {
|
|
29536
|
-
|
|
29950
|
+
execSync7(`gemini`, {
|
|
29537
29951
|
stdio: "inherit",
|
|
29538
29952
|
env: {
|
|
29539
29953
|
...process.env,
|
|
@@ -29546,7 +29960,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29546
29960
|
} catch {
|
|
29547
29961
|
}
|
|
29548
29962
|
const oauthPath = join32(slot.configHome, ".gemini", "oauth_creds.json");
|
|
29549
|
-
if (!
|
|
29963
|
+
if (!existsSync34(oauthPath)) {
|
|
29550
29964
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
29551
29965
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
29552
29966
|
`);
|
|
@@ -29556,7 +29970,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
29556
29970
|
setGeminiSlotEnabled2(slotId, true);
|
|
29557
29971
|
let accountEmail = slot.label;
|
|
29558
29972
|
try {
|
|
29559
|
-
const accounts = JSON.parse(
|
|
29973
|
+
const accounts = JSON.parse(readFileSync24(join32(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
29560
29974
|
if (accounts.active) accountEmail = accounts.active;
|
|
29561
29975
|
} catch {
|
|
29562
29976
|
}
|
|
@@ -29615,11 +30029,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
29615
30029
|
makeReorder: () => makeReorder,
|
|
29616
30030
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
29617
30031
|
});
|
|
29618
|
-
import { existsSync as
|
|
30032
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync15, readFileSync as readFileSync25 } from "fs";
|
|
29619
30033
|
import { join as join33 } from "path";
|
|
29620
30034
|
import { createInterface as createInterface9 } from "readline";
|
|
29621
30035
|
function requireDb2() {
|
|
29622
|
-
if (!
|
|
30036
|
+
if (!existsSync35(DB_PATH)) {
|
|
29623
30037
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
29624
30038
|
process.exit(1);
|
|
29625
30039
|
}
|
|
@@ -29709,7 +30123,7 @@ function makeAddAccount(backend2, displayName) {
|
|
|
29709
30123
|
}
|
|
29710
30124
|
await requireWriteDb2();
|
|
29711
30125
|
const slotsDir = join33(CC_CLAW_HOME, config2.slotsSubdir);
|
|
29712
|
-
if (!
|
|
30126
|
+
if (!existsSync35(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
29713
30127
|
const tempId = Date.now();
|
|
29714
30128
|
const slotDir = join33(slotsDir, `slot-${tempId}`);
|
|
29715
30129
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
@@ -29719,14 +30133,14 @@ function makeAddAccount(backend2, displayName) {
|
|
|
29719
30133
|
console.log(` Sign in with the account you want for this slot.`);
|
|
29720
30134
|
console.log(` After sign-in completes, return here.`);
|
|
29721
30135
|
console.log("");
|
|
29722
|
-
const { execSync:
|
|
30136
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29723
30137
|
const loginEnv = {
|
|
29724
30138
|
...process.env,
|
|
29725
30139
|
[config2.envKey]: config2.envValue(slotDir),
|
|
29726
30140
|
...config2.envOverrides
|
|
29727
30141
|
};
|
|
29728
30142
|
try {
|
|
29729
|
-
|
|
30143
|
+
execSync7(config2.loginCommand.join(" "), {
|
|
29730
30144
|
stdio: "inherit",
|
|
29731
30145
|
env: loginEnv,
|
|
29732
30146
|
cwd: slotDir
|
|
@@ -29871,14 +30285,14 @@ function makeRelogin(backend2, displayName) {
|
|
|
29871
30285
|
console.log(` Re-authenticating ${displayName} slot "${slot.label || `#${slot.id}`}"...`);
|
|
29872
30286
|
console.log(` Sign in with the same account when the browser opens.`);
|
|
29873
30287
|
console.log("");
|
|
29874
|
-
const { execSync:
|
|
30288
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
29875
30289
|
const loginEnv = {
|
|
29876
30290
|
...process.env,
|
|
29877
30291
|
[config2.envKey]: config2.envValue(slot.configHome),
|
|
29878
30292
|
...config2.envOverrides
|
|
29879
30293
|
};
|
|
29880
30294
|
try {
|
|
29881
|
-
|
|
30295
|
+
execSync7(config2.loginCommand.join(" "), {
|
|
29882
30296
|
stdio: "inherit",
|
|
29883
30297
|
env: loginEnv,
|
|
29884
30298
|
cwd: slot.configHome
|
|
@@ -29967,17 +30381,17 @@ var init_backend_cmd_factory = __esm({
|
|
|
29967
30381
|
verifyCredentials: (slotDir) => {
|
|
29968
30382
|
const claudeJson = join33(slotDir, ".claude.json");
|
|
29969
30383
|
const claudeJsonNested = join33(slotDir, ".claude", ".claude.json");
|
|
29970
|
-
if (
|
|
30384
|
+
if (existsSync35(claudeJson)) {
|
|
29971
30385
|
try {
|
|
29972
|
-
const data = JSON.parse(
|
|
30386
|
+
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
29973
30387
|
return Boolean(data.oauthAccount);
|
|
29974
30388
|
} catch {
|
|
29975
30389
|
return false;
|
|
29976
30390
|
}
|
|
29977
30391
|
}
|
|
29978
|
-
if (
|
|
30392
|
+
if (existsSync35(claudeJsonNested)) {
|
|
29979
30393
|
try {
|
|
29980
|
-
const data = JSON.parse(
|
|
30394
|
+
const data = JSON.parse(readFileSync25(claudeJsonNested, "utf-8"));
|
|
29981
30395
|
return Boolean(data.oauthAccount);
|
|
29982
30396
|
} catch {
|
|
29983
30397
|
return false;
|
|
@@ -29987,8 +30401,8 @@ var init_backend_cmd_factory = __esm({
|
|
|
29987
30401
|
},
|
|
29988
30402
|
extractLabel: (slotDir) => {
|
|
29989
30403
|
try {
|
|
29990
|
-
const { execSync:
|
|
29991
|
-
const out =
|
|
30404
|
+
const { execSync: execSync7 } = __require("child_process");
|
|
30405
|
+
const out = execSync7("claude auth status", {
|
|
29992
30406
|
encoding: "utf-8",
|
|
29993
30407
|
env: { ...process.env, HOME: slotDir, ANTHROPIC_API_KEY: void 0 },
|
|
29994
30408
|
timeout: 1e4
|
|
@@ -29999,8 +30413,8 @@ var init_backend_cmd_factory = __esm({
|
|
|
29999
30413
|
}
|
|
30000
30414
|
try {
|
|
30001
30415
|
const claudeJson = join33(slotDir, ".claude.json");
|
|
30002
|
-
if (
|
|
30003
|
-
const data = JSON.parse(
|
|
30416
|
+
if (existsSync35(claudeJson)) {
|
|
30417
|
+
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
30004
30418
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
30005
30419
|
}
|
|
30006
30420
|
} catch {
|
|
@@ -30015,11 +30429,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
30015
30429
|
envValue: (slotDir) => slotDir,
|
|
30016
30430
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
30017
30431
|
verifyCredentials: (slotDir) => {
|
|
30018
|
-
return
|
|
30432
|
+
return existsSync35(join33(slotDir, "auth.json"));
|
|
30019
30433
|
},
|
|
30020
30434
|
extractLabel: (slotDir) => {
|
|
30021
30435
|
try {
|
|
30022
|
-
const authData = JSON.parse(
|
|
30436
|
+
const authData = JSON.parse(readFileSync25(join33(slotDir, "auth.json"), "utf-8"));
|
|
30023
30437
|
if (authData.email) return authData.email;
|
|
30024
30438
|
if (authData.account_name) return authData.account_name;
|
|
30025
30439
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -30043,9 +30457,9 @@ __export(ollama_exports3, {
|
|
|
30043
30457
|
ollamaRemove: () => ollamaRemove,
|
|
30044
30458
|
ollamaTest: () => ollamaTest
|
|
30045
30459
|
});
|
|
30046
|
-
import { existsSync as
|
|
30460
|
+
import { existsSync as existsSync36 } from "fs";
|
|
30047
30461
|
function requireDb3() {
|
|
30048
|
-
if (!
|
|
30462
|
+
if (!existsSync36(DB_PATH)) {
|
|
30049
30463
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30050
30464
|
process.exit(1);
|
|
30051
30465
|
}
|
|
@@ -30304,12 +30718,12 @@ __export(backend_exports, {
|
|
|
30304
30718
|
backendList: () => backendList,
|
|
30305
30719
|
backendSet: () => backendSet
|
|
30306
30720
|
});
|
|
30307
|
-
import { existsSync as
|
|
30721
|
+
import { existsSync as existsSync37 } from "fs";
|
|
30308
30722
|
async function backendList(globalOpts) {
|
|
30309
30723
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
30310
30724
|
const chatId = resolveChatId2(globalOpts);
|
|
30311
30725
|
let activeBackend = null;
|
|
30312
|
-
if (
|
|
30726
|
+
if (existsSync37(DB_PATH)) {
|
|
30313
30727
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30314
30728
|
const readDb = openDatabaseReadOnly2();
|
|
30315
30729
|
try {
|
|
@@ -30340,7 +30754,7 @@ async function backendList(globalOpts) {
|
|
|
30340
30754
|
}
|
|
30341
30755
|
async function backendGet(globalOpts) {
|
|
30342
30756
|
const chatId = resolveChatId2(globalOpts);
|
|
30343
|
-
if (!
|
|
30757
|
+
if (!existsSync37(DB_PATH)) {
|
|
30344
30758
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30345
30759
|
process.exit(1);
|
|
30346
30760
|
}
|
|
@@ -30384,13 +30798,13 @@ __export(model_exports, {
|
|
|
30384
30798
|
modelList: () => modelList,
|
|
30385
30799
|
modelSet: () => modelSet
|
|
30386
30800
|
});
|
|
30387
|
-
import { existsSync as
|
|
30801
|
+
import { existsSync as existsSync38 } from "fs";
|
|
30388
30802
|
async function modelList(globalOpts) {
|
|
30389
30803
|
const chatId = resolveChatId2(globalOpts);
|
|
30390
30804
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30391
|
-
const { getAdapter:
|
|
30805
|
+
const { getAdapter: getAdapter5, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
30392
30806
|
let backendId = "claude";
|
|
30393
|
-
if (
|
|
30807
|
+
if (existsSync38(DB_PATH)) {
|
|
30394
30808
|
const readDb = openDatabaseReadOnly2();
|
|
30395
30809
|
try {
|
|
30396
30810
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -30400,7 +30814,7 @@ async function modelList(globalOpts) {
|
|
|
30400
30814
|
readDb.close();
|
|
30401
30815
|
}
|
|
30402
30816
|
try {
|
|
30403
|
-
const adapter =
|
|
30817
|
+
const adapter = getAdapter5(backendId);
|
|
30404
30818
|
const models = Object.entries(adapter.availableModels).map(([id, info]) => ({
|
|
30405
30819
|
id,
|
|
30406
30820
|
label: info.label,
|
|
@@ -30423,7 +30837,7 @@ async function modelList(globalOpts) {
|
|
|
30423
30837
|
}
|
|
30424
30838
|
async function modelGet(globalOpts) {
|
|
30425
30839
|
const chatId = resolveChatId2(globalOpts);
|
|
30426
|
-
if (!
|
|
30840
|
+
if (!existsSync38(DB_PATH)) {
|
|
30427
30841
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30428
30842
|
process.exit(1);
|
|
30429
30843
|
}
|
|
@@ -30467,9 +30881,9 @@ __export(memory_exports2, {
|
|
|
30467
30881
|
memoryList: () => memoryList,
|
|
30468
30882
|
memorySearch: () => memorySearch
|
|
30469
30883
|
});
|
|
30470
|
-
import { existsSync as
|
|
30884
|
+
import { existsSync as existsSync39 } from "fs";
|
|
30471
30885
|
async function memoryList(globalOpts) {
|
|
30472
|
-
if (!
|
|
30886
|
+
if (!existsSync39(DB_PATH)) {
|
|
30473
30887
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30474
30888
|
process.exit(1);
|
|
30475
30889
|
}
|
|
@@ -30493,7 +30907,7 @@ async function memoryList(globalOpts) {
|
|
|
30493
30907
|
});
|
|
30494
30908
|
}
|
|
30495
30909
|
async function memorySearch(globalOpts, query) {
|
|
30496
|
-
if (!
|
|
30910
|
+
if (!existsSync39(DB_PATH)) {
|
|
30497
30911
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30498
30912
|
process.exit(1);
|
|
30499
30913
|
}
|
|
@@ -30515,7 +30929,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
30515
30929
|
});
|
|
30516
30930
|
}
|
|
30517
30931
|
async function memoryHistory(globalOpts, opts) {
|
|
30518
|
-
if (!
|
|
30932
|
+
if (!existsSync39(DB_PATH)) {
|
|
30519
30933
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30520
30934
|
process.exit(1);
|
|
30521
30935
|
}
|
|
@@ -30563,7 +30977,7 @@ __export(cron_exports2, {
|
|
|
30563
30977
|
cronList: () => cronList,
|
|
30564
30978
|
cronRuns: () => cronRuns
|
|
30565
30979
|
});
|
|
30566
|
-
import { existsSync as
|
|
30980
|
+
import { existsSync as existsSync40 } from "fs";
|
|
30567
30981
|
function parseFallbacks(raw) {
|
|
30568
30982
|
return raw.slice(0, 3).map((f) => {
|
|
30569
30983
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -30584,7 +30998,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
30584
30998
|
return val;
|
|
30585
30999
|
}
|
|
30586
31000
|
async function cronList(globalOpts) {
|
|
30587
|
-
if (!
|
|
31001
|
+
if (!existsSync40(DB_PATH)) {
|
|
30588
31002
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30589
31003
|
process.exit(1);
|
|
30590
31004
|
}
|
|
@@ -30622,7 +31036,7 @@ async function cronList(globalOpts) {
|
|
|
30622
31036
|
});
|
|
30623
31037
|
}
|
|
30624
31038
|
async function cronHealth(globalOpts) {
|
|
30625
|
-
if (!
|
|
31039
|
+
if (!existsSync40(DB_PATH)) {
|
|
30626
31040
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30627
31041
|
process.exit(1);
|
|
30628
31042
|
}
|
|
@@ -30783,7 +31197,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
30783
31197
|
}
|
|
30784
31198
|
}
|
|
30785
31199
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
30786
|
-
if (!
|
|
31200
|
+
if (!existsSync40(DB_PATH)) {
|
|
30787
31201
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30788
31202
|
process.exit(1);
|
|
30789
31203
|
}
|
|
@@ -30830,9 +31244,9 @@ __export(agents_exports, {
|
|
|
30830
31244
|
runnersList: () => runnersList,
|
|
30831
31245
|
tasksList: () => tasksList
|
|
30832
31246
|
});
|
|
30833
|
-
import { existsSync as
|
|
31247
|
+
import { existsSync as existsSync41 } from "fs";
|
|
30834
31248
|
async function agentsList(globalOpts) {
|
|
30835
|
-
if (!
|
|
31249
|
+
if (!existsSync41(DB_PATH)) {
|
|
30836
31250
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30837
31251
|
process.exit(1);
|
|
30838
31252
|
}
|
|
@@ -30863,7 +31277,7 @@ async function agentsList(globalOpts) {
|
|
|
30863
31277
|
});
|
|
30864
31278
|
}
|
|
30865
31279
|
async function tasksList(globalOpts) {
|
|
30866
|
-
if (!
|
|
31280
|
+
if (!existsSync41(DB_PATH)) {
|
|
30867
31281
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
30868
31282
|
process.exit(1);
|
|
30869
31283
|
}
|
|
@@ -30991,10 +31405,10 @@ __export(db_exports, {
|
|
|
30991
31405
|
dbPath: () => dbPath,
|
|
30992
31406
|
dbStats: () => dbStats
|
|
30993
31407
|
});
|
|
30994
|
-
import { existsSync as
|
|
31408
|
+
import { existsSync as existsSync42, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
30995
31409
|
import { dirname as dirname7 } from "path";
|
|
30996
31410
|
async function dbStats(globalOpts) {
|
|
30997
|
-
if (!
|
|
31411
|
+
if (!existsSync42(DB_PATH)) {
|
|
30998
31412
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
30999
31413
|
process.exit(1);
|
|
31000
31414
|
}
|
|
@@ -31002,7 +31416,7 @@ async function dbStats(globalOpts) {
|
|
|
31002
31416
|
const readDb = openDatabaseReadOnly2();
|
|
31003
31417
|
const mainSize = statSync11(DB_PATH).size;
|
|
31004
31418
|
const walPath = DB_PATH + "-wal";
|
|
31005
|
-
const walSize =
|
|
31419
|
+
const walSize = existsSync42(walPath) ? statSync11(walPath).size : 0;
|
|
31006
31420
|
const tableNames = readDb.prepare(
|
|
31007
31421
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
31008
31422
|
).all();
|
|
@@ -31036,7 +31450,7 @@ async function dbPath(globalOpts) {
|
|
|
31036
31450
|
output({ path: DB_PATH }, (d) => d.path);
|
|
31037
31451
|
}
|
|
31038
31452
|
async function dbBackup(globalOpts, destPath) {
|
|
31039
|
-
if (!
|
|
31453
|
+
if (!existsSync42(DB_PATH)) {
|
|
31040
31454
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
31041
31455
|
process.exit(1);
|
|
31042
31456
|
}
|
|
@@ -31045,7 +31459,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
31045
31459
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
31046
31460
|
copyFileSync3(DB_PATH, dest);
|
|
31047
31461
|
const walPath = DB_PATH + "-wal";
|
|
31048
|
-
if (
|
|
31462
|
+
if (existsSync42(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
31049
31463
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
31050
31464
|
const b = d;
|
|
31051
31465
|
return `
|
|
@@ -31074,9 +31488,9 @@ __export(usage_exports, {
|
|
|
31074
31488
|
usageCost: () => usageCost,
|
|
31075
31489
|
usageTokens: () => usageTokens
|
|
31076
31490
|
});
|
|
31077
|
-
import { existsSync as
|
|
31491
|
+
import { existsSync as existsSync43 } from "fs";
|
|
31078
31492
|
function ensureDb() {
|
|
31079
|
-
if (!
|
|
31493
|
+
if (!existsSync43(DB_PATH)) {
|
|
31080
31494
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31081
31495
|
process.exit(1);
|
|
31082
31496
|
}
|
|
@@ -31266,9 +31680,9 @@ __export(config_exports2, {
|
|
|
31266
31680
|
configList: () => configList,
|
|
31267
31681
|
configSet: () => configSet
|
|
31268
31682
|
});
|
|
31269
|
-
import { existsSync as
|
|
31683
|
+
import { existsSync as existsSync44, readFileSync as readFileSync26 } from "fs";
|
|
31270
31684
|
async function configList(globalOpts) {
|
|
31271
|
-
if (!
|
|
31685
|
+
if (!existsSync44(DB_PATH)) {
|
|
31272
31686
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31273
31687
|
process.exit(1);
|
|
31274
31688
|
}
|
|
@@ -31302,7 +31716,7 @@ async function configGet(globalOpts, key) {
|
|
|
31302
31716
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
31303
31717
|
process.exit(1);
|
|
31304
31718
|
}
|
|
31305
|
-
if (!
|
|
31719
|
+
if (!existsSync44(DB_PATH)) {
|
|
31306
31720
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31307
31721
|
process.exit(1);
|
|
31308
31722
|
}
|
|
@@ -31348,11 +31762,11 @@ async function configSet(globalOpts, key, value) {
|
|
|
31348
31762
|
}
|
|
31349
31763
|
}
|
|
31350
31764
|
async function configEnv(_globalOpts) {
|
|
31351
|
-
if (!
|
|
31765
|
+
if (!existsSync44(ENV_PATH)) {
|
|
31352
31766
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
31353
31767
|
process.exit(1);
|
|
31354
31768
|
}
|
|
31355
|
-
const content =
|
|
31769
|
+
const content = readFileSync26(ENV_PATH, "utf-8");
|
|
31356
31770
|
const entries = {};
|
|
31357
31771
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
31358
31772
|
for (const line of content.split("\n")) {
|
|
@@ -31402,9 +31816,9 @@ __export(session_exports, {
|
|
|
31402
31816
|
sessionGet: () => sessionGet,
|
|
31403
31817
|
sessionNew: () => sessionNew
|
|
31404
31818
|
});
|
|
31405
|
-
import { existsSync as
|
|
31819
|
+
import { existsSync as existsSync45 } from "fs";
|
|
31406
31820
|
async function sessionGet(globalOpts) {
|
|
31407
|
-
if (!
|
|
31821
|
+
if (!existsSync45(DB_PATH)) {
|
|
31408
31822
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31409
31823
|
process.exit(1);
|
|
31410
31824
|
}
|
|
@@ -31465,9 +31879,9 @@ __export(permissions_exports, {
|
|
|
31465
31879
|
verboseGet: () => verboseGet,
|
|
31466
31880
|
verboseSet: () => verboseSet
|
|
31467
31881
|
});
|
|
31468
|
-
import { existsSync as
|
|
31882
|
+
import { existsSync as existsSync46 } from "fs";
|
|
31469
31883
|
function ensureDb2() {
|
|
31470
|
-
if (!
|
|
31884
|
+
if (!existsSync46(DB_PATH)) {
|
|
31471
31885
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31472
31886
|
process.exit(1);
|
|
31473
31887
|
}
|
|
@@ -31614,9 +32028,9 @@ __export(cwd_exports, {
|
|
|
31614
32028
|
cwdGet: () => cwdGet,
|
|
31615
32029
|
cwdSet: () => cwdSet
|
|
31616
32030
|
});
|
|
31617
|
-
import { existsSync as
|
|
32031
|
+
import { existsSync as existsSync47 } from "fs";
|
|
31618
32032
|
async function cwdGet(globalOpts) {
|
|
31619
|
-
if (!
|
|
32033
|
+
if (!existsSync47(DB_PATH)) {
|
|
31620
32034
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31621
32035
|
process.exit(1);
|
|
31622
32036
|
}
|
|
@@ -31678,9 +32092,9 @@ __export(voice_exports, {
|
|
|
31678
32092
|
voiceGet: () => voiceGet,
|
|
31679
32093
|
voiceSet: () => voiceSet
|
|
31680
32094
|
});
|
|
31681
|
-
import { existsSync as
|
|
32095
|
+
import { existsSync as existsSync48 } from "fs";
|
|
31682
32096
|
async function voiceGet(globalOpts) {
|
|
31683
|
-
if (!
|
|
32097
|
+
if (!existsSync48(DB_PATH)) {
|
|
31684
32098
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31685
32099
|
process.exit(1);
|
|
31686
32100
|
}
|
|
@@ -31729,9 +32143,9 @@ __export(heartbeat_exports, {
|
|
|
31729
32143
|
heartbeatGet: () => heartbeatGet,
|
|
31730
32144
|
heartbeatSet: () => heartbeatSet
|
|
31731
32145
|
});
|
|
31732
|
-
import { existsSync as
|
|
32146
|
+
import { existsSync as existsSync49 } from "fs";
|
|
31733
32147
|
async function heartbeatGet(globalOpts) {
|
|
31734
|
-
if (!
|
|
32148
|
+
if (!existsSync49(DB_PATH)) {
|
|
31735
32149
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31736
32150
|
process.exit(1);
|
|
31737
32151
|
}
|
|
@@ -31746,8 +32160,25 @@ async function heartbeatGet(globalOpts) {
|
|
|
31746
32160
|
intervalMinutes: row.interval_ms / 6e4,
|
|
31747
32161
|
activeStart: row.active_start,
|
|
31748
32162
|
activeEnd: row.active_end,
|
|
31749
|
-
lastBeatAt: row.last_beat_at
|
|
31750
|
-
|
|
32163
|
+
lastBeatAt: row.last_beat_at,
|
|
32164
|
+
backend: row.backend ?? null,
|
|
32165
|
+
model: row.model ?? null,
|
|
32166
|
+
fallbacks: row.fallbacks ?? null,
|
|
32167
|
+
thinking: row.thinking ?? null,
|
|
32168
|
+
target: row.target ?? null
|
|
32169
|
+
} : {
|
|
32170
|
+
enabled: false,
|
|
32171
|
+
intervalMs: 18e5,
|
|
32172
|
+
intervalMinutes: 30,
|
|
32173
|
+
activeStart: "08:00",
|
|
32174
|
+
activeEnd: "22:00",
|
|
32175
|
+
lastBeatAt: null,
|
|
32176
|
+
backend: null,
|
|
32177
|
+
model: null,
|
|
32178
|
+
fallbacks: null,
|
|
32179
|
+
thinking: null,
|
|
32180
|
+
target: null
|
|
32181
|
+
};
|
|
31751
32182
|
output(data, (d) => {
|
|
31752
32183
|
const h = d;
|
|
31753
32184
|
const lines = [
|
|
@@ -31757,9 +32188,30 @@ async function heartbeatGet(globalOpts) {
|
|
|
31757
32188
|
kvLine("Status", h.enabled ? success("on") : muted("off")),
|
|
31758
32189
|
kvLine("Interval", `${h.intervalMinutes}m`),
|
|
31759
32190
|
kvLine("Active hours", `${h.activeStart} - ${h.activeEnd}`),
|
|
31760
|
-
kvLine("
|
|
31761
|
-
""
|
|
32191
|
+
kvLine("Backend", h.backend ?? muted("default")),
|
|
32192
|
+
kvLine("Model", h.model ?? muted("default")),
|
|
32193
|
+
kvLine("Thinking", h.thinking ?? muted("off"))
|
|
31762
32194
|
];
|
|
32195
|
+
if (h.fallbacks) {
|
|
32196
|
+
try {
|
|
32197
|
+
const parsed = JSON.parse(h.fallbacks);
|
|
32198
|
+
const chain = parsed.map((f) => `${f.backend}${f.model ? `:${f.model}` : ""}`).join(" \u2192 ");
|
|
32199
|
+
lines.push(kvLine("Fallbacks", chain));
|
|
32200
|
+
} catch {
|
|
32201
|
+
lines.push(kvLine("Fallbacks", h.fallbacks));
|
|
32202
|
+
}
|
|
32203
|
+
} else {
|
|
32204
|
+
lines.push(kvLine("Fallbacks", muted("none")));
|
|
32205
|
+
}
|
|
32206
|
+
if (h.target) {
|
|
32207
|
+
lines.push(kvLine("Target", h.target));
|
|
32208
|
+
}
|
|
32209
|
+
lines.push(kvLine("Last beat", h.lastBeatAt ?? muted("never")));
|
|
32210
|
+
if (!h.enabled) {
|
|
32211
|
+
lines.push("");
|
|
32212
|
+
lines.push(` ${warning("\u26A0 Heartbeat is disabled \u2014 you won't receive proactive alerts.")}`);
|
|
32213
|
+
}
|
|
32214
|
+
lines.push("");
|
|
31763
32215
|
return lines.join("\n");
|
|
31764
32216
|
});
|
|
31765
32217
|
}
|
|
@@ -31781,7 +32233,7 @@ async function heartbeatSet(globalOpts, subcommand, value) {
|
|
|
31781
32233
|
body.enabled = false;
|
|
31782
32234
|
successMsg = "Heartbeat disabled.";
|
|
31783
32235
|
break;
|
|
31784
|
-
case "interval":
|
|
32236
|
+
case "interval": {
|
|
31785
32237
|
if (!value) {
|
|
31786
32238
|
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set interval <value>");
|
|
31787
32239
|
process.exit(1);
|
|
@@ -31797,7 +32249,8 @@ async function heartbeatSet(globalOpts, subcommand, value) {
|
|
|
31797
32249
|
body.intervalMs = ms;
|
|
31798
32250
|
successMsg = `Heartbeat interval: ${ms / 6e4}m`;
|
|
31799
32251
|
break;
|
|
31800
|
-
|
|
32252
|
+
}
|
|
32253
|
+
case "hours": {
|
|
31801
32254
|
if (!value) {
|
|
31802
32255
|
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set hours <start>-<end>");
|
|
31803
32256
|
process.exit(1);
|
|
@@ -31811,8 +32264,71 @@ async function heartbeatSet(globalOpts, subcommand, value) {
|
|
|
31811
32264
|
body.activeEnd = `${hourMatch[2].padStart(2, "0")}:00`;
|
|
31812
32265
|
successMsg = `Active hours: ${body.activeStart} - ${body.activeEnd}`;
|
|
31813
32266
|
break;
|
|
32267
|
+
}
|
|
32268
|
+
case "backend": {
|
|
32269
|
+
if (!value) {
|
|
32270
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set backend <name|default>");
|
|
32271
|
+
process.exit(1);
|
|
32272
|
+
}
|
|
32273
|
+
if (value === "default") {
|
|
32274
|
+
body.backend = null;
|
|
32275
|
+
body.model = null;
|
|
32276
|
+
successMsg = "Heartbeat backend: default (chat-level)";
|
|
32277
|
+
} else {
|
|
32278
|
+
body.backend = value;
|
|
32279
|
+
body.model = null;
|
|
32280
|
+
successMsg = `Heartbeat backend: ${value}`;
|
|
32281
|
+
}
|
|
32282
|
+
break;
|
|
32283
|
+
}
|
|
32284
|
+
case "model": {
|
|
32285
|
+
if (!value) {
|
|
32286
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set model <name|default>");
|
|
32287
|
+
process.exit(1);
|
|
32288
|
+
}
|
|
32289
|
+
if (value === "default") {
|
|
32290
|
+
body.model = null;
|
|
32291
|
+
successMsg = "Heartbeat model: default";
|
|
32292
|
+
} else {
|
|
32293
|
+
body.model = value;
|
|
32294
|
+
successMsg = `Heartbeat model: ${value}`;
|
|
32295
|
+
}
|
|
32296
|
+
break;
|
|
32297
|
+
}
|
|
32298
|
+
case "thinking": {
|
|
32299
|
+
if (!value) {
|
|
32300
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set thinking <off|low|medium|high>");
|
|
32301
|
+
process.exit(1);
|
|
32302
|
+
}
|
|
32303
|
+
const valid = ["off", "low", "medium", "high"];
|
|
32304
|
+
if (!valid.includes(value)) {
|
|
32305
|
+
outputError("INVALID_THINKING", `Valid values: ${valid.join(", ")}`);
|
|
32306
|
+
process.exit(1);
|
|
32307
|
+
}
|
|
32308
|
+
body.thinking = value === "off" ? null : value;
|
|
32309
|
+
successMsg = `Heartbeat thinking: ${value}`;
|
|
32310
|
+
break;
|
|
32311
|
+
}
|
|
32312
|
+
case "fallbacks": {
|
|
32313
|
+
if (!value) {
|
|
32314
|
+
outputError("MISSING_VALUE", "Usage: cc-claw heartbeat set fallbacks <backend1,backend2|clear>");
|
|
32315
|
+
process.exit(1);
|
|
32316
|
+
}
|
|
32317
|
+
if (value === "clear" || value === "none") {
|
|
32318
|
+
body.fallbacks = null;
|
|
32319
|
+
successMsg = "Heartbeat fallbacks: cleared";
|
|
32320
|
+
} else {
|
|
32321
|
+
const chain = value.split(",").map((s) => {
|
|
32322
|
+
const [backend2, model2] = s.trim().split(":");
|
|
32323
|
+
return model2 ? { backend: backend2, model: model2 } : { backend: backend2 };
|
|
32324
|
+
});
|
|
32325
|
+
body.fallbacks = JSON.stringify(chain);
|
|
32326
|
+
successMsg = `Heartbeat fallbacks: ${chain.map((f) => f.backend).join(" \u2192 ")}`;
|
|
32327
|
+
}
|
|
32328
|
+
break;
|
|
32329
|
+
}
|
|
31814
32330
|
default:
|
|
31815
|
-
outputError("UNKNOWN_SUBCOMMAND", `Unknown: "${subcommand}". Use: on, off, interval, hours`);
|
|
32331
|
+
outputError("UNKNOWN_SUBCOMMAND", `Unknown: "${subcommand}". Use: on, off, interval, hours, backend, model, thinking, fallbacks`);
|
|
31816
32332
|
process.exit(1);
|
|
31817
32333
|
}
|
|
31818
32334
|
const res = await apiPost2("/api/heartbeat/set", body);
|
|
@@ -31840,9 +32356,9 @@ __export(summarizer_exports, {
|
|
|
31840
32356
|
summarizerGet: () => summarizerGet,
|
|
31841
32357
|
summarizerSet: () => summarizerSet
|
|
31842
32358
|
});
|
|
31843
|
-
import { existsSync as
|
|
32359
|
+
import { existsSync as existsSync50 } from "fs";
|
|
31844
32360
|
async function summarizerGet(globalOpts) {
|
|
31845
|
-
if (!
|
|
32361
|
+
if (!existsSync50(DB_PATH)) {
|
|
31846
32362
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31847
32363
|
process.exit(1);
|
|
31848
32364
|
}
|
|
@@ -31886,9 +32402,9 @@ __export(thinking_exports, {
|
|
|
31886
32402
|
thinkingGet: () => thinkingGet,
|
|
31887
32403
|
thinkingSet: () => thinkingSet
|
|
31888
32404
|
});
|
|
31889
|
-
import { existsSync as
|
|
32405
|
+
import { existsSync as existsSync51 } from "fs";
|
|
31890
32406
|
async function thinkingGet(globalOpts) {
|
|
31891
|
-
if (!
|
|
32407
|
+
if (!existsSync51(DB_PATH)) {
|
|
31892
32408
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31893
32409
|
process.exit(1);
|
|
31894
32410
|
}
|
|
@@ -31932,9 +32448,9 @@ __export(chats_exports, {
|
|
|
31932
32448
|
chatsList: () => chatsList,
|
|
31933
32449
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
31934
32450
|
});
|
|
31935
|
-
import { existsSync as
|
|
32451
|
+
import { existsSync as existsSync52 } from "fs";
|
|
31936
32452
|
async function chatsList(_globalOpts) {
|
|
31937
|
-
if (!
|
|
32453
|
+
if (!existsSync52(DB_PATH)) {
|
|
31938
32454
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31939
32455
|
process.exit(1);
|
|
31940
32456
|
}
|
|
@@ -32062,9 +32578,9 @@ var mcps_exports2 = {};
|
|
|
32062
32578
|
__export(mcps_exports2, {
|
|
32063
32579
|
mcpsList: () => mcpsList
|
|
32064
32580
|
});
|
|
32065
|
-
import { existsSync as
|
|
32581
|
+
import { existsSync as existsSync53 } from "fs";
|
|
32066
32582
|
async function mcpsList(_globalOpts) {
|
|
32067
|
-
if (!
|
|
32583
|
+
if (!existsSync53(DB_PATH)) {
|
|
32068
32584
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32069
32585
|
process.exit(1);
|
|
32070
32586
|
}
|
|
@@ -32101,11 +32617,11 @@ __export(chat_exports2, {
|
|
|
32101
32617
|
chatSend: () => chatSend
|
|
32102
32618
|
});
|
|
32103
32619
|
import { request as httpRequest2 } from "http";
|
|
32104
|
-
import { readFileSync as
|
|
32620
|
+
import { readFileSync as readFileSync27, existsSync as existsSync54 } from "fs";
|
|
32105
32621
|
function getToken2() {
|
|
32106
32622
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
32107
32623
|
try {
|
|
32108
|
-
if (
|
|
32624
|
+
if (existsSync54(TOKEN_PATH2)) return readFileSync27(TOKEN_PATH2, "utf-8").trim();
|
|
32109
32625
|
} catch {
|
|
32110
32626
|
}
|
|
32111
32627
|
return null;
|
|
@@ -32785,9 +33301,9 @@ __export(evolve_exports2, {
|
|
|
32785
33301
|
evolveStatus: () => evolveStatus,
|
|
32786
33302
|
evolveUndo: () => evolveUndo
|
|
32787
33303
|
});
|
|
32788
|
-
import { existsSync as
|
|
33304
|
+
import { existsSync as existsSync55 } from "fs";
|
|
32789
33305
|
function ensureDb3() {
|
|
32790
|
-
if (!
|
|
33306
|
+
if (!existsSync55(DB_PATH)) {
|
|
32791
33307
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32792
33308
|
process.exit(1);
|
|
32793
33309
|
}
|
|
@@ -33261,8 +33777,8 @@ var init_optimize2 = __esm({
|
|
|
33261
33777
|
|
|
33262
33778
|
// src/setup.ts
|
|
33263
33779
|
var setup_exports = {};
|
|
33264
|
-
import { existsSync as
|
|
33265
|
-
import { execFileSync as
|
|
33780
|
+
import { existsSync as existsSync56, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
33781
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
33266
33782
|
import { createInterface as createInterface11 } from "readline";
|
|
33267
33783
|
import { join as join35 } from "path";
|
|
33268
33784
|
function divider2() {
|
|
@@ -33326,7 +33842,7 @@ async function setup() {
|
|
|
33326
33842
|
let foundAnyBackend = false;
|
|
33327
33843
|
for (const bk of backends) {
|
|
33328
33844
|
try {
|
|
33329
|
-
const path =
|
|
33845
|
+
const path = execFileSync6("which", [bk.cmd], { encoding: "utf-8" }).trim();
|
|
33330
33846
|
console.log(green(` ${bk.name} CLI found: ${path}`));
|
|
33331
33847
|
foundAnyBackend = true;
|
|
33332
33848
|
} catch {
|
|
@@ -33339,21 +33855,21 @@ async function setup() {
|
|
|
33339
33855
|
}
|
|
33340
33856
|
console.log("");
|
|
33341
33857
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
33342
|
-
if (!
|
|
33858
|
+
if (!existsSync56(dir)) mkdirSync18(dir, { recursive: true });
|
|
33343
33859
|
}
|
|
33344
33860
|
const env = {};
|
|
33345
|
-
const envSource =
|
|
33861
|
+
const envSource = existsSync56(ENV_PATH) ? ENV_PATH : existsSync56(".env") ? ".env" : null;
|
|
33346
33862
|
if (envSource) {
|
|
33347
33863
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
33348
33864
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
33349
|
-
const existing =
|
|
33865
|
+
const existing = readFileSync28(envSource, "utf-8");
|
|
33350
33866
|
for (const line of existing.split("\n")) {
|
|
33351
33867
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
33352
33868
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
33353
33869
|
}
|
|
33354
33870
|
}
|
|
33355
33871
|
const cwdDb = join35(process.cwd(), "cc-claw.db");
|
|
33356
|
-
if (
|
|
33872
|
+
if (existsSync56(cwdDb) && !existsSync56(DB_PATH)) {
|
|
33357
33873
|
const { size } = statSync12(cwdDb);
|
|
33358
33874
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
33359
33875
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
@@ -34073,7 +34589,7 @@ heartbeat.command("get").description("Show heartbeat status and config").action(
|
|
|
34073
34589
|
const { heartbeatGet: heartbeatGet2 } = await Promise.resolve().then(() => (init_heartbeat3(), heartbeat_exports));
|
|
34074
34590
|
await heartbeatGet2(program.opts());
|
|
34075
34591
|
});
|
|
34076
|
-
heartbeat.command("set <subcommand> [value]").description("Configure heartbeat (on/off/interval
|
|
34592
|
+
heartbeat.command("set <subcommand> [value]").description("Configure heartbeat (on/off/interval/hours/backend/model/thinking/fallbacks)").action(async (subcommand, value) => {
|
|
34077
34593
|
const { heartbeatSet: heartbeatSet2 } = await Promise.resolve().then(() => (init_heartbeat3(), heartbeat_exports));
|
|
34078
34594
|
await heartbeatSet2(program.opts(), subcommand, value);
|
|
34079
34595
|
});
|
|
@@ -34246,8 +34762,8 @@ async function run(argv = process.argv) {
|
|
|
34246
34762
|
if (argv.includes("--version") || argv.includes("-V")) {
|
|
34247
34763
|
console.log(VERSION);
|
|
34248
34764
|
try {
|
|
34249
|
-
const { execFileSync:
|
|
34250
|
-
const latest =
|
|
34765
|
+
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
34766
|
+
const latest = execFileSync7("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
34251
34767
|
if (latest && latest !== VERSION) {
|
|
34252
34768
|
console.log(`
|
|
34253
34769
|
Update available: v${latest} (current: v${VERSION})`);
|