cc-claw 0.20.20 → 0.21.0
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 +837 -522
- 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.
|
|
36
|
+
VERSION = true ? "0.21.0" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -59,6 +59,7 @@ __export(paths_exports, {
|
|
|
59
59
|
RUNNERS_PATH: () => RUNNERS_PATH,
|
|
60
60
|
SESSION_LOGS_PATH: () => SESSION_LOGS_PATH,
|
|
61
61
|
SKILLS_PATH: () => SKILLS_PATH,
|
|
62
|
+
WHISPER_MODELS_PATH: () => WHISPER_MODELS_PATH,
|
|
62
63
|
WORKSPACE_PATH: () => WORKSPACE_PATH
|
|
63
64
|
});
|
|
64
65
|
import { homedir, userInfo } from "os";
|
|
@@ -71,7 +72,7 @@ function resolveRealHome() {
|
|
|
71
72
|
}
|
|
72
73
|
return homedir();
|
|
73
74
|
}
|
|
74
|
-
var CC_CLAW_HOME, ENV_PATH, DATA_PATH, DB_PATH, LOGS_PATH, LOG_PATH, ERROR_LOG_PATH, IDENTITY_PATH, WORKSPACE_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH, SESSION_LOGS_PATH, MEDIA_PATH;
|
|
75
|
+
var CC_CLAW_HOME, ENV_PATH, DATA_PATH, DB_PATH, LOGS_PATH, LOG_PATH, ERROR_LOG_PATH, IDENTITY_PATH, WORKSPACE_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH, SESSION_LOGS_PATH, MEDIA_PATH, WHISPER_MODELS_PATH;
|
|
75
76
|
var init_paths = __esm({
|
|
76
77
|
"src/paths.ts"() {
|
|
77
78
|
"use strict";
|
|
@@ -89,6 +90,7 @@ var init_paths = __esm({
|
|
|
89
90
|
AGENTS_PATH = join2(CC_CLAW_HOME, "agents");
|
|
90
91
|
SESSION_LOGS_PATH = join2(LOGS_PATH, "sessions");
|
|
91
92
|
MEDIA_PATH = join2(CC_CLAW_HOME, "media");
|
|
93
|
+
WHISPER_MODELS_PATH = join2(DATA_PATH, "whisper-models");
|
|
92
94
|
}
|
|
93
95
|
});
|
|
94
96
|
|
|
@@ -1592,6 +1594,14 @@ function initSchema(db3) {
|
|
|
1592
1594
|
db3.exec(`ALTER TABLE chat_voice ADD COLUMN voice_id TEXT`);
|
|
1593
1595
|
} catch {
|
|
1594
1596
|
}
|
|
1597
|
+
try {
|
|
1598
|
+
db3.exec(`ALTER TABLE chat_voice ADD COLUMN stt_provider TEXT DEFAULT 'groq'`);
|
|
1599
|
+
} catch {
|
|
1600
|
+
}
|
|
1601
|
+
try {
|
|
1602
|
+
db3.exec(`ALTER TABLE chat_voice ADD COLUMN stt_model TEXT DEFAULT 'small.en'`);
|
|
1603
|
+
} catch {
|
|
1604
|
+
}
|
|
1595
1605
|
db3.exec(`
|
|
1596
1606
|
CREATE TABLE IF NOT EXISTS backend_limits (
|
|
1597
1607
|
backend TEXT NOT NULL,
|
|
@@ -4307,7 +4317,7 @@ var init_resolve_executable = __esm({
|
|
|
4307
4317
|
});
|
|
4308
4318
|
|
|
4309
4319
|
// src/backends/claude.ts
|
|
4310
|
-
import { existsSync as existsSync3 } from "fs";
|
|
4320
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
4311
4321
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4312
4322
|
import { join as join4 } from "path";
|
|
4313
4323
|
var ADAPTIVE_MODELS, ClaudeAdapter;
|
|
@@ -4433,6 +4443,13 @@ var init_claude = __esm({
|
|
|
4433
4443
|
} else if (slot.slotType === "oauth" && slot.configHome) {
|
|
4434
4444
|
env.HOME = slot.configHome;
|
|
4435
4445
|
delete env.ANTHROPIC_API_KEY;
|
|
4446
|
+
try {
|
|
4447
|
+
const credsPath = join4(slot.configHome, ".claude", ".credentials.json");
|
|
4448
|
+
const creds = JSON.parse(readFileSync2(credsPath, "utf-8"));
|
|
4449
|
+
const token = creds?.claudeAiOauth?.accessToken;
|
|
4450
|
+
if (token) env.CLAUDE_CODE_OAUTH_TOKEN = token;
|
|
4451
|
+
} catch {
|
|
4452
|
+
}
|
|
4436
4453
|
}
|
|
4437
4454
|
return env;
|
|
4438
4455
|
}
|
|
@@ -6645,7 +6662,11 @@ function djb2Hash(str) {
|
|
|
6645
6662
|
return hash.toString(36);
|
|
6646
6663
|
}
|
|
6647
6664
|
function extractKeyField(input) {
|
|
6648
|
-
|
|
6665
|
+
if (input.pattern !== void 0) {
|
|
6666
|
+
const loc = input.path ?? input.file ?? "";
|
|
6667
|
+
return `${input.pattern}@${loc}`;
|
|
6668
|
+
}
|
|
6669
|
+
const key = input.file_path ?? input.path ?? input.file ?? input.command ?? input.cmd ?? input.url ?? input.query ?? input.search_query ?? input.content;
|
|
6649
6670
|
if (key !== void 0 && key !== null) {
|
|
6650
6671
|
return String(key);
|
|
6651
6672
|
}
|
|
@@ -7039,7 +7060,7 @@ import {
|
|
|
7039
7060
|
existsSync as existsSync7,
|
|
7040
7061
|
writeFileSync,
|
|
7041
7062
|
mkdirSync as mkdirSync3,
|
|
7042
|
-
readFileSync as
|
|
7063
|
+
readFileSync as readFileSync3,
|
|
7043
7064
|
statSync as statSync3,
|
|
7044
7065
|
copyFileSync as copyFileSync2,
|
|
7045
7066
|
unlinkSync as unlinkSync2
|
|
@@ -7049,7 +7070,7 @@ function migrateFile(legacyPath, newPath, label2) {
|
|
|
7049
7070
|
if (existsSync7(newPath)) return false;
|
|
7050
7071
|
if (existsSync7(legacyPath)) {
|
|
7051
7072
|
copyFileSync2(legacyPath, newPath);
|
|
7052
|
-
const copied =
|
|
7073
|
+
const copied = readFileSync3(newPath, "utf-8");
|
|
7053
7074
|
if (copied.length > 0) {
|
|
7054
7075
|
unlinkSync2(legacyPath);
|
|
7055
7076
|
log(`[bootstrap] Migrated ${label2} from workspace/ to identity/`);
|
|
@@ -7132,18 +7153,18 @@ function syncNativeCliFiles() {
|
|
|
7132
7153
|
let soul = "";
|
|
7133
7154
|
let user = "";
|
|
7134
7155
|
try {
|
|
7135
|
-
soul =
|
|
7156
|
+
soul = readFileSync3(SOUL_PATH, "utf-8").trim();
|
|
7136
7157
|
} catch {
|
|
7137
7158
|
try {
|
|
7138
|
-
soul =
|
|
7159
|
+
soul = readFileSync3(LEGACY_SOUL_PATH, "utf-8").trim();
|
|
7139
7160
|
} catch {
|
|
7140
7161
|
}
|
|
7141
7162
|
}
|
|
7142
7163
|
try {
|
|
7143
|
-
user =
|
|
7164
|
+
user = readFileSync3(USER_PATH, "utf-8").trim();
|
|
7144
7165
|
} catch {
|
|
7145
7166
|
try {
|
|
7146
|
-
user =
|
|
7167
|
+
user = readFileSync3(LEGACY_USER_PATH, "utf-8").trim();
|
|
7147
7168
|
} catch {
|
|
7148
7169
|
}
|
|
7149
7170
|
}
|
|
@@ -7241,7 +7262,7 @@ var init_init = __esm({
|
|
|
7241
7262
|
});
|
|
7242
7263
|
|
|
7243
7264
|
// src/bootstrap/loader.ts
|
|
7244
|
-
import { readFileSync as
|
|
7265
|
+
import { readFileSync as readFileSync4, existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
|
|
7245
7266
|
import { join as join8 } from "path";
|
|
7246
7267
|
function loadContextFiles() {
|
|
7247
7268
|
if (contextCache && Date.now() - contextCache.timestamp < CONTEXT_CACHE_TTL_MS) {
|
|
@@ -7252,7 +7273,7 @@ function loadContextFiles() {
|
|
|
7252
7273
|
const entries = readdirSync3(CONTEXT_DIR2).filter((f) => f.endsWith(".md"));
|
|
7253
7274
|
for (const entry of entries) {
|
|
7254
7275
|
try {
|
|
7255
|
-
const content =
|
|
7276
|
+
const content = readFileSync4(join8(CONTEXT_DIR2, entry), "utf-8").trim();
|
|
7256
7277
|
if (content) files.set(entry, content);
|
|
7257
7278
|
} catch {
|
|
7258
7279
|
continue;
|
|
@@ -8800,8 +8821,7 @@ var init_helpers = __esm({
|
|
|
8800
8821
|
{ cmd: "/runners", desc: "List CLI runners" }
|
|
8801
8822
|
],
|
|
8802
8823
|
Settings: [
|
|
8803
|
-
{ cmd: "/voice", desc: "
|
|
8804
|
-
{ cmd: "/voice_config", desc: "Configure voice provider" },
|
|
8824
|
+
{ cmd: "/voice", desc: "Voice settings (STT transcription + TTS replies)" },
|
|
8805
8825
|
{ cmd: "/cwd", desc: "Set working directory" },
|
|
8806
8826
|
{ cmd: "/tools", desc: "Configure allowed tools" },
|
|
8807
8827
|
{ cmd: "/verbose", desc: "Tool visibility level" },
|
|
@@ -8889,7 +8909,7 @@ You have access to cc-claw orchestrator tools via MCP:
|
|
|
8889
8909
|
// src/agents/spawn.ts
|
|
8890
8910
|
import { spawn as spawn2 } from "child_process";
|
|
8891
8911
|
import { createInterface as createInterface2 } from "readline";
|
|
8892
|
-
import { readFileSync as
|
|
8912
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
8893
8913
|
function stripFrontmatter(text) {
|
|
8894
8914
|
const lines = text.split("\n");
|
|
8895
8915
|
if (lines.length < 2 || lines[0].trim() !== "---") return text;
|
|
@@ -8905,7 +8925,7 @@ function buildAgentPrompt(opts, runnerSkillPath) {
|
|
|
8905
8925
|
const parts = [];
|
|
8906
8926
|
if (runnerSkillPath) {
|
|
8907
8927
|
try {
|
|
8908
|
-
const raw =
|
|
8928
|
+
const raw = readFileSync5(runnerSkillPath, "utf-8");
|
|
8909
8929
|
const content = stripFrontmatter(raw).trim();
|
|
8910
8930
|
if (content) parts.push(content);
|
|
8911
8931
|
parts.push("");
|
|
@@ -9288,7 +9308,7 @@ __export(loader_exports, {
|
|
|
9288
9308
|
getTemplate: () => getTemplate,
|
|
9289
9309
|
listTemplates: () => listTemplates
|
|
9290
9310
|
});
|
|
9291
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
9311
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync6 } from "fs";
|
|
9292
9312
|
import { join as join10 } from "path";
|
|
9293
9313
|
function parseFrontmatter(content) {
|
|
9294
9314
|
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/m);
|
|
@@ -9329,7 +9349,7 @@ function scanTemplates() {
|
|
|
9329
9349
|
for (const file of files) {
|
|
9330
9350
|
const filePath = join10(AGENTS_PATH, file);
|
|
9331
9351
|
try {
|
|
9332
|
-
const raw =
|
|
9352
|
+
const raw = readFileSync6(filePath, "utf-8");
|
|
9333
9353
|
const { meta, body } = parseFrontmatter(raw);
|
|
9334
9354
|
const name = meta.name ?? file.replace(/\.md$/, "");
|
|
9335
9355
|
const template = {
|
|
@@ -11562,7 +11582,7 @@ __export(analyze_exports, {
|
|
|
11562
11582
|
});
|
|
11563
11583
|
import { spawn as spawn4 } from "child_process";
|
|
11564
11584
|
import { createInterface as createInterface3 } from "readline";
|
|
11565
|
-
import { readFileSync as
|
|
11585
|
+
import { readFileSync as readFileSync7, existsSync as existsSync12, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
|
|
11566
11586
|
import { join as join12 } from "path";
|
|
11567
11587
|
import { homedir as homedir4 } from "os";
|
|
11568
11588
|
function applySignalDecay(confidence, createdAt) {
|
|
@@ -11584,7 +11604,7 @@ function discoverReflectionTargets() {
|
|
|
11584
11604
|
if (!existsSync12(skillFile)) continue;
|
|
11585
11605
|
let desc = "skill";
|
|
11586
11606
|
try {
|
|
11587
|
-
const content =
|
|
11607
|
+
const content = readFileSync7(skillFile, "utf-8");
|
|
11588
11608
|
const descMatch = content.match(/description:\s*["']?([^"'\n]+)/);
|
|
11589
11609
|
if (descMatch) desc = descMatch[1].trim().slice(0, 80);
|
|
11590
11610
|
} catch {
|
|
@@ -11793,7 +11813,7 @@ function resolveReflectionAdapter(chatId) {
|
|
|
11793
11813
|
}
|
|
11794
11814
|
function readIdentityFile(filename) {
|
|
11795
11815
|
try {
|
|
11796
|
-
return
|
|
11816
|
+
return readFileSync7(join12(IDENTITY_PATH, filename), "utf-8");
|
|
11797
11817
|
} catch {
|
|
11798
11818
|
return "";
|
|
11799
11819
|
}
|
|
@@ -11952,7 +11972,7 @@ async function runAnalysisImpl(chatId, opts) {
|
|
|
11952
11972
|
try {
|
|
11953
11973
|
const fullPath = join12(ccClawHome, target.path);
|
|
11954
11974
|
if (existsSync12(fullPath)) {
|
|
11955
|
-
const content =
|
|
11975
|
+
const content = readFileSync7(fullPath, "utf-8");
|
|
11956
11976
|
if (totalSkillChars + content.length > SKILL_CONTENT_CAP) break;
|
|
11957
11977
|
skillContents.push({ path: target.path, content });
|
|
11958
11978
|
totalSkillChars += content.length;
|
|
@@ -12332,7 +12352,7 @@ __export(apply_exports, {
|
|
|
12332
12352
|
isTargetAllowed: () => isTargetAllowed,
|
|
12333
12353
|
rollbackInsight: () => rollbackInsight
|
|
12334
12354
|
});
|
|
12335
|
-
import { readFileSync as
|
|
12355
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
|
|
12336
12356
|
import { join as join13, dirname as dirname3 } from "path";
|
|
12337
12357
|
function isTargetAllowed(relativePath) {
|
|
12338
12358
|
if (relativePath.includes("..")) return false;
|
|
@@ -12416,7 +12436,7 @@ async function applyInsight(insightId) {
|
|
|
12416
12436
|
const absolutePath = join13(CC_CLAW_HOME, insight.targetFile);
|
|
12417
12437
|
if (insight.proposedAction === "append" && insight.targetFile === "identity/SOUL.md") {
|
|
12418
12438
|
if (existsSync13(absolutePath)) {
|
|
12419
|
-
const currentContent =
|
|
12439
|
+
const currentContent = readFileSync8(absolutePath, "utf-8");
|
|
12420
12440
|
const lineCount = currentContent.split("\n").length;
|
|
12421
12441
|
if (lineCount >= SOUL_LINE_CAP) {
|
|
12422
12442
|
return {
|
|
@@ -12438,7 +12458,7 @@ async function applyInsight(insightId) {
|
|
|
12438
12458
|
}
|
|
12439
12459
|
let original = "";
|
|
12440
12460
|
if (existsSync13(absolutePath)) {
|
|
12441
|
-
original =
|
|
12461
|
+
original = readFileSync8(absolutePath, "utf-8");
|
|
12442
12462
|
} else if (insight.proposedAction !== "create") {
|
|
12443
12463
|
return { success: false, message: `Target file "${insight.targetFile}" does not exist` };
|
|
12444
12464
|
}
|
|
@@ -12578,7 +12598,7 @@ function computeLineDrift(baseline, absolutePath) {
|
|
|
12578
12598
|
let current = "";
|
|
12579
12599
|
try {
|
|
12580
12600
|
if (existsSync13(absolutePath)) {
|
|
12581
|
-
current =
|
|
12601
|
+
current = readFileSync8(absolutePath, "utf-8");
|
|
12582
12602
|
}
|
|
12583
12603
|
} catch {
|
|
12584
12604
|
return 1;
|
|
@@ -12677,12 +12697,12 @@ var init_evolve = __esm({
|
|
|
12677
12697
|
const body = JSON.parse(await readBody(req));
|
|
12678
12698
|
const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
12679
12699
|
const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
|
|
12680
|
-
const { join:
|
|
12700
|
+
const { join: join38 } = await import("path");
|
|
12681
12701
|
const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
12682
12702
|
const chatId = resolveChatId(body);
|
|
12683
12703
|
if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
|
|
12684
|
-
const soulPath =
|
|
12685
|
-
const userPath =
|
|
12704
|
+
const soulPath = join38(home, "identity/SOUL.md");
|
|
12705
|
+
const userPath = join38(home, "identity/USER.md");
|
|
12686
12706
|
const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
|
|
12687
12707
|
const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
|
|
12688
12708
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
@@ -13658,6 +13678,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13658
13678
|
let sawToolEvents = false;
|
|
13659
13679
|
let sawResultEvent = false;
|
|
13660
13680
|
let toolTurnCount = 0;
|
|
13681
|
+
let loopKillReason;
|
|
13661
13682
|
const loopDetector = new ToolLoopDetector();
|
|
13662
13683
|
const t0 = Date.now();
|
|
13663
13684
|
const elapsed = () => `${((Date.now() - t0) / 1e3).toFixed(1)}s`;
|
|
@@ -13694,6 +13715,12 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13694
13715
|
if (contentSilenceTimer) clearTimeout(contentSilenceTimer);
|
|
13695
13716
|
contentSilenceTimer = setTimeout(() => {
|
|
13696
13717
|
if (cancelState.cancelled || timedOut) return;
|
|
13718
|
+
if (pendingTools.size > 0) {
|
|
13719
|
+
const tools2 = Array.from(pendingTools.values()).map((t) => typeof t === "string" ? t : t.name).join(", ");
|
|
13720
|
+
log(`[agent] Content silence timer fired but ${pendingTools.size} tool(s) still running (${tools2}) \u2014 resetting`);
|
|
13721
|
+
resetContentSilenceTimer();
|
|
13722
|
+
return;
|
|
13723
|
+
}
|
|
13697
13724
|
warn(`[agent] Content silence timeout after ${silenceTimeoutMs / 1e3}s for ${adapter.id} \u2014 no content events, killing`);
|
|
13698
13725
|
timedOut = true;
|
|
13699
13726
|
timeoutState.contentSilence = true;
|
|
@@ -13772,6 +13799,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13772
13799
|
const check = loopDetector.addCall(ev.toolName, ev.toolInput ?? {});
|
|
13773
13800
|
if (check.isLoop) {
|
|
13774
13801
|
warn(`[agent] Loop detected for ${adapter.id}: ${check.reason} \u2014 stopping`);
|
|
13802
|
+
loopKillReason = check.reason;
|
|
13775
13803
|
killProcessGroup(proc, "SIGTERM");
|
|
13776
13804
|
}
|
|
13777
13805
|
}
|
|
@@ -13896,8 +13924,12 @@ Partial output: ${accumulatedText.slice(-500)}`;
|
|
|
13896
13924
|
return;
|
|
13897
13925
|
}
|
|
13898
13926
|
if (code && code !== 0 && !cancelState.cancelled && !resultText) {
|
|
13899
|
-
|
|
13900
|
-
|
|
13927
|
+
if (code === 143 && loopKillReason) {
|
|
13928
|
+
reject(new Error(`Stopped: agent was repeating the same action (${loopKillReason}). Try rephrasing your request.`));
|
|
13929
|
+
} else {
|
|
13930
|
+
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
13931
|
+
reject(new Error(`CLI exited with code ${code}${stderr ? `: ${stderr.slice(0, 500)}` : ""}`));
|
|
13932
|
+
}
|
|
13901
13933
|
return;
|
|
13902
13934
|
}
|
|
13903
13935
|
const cleanedResult = stripThinkingContent(resultText || accumulatedText);
|
|
@@ -14448,7 +14480,7 @@ var init_agent = __esm({
|
|
|
14448
14480
|
chatLocks = /* @__PURE__ */ new Map();
|
|
14449
14481
|
SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
14450
14482
|
FIRST_RESPONSE_TIMEOUT_MS = parseInt(process.env.GEMINI_FIRST_RESPONSE_TIMEOUT_MS ?? "30000", 10);
|
|
14451
|
-
CONTENT_SILENCE_TIMEOUT_MS = parseInt(process.env.CONTENT_SILENCE_TIMEOUT_MS ?? "
|
|
14483
|
+
CONTENT_SILENCE_TIMEOUT_MS = parseInt(process.env.CONTENT_SILENCE_TIMEOUT_MS ?? "180000", 10);
|
|
14452
14484
|
CONTENT_SILENCE_TIMEOUT_ERROR = "CONTENT_SILENCE_TIMEOUT";
|
|
14453
14485
|
FIRST_RESPONSE_TIMEOUT_ERROR = "FIRST_RESPONSE_TIMEOUT";
|
|
14454
14486
|
FREE_SLOTS_EXHAUSTED = "FREE_SLOTS_EXHAUSTED";
|
|
@@ -14970,15 +15002,17 @@ var init_telegram_throttle = __esm({
|
|
|
14970
15002
|
* Used for cosmetic calls (typing indicators, reactions) that should count toward
|
|
14971
15003
|
* rate limits but must never queue up or amplify 429 spirals.
|
|
14972
15004
|
*/
|
|
14973
|
-
async tryBestEffort(chatId, label2, fn) {
|
|
15005
|
+
async tryBestEffort(chatId, label2, fn, opts) {
|
|
14974
15006
|
if (this.isPaused()) return void 0;
|
|
14975
15007
|
if (this.queue.length > 10) return void 0;
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
|
|
15008
|
+
if (!opts?.skipRecord) {
|
|
15009
|
+
const lastChat = this.lastSendPerChat.get(chatId) ?? 0;
|
|
15010
|
+
if (Date.now() - lastChat < PER_CHAT_INTERVAL_MS) return void 0;
|
|
15011
|
+
if (Date.now() - this.lastGlobalSend < GLOBAL_INTERVAL_MS) return void 0;
|
|
15012
|
+
}
|
|
14979
15013
|
try {
|
|
14980
15014
|
const result = await fn();
|
|
14981
|
-
this.recordSend(chatId);
|
|
15015
|
+
if (!opts?.skipRecord) this.recordSend(chatId);
|
|
14982
15016
|
return result;
|
|
14983
15017
|
} catch (err) {
|
|
14984
15018
|
if (is429(err)) {
|
|
@@ -15120,11 +15154,11 @@ var init_telegram_throttle = __esm({
|
|
|
15120
15154
|
});
|
|
15121
15155
|
|
|
15122
15156
|
// src/health/checks.ts
|
|
15123
|
-
import { existsSync as existsSync15, statSync as statSync6, readFileSync as
|
|
15157
|
+
import { existsSync as existsSync15, statSync as statSync6, readFileSync as readFileSync9 } from "fs";
|
|
15124
15158
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
15125
15159
|
function getRecentErrors() {
|
|
15126
15160
|
if (!existsSync15(ERROR_LOG_PATH)) return null;
|
|
15127
|
-
const logContent =
|
|
15161
|
+
const logContent = readFileSync9(ERROR_LOG_PATH, "utf-8");
|
|
15128
15162
|
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
15129
15163
|
const last24h = Date.now() - 864e5;
|
|
15130
15164
|
const lines = allLines.filter((line) => {
|
|
@@ -15353,7 +15387,7 @@ __export(heartbeat_exports, {
|
|
|
15353
15387
|
stopHeartbeatForChat: () => stopHeartbeatForChat,
|
|
15354
15388
|
updateHeartbeatConfig: () => updateHeartbeatConfig
|
|
15355
15389
|
});
|
|
15356
|
-
import { readFileSync as
|
|
15390
|
+
import { readFileSync as readFileSync10, existsSync as existsSync16 } from "fs";
|
|
15357
15391
|
import { join as join15 } from "path";
|
|
15358
15392
|
function findHeartbeatJob() {
|
|
15359
15393
|
try {
|
|
@@ -15487,7 +15521,7 @@ ${watchLines.join("\n")}`);
|
|
|
15487
15521
|
}
|
|
15488
15522
|
if (existsSync16(HEARTBEAT_MD_PATH)) {
|
|
15489
15523
|
try {
|
|
15490
|
-
const custom =
|
|
15524
|
+
const custom = readFileSync10(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
15491
15525
|
if (custom) {
|
|
15492
15526
|
sections.push(`[Custom checks from HEARTBEAT.md]
|
|
15493
15527
|
${custom}`);
|
|
@@ -15556,7 +15590,7 @@ var init_heartbeat2 = __esm({
|
|
|
15556
15590
|
});
|
|
15557
15591
|
|
|
15558
15592
|
// src/bootstrap/profile.ts
|
|
15559
|
-
import { readFileSync as
|
|
15593
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync17 } from "fs";
|
|
15560
15594
|
import { join as join16 } from "path";
|
|
15561
15595
|
function hasActiveProfile(chatId) {
|
|
15562
15596
|
return activeProfiles.has(chatId);
|
|
@@ -15687,7 +15721,7 @@ function extractUserUpdates(text) {
|
|
|
15687
15721
|
}
|
|
15688
15722
|
function appendToUserProfile(key, value) {
|
|
15689
15723
|
if (!existsSync17(USER_PATH2)) return;
|
|
15690
|
-
const content =
|
|
15724
|
+
const content = readFileSync11(USER_PATH2, "utf-8");
|
|
15691
15725
|
const line = `- **${key}**: ${value}`;
|
|
15692
15726
|
if (content.includes(line)) return;
|
|
15693
15727
|
const updated = content.trimEnd() + `
|
|
@@ -15796,8 +15830,9 @@ async function classifyWithOllama(text) {
|
|
|
15796
15830
|
(a, b) => (a.sizeBytes ?? Infinity) - (b.sizeBytes ?? Infinity)
|
|
15797
15831
|
);
|
|
15798
15832
|
const model2 = sorted[0].name;
|
|
15833
|
+
const baseUrl = ollamaStore.getBaseUrl(onlineServer);
|
|
15799
15834
|
const result = await ollamaClient.chat(
|
|
15800
|
-
|
|
15835
|
+
baseUrl,
|
|
15801
15836
|
model2,
|
|
15802
15837
|
[{ role: "user", content: LLM_CLASSIFY_PROMPT + text.slice(0, 500) }],
|
|
15803
15838
|
{ timeoutMs: LLM_CLASSIFY_TIMEOUT_MS, maxTokens: 5, temperature: 0 }
|
|
@@ -15822,7 +15857,7 @@ async function classifyWithSummarizerCli(text) {
|
|
|
15822
15857
|
const model2 = modelName ?? adapter.summarizerModel;
|
|
15823
15858
|
const { spawn: spawn8 } = await import("child_process");
|
|
15824
15859
|
const { resolveExecutable: resolveExecutable4 } = await Promise.resolve().then(() => (init_resolve_executable(), resolve_executable_exports));
|
|
15825
|
-
const exe = resolveExecutable4(adapter.id);
|
|
15860
|
+
const exe = resolveExecutable4({ envVar: `${adapter.id.toUpperCase()}_EXECUTABLE`, binaryName: adapter.id, candidates: [] });
|
|
15826
15861
|
if (!exe) return null;
|
|
15827
15862
|
return new Promise((resolve) => {
|
|
15828
15863
|
const timeout = setTimeout(() => {
|
|
@@ -17695,7 +17730,9 @@ var init_gate = __esm({
|
|
|
17695
17730
|
// src/voice/stt.ts
|
|
17696
17731
|
import crypto from "crypto";
|
|
17697
17732
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
17698
|
-
import { readFile as readFile2, unlink as unlink2 } from "fs/promises";
|
|
17733
|
+
import { readFile as readFile2, unlink as unlink2, mkdir as mkdir2, writeFile } from "fs/promises";
|
|
17734
|
+
import { existsSync as existsSync19 } from "fs";
|
|
17735
|
+
import { join as join18 } from "path";
|
|
17699
17736
|
import { promisify as promisify2 } from "util";
|
|
17700
17737
|
function ensureFfmpeg() {
|
|
17701
17738
|
if (ffmpegAvailable === true) return;
|
|
@@ -17740,11 +17777,120 @@ function setVoiceProvider(chatId, provider, voiceId) {
|
|
|
17740
17777
|
ON CONFLICT(chat_id) DO UPDATE SET provider = ?, voice_id = ?, enabled = 1
|
|
17741
17778
|
`).run(chatId, provider, voiceId, provider, voiceId);
|
|
17742
17779
|
}
|
|
17743
|
-
|
|
17780
|
+
function getSttProvider(chatId) {
|
|
17781
|
+
const db3 = getDb();
|
|
17782
|
+
const row = db3.prepare("SELECT stt_provider FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
17783
|
+
return row?.stt_provider ?? "groq";
|
|
17784
|
+
}
|
|
17785
|
+
function setSttProvider(chatId, provider) {
|
|
17786
|
+
const db3 = getDb();
|
|
17787
|
+
db3.prepare(`
|
|
17788
|
+
INSERT INTO chat_voice (chat_id, enabled, stt_provider) VALUES (?, 0, ?)
|
|
17789
|
+
ON CONFLICT(chat_id) DO UPDATE SET stt_provider = ?
|
|
17790
|
+
`).run(chatId, provider, provider);
|
|
17791
|
+
}
|
|
17792
|
+
function getSttModel(chatId) {
|
|
17793
|
+
const db3 = getDb();
|
|
17794
|
+
const row = db3.prepare("SELECT stt_model FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
17795
|
+
const model2 = row?.stt_model ?? "small.en";
|
|
17796
|
+
return model2 in LOCAL_WHISPER_MODELS ? model2 : "small.en";
|
|
17797
|
+
}
|
|
17798
|
+
function setSttModel(chatId, model2) {
|
|
17799
|
+
const db3 = getDb();
|
|
17800
|
+
db3.prepare(`
|
|
17801
|
+
INSERT INTO chat_voice (chat_id, enabled, stt_model) VALUES (?, 0, ?)
|
|
17802
|
+
ON CONFLICT(chat_id) DO UPDATE SET stt_model = ?
|
|
17803
|
+
`).run(chatId, model2, model2);
|
|
17804
|
+
}
|
|
17805
|
+
function isWhisperCliAvailable() {
|
|
17806
|
+
if (whisperCliAvailableCache !== null) return whisperCliAvailableCache;
|
|
17807
|
+
try {
|
|
17808
|
+
execFileSync2("whisper-cli", ["--help"], { stdio: "ignore" });
|
|
17809
|
+
whisperCliAvailableCache = true;
|
|
17810
|
+
} catch {
|
|
17811
|
+
try {
|
|
17812
|
+
execFileSync2("whisper", ["--help"], { stdio: "ignore" });
|
|
17813
|
+
whisperCliAvailableCache = true;
|
|
17814
|
+
} catch {
|
|
17815
|
+
whisperCliAvailableCache = false;
|
|
17816
|
+
}
|
|
17817
|
+
}
|
|
17818
|
+
return whisperCliAvailableCache;
|
|
17819
|
+
}
|
|
17820
|
+
function getWhisperBin() {
|
|
17821
|
+
try {
|
|
17822
|
+
execFileSync2("whisper-cli", ["--help"], { stdio: "ignore" });
|
|
17823
|
+
return "whisper-cli";
|
|
17824
|
+
} catch {
|
|
17825
|
+
}
|
|
17826
|
+
try {
|
|
17827
|
+
execFileSync2("whisper", ["--help"], { stdio: "ignore" });
|
|
17828
|
+
return "whisper";
|
|
17829
|
+
} catch {
|
|
17830
|
+
}
|
|
17831
|
+
return null;
|
|
17832
|
+
}
|
|
17833
|
+
function whisperModelPath(model2) {
|
|
17834
|
+
return join18(WHISPER_MODELS_PATH, `ggml-${model2}.bin`);
|
|
17835
|
+
}
|
|
17836
|
+
function isWhisperModelDownloaded(model2) {
|
|
17837
|
+
return existsSync19(whisperModelPath(model2));
|
|
17838
|
+
}
|
|
17839
|
+
async function downloadWhisperModel(model2, onProgress) {
|
|
17840
|
+
await mkdir2(WHISPER_MODELS_PATH, { recursive: true });
|
|
17841
|
+
const dest = whisperModelPath(model2);
|
|
17842
|
+
const url = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model2}.bin`;
|
|
17843
|
+
const info = LOCAL_WHISPER_MODELS[model2];
|
|
17844
|
+
onProgress?.(`\u2B07\uFE0F Downloading Whisper model ${model2} (${info.size})...`);
|
|
17845
|
+
log(`[stt] Downloading model ${model2} from ${url}`);
|
|
17846
|
+
const response = await fetch(url);
|
|
17847
|
+
if (!response.ok) throw new Error(`Failed to download model: ${response.status} ${response.statusText}`);
|
|
17848
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
17849
|
+
await writeFile(dest, Buffer.from(arrayBuffer));
|
|
17850
|
+
log(`[stt] Model ${model2} downloaded to ${dest}`);
|
|
17851
|
+
}
|
|
17852
|
+
async function transcribeWithLocalWhisper(audioBuffer, model2, onProgress) {
|
|
17853
|
+
ensureFfmpeg();
|
|
17854
|
+
const bin = getWhisperBin();
|
|
17855
|
+
if (!bin) throw new Error("whisper-cli not found. Install it: brew install whisper-cpp (macOS) or see https://github.com/ggml-org/whisper.cpp");
|
|
17856
|
+
if (!isWhisperModelDownloaded(model2)) {
|
|
17857
|
+
await downloadWhisperModel(model2, onProgress);
|
|
17858
|
+
}
|
|
17859
|
+
const id = crypto.randomUUID();
|
|
17860
|
+
const tmpOgg = `/tmp/cc-claw-stt-${id}.ogg`;
|
|
17861
|
+
const tmpWav = `/tmp/cc-claw-stt-${id}.wav`;
|
|
17862
|
+
try {
|
|
17863
|
+
await writeFile(tmpOgg, audioBuffer);
|
|
17864
|
+
await execFileAsync2("ffmpeg", ["-y", "-i", tmpOgg, "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le", tmpWav]);
|
|
17865
|
+
const modelFile = whisperModelPath(model2);
|
|
17866
|
+
const result = await execFileAsync2(bin, ["-m", modelFile, "-f", tmpWav, "-nt", "--output-txt", "-of", `/tmp/cc-claw-stt-${id}`]);
|
|
17867
|
+
const txtFile = `/tmp/cc-claw-stt-${id}.txt`;
|
|
17868
|
+
let transcript = "";
|
|
17869
|
+
if (existsSync19(txtFile)) {
|
|
17870
|
+
transcript = (await readFile2(txtFile, "utf-8")).trim();
|
|
17871
|
+
unlink2(txtFile).catch(() => {
|
|
17872
|
+
});
|
|
17873
|
+
} else {
|
|
17874
|
+
transcript = (result.stdout ?? "").trim();
|
|
17875
|
+
}
|
|
17876
|
+
return transcript;
|
|
17877
|
+
} finally {
|
|
17878
|
+
unlink2(tmpOgg).catch(() => {
|
|
17879
|
+
});
|
|
17880
|
+
unlink2(tmpWav).catch(() => {
|
|
17881
|
+
});
|
|
17882
|
+
}
|
|
17883
|
+
}
|
|
17884
|
+
async function transcribeAudio(audioBuffer, chatId, onProgress) {
|
|
17885
|
+
const provider = chatId ? getSttProvider(chatId) : "groq";
|
|
17886
|
+
if (provider === "local-whisper") {
|
|
17887
|
+
const model2 = chatId ? getSttModel(chatId) : "small.en";
|
|
17888
|
+
return await transcribeWithLocalWhisper(audioBuffer, model2, onProgress);
|
|
17889
|
+
}
|
|
17744
17890
|
const GROQ_API_KEY = process.env.GROQ_API_KEY;
|
|
17745
17891
|
if (!GROQ_API_KEY) return null;
|
|
17746
17892
|
const formData = new FormData();
|
|
17747
|
-
formData.append("file", new Blob([new Uint8Array(audioBuffer)], { type:
|
|
17893
|
+
formData.append("file", new Blob([new Uint8Array(audioBuffer)], { type: "audio/ogg" }), "voice.ogg");
|
|
17748
17894
|
formData.append("model", "whisper-large-v3");
|
|
17749
17895
|
formData.append("response_format", "text");
|
|
17750
17896
|
const response = await fetch("https://api.groq.com/openai/v1/audio/transcriptions", {
|
|
@@ -17845,8 +17991,8 @@ async function mp3ToOgg(mp3Buffer) {
|
|
|
17845
17991
|
const id = crypto.randomUUID();
|
|
17846
17992
|
const tmpMp3 = `/tmp/cc-claw-tts-${id}.mp3`;
|
|
17847
17993
|
const tmpOgg = `/tmp/cc-claw-tts-${id}.ogg`;
|
|
17848
|
-
const { writeFile:
|
|
17849
|
-
await
|
|
17994
|
+
const { writeFile: writeFile7 } = await import("fs/promises");
|
|
17995
|
+
await writeFile7(tmpMp3, mp3Buffer);
|
|
17850
17996
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17851
17997
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17852
17998
|
unlink2(tmpMp3).catch((err) => {
|
|
@@ -17873,14 +18019,24 @@ async function macOsTts(text, voice2 = "Samantha") {
|
|
|
17873
18019
|
});
|
|
17874
18020
|
return oggBuffer;
|
|
17875
18021
|
}
|
|
17876
|
-
var execFileAsync2, ffmpegAvailable, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES;
|
|
18022
|
+
var execFileAsync2, ffmpegAvailable, LOCAL_WHISPER_MODELS, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES, whisperCliAvailableCache;
|
|
17877
18023
|
var init_stt = __esm({
|
|
17878
18024
|
"src/voice/stt.ts"() {
|
|
17879
18025
|
"use strict";
|
|
17880
18026
|
init_log();
|
|
17881
18027
|
init_store5();
|
|
18028
|
+
init_paths();
|
|
17882
18029
|
execFileAsync2 = promisify2(execFile2);
|
|
17883
18030
|
ffmpegAvailable = null;
|
|
18031
|
+
LOCAL_WHISPER_MODELS = {
|
|
18032
|
+
"tiny.en": { size: "75MB", lang: "English only", speed: "~0.3s", quality: "Basic" },
|
|
18033
|
+
"base.en": { size: "150MB", lang: "English only", speed: "~0.8s", quality: "Good" },
|
|
18034
|
+
"small.en": { size: "500MB", lang: "English only", speed: "~1.5s", quality: "Very good \u2B50" },
|
|
18035
|
+
"small": { size: "500MB", lang: "Multilingual", speed: "~2s", quality: "Very good" },
|
|
18036
|
+
"medium.en": { size: "1.5GB", lang: "English only", speed: "~5s", quality: "Excellent" },
|
|
18037
|
+
"medium": { size: "1.5GB", lang: "Multilingual", speed: "~6s", quality: "Excellent" },
|
|
18038
|
+
"large-v3-turbo": { size: "1.5GB", lang: "Multilingual", speed: "~4s", quality: "Best" }
|
|
18039
|
+
};
|
|
17884
18040
|
ELEVENLABS_VOICES = {
|
|
17885
18041
|
"21m00Tcm4TlvDq8ikWAM": { name: "Rachel", gender: "F" },
|
|
17886
18042
|
"EXAVITQu4vr4xnSDxMaL": { name: "Sarah", gender: "F" },
|
|
@@ -17896,13 +18052,14 @@ var init_stt = __esm({
|
|
|
17896
18052
|
"Samantha": { name: "Samantha", gender: "F" },
|
|
17897
18053
|
"Albert": { name: "Albert", gender: "M" }
|
|
17898
18054
|
};
|
|
18055
|
+
whisperCliAvailableCache = null;
|
|
17899
18056
|
}
|
|
17900
18057
|
});
|
|
17901
18058
|
|
|
17902
18059
|
// src/media/image-gen.ts
|
|
17903
|
-
import { mkdirSync as mkdirSync9, existsSync as
|
|
17904
|
-
import { writeFile } from "fs/promises";
|
|
17905
|
-
import { join as
|
|
18060
|
+
import { mkdirSync as mkdirSync9, existsSync as existsSync20, unlink as unlink3, readdir as readdir2, stat as stat2 } from "fs";
|
|
18061
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
18062
|
+
import { join as join19 } from "path";
|
|
17906
18063
|
async function generateImage(prompt) {
|
|
17907
18064
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
17908
18065
|
if (!apiKey) {
|
|
@@ -17949,14 +18106,14 @@ async function generateImage(prompt) {
|
|
|
17949
18106
|
if (!imageData) {
|
|
17950
18107
|
throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
|
|
17951
18108
|
}
|
|
17952
|
-
if (!
|
|
18109
|
+
if (!existsSync20(IMAGE_OUTPUT_DIR)) {
|
|
17953
18110
|
mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
|
|
17954
18111
|
}
|
|
17955
18112
|
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
17956
18113
|
const filename = `img_${Date.now()}.${ext}`;
|
|
17957
|
-
const filePath =
|
|
18114
|
+
const filePath = join19(IMAGE_OUTPUT_DIR, filename);
|
|
17958
18115
|
const buffer = Buffer.from(imageData, "base64");
|
|
17959
|
-
await
|
|
18116
|
+
await writeFile2(filePath, buffer);
|
|
17960
18117
|
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
17961
18118
|
return { filePath, text: textResponse, mimeType };
|
|
17962
18119
|
}
|
|
@@ -17972,7 +18129,7 @@ function cleanupGeneratedImage(filePath) {
|
|
|
17972
18129
|
function pruneImageCache() {
|
|
17973
18130
|
readdir2(IMAGE_OUTPUT_DIR, (err, files) => {
|
|
17974
18131
|
if (err || !files) return;
|
|
17975
|
-
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) =>
|
|
18132
|
+
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) => join19(IMAGE_OUTPUT_DIR, f));
|
|
17976
18133
|
if (imageFiles.length === 0) return;
|
|
17977
18134
|
const now = Date.now();
|
|
17978
18135
|
let statsPending = imageFiles.length;
|
|
@@ -18004,8 +18161,8 @@ var init_image_gen = __esm({
|
|
|
18004
18161
|
MAX_GENERATED_IMAGES = 20;
|
|
18005
18162
|
IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
18006
18163
|
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
18007
|
-
IMAGE_OUTPUT_DIR =
|
|
18008
|
-
process.env.CC_CLAW_HOME ??
|
|
18164
|
+
IMAGE_OUTPUT_DIR = join19(
|
|
18165
|
+
process.env.CC_CLAW_HOME ?? join19(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
18009
18166
|
"data",
|
|
18010
18167
|
"images"
|
|
18011
18168
|
);
|
|
@@ -18443,22 +18600,22 @@ var init_video = __esm({
|
|
|
18443
18600
|
});
|
|
18444
18601
|
|
|
18445
18602
|
// src/router/media.ts
|
|
18446
|
-
import { join as
|
|
18447
|
-
import { mkdir as
|
|
18603
|
+
import { join as join20 } from "path";
|
|
18604
|
+
import { mkdir as mkdir3, writeFile as writeFile3, readdir as readdir3, stat as stat3, unlink as unlink4 } from "fs/promises";
|
|
18448
18605
|
function getMediaRetentionMs() {
|
|
18449
18606
|
const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
|
|
18450
18607
|
return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
|
|
18451
18608
|
}
|
|
18452
18609
|
async function saveMedia(buffer, prefix, ext) {
|
|
18453
|
-
await
|
|
18610
|
+
await mkdir3(MEDIA_INCOMING_PATH, { recursive: true });
|
|
18454
18611
|
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
18455
|
-
const fullPath =
|
|
18456
|
-
await
|
|
18612
|
+
const fullPath = join20(MEDIA_INCOMING_PATH, filename);
|
|
18613
|
+
await writeFile3(fullPath, buffer);
|
|
18457
18614
|
return fullPath;
|
|
18458
18615
|
}
|
|
18459
18616
|
async function cleanupOldMedia() {
|
|
18460
18617
|
try {
|
|
18461
|
-
await
|
|
18618
|
+
await mkdir3(MEDIA_INCOMING_PATH, { recursive: true });
|
|
18462
18619
|
const retentionMs = getMediaRetentionMs();
|
|
18463
18620
|
const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
|
|
18464
18621
|
const files = await readdir3(MEDIA_INCOMING_PATH);
|
|
@@ -18466,7 +18623,7 @@ async function cleanupOldMedia() {
|
|
|
18466
18623
|
let removed = 0;
|
|
18467
18624
|
for (const file of files) {
|
|
18468
18625
|
try {
|
|
18469
|
-
const filePath =
|
|
18626
|
+
const filePath = join20(MEDIA_INCOMING_PATH, file);
|
|
18470
18627
|
const s = await stat3(filePath);
|
|
18471
18628
|
if (now - s.mtimeMs > retentionMs) {
|
|
18472
18629
|
await unlink4(filePath);
|
|
@@ -18488,9 +18645,11 @@ async function handleVoice(msg, channel) {
|
|
|
18488
18645
|
return;
|
|
18489
18646
|
}
|
|
18490
18647
|
const audioBuffer = await channel.downloadFile(fileName);
|
|
18491
|
-
const transcript = await transcribeAudio(audioBuffer)
|
|
18648
|
+
const transcript = await transcribeAudio(audioBuffer, chatId, async (msg2) => {
|
|
18649
|
+
await channel.sendText(chatId, msg2, { parseMode: "plain" });
|
|
18650
|
+
});
|
|
18492
18651
|
if (!transcript) {
|
|
18493
|
-
await channel.sendText(chatId, "Couldn't transcribe the voice message.", { parseMode: "plain" });
|
|
18652
|
+
await channel.sendText(chatId, "Couldn't transcribe the voice message. Make sure a transcription provider is configured via /voice.", { parseMode: "plain" });
|
|
18494
18653
|
return;
|
|
18495
18654
|
}
|
|
18496
18655
|
const vBackendId = getBackend(chatId) ?? "claude";
|
|
@@ -18740,7 +18899,7 @@ var init_media = __esm({
|
|
|
18740
18899
|
init_helpers();
|
|
18741
18900
|
init_response();
|
|
18742
18901
|
init_live_status();
|
|
18743
|
-
MEDIA_INCOMING_PATH =
|
|
18902
|
+
MEDIA_INCOMING_PATH = join20(MEDIA_PATH, "incoming");
|
|
18744
18903
|
}
|
|
18745
18904
|
});
|
|
18746
18905
|
|
|
@@ -19052,9 +19211,9 @@ var install_exports = {};
|
|
|
19052
19211
|
__export(install_exports, {
|
|
19053
19212
|
installSkillFromGitHub: () => installSkillFromGitHub
|
|
19054
19213
|
});
|
|
19055
|
-
import { mkdir as
|
|
19056
|
-
import { existsSync as
|
|
19057
|
-
import { join as
|
|
19214
|
+
import { mkdir as mkdir4, readdir as readdir4, readFile as readFile4, cp } from "fs/promises";
|
|
19215
|
+
import { existsSync as existsSync21 } from "fs";
|
|
19216
|
+
import { join as join21, basename as basename2 } from "path";
|
|
19058
19217
|
import { execSync as execSync4 } from "child_process";
|
|
19059
19218
|
async function installSkillFromGitHub(urlOrShorthand) {
|
|
19060
19219
|
let repoUrl;
|
|
@@ -19065,36 +19224,36 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
19065
19224
|
}
|
|
19066
19225
|
repoUrl = parsed.cloneUrl;
|
|
19067
19226
|
subPath = parsed.subPath;
|
|
19068
|
-
const tmpDir =
|
|
19227
|
+
const tmpDir = join21("/tmp", `cc-claw-skill-${Date.now()}`);
|
|
19069
19228
|
try {
|
|
19070
19229
|
log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
|
|
19071
19230
|
execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
19072
19231
|
stdio: "pipe",
|
|
19073
19232
|
timeout: 3e4
|
|
19074
19233
|
});
|
|
19075
|
-
if (!
|
|
19234
|
+
if (!existsSync21(join21(tmpDir, ".git"))) {
|
|
19076
19235
|
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
19077
19236
|
}
|
|
19078
|
-
const searchRoot = subPath ?
|
|
19237
|
+
const searchRoot = subPath ? join21(tmpDir, subPath) : tmpDir;
|
|
19079
19238
|
const skillDir = await findSkillDir(searchRoot);
|
|
19080
19239
|
if (!skillDir) {
|
|
19081
19240
|
return { success: false, error: "No SKILL.md found in the repository." };
|
|
19082
19241
|
}
|
|
19083
19242
|
const skillFolderName = basename2(skillDir);
|
|
19084
|
-
const destDir =
|
|
19085
|
-
if (
|
|
19243
|
+
const destDir = join21(SKILLS_PATH, skillFolderName);
|
|
19244
|
+
if (existsSync21(destDir)) {
|
|
19086
19245
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
19087
19246
|
}
|
|
19088
|
-
await
|
|
19247
|
+
await mkdir4(destDir, { recursive: true });
|
|
19089
19248
|
await cp(skillDir, destDir, { recursive: true });
|
|
19090
19249
|
let skillName = skillFolderName;
|
|
19091
19250
|
try {
|
|
19092
|
-
const content = await readFile4(
|
|
19251
|
+
const content = await readFile4(join21(destDir, "SKILL.md"), "utf-8");
|
|
19093
19252
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
19094
19253
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
19095
19254
|
} catch {
|
|
19096
19255
|
try {
|
|
19097
|
-
const content = await readFile4(
|
|
19256
|
+
const content = await readFile4(join21(destDir, "skill.md"), "utf-8");
|
|
19098
19257
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
19099
19258
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
19100
19259
|
} catch {
|
|
@@ -19129,15 +19288,15 @@ function parseGitHubUrl(input) {
|
|
|
19129
19288
|
async function findSkillDir(root) {
|
|
19130
19289
|
const candidates = ["SKILL.md", "skill.md"];
|
|
19131
19290
|
for (const c of candidates) {
|
|
19132
|
-
if (
|
|
19291
|
+
if (existsSync21(join21(root, c))) return root;
|
|
19133
19292
|
}
|
|
19134
19293
|
try {
|
|
19135
19294
|
const entries = await readdir4(root, { withFileTypes: true });
|
|
19136
19295
|
for (const entry of entries) {
|
|
19137
19296
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19138
19297
|
for (const c of candidates) {
|
|
19139
|
-
if (
|
|
19140
|
-
return
|
|
19298
|
+
if (existsSync21(join21(root, entry.name, c))) {
|
|
19299
|
+
return join21(root, entry.name);
|
|
19141
19300
|
}
|
|
19142
19301
|
}
|
|
19143
19302
|
}
|
|
@@ -19149,15 +19308,15 @@ async function findSkillDir(root) {
|
|
|
19149
19308
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19150
19309
|
let subEntries;
|
|
19151
19310
|
try {
|
|
19152
|
-
subEntries = await readdir4(
|
|
19311
|
+
subEntries = await readdir4(join21(root, entry.name), { withFileTypes: true });
|
|
19153
19312
|
} catch {
|
|
19154
19313
|
continue;
|
|
19155
19314
|
}
|
|
19156
19315
|
for (const sub of subEntries) {
|
|
19157
19316
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
19158
19317
|
for (const c of candidates) {
|
|
19159
|
-
if (
|
|
19160
|
-
return
|
|
19318
|
+
if (existsSync21(join21(root, entry.name, sub.name, c))) {
|
|
19319
|
+
return join21(root, entry.name, sub.name);
|
|
19161
19320
|
}
|
|
19162
19321
|
}
|
|
19163
19322
|
}
|
|
@@ -19184,7 +19343,7 @@ __export(discover_exports, {
|
|
|
19184
19343
|
import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
|
|
19185
19344
|
import { createHash } from "crypto";
|
|
19186
19345
|
import { homedir as homedir5 } from "os";
|
|
19187
|
-
import { join as
|
|
19346
|
+
import { join as join22 } from "path";
|
|
19188
19347
|
function invalidateSkillCache() {
|
|
19189
19348
|
cachedSkills = null;
|
|
19190
19349
|
cacheTimestamp = 0;
|
|
@@ -19202,7 +19361,7 @@ async function discoverAllSkills() {
|
|
|
19202
19361
|
const rawSkills = [];
|
|
19203
19362
|
rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
|
|
19204
19363
|
for (const backendId of getAllBackendIds()) {
|
|
19205
|
-
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [
|
|
19364
|
+
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join22(homedir5(), `.${backendId}`, "skills")];
|
|
19206
19365
|
for (const dir of dirs) {
|
|
19207
19366
|
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
19208
19367
|
}
|
|
@@ -19230,7 +19389,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
19230
19389
|
let content;
|
|
19231
19390
|
let resolvedPath;
|
|
19232
19391
|
for (const candidate of SKILL_FILE_CANDIDATES) {
|
|
19233
|
-
const p =
|
|
19392
|
+
const p = join22(skillsDir, entry.name, candidate);
|
|
19234
19393
|
try {
|
|
19235
19394
|
content = await readFile5(p, "utf-8");
|
|
19236
19395
|
resolvedPath = p;
|
|
@@ -19324,15 +19483,15 @@ var init_discover = __esm({
|
|
|
19324
19483
|
init_backends();
|
|
19325
19484
|
SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
|
|
19326
19485
|
BACKEND_SKILL_DIRS = {
|
|
19327
|
-
claude: [
|
|
19328
|
-
gemini: [
|
|
19486
|
+
claude: [join22(homedir5(), ".claude", "skills")],
|
|
19487
|
+
gemini: [join22(homedir5(), ".gemini", "skills")],
|
|
19329
19488
|
codex: [
|
|
19330
|
-
|
|
19331
|
-
|
|
19489
|
+
join22(homedir5(), ".agents", "skills"),
|
|
19490
|
+
join22(homedir5(), ".codex", "skills")
|
|
19332
19491
|
],
|
|
19333
19492
|
cursor: [
|
|
19334
|
-
|
|
19335
|
-
|
|
19493
|
+
join22(homedir5(), ".cursor", "skills"),
|
|
19494
|
+
join22(homedir5(), ".cursor", "skills-cursor")
|
|
19336
19495
|
]
|
|
19337
19496
|
};
|
|
19338
19497
|
CACHE_TTL_MS2 = 3e5;
|
|
@@ -19690,43 +19849,76 @@ async function sendVoiceConfigKeyboard(chatId, channel) {
|
|
|
19690
19849
|
await channel.sendText(chatId, "Voice configuration requires an interactive channel (Telegram).", { parseMode: "plain" });
|
|
19691
19850
|
return;
|
|
19692
19851
|
}
|
|
19693
|
-
const
|
|
19694
|
-
const
|
|
19695
|
-
const
|
|
19696
|
-
const
|
|
19697
|
-
Provider: ${providerLabel}
|
|
19698
|
-
Voice: ${currentVoiceName}
|
|
19699
|
-
Status: ${config2.enabled ? "ON" : "OFF"}`;
|
|
19852
|
+
const ttsConfig = getVoiceConfig(chatId);
|
|
19853
|
+
const sttProvider = getSttProvider(chatId);
|
|
19854
|
+
const sttModel = getSttModel(chatId);
|
|
19855
|
+
const whisperAvailable = isWhisperCliAvailable();
|
|
19700
19856
|
const buttons = [];
|
|
19857
|
+
const groqAvailable = !!process.env.GROQ_API_KEY;
|
|
19701
19858
|
buttons.push([
|
|
19702
|
-
{
|
|
19703
|
-
|
|
19704
|
-
|
|
19859
|
+
{
|
|
19860
|
+
label: `${sttProvider === "groq" ? "\u2713 " : ""}\u{1F310} Groq${!groqAvailable ? " (no key)" : ""}`,
|
|
19861
|
+
data: "vcfg:stt:groq",
|
|
19862
|
+
...sttProvider === "groq" ? { style: "primary" } : {}
|
|
19863
|
+
},
|
|
19864
|
+
{
|
|
19865
|
+
label: `${sttProvider === "local-whisper" ? "\u2713 " : ""}\u{1F4BB} Local Whisper${!whisperAvailable ? " (install first)" : ""}`,
|
|
19866
|
+
data: "vcfg:stt:local-whisper",
|
|
19867
|
+
...sttProvider === "local-whisper" ? { style: "primary" } : {}
|
|
19868
|
+
}
|
|
19705
19869
|
]);
|
|
19706
|
-
if (
|
|
19707
|
-
const
|
|
19708
|
-
|
|
19709
|
-
|
|
19710
|
-
|
|
19711
|
-
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
label: `${config2.voiceId === v ? "\u2713 " : ""}${capitalize(v)}`,
|
|
19721
|
-
data: `vcfg:v:${v}`
|
|
19722
|
-
})));
|
|
19723
|
-
} else {
|
|
19724
|
-
const entries = Object.entries(MACOS_VOICES);
|
|
19725
|
-
buttons.push(entries.map(([id, v]) => ({
|
|
19726
|
-
label: `${config2.voiceId === id ? "\u2713 " : ""}${v.name}`,
|
|
19727
|
-
data: `vcfg:v:${id}`
|
|
19728
|
-
})));
|
|
19870
|
+
if (sttProvider === "local-whisper" || whisperAvailable) {
|
|
19871
|
+
const modelEntries = Object.entries(LOCAL_WHISPER_MODELS);
|
|
19872
|
+
for (let i = 0; i < modelEntries.length; i += 2) {
|
|
19873
|
+
const row = modelEntries.slice(i, i + 2).map(([id, info]) => {
|
|
19874
|
+
const downloaded = isWhisperModelDownloaded(id);
|
|
19875
|
+
const active = sttModel === id;
|
|
19876
|
+
return {
|
|
19877
|
+
label: `${active ? "\u2713 " : ""}${id} ${downloaded ? "\u25CF" : "\u25CB"} ${info.size}`,
|
|
19878
|
+
data: `vcfg:stt-model:${id}`,
|
|
19879
|
+
...active ? { style: "primary" } : {}
|
|
19880
|
+
};
|
|
19881
|
+
});
|
|
19882
|
+
buttons.push(row);
|
|
19883
|
+
}
|
|
19729
19884
|
}
|
|
19885
|
+
buttons.push([
|
|
19886
|
+
{
|
|
19887
|
+
label: `${!ttsConfig.enabled ? "\u2713 " : ""}\u{1F507} Replies Off`,
|
|
19888
|
+
data: "voice:off",
|
|
19889
|
+
...!ttsConfig.enabled ? { style: "danger" } : {}
|
|
19890
|
+
},
|
|
19891
|
+
{
|
|
19892
|
+
label: `${ttsConfig.enabled ? "\u2713 " : ""}\u{1F50A} Replies On`,
|
|
19893
|
+
data: "voice:on",
|
|
19894
|
+
...ttsConfig.enabled ? { style: "success" } : {}
|
|
19895
|
+
}
|
|
19896
|
+
]);
|
|
19897
|
+
buttons.push([
|
|
19898
|
+
{ label: `${ttsConfig.provider === "elevenlabs" ? "\u2713 " : ""}ElevenLabs`, data: "vcfg:p:elevenlabs", ...ttsConfig.provider === "elevenlabs" ? { style: "primary" } : {} },
|
|
19899
|
+
{ label: `${ttsConfig.provider === "grok" ? "\u2713 " : ""}Grok`, data: "vcfg:p:grok", ...ttsConfig.provider === "grok" ? { style: "primary" } : {} },
|
|
19900
|
+
{ label: `${ttsConfig.provider === "macos" ? "\u2713 " : ""}macOS`, data: "vcfg:p:macos", ...ttsConfig.provider === "macos" ? { style: "primary" } : {} }
|
|
19901
|
+
]);
|
|
19902
|
+
if (ttsConfig.enabled) {
|
|
19903
|
+
if (ttsConfig.provider === "elevenlabs") {
|
|
19904
|
+
const entries = Object.entries(ELEVENLABS_VOICES);
|
|
19905
|
+
const female = entries.filter(([, v]) => v.gender === "F");
|
|
19906
|
+
const male = entries.filter(([, v]) => v.gender === "M");
|
|
19907
|
+
buttons.push(female.map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
19908
|
+
buttons.push(male.map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
19909
|
+
} else if (ttsConfig.provider === "grok") {
|
|
19910
|
+
buttons.push(GROK_VOICES.map((v) => ({ label: `${ttsConfig.voiceId === v ? "\u2713 " : ""}${capitalize(v)}`, data: `vcfg:v:${v}` })));
|
|
19911
|
+
} else {
|
|
19912
|
+
buttons.push(Object.entries(MACOS_VOICES).map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
19913
|
+
}
|
|
19914
|
+
}
|
|
19915
|
+
const sttLabel = sttProvider === "groq" ? "Groq (cloud)" : `Local Whisper \xB7 ${sttModel}`;
|
|
19916
|
+
const ttsLabel = ttsConfig.enabled ? `${ttsConfig.provider === "grok" ? "Grok" : ttsConfig.provider === "macos" ? "macOS" : "ElevenLabs"} replies ON` : "Replies OFF";
|
|
19917
|
+
const modelLegend = sttProvider === "local-whisper" || whisperAvailable ? "\n\u25CF = downloaded \u25CB = not yet downloaded" : "";
|
|
19918
|
+
const header2 = `\u{1F399}\uFE0F Voice Settings
|
|
19919
|
+
|
|
19920
|
+
\u{1F3A4} Transcription: ${sttLabel}
|
|
19921
|
+
\u{1F50A} Text-to-Speech: ${ttsLabel}${modelLegend}`;
|
|
19730
19922
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
19731
19923
|
}
|
|
19732
19924
|
async function sendSkillsPage(chatId, channel, skills2, page, messageId) {
|
|
@@ -19823,28 +20015,14 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
19823
20015
|
const backendDisplay = config2?.backend ? capitalize(config2.backend) : "Default";
|
|
19824
20016
|
const modelDisplay = config2?.model ?? "default";
|
|
19825
20017
|
const thinkingDisplay = config2?.thinking ?? "off";
|
|
19826
|
-
const
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
|
|
19830
|
-
"
|
|
19831
|
-
|
|
19832
|
-
|
|
19833
|
-
|
|
19834
|
-
`Active hours: ${activeStart}-${activeEnd}`,
|
|
19835
|
-
`Backend: ${backendDisplay} | Model: ${modelDisplay}`
|
|
19836
|
-
];
|
|
19837
|
-
if (fallbacks.length > 0) {
|
|
19838
|
-
lines.push(`Fallbacks: ${fallbacks.map((f) => `${capitalize(f.backend)}${f.model ? `:${f.model}` : ""}`).join(", ")}`);
|
|
19839
|
-
}
|
|
19840
|
-
if (config2?.target) {
|
|
19841
|
-
lines.push(`Target: ${config2.target}`);
|
|
19842
|
-
}
|
|
19843
|
-
if (watches.length > 0) {
|
|
19844
|
-
lines.push(`Active watches: ${watches.length}`);
|
|
19845
|
-
}
|
|
19846
|
-
const lastBeat = config2?.lastBeatAt ?? "never";
|
|
19847
|
-
lines.push(`Last beat: ${lastBeat}`);
|
|
20018
|
+
const lastBeat = config2?.lastBeatAt ? config2.lastBeatAt.replace("T", " ").slice(0, 16) : "never";
|
|
20019
|
+
const watchNote = watches.length > 0 ? ` \xB7 ${watches.length} watch${watches.length !== 1 ? "es" : ""}` : "";
|
|
20020
|
+
const fallbackNote = fallbacks.length > 0 ? `Fallbacks: ${fallbacks.map((f) => `${capitalize(f.backend)}${f.model ? ` (${shortModelName(f.model)})` : ""}`).join(" \u2192 ")}` : "";
|
|
20021
|
+
const header2 = [
|
|
20022
|
+
`\u{1FAC0} Heartbeat \u2014 ${enabled ? "ON" : "OFF"} \xB7 Every ${intervalMin} min \xB7 ${activeStart}\u2013${activeEnd}${watchNote}`,
|
|
20023
|
+
`Backend: ${backendDisplay} \xB7 Model: ${modelDisplay} \xB7 Last: ${lastBeat}`,
|
|
20024
|
+
fallbackNote
|
|
20025
|
+
].filter(Boolean).join("\n");
|
|
19848
20026
|
const buttons = [];
|
|
19849
20027
|
buttons.push([
|
|
19850
20028
|
{ label: `${enabled ? "\u2713 " : ""}On`, data: "hb:on", ...enabled ? { style: "success" } : {} },
|
|
@@ -19858,63 +20036,62 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
19858
20036
|
...m === intervalMin ? { style: "primary" } : {}
|
|
19859
20037
|
})));
|
|
19860
20038
|
const available = getAvailableBackendIds();
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
20039
|
+
const backendRow = [
|
|
20040
|
+
{
|
|
20041
|
+
label: `${!config2?.backend ? "\u2713 " : ""}Default`,
|
|
20042
|
+
data: "hb:backend:default",
|
|
20043
|
+
...!config2?.backend ? { style: "primary" } : {}
|
|
20044
|
+
},
|
|
20045
|
+
...available.map((bid) => ({
|
|
20046
|
+
label: `${config2?.backend === bid ? "\u2713 " : ""}${capitalize(bid)}`,
|
|
20047
|
+
data: `hb:backend:${bid}`,
|
|
20048
|
+
...config2?.backend === bid ? { style: "primary" } : {}
|
|
20049
|
+
}))
|
|
20050
|
+
];
|
|
19870
20051
|
buttons.push(backendRow);
|
|
19871
20052
|
const targetBackend = config2?.backend ?? getBackend(chatId) ?? "claude";
|
|
19872
20053
|
try {
|
|
19873
20054
|
const adapter = getAdapter(targetBackend);
|
|
19874
20055
|
const models = Object.entries(adapter.availableModels);
|
|
19875
20056
|
if (models.length > 0) {
|
|
19876
|
-
const modelRow =
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
19883
|
-
|
|
20057
|
+
const modelRow = [
|
|
20058
|
+
{
|
|
20059
|
+
label: `${!config2?.model ? "\u2713 " : ""}Default`,
|
|
20060
|
+
data: "hb:model:default",
|
|
20061
|
+
...!config2?.model ? { style: "primary" } : {}
|
|
20062
|
+
},
|
|
20063
|
+
...models.slice(0, 4).map(([key]) => ({
|
|
20064
|
+
label: `${config2?.model === key ? "\u2713 " : ""}${shortModelName(key)}`,
|
|
20065
|
+
data: `hb:model:${key}`,
|
|
20066
|
+
...config2?.model === key ? { style: "primary" } : {}
|
|
20067
|
+
}))
|
|
20068
|
+
];
|
|
19884
20069
|
buttons.push(modelRow);
|
|
19885
20070
|
}
|
|
19886
20071
|
} catch {
|
|
19887
20072
|
}
|
|
19888
20073
|
const fbCandidates = available.filter((bid) => !fallbacks.some((f) => f.backend === bid) && bid !== config2?.backend);
|
|
19889
|
-
if (fallbacks.length > 0 || fbCandidates.length > 0) {
|
|
19890
|
-
buttons.push([{ label: "\u{1F504} Fallback Chain", data: "hb:noop" }]);
|
|
19891
|
-
}
|
|
19892
20074
|
if (fallbacks.length > 0) {
|
|
19893
20075
|
buttons.push([
|
|
19894
|
-
{ label: fallbacks.map((f) =>
|
|
19895
|
-
|
|
19896
|
-
return f.model ? `${name} (${shortModelName(f.model)})` : name;
|
|
19897
|
-
}).join(" \u2192 "), data: "hb:noop" },
|
|
19898
|
-
{ label: "Clear All", data: "hb:fb:clear", style: "danger" }
|
|
20076
|
+
{ label: `\u{1F504} ${fallbacks.map((f) => capitalize(f.backend)).join(" \u2192 ")}`, data: "hb:noop" },
|
|
20077
|
+
{ label: "\u2715 Clear", data: "hb:fb:clear", style: "danger" }
|
|
19899
20078
|
]);
|
|
19900
20079
|
}
|
|
19901
20080
|
if (fbCandidates.length > 0) {
|
|
19902
20081
|
buttons.push(fbCandidates.slice(0, 4).map((bid) => ({
|
|
19903
|
-
label: `+ ${capitalize(bid)}`,
|
|
20082
|
+
label: `+ ${capitalize(bid)} fallback`,
|
|
19904
20083
|
data: `hb:fb:add:${bid}`
|
|
19905
20084
|
})));
|
|
19906
20085
|
}
|
|
19907
|
-
const optionsRow = [
|
|
19908
|
-
{ label: `\u{1F4AD} Thinking: ${thinkingDisplay}`, data: "hb:thinking" }
|
|
19909
|
-
];
|
|
20086
|
+
const optionsRow = [];
|
|
19910
20087
|
if (watches.length > 0) {
|
|
19911
20088
|
optionsRow.push({ label: `\u{1F441} Watches (${watches.length})`, data: "hb:watches" });
|
|
19912
20089
|
} else {
|
|
19913
|
-
optionsRow.push({ label: "
|
|
20090
|
+
optionsRow.push({ label: "\u{1F441} Add Watch", data: "hb:addwatch" });
|
|
19914
20091
|
}
|
|
20092
|
+
optionsRow.push({ label: `\u23F0 Hours: ${activeStart}\u2013${activeEnd}`, data: "hb:noop" });
|
|
19915
20093
|
buttons.push(optionsRow);
|
|
19916
|
-
|
|
19917
|
-
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
20094
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
19918
20095
|
}
|
|
19919
20096
|
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
19920
20097
|
const memories = listMemories();
|
|
@@ -20563,13 +20740,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
20563
20740
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
20564
20741
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
20565
20742
|
if (current === "frozen") {
|
|
20566
|
-
const { readFileSync:
|
|
20567
|
-
const { join:
|
|
20743
|
+
const { readFileSync: readFileSync30, existsSync: existsSync59 } = await import("fs");
|
|
20744
|
+
const { join: join38 } = await import("path");
|
|
20568
20745
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
20569
|
-
const soulPath =
|
|
20570
|
-
const userPath =
|
|
20571
|
-
const soul =
|
|
20572
|
-
const user =
|
|
20746
|
+
const soulPath = join38(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
20747
|
+
const userPath = join38(CC_CLAW_HOME3, "identity/USER.md");
|
|
20748
|
+
const soul = existsSync59(soulPath) ? readFileSync30(soulPath, "utf-8") : "";
|
|
20749
|
+
const user = existsSync59(userPath) ? readFileSync30(userPath, "utf-8") : "";
|
|
20573
20750
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
20574
20751
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
20575
20752
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -20646,11 +20823,11 @@ var init_evolve2 = __esm({
|
|
|
20646
20823
|
});
|
|
20647
20824
|
|
|
20648
20825
|
// src/optimizer/identity-audit.ts
|
|
20649
|
-
import { readFileSync as
|
|
20650
|
-
import { join as
|
|
20826
|
+
import { readFileSync as readFileSync12, existsSync as existsSync22, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
20827
|
+
import { join as join23 } from "path";
|
|
20651
20828
|
function readIdentityFile2(filename) {
|
|
20652
20829
|
try {
|
|
20653
|
-
return
|
|
20830
|
+
return readFileSync12(join23(IDENTITY_PATH, filename), "utf-8");
|
|
20654
20831
|
} catch {
|
|
20655
20832
|
return "";
|
|
20656
20833
|
}
|
|
@@ -20665,13 +20842,13 @@ function getMtime(filepath) {
|
|
|
20665
20842
|
function findBackupFiles() {
|
|
20666
20843
|
const backups = [];
|
|
20667
20844
|
const dirs = [IDENTITY_PATH];
|
|
20668
|
-
const contextDir =
|
|
20669
|
-
if (
|
|
20845
|
+
const contextDir = join23(IDENTITY_PATH, "..", "workspace", "context");
|
|
20846
|
+
if (existsSync22(contextDir)) dirs.push(contextDir);
|
|
20670
20847
|
for (const dir of dirs) {
|
|
20671
20848
|
try {
|
|
20672
20849
|
for (const entry of readdirSync10(dir)) {
|
|
20673
20850
|
if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
|
|
20674
|
-
backups.push(
|
|
20851
|
+
backups.push(join23(dir, entry));
|
|
20675
20852
|
}
|
|
20676
20853
|
}
|
|
20677
20854
|
} catch {
|
|
@@ -20692,9 +20869,9 @@ function computeIdentityStats(pendingProposals, driftPercent) {
|
|
|
20692
20869
|
userChars,
|
|
20693
20870
|
ccClawChars,
|
|
20694
20871
|
boilerplateChars,
|
|
20695
|
-
soulMtime: getMtime(
|
|
20696
|
-
userMtime: getMtime(
|
|
20697
|
-
ccClawMtime: getMtime(
|
|
20872
|
+
soulMtime: getMtime(join23(IDENTITY_PATH, "SOUL.md")),
|
|
20873
|
+
userMtime: getMtime(join23(IDENTITY_PATH, "USER.md")),
|
|
20874
|
+
ccClawMtime: getMtime(join23(IDENTITY_PATH, "CC-CLAW.md")),
|
|
20698
20875
|
backupFiles: findBackupFiles(),
|
|
20699
20876
|
estimatedTokens: Math.ceil(ccClawChars / 4),
|
|
20700
20877
|
pendingEvolveProposals: pendingProposals,
|
|
@@ -20803,8 +20980,8 @@ var init_identity_audit = __esm({
|
|
|
20803
20980
|
});
|
|
20804
20981
|
|
|
20805
20982
|
// src/optimizer/skill-audit.ts
|
|
20806
|
-
import { readFileSync as
|
|
20807
|
-
import { join as
|
|
20983
|
+
import { readFileSync as readFileSync13, existsSync as existsSync23 } from "fs";
|
|
20984
|
+
import { join as join24, basename as basename3 } from "path";
|
|
20808
20985
|
function parseFrontmatter3(content) {
|
|
20809
20986
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
20810
20987
|
if (!fmMatch) return {};
|
|
@@ -20840,10 +21017,10 @@ function detectDependentSkills(content) {
|
|
|
20840
21017
|
return Array.from(deps);
|
|
20841
21018
|
}
|
|
20842
21019
|
function computeSkillStats(skillPath) {
|
|
20843
|
-
const content =
|
|
21020
|
+
const content = readFileSync13(skillPath, "utf-8");
|
|
20844
21021
|
const lines = content.split("\n");
|
|
20845
21022
|
return {
|
|
20846
|
-
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(
|
|
21023
|
+
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join24(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
20847
21024
|
skillPath,
|
|
20848
21025
|
lineCount: lines.length,
|
|
20849
21026
|
charCount: content.length,
|
|
@@ -20863,13 +21040,13 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
20863
21040
|
const results = [];
|
|
20864
21041
|
for (const name of depNames) {
|
|
20865
21042
|
const candidates = [
|
|
20866
|
-
|
|
20867
|
-
|
|
21043
|
+
join24(ccClawSkillsDir, name, "SKILL.md"),
|
|
21044
|
+
join24(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
20868
21045
|
];
|
|
20869
21046
|
for (const candidate of candidates) {
|
|
20870
|
-
if (
|
|
21047
|
+
if (existsSync23(candidate)) {
|
|
20871
21048
|
try {
|
|
20872
|
-
const content =
|
|
21049
|
+
const content = readFileSync13(candidate, "utf-8");
|
|
20873
21050
|
results.push({
|
|
20874
21051
|
name,
|
|
20875
21052
|
content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
|
|
@@ -20981,8 +21158,8 @@ __export(analyze_exports2, {
|
|
|
20981
21158
|
});
|
|
20982
21159
|
import { spawn as spawn7 } from "child_process";
|
|
20983
21160
|
import { createInterface as createInterface7 } from "readline";
|
|
20984
|
-
import { readFileSync as
|
|
20985
|
-
import { join as
|
|
21161
|
+
import { readFileSync as readFileSync14, existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
|
|
21162
|
+
import { join as join25 } from "path";
|
|
20986
21163
|
import { homedir as homedir7 } from "os";
|
|
20987
21164
|
function parseOptimizeOutput(raw, validAreas) {
|
|
20988
21165
|
if (!raw || raw.includes("NO_FINDINGS")) return [];
|
|
@@ -21112,20 +21289,20 @@ function getModelDisplayInfo(chatId) {
|
|
|
21112
21289
|
}
|
|
21113
21290
|
function readIdentityFile3(filename) {
|
|
21114
21291
|
try {
|
|
21115
|
-
return
|
|
21292
|
+
return readFileSync14(join25(IDENTITY_PATH, filename), "utf-8");
|
|
21116
21293
|
} catch {
|
|
21117
21294
|
return "";
|
|
21118
21295
|
}
|
|
21119
21296
|
}
|
|
21120
21297
|
function loadContextFiles2() {
|
|
21121
|
-
const contextDir =
|
|
21298
|
+
const contextDir = join25(homedir7(), ".cc-claw", "workspace", "context");
|
|
21122
21299
|
const results = [];
|
|
21123
|
-
if (!
|
|
21300
|
+
if (!existsSync24(contextDir)) return results;
|
|
21124
21301
|
try {
|
|
21125
21302
|
for (const entry of readdirSync12(contextDir)) {
|
|
21126
21303
|
if (!entry.endsWith(".md")) continue;
|
|
21127
21304
|
try {
|
|
21128
|
-
const content =
|
|
21305
|
+
const content = readFileSync14(join25(contextDir, entry), "utf-8");
|
|
21129
21306
|
results.push({ name: entry, content });
|
|
21130
21307
|
} catch {
|
|
21131
21308
|
}
|
|
@@ -21176,8 +21353,8 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
21176
21353
|
const stats = computeSkillStats(skillPath);
|
|
21177
21354
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
21178
21355
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
21179
|
-
const ccClawSkillsDir =
|
|
21180
|
-
const skillContent =
|
|
21356
|
+
const ccClawSkillsDir = join25(homedir7(), ".cc-claw", "workspace", "skills");
|
|
21357
|
+
const skillContent = readFileSync14(skillPath, "utf-8");
|
|
21181
21358
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
21182
21359
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
21183
21360
|
const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
|
|
@@ -21191,16 +21368,16 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
21191
21368
|
};
|
|
21192
21369
|
}
|
|
21193
21370
|
function listCcClawSkills() {
|
|
21194
|
-
const skillsDir =
|
|
21371
|
+
const skillsDir = join25(homedir7(), ".cc-claw", "workspace", "skills");
|
|
21195
21372
|
const entries = [];
|
|
21196
|
-
if (!
|
|
21373
|
+
if (!existsSync24(skillsDir)) return entries;
|
|
21197
21374
|
try {
|
|
21198
21375
|
for (const dir of readdirSync12(skillsDir)) {
|
|
21199
|
-
const skillFile =
|
|
21200
|
-
if (!
|
|
21376
|
+
const skillFile = join25(skillsDir, dir, "SKILL.md");
|
|
21377
|
+
if (!existsSync24(skillFile)) continue;
|
|
21201
21378
|
let description = "skill";
|
|
21202
21379
|
try {
|
|
21203
|
-
const content =
|
|
21380
|
+
const content = readFileSync14(skillFile, "utf-8");
|
|
21204
21381
|
const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
|
|
21205
21382
|
if (descMatch) description = descMatch[1].trim().slice(0, 60);
|
|
21206
21383
|
} catch {
|
|
@@ -21520,8 +21697,8 @@ var init_ui2 = __esm({
|
|
|
21520
21697
|
});
|
|
21521
21698
|
|
|
21522
21699
|
// src/router/optimize.ts
|
|
21523
|
-
import { readFileSync as
|
|
21524
|
-
import { join as
|
|
21700
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, existsSync as existsSync25, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
21701
|
+
import { join as join26, dirname as dirname4 } from "path";
|
|
21525
21702
|
import { homedir as homedir8 } from "os";
|
|
21526
21703
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
21527
21704
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
@@ -21702,7 +21879,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
21702
21879
|
} = await Promise.resolve().then(() => (init_ui2(), ui_exports));
|
|
21703
21880
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
21704
21881
|
if (!modelInfo) return;
|
|
21705
|
-
const skillPath =
|
|
21882
|
+
const skillPath = join26(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
|
|
21706
21883
|
const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
|
|
21707
21884
|
chatId,
|
|
21708
21885
|
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
@@ -21791,13 +21968,13 @@ async function applyFinding(chatId, channel, index) {
|
|
|
21791
21968
|
await showFinding(chatId, channel, index + 1);
|
|
21792
21969
|
return;
|
|
21793
21970
|
}
|
|
21794
|
-
if (!
|
|
21971
|
+
if (!existsSync25(targetPath)) {
|
|
21795
21972
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
21796
21973
|
session2.skipped.push(index);
|
|
21797
21974
|
await showFinding(chatId, channel, index + 1);
|
|
21798
21975
|
return;
|
|
21799
21976
|
}
|
|
21800
|
-
const original =
|
|
21977
|
+
const original = readFileSync15(targetPath, "utf-8");
|
|
21801
21978
|
const backupPath = targetPath + `.bak.${Date.now()}`;
|
|
21802
21979
|
writeFileSync7(backupPath, original, "utf-8");
|
|
21803
21980
|
pruneBackups2(targetPath);
|
|
@@ -21873,14 +22050,14 @@ async function finishReview(chatId, channel) {
|
|
|
21873
22050
|
activeSessions.delete(chatId);
|
|
21874
22051
|
}
|
|
21875
22052
|
function resolveTargetFile(location, auditTarget) {
|
|
21876
|
-
const ccClawHome =
|
|
22053
|
+
const ccClawHome = join26(homedir8(), ".cc-claw");
|
|
21877
22054
|
const filePart = location.split(":")[0]?.trim();
|
|
21878
22055
|
if (!filePart) return null;
|
|
21879
|
-
if (filePart === "SOUL.md") return
|
|
21880
|
-
if (filePart === "USER.md") return
|
|
21881
|
-
if (filePart === "CC-CLAW.md") return
|
|
22056
|
+
if (filePart === "SOUL.md") return join26(ccClawHome, "identity", "SOUL.md");
|
|
22057
|
+
if (filePart === "USER.md") return join26(ccClawHome, "identity", "USER.md");
|
|
22058
|
+
if (filePart === "CC-CLAW.md") return join26(ccClawHome, "identity", "CC-CLAW.md");
|
|
21882
22059
|
if (filePart === "SKILL.md" && auditTarget !== "identity") {
|
|
21883
|
-
return
|
|
22060
|
+
return join26(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
|
|
21884
22061
|
}
|
|
21885
22062
|
return null;
|
|
21886
22063
|
}
|
|
@@ -21888,7 +22065,7 @@ function pruneBackups2(absolutePath) {
|
|
|
21888
22065
|
const dir = dirname4(absolutePath);
|
|
21889
22066
|
const baseName = absolutePath.split("/").pop() ?? "";
|
|
21890
22067
|
try {
|
|
21891
|
-
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) =>
|
|
22068
|
+
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join26(dir, f));
|
|
21892
22069
|
while (backups.length > 3) {
|
|
21893
22070
|
const oldest = backups.shift();
|
|
21894
22071
|
try {
|
|
@@ -21922,8 +22099,8 @@ __export(auto_create_exports, {
|
|
|
21922
22099
|
saveSkill: () => saveSkill,
|
|
21923
22100
|
storePendingDraft: () => storePendingDraft
|
|
21924
22101
|
});
|
|
21925
|
-
import { join as
|
|
21926
|
-
import { writeFile as
|
|
22102
|
+
import { join as join27 } from "path";
|
|
22103
|
+
import { writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
21927
22104
|
function isSkillWorthy(signals) {
|
|
21928
22105
|
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
21929
22106
|
if (toolUseCount < 12) return false;
|
|
@@ -22069,10 +22246,10 @@ function parseExtractedSkill(llmResponse) {
|
|
|
22069
22246
|
return { name, content };
|
|
22070
22247
|
}
|
|
22071
22248
|
async function saveSkill(name, content) {
|
|
22072
|
-
const dir =
|
|
22073
|
-
await
|
|
22074
|
-
const filePath =
|
|
22075
|
-
await
|
|
22249
|
+
const dir = join27(SKILLS_PATH, name);
|
|
22250
|
+
await mkdir5(dir, { recursive: true });
|
|
22251
|
+
const filePath = join27(dir, "SKILL.md");
|
|
22252
|
+
await writeFile5(filePath, content, "utf-8");
|
|
22076
22253
|
invalidateSkillCache();
|
|
22077
22254
|
log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
|
|
22078
22255
|
return { path: filePath };
|
|
@@ -22242,18 +22419,7 @@ async function handleStopCommand(chatId, commandArgs, msg, channel) {
|
|
|
22242
22419
|
}
|
|
22243
22420
|
}
|
|
22244
22421
|
async function handleVoiceCommand(chatId, commandArgs, msg, channel) {
|
|
22245
|
-
|
|
22246
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
22247
|
-
await channel.sendKeyboard(chatId, `\u{1F3A7} Voice responses: ${vcEnabled ? "ON" : "OFF"}`, [
|
|
22248
|
-
[
|
|
22249
|
-
{ label: `${vcEnabled ? "" : "\u2713 "}\u{1F507} Off`, data: "voice:off", ...!vcEnabled ? { style: "danger" } : {} },
|
|
22250
|
-
{ label: `${vcEnabled ? "\u2713 " : ""}\u{1F50A} On`, data: "voice:on", ...vcEnabled ? { style: "success" } : {} }
|
|
22251
|
-
]
|
|
22252
|
-
]);
|
|
22253
|
-
} else {
|
|
22254
|
-
const toggled = toggleVoice(chatId);
|
|
22255
|
-
await channel.sendText(chatId, toggled ? "Voice responses enabled." : "Voice responses disabled.", { parseMode: "plain" });
|
|
22256
|
-
}
|
|
22422
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
22257
22423
|
}
|
|
22258
22424
|
async function handleVoiceConfigCommand(chatId, commandArgs, msg, channel) {
|
|
22259
22425
|
await sendVoiceConfigKeyboard(chatId, channel);
|
|
@@ -22452,42 +22618,40 @@ Use /skills to see it.`, { parseMode: "plain" });
|
|
|
22452
22618
|
async function handleExtractSkillCommand(chatId, commandArgs, msg, channel) {
|
|
22453
22619
|
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
22454
22620
|
const sessionMessages = getLog2(chatId);
|
|
22621
|
+
const exchangeCount = Math.floor(sessionMessages.length / 2);
|
|
22455
22622
|
if (sessionMessages.length < 2) {
|
|
22456
22623
|
await channel.sendText(chatId, "No session history to extract from. Have a conversation first, then run /extract_skill.", { parseMode: "plain" });
|
|
22457
22624
|
return;
|
|
22458
22625
|
}
|
|
22459
|
-
await channel.sendText(chatId, `Reviewing session (${Math.floor(sessionMessages.length / 2)} exchanges)...`, { parseMode: "plain" });
|
|
22460
|
-
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22461
|
-
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
22462
|
-
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22463
|
-
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
22464
|
-
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
22465
|
-
const extracted = parseExtractedSkill2(response.text);
|
|
22466
|
-
if (!extracted) {
|
|
22467
|
-
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
22468
|
-
return;
|
|
22469
|
-
}
|
|
22470
|
-
storePendingDraft2(chatId, {
|
|
22471
|
-
name: extracted.name,
|
|
22472
|
-
content: extracted.content,
|
|
22473
|
-
userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
|
|
22474
|
-
assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
|
|
22475
|
-
});
|
|
22476
|
-
const preview = extracted.content.length > 3500 ? extracted.content.slice(0, 3500) + "\n\n[...truncated...]" : extracted.content;
|
|
22477
22626
|
if (typeof channel.sendKeyboard === "function") {
|
|
22478
|
-
await channel.sendText(chatId, `Extracted skill: "${extracted.name}"
|
|
22479
|
-
|
|
22480
|
-
${preview}`, { parseMode: "plain" });
|
|
22481
22627
|
await channel.sendKeyboard(
|
|
22482
22628
|
chatId,
|
|
22483
|
-
|
|
22629
|
+
`\u{1F9E0} Extract Reusable Skill
|
|
22630
|
+
|
|
22631
|
+
This will analyze your current session (${exchangeCount} exchange${exchangeCount !== 1 ? "s" : ""}) and generate a reusable skill file.
|
|
22632
|
+
|
|
22633
|
+
\u2022 The AI reviews your conversation to identify the workflow
|
|
22634
|
+
\u2022 Generates a structured SKILL.md you can reuse in future tasks
|
|
22635
|
+
\u2022 You'll preview it before anything is saved
|
|
22636
|
+
|
|
22637
|
+
Ready to start?`,
|
|
22484
22638
|
[[
|
|
22485
|
-
{ label: "\
|
|
22486
|
-
{ label: "\u2715
|
|
22639
|
+
{ label: "\u26A1 Start Extraction", data: "skill:start-extract", style: "success" },
|
|
22640
|
+
{ label: "\u2715 Cancel", data: "skill:discard" }
|
|
22487
22641
|
]]
|
|
22488
22642
|
);
|
|
22489
22643
|
} else {
|
|
22490
|
-
|
|
22644
|
+
await channel.sendText(chatId, `Reviewing session (${exchangeCount} exchanges)...`, { parseMode: "plain" });
|
|
22645
|
+
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22646
|
+
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
22647
|
+
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22648
|
+
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
22649
|
+
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
22650
|
+
const extracted = parseExtractedSkill2(response.text);
|
|
22651
|
+
if (!extracted) {
|
|
22652
|
+
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
22653
|
+
return;
|
|
22654
|
+
}
|
|
22491
22655
|
const result = await saveSkill2(extracted.name, extracted.content);
|
|
22492
22656
|
await channel.sendText(chatId, `Skill "${extracted.name}" saved to ${result.path}`, { parseMode: "plain" });
|
|
22493
22657
|
}
|
|
@@ -22598,6 +22762,22 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22598
22762
|
stopAllSideQuests(chatId);
|
|
22599
22763
|
const oldSessionId = getSessionId(chatId);
|
|
22600
22764
|
const exchangeCount = getMessagePairCount(chatId);
|
|
22765
|
+
const needsSummary = exchangeCount > 0;
|
|
22766
|
+
let ackMsgId;
|
|
22767
|
+
if (needsSummary) {
|
|
22768
|
+
ackMsgId = await channel.sendTextReturningId?.(
|
|
22769
|
+
chatId,
|
|
22770
|
+
`\u23F3 Archiving session (${exchangeCount} exchanges)...`,
|
|
22771
|
+
"plain"
|
|
22772
|
+
);
|
|
22773
|
+
if (!ackMsgId) {
|
|
22774
|
+
await channel.sendText(
|
|
22775
|
+
chatId,
|
|
22776
|
+
`\u23F3 Archiving session (${exchangeCount} exchanges)...`,
|
|
22777
|
+
{ parseMode: "plain" }
|
|
22778
|
+
);
|
|
22779
|
+
}
|
|
22780
|
+
}
|
|
22601
22781
|
const summarized = await summarizeSession(chatId);
|
|
22602
22782
|
clearSession(chatId);
|
|
22603
22783
|
clearChatPaidSlots(chatId);
|
|
@@ -22616,6 +22796,7 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22616
22796
|
const text = `\u2705 New session started. Previous session archived${exchangeCount > 0 ? ` (${exchangeCount} exchanges)` : ""}.
|
|
22617
22797
|
|
|
22618
22798
|
\u{1F9E0} ${backendLabel} \xB7 ${modelLabel}`;
|
|
22799
|
+
if (ackMsgId) await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
22619
22800
|
const kbMsgId = await channel.sendKeyboard(chatId, text, [
|
|
22620
22801
|
[
|
|
22621
22802
|
{ label: "Switch Backend", data: "menu:backend", style: "primary" },
|
|
@@ -22634,7 +22815,11 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22634
22815
|
}
|
|
22635
22816
|
} else {
|
|
22636
22817
|
const text = summarized ? "Session summarized and saved. Fresh conversation started!" : "Fresh conversation started. What's on your mind?";
|
|
22637
|
-
|
|
22818
|
+
if (ackMsgId) {
|
|
22819
|
+
await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
22820
|
+
} else {
|
|
22821
|
+
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
22822
|
+
}
|
|
22638
22823
|
}
|
|
22639
22824
|
}
|
|
22640
22825
|
async function handleSummarizeCommand(chatId, commandArgs, msg, channel) {
|
|
@@ -24516,6 +24701,44 @@ ${plan.originalMessage}`;
|
|
|
24516
24701
|
if (current !== desired) toggleVoice(chatId);
|
|
24517
24702
|
await channel.sendText(chatId, desired ? "\u{1F50A} Voice responses enabled." : "\u{1F507} Voice responses disabled.", { parseMode: "plain" });
|
|
24518
24703
|
}
|
|
24704
|
+
} else if (data.startsWith("vcfg:stt-model:")) {
|
|
24705
|
+
const model2 = data.slice(15);
|
|
24706
|
+
if (!(model2 in LOCAL_WHISPER_MODELS)) {
|
|
24707
|
+
await channel.sendText(chatId, "Unknown model.", { parseMode: "plain" });
|
|
24708
|
+
return;
|
|
24709
|
+
}
|
|
24710
|
+
setSttProvider(chatId, "local-whisper");
|
|
24711
|
+
setSttModel(chatId, model2);
|
|
24712
|
+
const info = LOCAL_WHISPER_MODELS[model2];
|
|
24713
|
+
const downloaded = isWhisperModelDownloaded(model2);
|
|
24714
|
+
const notice = downloaded ? `\u2705 Transcription model set to ${model2} (${info.size}, ${info.lang}). Already downloaded.` : `\u2705 Transcription model set to ${model2} (${info.size}, ${info.lang}).
|
|
24715
|
+
|
|
24716
|
+
\u2B07\uFE0F Model will be downloaded on your first voice message (~${info.size}). This is a one-time download.`;
|
|
24717
|
+
await channel.sendText(chatId, notice, { parseMode: "plain" });
|
|
24718
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
24719
|
+
} else if (data.startsWith("vcfg:stt:")) {
|
|
24720
|
+
const provider = data.slice(9);
|
|
24721
|
+
if (provider === "local-whisper") {
|
|
24722
|
+
if (!isWhisperCliAvailable()) {
|
|
24723
|
+
await channel.sendText(
|
|
24724
|
+
chatId,
|
|
24725
|
+
"\u26A0\uFE0F Local Whisper requires `whisper-cli` to be installed.\n\nInstall it:\n```\nbrew install whisper-cpp\n```\n(macOS/Linux with Homebrew)\n\nOr download from: https://github.com/ggml-org/whisper.cpp/releases\n\nOnce installed, select Local Whisper here and pick a model.",
|
|
24726
|
+
{ parseMode: "markdown" }
|
|
24727
|
+
);
|
|
24728
|
+
return;
|
|
24729
|
+
}
|
|
24730
|
+
}
|
|
24731
|
+
if (provider === "groq" && !process.env.GROQ_API_KEY) {
|
|
24732
|
+
await channel.sendText(
|
|
24733
|
+
chatId,
|
|
24734
|
+
"\u26A0\uFE0F Groq requires `GROQ_API_KEY` to be set.\n\nGet a free key at https://console.groq.com/keys, then add it:\n```\necho 'GROQ_API_KEY=your-key' >> ~/.cc-claw/.env\ncc-claw service restart\n```",
|
|
24735
|
+
{ parseMode: "markdown" }
|
|
24736
|
+
);
|
|
24737
|
+
}
|
|
24738
|
+
setSttProvider(chatId, provider);
|
|
24739
|
+
const label2 = provider === "groq" ? "Groq (cloud)" : "Local Whisper";
|
|
24740
|
+
await channel.sendText(chatId, `\u2705 Transcription provider set to: ${label2}`, { parseMode: "plain" });
|
|
24741
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
24519
24742
|
} else if (data.startsWith("vcfg:")) {
|
|
24520
24743
|
const parts = data.slice(5).split(":");
|
|
24521
24744
|
const action = parts[0];
|
|
@@ -25423,15 +25646,16 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
25423
25646
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25424
25647
|
} else if (rest.startsWith("backend:")) {
|
|
25425
25648
|
const bid = rest.slice(8);
|
|
25426
|
-
|
|
25427
|
-
|
|
25428
|
-
|
|
25429
|
-
|
|
25430
|
-
}
|
|
25649
|
+
const newBackend = bid === "default" ? null : bid;
|
|
25650
|
+
updateHeartbeatConfig2({ backend: newBackend, model: null });
|
|
25651
|
+
updateHeartbeatField(chatId, "backend", newBackend);
|
|
25652
|
+
updateHeartbeatField(chatId, "model", null);
|
|
25431
25653
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25432
25654
|
} else if (rest.startsWith("model:")) {
|
|
25433
25655
|
const model2 = rest.slice(6);
|
|
25434
|
-
|
|
25656
|
+
const newModel = model2 === "default" ? null : model2;
|
|
25657
|
+
updateHeartbeatConfig2({ model: newModel });
|
|
25658
|
+
updateHeartbeatField(chatId, "model", newModel);
|
|
25435
25659
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25436
25660
|
} else if (rest === "thinking") {
|
|
25437
25661
|
await channel.sendText(chatId, "Thinking level for heartbeat: use /editjob to configure.", { parseMode: "plain" });
|
|
@@ -25551,6 +25775,71 @@ Example: /limits ${bid} daily 500000`, { parseMode: "plain" });
|
|
|
25551
25775
|
const page = parseInt(data.slice(12), 10);
|
|
25552
25776
|
const skills2 = await discoverAllSkills();
|
|
25553
25777
|
await sendSkillsPage(chatId, channel, skills2, page, messageId);
|
|
25778
|
+
} else if (data === "skill:start-extract") {
|
|
25779
|
+
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
25780
|
+
const sessionMessages = getLog2(chatId);
|
|
25781
|
+
if (sessionMessages.length < 2) {
|
|
25782
|
+
if (messageId) await replaceWithText("No session history to extract from.");
|
|
25783
|
+
else await channel.sendText(chatId, "No session history to extract from.", { parseMode: "plain" });
|
|
25784
|
+
return;
|
|
25785
|
+
}
|
|
25786
|
+
const exchangeCount = Math.floor(sessionMessages.length / 2);
|
|
25787
|
+
if (messageId) await replaceWithText(`\u{1F50D} Reviewing session (${exchangeCount} exchanges)...`);
|
|
25788
|
+
else await channel.sendText(chatId, `\u{1F50D} Reviewing session (${exchangeCount} exchanges)...`, { parseMode: "plain" });
|
|
25789
|
+
try {
|
|
25790
|
+
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25791
|
+
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
25792
|
+
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
25793
|
+
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
25794
|
+
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
25795
|
+
const extracted = parseExtractedSkill2(response.text);
|
|
25796
|
+
if (!extracted) {
|
|
25797
|
+
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
25798
|
+
return;
|
|
25799
|
+
}
|
|
25800
|
+
storePendingDraft2(chatId, {
|
|
25801
|
+
name: extracted.name,
|
|
25802
|
+
content: extracted.content,
|
|
25803
|
+
userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
|
|
25804
|
+
assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
|
|
25805
|
+
});
|
|
25806
|
+
const escaped = extracted.content.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
25807
|
+
const header2 = `\u{1F4CB} <b>Skill Preview: "${extracted.name}"</b>
|
|
25808
|
+
|
|
25809
|
+
`;
|
|
25810
|
+
const MAX_PREVIEW = 3500 - header2.length;
|
|
25811
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
25812
|
+
if (escaped.length > MAX_PREVIEW) {
|
|
25813
|
+
await channel.sendText(chatId, `${header2}<pre>${escaped.slice(0, 3500)}</pre>
|
|
25814
|
+
|
|
25815
|
+
\u2026(truncated \u2014 full skill will be saved)`, { parseMode: "html" });
|
|
25816
|
+
await channel.sendKeyboard(
|
|
25817
|
+
chatId,
|
|
25818
|
+
`Save "${extracted.name}" as a reusable skill?`,
|
|
25819
|
+
[[
|
|
25820
|
+
{ label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
|
|
25821
|
+
{ label: "\u2715 Discard", data: "skill:discard" }
|
|
25822
|
+
]]
|
|
25823
|
+
);
|
|
25824
|
+
} else {
|
|
25825
|
+
await channel.sendKeyboard(
|
|
25826
|
+
chatId,
|
|
25827
|
+
`${header2}<pre>${escaped}</pre>`,
|
|
25828
|
+
[[
|
|
25829
|
+
{ label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
|
|
25830
|
+
{ label: "\u2715 Discard", data: "skill:discard" }
|
|
25831
|
+
]]
|
|
25832
|
+
);
|
|
25833
|
+
}
|
|
25834
|
+
} else {
|
|
25835
|
+
const { saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25836
|
+
const { path } = await saveSkill2(extracted.name, extracted.content);
|
|
25837
|
+
await channel.sendText(chatId, `\u2705 Skill "${extracted.name}" saved.
|
|
25838
|
+
Path: ${path}`, { parseMode: "plain" });
|
|
25839
|
+
}
|
|
25840
|
+
} catch (e) {
|
|
25841
|
+
await channel.sendText(chatId, `Skill extraction failed: ${e.message}`, { parseMode: "plain" });
|
|
25842
|
+
}
|
|
25554
25843
|
} else if (data === "skill:extract") {
|
|
25555
25844
|
const { getPendingDraft: getPendingDraft2, clearPendingDraft: clearPendingDraft2, buildSkillExtractionPrompt: buildSkillExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25556
25845
|
const draft = getPendingDraft2(chatId);
|
|
@@ -26426,7 +26715,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26426
26715
|
})) {
|
|
26427
26716
|
const planDirective = buildPlanningDirective();
|
|
26428
26717
|
let typingActive2 = true;
|
|
26429
|
-
const
|
|
26718
|
+
const typingLoop2 = async () => {
|
|
26430
26719
|
while (typingActive2) {
|
|
26431
26720
|
try {
|
|
26432
26721
|
await channel.sendTyping?.(chatId);
|
|
@@ -26435,7 +26724,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26435
26724
|
await new Promise((r) => setTimeout(r, 4e3));
|
|
26436
26725
|
}
|
|
26437
26726
|
};
|
|
26438
|
-
|
|
26727
|
+
typingLoop2().catch(() => {
|
|
26439
26728
|
});
|
|
26440
26729
|
try {
|
|
26441
26730
|
const planResponse = await askAgent(chatId, cleanText || text, {
|
|
@@ -26476,23 +26765,18 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26476
26765
|
}
|
|
26477
26766
|
return;
|
|
26478
26767
|
}
|
|
26479
|
-
|
|
26480
|
-
const
|
|
26481
|
-
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
while (typingActive) {
|
|
26486
|
-
try {
|
|
26487
|
-
await channel.sendTyping?.(chatId);
|
|
26488
|
-
} catch {
|
|
26489
|
-
}
|
|
26490
|
-
await new Promise((r) => setTimeout(r, 4e3));
|
|
26768
|
+
let typingActive = true;
|
|
26769
|
+
const typingLoop = async () => {
|
|
26770
|
+
while (typingActive) {
|
|
26771
|
+
try {
|
|
26772
|
+
await channel.sendTyping?.(chatId);
|
|
26773
|
+
} catch {
|
|
26491
26774
|
}
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26775
|
+
await new Promise((r) => setTimeout(r, 4e3));
|
|
26776
|
+
}
|
|
26777
|
+
};
|
|
26778
|
+
typingLoop().catch(() => {
|
|
26779
|
+
});
|
|
26496
26780
|
try {
|
|
26497
26781
|
const tMode = settings.getMode();
|
|
26498
26782
|
const tVerbose = settings.getVerboseLevel();
|
|
@@ -26523,6 +26807,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26523
26807
|
}
|
|
26524
26808
|
};
|
|
26525
26809
|
await liveStatus.init();
|
|
26810
|
+
typingActive = false;
|
|
26526
26811
|
if (showThinkingUi && adapter.id !== "claude") {
|
|
26527
26812
|
liveStatus.addInfo(`\u{1F4AD} Thinking display not available for ${adapter.displayName}`);
|
|
26528
26813
|
}
|
|
@@ -26616,7 +26901,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26616
26901
|
}
|
|
26617
26902
|
responseText += `
|
|
26618
26903
|
|
|
26619
|
-
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}${slotTag}] \u23F1\uFE0F ${elapsedSec}s`;
|
|
26904
|
+
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}${slotTag}] \u23F1\uFE0F ${elapsedSec}s \xB7 \u{1F195}/new`;
|
|
26620
26905
|
}
|
|
26621
26906
|
if (observedSubagents.size > 0) {
|
|
26622
26907
|
const names = [...observedSubagents].join(", ");
|
|
@@ -27171,7 +27456,19 @@ function resolveJobBackendId(job) {
|
|
|
27171
27456
|
})();
|
|
27172
27457
|
}
|
|
27173
27458
|
function resolveJobModel(job) {
|
|
27174
|
-
if (job.model)
|
|
27459
|
+
if (job.model) {
|
|
27460
|
+
if (job.backend) {
|
|
27461
|
+
try {
|
|
27462
|
+
const adapter = getAdapter(job.backend);
|
|
27463
|
+
if (!(job.model in adapter.availableModels)) {
|
|
27464
|
+
warn(`[scheduler] job #${job.id}: model "${job.model}" not valid for backend "${job.backend}" \u2014 using default`);
|
|
27465
|
+
return adapter.defaultModel;
|
|
27466
|
+
}
|
|
27467
|
+
} catch {
|
|
27468
|
+
}
|
|
27469
|
+
}
|
|
27470
|
+
return job.model;
|
|
27471
|
+
}
|
|
27175
27472
|
const backendId = resolveJobBackendId(job);
|
|
27176
27473
|
try {
|
|
27177
27474
|
const adapter = getAdapter(backendId);
|
|
@@ -27205,7 +27502,7 @@ var init_cron = __esm({
|
|
|
27205
27502
|
});
|
|
27206
27503
|
|
|
27207
27504
|
// src/agents/runners/wrap-backend.ts
|
|
27208
|
-
import { join as
|
|
27505
|
+
import { join as join28 } from "path";
|
|
27209
27506
|
function buildMcpCommands(backendId) {
|
|
27210
27507
|
const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
|
|
27211
27508
|
return {
|
|
@@ -27299,7 +27596,7 @@ function wrapBackendAdapter(adapter) {
|
|
|
27299
27596
|
const configPath = writeMcpConfigFile(server);
|
|
27300
27597
|
return ["--mcp-config", configPath];
|
|
27301
27598
|
},
|
|
27302
|
-
getSkillPath: () =>
|
|
27599
|
+
getSkillPath: () => join28(SKILLS_PATH, `agent-${adapter.id}.md`)
|
|
27303
27600
|
};
|
|
27304
27601
|
}
|
|
27305
27602
|
var BACKEND_CAPABILITIES;
|
|
@@ -27361,18 +27658,18 @@ var init_wrap_backend = __esm({
|
|
|
27361
27658
|
});
|
|
27362
27659
|
|
|
27363
27660
|
// src/agents/runners/config-loader.ts
|
|
27364
|
-
import { readFileSync as
|
|
27365
|
-
import { join as
|
|
27661
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync14, existsSync as existsSync26, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
27662
|
+
import { join as join29 } from "path";
|
|
27366
27663
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
27367
27664
|
function resolveExecutable2(config2) {
|
|
27368
|
-
if (
|
|
27665
|
+
if (existsSync26(config2.executable)) return config2.executable;
|
|
27369
27666
|
try {
|
|
27370
27667
|
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
27371
27668
|
} catch {
|
|
27372
27669
|
}
|
|
27373
27670
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
27374
27671
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
27375
|
-
if (
|
|
27672
|
+
if (existsSync26(resolved)) return resolved;
|
|
27376
27673
|
}
|
|
27377
27674
|
return config2.executable;
|
|
27378
27675
|
}
|
|
@@ -27498,12 +27795,12 @@ function configToRunner(config2) {
|
|
|
27498
27795
|
prepareMcpInjection() {
|
|
27499
27796
|
return [];
|
|
27500
27797
|
},
|
|
27501
|
-
getSkillPath: () =>
|
|
27798
|
+
getSkillPath: () => join29(SKILLS_PATH, `agent-${config2.id}.md`)
|
|
27502
27799
|
};
|
|
27503
27800
|
}
|
|
27504
27801
|
function loadRunnerConfig(filePath) {
|
|
27505
27802
|
try {
|
|
27506
|
-
const content =
|
|
27803
|
+
const content = readFileSync16(filePath, "utf-8");
|
|
27507
27804
|
return JSON.parse(content);
|
|
27508
27805
|
} catch (err) {
|
|
27509
27806
|
warn(`[runners] Failed to load config ${filePath}: ${err}`);
|
|
@@ -27511,14 +27808,14 @@ function loadRunnerConfig(filePath) {
|
|
|
27511
27808
|
}
|
|
27512
27809
|
}
|
|
27513
27810
|
function loadAllRunnerConfigs() {
|
|
27514
|
-
if (!
|
|
27811
|
+
if (!existsSync26(RUNNERS_PATH)) {
|
|
27515
27812
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
27516
27813
|
return [];
|
|
27517
27814
|
}
|
|
27518
27815
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
27519
27816
|
const configs = [];
|
|
27520
27817
|
for (const file of files) {
|
|
27521
|
-
const config2 = loadRunnerConfig(
|
|
27818
|
+
const config2 = loadRunnerConfig(join29(RUNNERS_PATH, file));
|
|
27522
27819
|
if (config2) configs.push(config2);
|
|
27523
27820
|
}
|
|
27524
27821
|
return configs;
|
|
@@ -27539,16 +27836,16 @@ function registerConfigRunners() {
|
|
|
27539
27836
|
return count;
|
|
27540
27837
|
}
|
|
27541
27838
|
function watchRunnerConfigs(onChange) {
|
|
27542
|
-
if (!
|
|
27839
|
+
if (!existsSync26(RUNNERS_PATH)) return;
|
|
27543
27840
|
for (const prev of watchedFiles) {
|
|
27544
|
-
if (!
|
|
27841
|
+
if (!existsSync26(prev)) {
|
|
27545
27842
|
unwatchFile(prev);
|
|
27546
27843
|
watchedFiles.delete(prev);
|
|
27547
27844
|
}
|
|
27548
27845
|
}
|
|
27549
27846
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
27550
27847
|
for (const file of files) {
|
|
27551
|
-
const fullPath =
|
|
27848
|
+
const fullPath = join29(RUNNERS_PATH, file);
|
|
27552
27849
|
if (watchedFiles.has(fullPath)) continue;
|
|
27553
27850
|
watchedFiles.add(fullPath);
|
|
27554
27851
|
watchFile(fullPath, { interval: 5e3 }, () => {
|
|
@@ -28070,20 +28367,6 @@ var init_telegram2 = __esm({
|
|
|
28070
28367
|
this.allowedChatIds = new Set(ids);
|
|
28071
28368
|
this.bot = new Bot(token);
|
|
28072
28369
|
this.throttle = new TelegramThrottle();
|
|
28073
|
-
this.throttle.setResumeNotifier(async (chatId, pausedSec, queuedCount) => {
|
|
28074
|
-
if (pausedSec > 60) {
|
|
28075
|
-
log(`[telegram] Skipping resume notification (paused ${pausedSec}s \u2014 too long, would risk another 429)`);
|
|
28076
|
-
return;
|
|
28077
|
-
}
|
|
28078
|
-
try {
|
|
28079
|
-
await this.bot.api.sendMessage(
|
|
28080
|
-
numericChatId(chatId),
|
|
28081
|
-
`\u26A0\uFE0F Rate-limited by Telegram for ${pausedSec}s \u2014 delivering ${queuedCount} pending message(s) now.`
|
|
28082
|
-
);
|
|
28083
|
-
} catch (err) {
|
|
28084
|
-
warn("[telegram] Resume notification failed:", err instanceof Error ? err.message : err);
|
|
28085
|
-
}
|
|
28086
|
-
});
|
|
28087
28370
|
}
|
|
28088
28371
|
/** The first ID in ALLOWED_CHAT_ID — used for heartbeat, notifications, etc. */
|
|
28089
28372
|
getPrimaryChatId() {
|
|
@@ -28167,8 +28450,7 @@ var init_telegram2 = __esm({
|
|
|
28167
28450
|
{ command: "skills", description: "List and invoke skills" },
|
|
28168
28451
|
{ command: "extract_skill", description: "Extract a reusable skill from this session" },
|
|
28169
28452
|
{ command: "skill_install", description: "Install a skill from GitHub" },
|
|
28170
|
-
{ command: "voice", description: "
|
|
28171
|
-
{ command: "voice_config", description: "Configure voice provider and voice" },
|
|
28453
|
+
{ command: "voice", description: "Voice settings (STT transcription + TTS replies)" },
|
|
28172
28454
|
{ command: "response_style", description: "Set the AI response style (concise/normal/detailed)" },
|
|
28173
28455
|
{ command: "model_signature", description: "Toggle model+thinking signature on responses" },
|
|
28174
28456
|
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
@@ -28596,7 +28878,8 @@ var init_telegram2 = __esm({
|
|
|
28596
28878
|
"reaction",
|
|
28597
28879
|
() => this.bot.api.setMessageReaction(numericChatId(chatId), parseInt(messageId), [
|
|
28598
28880
|
{ type: "emoji", emoji }
|
|
28599
|
-
])
|
|
28881
|
+
]),
|
|
28882
|
+
{ skipRecord: true }
|
|
28600
28883
|
);
|
|
28601
28884
|
} catch (err) {
|
|
28602
28885
|
log(`[telegram] reactToMessage failed (chat=${chatId} msg=${messageId}): ${err}`);
|
|
@@ -28839,19 +29122,19 @@ var init_telegram2 = __esm({
|
|
|
28839
29122
|
});
|
|
28840
29123
|
|
|
28841
29124
|
// src/skills/bootstrap.ts
|
|
28842
|
-
import { existsSync as
|
|
28843
|
-
import { readdir as readdir6, readFile as readFile8, writeFile as
|
|
28844
|
-
import { join as
|
|
29125
|
+
import { existsSync as existsSync27 } from "fs";
|
|
29126
|
+
import { readdir as readdir6, readFile as readFile8, writeFile as writeFile6, copyFile } from "fs/promises";
|
|
29127
|
+
import { join as join30, dirname as dirname5 } from "path";
|
|
28845
29128
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
28846
29129
|
async function copyAgentManifestSkills() {
|
|
28847
|
-
if (!
|
|
29130
|
+
if (!existsSync27(PKG_SKILLS)) return;
|
|
28848
29131
|
try {
|
|
28849
29132
|
const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
|
|
28850
29133
|
for (const entry of entries) {
|
|
28851
29134
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
28852
|
-
const src =
|
|
28853
|
-
const dest =
|
|
28854
|
-
if (
|
|
29135
|
+
const src = join30(PKG_SKILLS, entry.name);
|
|
29136
|
+
const dest = join30(SKILLS_PATH, entry.name);
|
|
29137
|
+
if (existsSync27(dest)) continue;
|
|
28855
29138
|
await copyFile(src, dest);
|
|
28856
29139
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
28857
29140
|
}
|
|
@@ -28861,8 +29144,8 @@ async function copyAgentManifestSkills() {
|
|
|
28861
29144
|
}
|
|
28862
29145
|
async function bootstrapSkills() {
|
|
28863
29146
|
await copyAgentManifestSkills();
|
|
28864
|
-
const usmDir =
|
|
28865
|
-
if (
|
|
29147
|
+
const usmDir = join30(SKILLS_PATH, USM_DIR_NAME);
|
|
29148
|
+
if (existsSync27(usmDir)) return;
|
|
28866
29149
|
try {
|
|
28867
29150
|
const entries = await readdir6(SKILLS_PATH);
|
|
28868
29151
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
@@ -28885,8 +29168,8 @@ async function bootstrapSkills() {
|
|
|
28885
29168
|
}
|
|
28886
29169
|
}
|
|
28887
29170
|
async function patchUsmForCcClaw(usmDir) {
|
|
28888
|
-
const skillPath =
|
|
28889
|
-
if (!
|
|
29171
|
+
const skillPath = join30(usmDir, "SKILL.md");
|
|
29172
|
+
if (!existsSync27(skillPath)) return;
|
|
28890
29173
|
try {
|
|
28891
29174
|
let content = await readFile8(skillPath, "utf-8");
|
|
28892
29175
|
let patched = false;
|
|
@@ -28914,7 +29197,7 @@ async function patchUsmForCcClaw(usmDir) {
|
|
|
28914
29197
|
}
|
|
28915
29198
|
}
|
|
28916
29199
|
if (patched) {
|
|
28917
|
-
await
|
|
29200
|
+
await writeFile6(skillPath, content, "utf-8");
|
|
28918
29201
|
log("[skills] Patched USM SKILL.md with CC-Claw support");
|
|
28919
29202
|
}
|
|
28920
29203
|
} catch (err) {
|
|
@@ -28931,8 +29214,8 @@ var init_bootstrap = __esm({
|
|
|
28931
29214
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
28932
29215
|
USM_DIR_NAME = "universal-skills-manager";
|
|
28933
29216
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
28934
|
-
PKG_ROOT =
|
|
28935
|
-
PKG_SKILLS =
|
|
29217
|
+
PKG_ROOT = join30(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
|
|
29218
|
+
PKG_SKILLS = join30(PKG_ROOT, "skills");
|
|
28936
29219
|
}
|
|
28937
29220
|
});
|
|
28938
29221
|
|
|
@@ -29045,13 +29328,13 @@ __export(ai_skill_exports, {
|
|
|
29045
29328
|
generateAiSkill: () => generateAiSkill,
|
|
29046
29329
|
installAiSkill: () => installAiSkill
|
|
29047
29330
|
});
|
|
29048
|
-
import { existsSync as
|
|
29049
|
-
import { join as
|
|
29331
|
+
import { existsSync as existsSync28, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
29332
|
+
import { join as join31 } from "path";
|
|
29050
29333
|
import { homedir as homedir9 } from "os";
|
|
29051
29334
|
function generateAiSkill() {
|
|
29052
29335
|
const version = VERSION;
|
|
29053
29336
|
let systemState = "";
|
|
29054
|
-
if (
|
|
29337
|
+
if (existsSync28(DB_PATH)) {
|
|
29055
29338
|
try {
|
|
29056
29339
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
29057
29340
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -29491,8 +29774,8 @@ function installAiSkill() {
|
|
|
29491
29774
|
const failed = [];
|
|
29492
29775
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
29493
29776
|
for (const dir of dirs) {
|
|
29494
|
-
const skillDir =
|
|
29495
|
-
const skillPath =
|
|
29777
|
+
const skillDir = join31(dir, "cc-claw-cli");
|
|
29778
|
+
const skillPath = join31(skillDir, "SKILL.md");
|
|
29496
29779
|
try {
|
|
29497
29780
|
mkdirSync11(skillDir, { recursive: true });
|
|
29498
29781
|
writeFileSync8(skillPath, skill, "utf-8");
|
|
@@ -29511,11 +29794,11 @@ var init_ai_skill = __esm({
|
|
|
29511
29794
|
init_paths();
|
|
29512
29795
|
init_version();
|
|
29513
29796
|
BACKEND_SKILL_DIRS2 = {
|
|
29514
|
-
"cc-claw": [
|
|
29515
|
-
claude: [
|
|
29516
|
-
gemini: [
|
|
29517
|
-
codex: [
|
|
29518
|
-
cursor: [
|
|
29797
|
+
"cc-claw": [join31(homedir9(), ".cc-claw", "workspace", "skills")],
|
|
29798
|
+
claude: [join31(homedir9(), ".claude", "skills")],
|
|
29799
|
+
gemini: [join31(homedir9(), ".gemini", "skills")],
|
|
29800
|
+
codex: [join31(homedir9(), ".agents", "skills")],
|
|
29801
|
+
cursor: [join31(homedir9(), ".cursor", "skills"), join31(homedir9(), ".cursor", "skills-cursor")]
|
|
29519
29802
|
};
|
|
29520
29803
|
}
|
|
29521
29804
|
});
|
|
@@ -29525,21 +29808,21 @@ var index_exports = {};
|
|
|
29525
29808
|
__export(index_exports, {
|
|
29526
29809
|
main: () => main
|
|
29527
29810
|
});
|
|
29528
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
29529
|
-
import { join as
|
|
29811
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync29, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync18 } from "fs";
|
|
29812
|
+
import { join as join32 } from "path";
|
|
29530
29813
|
import dotenv from "dotenv";
|
|
29531
29814
|
function migrateLayout() {
|
|
29532
29815
|
const moves = [
|
|
29533
|
-
[
|
|
29534
|
-
[
|
|
29535
|
-
[
|
|
29536
|
-
[
|
|
29537
|
-
[
|
|
29538
|
-
[
|
|
29539
|
-
[
|
|
29816
|
+
[join32(CC_CLAW_HOME, "cc-claw.db"), join32(DATA_PATH, "cc-claw.db")],
|
|
29817
|
+
[join32(CC_CLAW_HOME, "cc-claw.db-shm"), join32(DATA_PATH, "cc-claw.db-shm")],
|
|
29818
|
+
[join32(CC_CLAW_HOME, "cc-claw.db-wal"), join32(DATA_PATH, "cc-claw.db-wal")],
|
|
29819
|
+
[join32(CC_CLAW_HOME, "cc-claw.log"), join32(LOGS_PATH, "cc-claw.log")],
|
|
29820
|
+
[join32(CC_CLAW_HOME, "cc-claw.log.1"), join32(LOGS_PATH, "cc-claw.log.1")],
|
|
29821
|
+
[join32(CC_CLAW_HOME, "cc-claw.error.log"), join32(LOGS_PATH, "cc-claw.error.log")],
|
|
29822
|
+
[join32(CC_CLAW_HOME, "cc-claw.error.log.1"), join32(LOGS_PATH, "cc-claw.error.log.1")]
|
|
29540
29823
|
];
|
|
29541
29824
|
for (const [from, to] of moves) {
|
|
29542
|
-
if (
|
|
29825
|
+
if (existsSync29(from) && !existsSync29(to)) {
|
|
29543
29826
|
try {
|
|
29544
29827
|
renameSync2(from, to);
|
|
29545
29828
|
} catch {
|
|
@@ -29568,7 +29851,7 @@ async function main() {
|
|
|
29568
29851
|
let version = "unknown";
|
|
29569
29852
|
try {
|
|
29570
29853
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
29571
|
-
version = JSON.parse(
|
|
29854
|
+
version = JSON.parse(readFileSync18(pkgPath, "utf-8")).version;
|
|
29572
29855
|
} catch {
|
|
29573
29856
|
}
|
|
29574
29857
|
log(`[cc-claw] Starting v${version}`);
|
|
@@ -29716,10 +29999,10 @@ async function main() {
|
|
|
29716
29999
|
try {
|
|
29717
30000
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
29718
30001
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
|
|
29719
|
-
const { join:
|
|
29720
|
-
const skillDir =
|
|
30002
|
+
const { join: join38 } = await import("path");
|
|
30003
|
+
const skillDir = join38(SKILLS_PATH, "cc-claw-cli");
|
|
29721
30004
|
mkdirSync19(skillDir, { recursive: true });
|
|
29722
|
-
writeFileSync13(
|
|
30005
|
+
writeFileSync13(join38(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
29723
30006
|
log("[cc-claw] AI skill updated");
|
|
29724
30007
|
} catch {
|
|
29725
30008
|
}
|
|
@@ -29819,10 +30102,10 @@ var init_index = __esm({
|
|
|
29819
30102
|
init_health3();
|
|
29820
30103
|
init_image_gen();
|
|
29821
30104
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
29822
|
-
if (!
|
|
30105
|
+
if (!existsSync29(dir)) mkdirSync12(dir, { recursive: true });
|
|
29823
30106
|
}
|
|
29824
30107
|
migrateLayout();
|
|
29825
|
-
if (
|
|
30108
|
+
if (existsSync29(ENV_PATH)) {
|
|
29826
30109
|
dotenv.config({ path: ENV_PATH });
|
|
29827
30110
|
} else {
|
|
29828
30111
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -29843,12 +30126,12 @@ __export(api_client_exports, {
|
|
|
29843
30126
|
apiPost: () => apiPost,
|
|
29844
30127
|
isDaemonRunning: () => isDaemonRunning
|
|
29845
30128
|
});
|
|
29846
|
-
import { readFileSync as
|
|
30129
|
+
import { readFileSync as readFileSync19, existsSync as existsSync30 } from "fs";
|
|
29847
30130
|
import { request as httpRequest, Agent } from "http";
|
|
29848
30131
|
function getToken() {
|
|
29849
30132
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
29850
30133
|
try {
|
|
29851
|
-
if (
|
|
30134
|
+
if (existsSync30(TOKEN_PATH)) return readFileSync19(TOKEN_PATH, "utf-8").trim();
|
|
29852
30135
|
} catch {
|
|
29853
30136
|
}
|
|
29854
30137
|
return null;
|
|
@@ -29947,10 +30230,10 @@ __export(service_exports2, {
|
|
|
29947
30230
|
serviceStatus: () => serviceStatus,
|
|
29948
30231
|
uninstallService: () => uninstallService
|
|
29949
30232
|
});
|
|
29950
|
-
import { existsSync as
|
|
30233
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
29951
30234
|
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
29952
30235
|
import { homedir as homedir10, platform } from "os";
|
|
29953
|
-
import { join as
|
|
30236
|
+
import { join as join33, dirname as dirname6 } from "path";
|
|
29954
30237
|
function xmlEscape(s) {
|
|
29955
30238
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
29956
30239
|
}
|
|
@@ -29959,7 +30242,7 @@ function resolveExecutable3(name) {
|
|
|
29959
30242
|
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
29960
30243
|
} catch {
|
|
29961
30244
|
const fallback = process.argv[1];
|
|
29962
|
-
if (fallback &&
|
|
30245
|
+
if (fallback && existsSync31(fallback)) return fallback;
|
|
29963
30246
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
29964
30247
|
}
|
|
29965
30248
|
}
|
|
@@ -29968,14 +30251,14 @@ function getPathDirs() {
|
|
|
29968
30251
|
const home = homedir10();
|
|
29969
30252
|
const dirs = /* @__PURE__ */ new Set([
|
|
29970
30253
|
nodeBin,
|
|
29971
|
-
|
|
30254
|
+
join33(home, ".local", "bin"),
|
|
29972
30255
|
"/usr/local/bin",
|
|
29973
30256
|
"/usr/bin",
|
|
29974
30257
|
"/bin"
|
|
29975
30258
|
]);
|
|
29976
30259
|
try {
|
|
29977
30260
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
29978
|
-
if (prefix) dirs.add(
|
|
30261
|
+
if (prefix) dirs.add(join33(prefix, "bin"));
|
|
29979
30262
|
} catch {
|
|
29980
30263
|
}
|
|
29981
30264
|
return [...dirs].join(":");
|
|
@@ -30034,9 +30317,9 @@ function generatePlist() {
|
|
|
30034
30317
|
}
|
|
30035
30318
|
function installMacOS() {
|
|
30036
30319
|
const agentsDir = dirname6(PLIST_PATH);
|
|
30037
|
-
if (!
|
|
30038
|
-
if (!
|
|
30039
|
-
if (
|
|
30320
|
+
if (!existsSync31(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
30321
|
+
if (!existsSync31(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
30322
|
+
if (existsSync31(PLIST_PATH)) {
|
|
30040
30323
|
try {
|
|
30041
30324
|
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
30042
30325
|
} catch {
|
|
@@ -30048,7 +30331,7 @@ function installMacOS() {
|
|
|
30048
30331
|
console.log(" Service loaded and starting.");
|
|
30049
30332
|
}
|
|
30050
30333
|
function uninstallMacOS() {
|
|
30051
|
-
if (!
|
|
30334
|
+
if (!existsSync31(PLIST_PATH)) {
|
|
30052
30335
|
console.log(" No service found to uninstall.");
|
|
30053
30336
|
return;
|
|
30054
30337
|
}
|
|
@@ -30123,8 +30406,8 @@ WantedBy=default.target
|
|
|
30123
30406
|
`;
|
|
30124
30407
|
}
|
|
30125
30408
|
function installLinux() {
|
|
30126
|
-
if (!
|
|
30127
|
-
if (!
|
|
30409
|
+
if (!existsSync31(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
30410
|
+
if (!existsSync31(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
30128
30411
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
30129
30412
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
30130
30413
|
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -30133,7 +30416,7 @@ function installLinux() {
|
|
|
30133
30416
|
console.log(" Service enabled and started.");
|
|
30134
30417
|
}
|
|
30135
30418
|
function uninstallLinux() {
|
|
30136
|
-
if (!
|
|
30419
|
+
if (!existsSync31(UNIT_PATH)) {
|
|
30137
30420
|
console.log(" No service found to uninstall.");
|
|
30138
30421
|
return;
|
|
30139
30422
|
}
|
|
@@ -30158,7 +30441,7 @@ function statusLinux() {
|
|
|
30158
30441
|
}
|
|
30159
30442
|
}
|
|
30160
30443
|
function installService() {
|
|
30161
|
-
if (!
|
|
30444
|
+
if (!existsSync31(join33(CC_CLAW_HOME, ".env"))) {
|
|
30162
30445
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
30163
30446
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
30164
30447
|
process.exitCode = 1;
|
|
@@ -30187,9 +30470,9 @@ var init_service2 = __esm({
|
|
|
30187
30470
|
"use strict";
|
|
30188
30471
|
init_paths();
|
|
30189
30472
|
PLIST_LABEL = "com.cc-claw";
|
|
30190
|
-
PLIST_PATH =
|
|
30191
|
-
SYSTEMD_DIR =
|
|
30192
|
-
UNIT_PATH =
|
|
30473
|
+
PLIST_PATH = join33(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
30474
|
+
SYSTEMD_DIR = join33(homedir10(), ".config", "systemd", "user");
|
|
30475
|
+
UNIT_PATH = join33(SYSTEMD_DIR, "cc-claw.service");
|
|
30193
30476
|
}
|
|
30194
30477
|
});
|
|
30195
30478
|
|
|
@@ -30356,13 +30639,13 @@ var init_daemon = __esm({
|
|
|
30356
30639
|
});
|
|
30357
30640
|
|
|
30358
30641
|
// src/cli/resolve-chat.ts
|
|
30359
|
-
import { readFileSync as
|
|
30642
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
30360
30643
|
function resolveChatId2(globalOpts) {
|
|
30361
30644
|
const explicit = globalOpts.chat;
|
|
30362
30645
|
if (explicit) return explicit;
|
|
30363
30646
|
if (_cachedDefault) return _cachedDefault;
|
|
30364
30647
|
try {
|
|
30365
|
-
const content =
|
|
30648
|
+
const content = readFileSync21(ENV_PATH, "utf-8");
|
|
30366
30649
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
30367
30650
|
if (match) {
|
|
30368
30651
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -30386,7 +30669,7 @@ var status_exports = {};
|
|
|
30386
30669
|
__export(status_exports, {
|
|
30387
30670
|
statusCommand: () => statusCommand
|
|
30388
30671
|
});
|
|
30389
|
-
import { existsSync as
|
|
30672
|
+
import { existsSync as existsSync32, statSync as statSync10 } from "fs";
|
|
30390
30673
|
async function statusCommand(globalOpts, localOpts) {
|
|
30391
30674
|
try {
|
|
30392
30675
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
@@ -30426,7 +30709,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
30426
30709
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
30427
30710
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
30428
30711
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
30429
|
-
const dbStat =
|
|
30712
|
+
const dbStat = existsSync32(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
30430
30713
|
let daemonRunning = false;
|
|
30431
30714
|
let daemonInfo = {};
|
|
30432
30715
|
try {
|
|
@@ -30538,13 +30821,13 @@ __export(doctor_exports, {
|
|
|
30538
30821
|
doctorCommand: () => doctorCommand,
|
|
30539
30822
|
doctorErrors: () => doctorErrors
|
|
30540
30823
|
});
|
|
30541
|
-
import { existsSync as
|
|
30824
|
+
import { existsSync as existsSync33, accessSync, constants } from "fs";
|
|
30542
30825
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
30543
30826
|
async function doctorCommand(globalOpts, localOpts) {
|
|
30544
30827
|
const checks = [];
|
|
30545
30828
|
const dbChecks = checkDatabase();
|
|
30546
30829
|
checks.push(...dbChecks);
|
|
30547
|
-
if (
|
|
30830
|
+
if (existsSync33(DB_PATH)) {
|
|
30548
30831
|
try {
|
|
30549
30832
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30550
30833
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -30570,7 +30853,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30570
30853
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
30571
30854
|
}
|
|
30572
30855
|
}
|
|
30573
|
-
if (
|
|
30856
|
+
if (existsSync33(ENV_PATH)) {
|
|
30574
30857
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
30575
30858
|
} else {
|
|
30576
30859
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
@@ -30613,7 +30896,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30613
30896
|
} catch {
|
|
30614
30897
|
}
|
|
30615
30898
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
30616
|
-
if (
|
|
30899
|
+
if (existsSync33(tokenPath)) {
|
|
30617
30900
|
try {
|
|
30618
30901
|
accessSync(tokenPath, constants.R_OK);
|
|
30619
30902
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -30679,7 +30962,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30679
30962
|
const errorChecks = checks.filter(
|
|
30680
30963
|
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
30681
30964
|
);
|
|
30682
|
-
if (errorChecks.length > 0 &&
|
|
30965
|
+
if (errorChecks.length > 0 && existsSync33(ERROR_LOG_PATH)) {
|
|
30683
30966
|
try {
|
|
30684
30967
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
30685
30968
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -30808,15 +31091,15 @@ var logs_exports = {};
|
|
|
30808
31091
|
__export(logs_exports, {
|
|
30809
31092
|
logsCommand: () => logsCommand
|
|
30810
31093
|
});
|
|
30811
|
-
import { existsSync as
|
|
31094
|
+
import { existsSync as existsSync34, readFileSync as readFileSync23, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
30812
31095
|
async function logsCommand(opts) {
|
|
30813
31096
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
30814
|
-
if (!
|
|
31097
|
+
if (!existsSync34(logFile)) {
|
|
30815
31098
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
30816
31099
|
process.exit(1);
|
|
30817
31100
|
}
|
|
30818
31101
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
30819
|
-
const content =
|
|
31102
|
+
const content = readFileSync23(logFile, "utf-8");
|
|
30820
31103
|
const allLines = content.split("\n");
|
|
30821
31104
|
const tailLines = allLines.slice(-maxLines);
|
|
30822
31105
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -30826,7 +31109,7 @@ async function logsCommand(opts) {
|
|
|
30826
31109
|
let lastLength = content.length;
|
|
30827
31110
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
30828
31111
|
try {
|
|
30829
|
-
const newContent =
|
|
31112
|
+
const newContent = readFileSync23(logFile, "utf-8");
|
|
30830
31113
|
if (newContent.length > lastLength) {
|
|
30831
31114
|
const newPart = newContent.slice(lastLength);
|
|
30832
31115
|
process.stdout.write(newPart);
|
|
@@ -30858,7 +31141,7 @@ __export(session_logs_exports, {
|
|
|
30858
31141
|
sessionLogsList: () => sessionLogsList,
|
|
30859
31142
|
sessionLogsTail: () => sessionLogsTail
|
|
30860
31143
|
});
|
|
30861
|
-
import { readFileSync as
|
|
31144
|
+
import { readFileSync as readFileSync24, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
30862
31145
|
async function sessionLogsList(opts) {
|
|
30863
31146
|
const logs = listSessionLogs();
|
|
30864
31147
|
if (logs.length === 0) {
|
|
@@ -30915,12 +31198,12 @@ async function sessionLogsTail(opts) {
|
|
|
30915
31198
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
30916
31199
|
let lastLength = 0;
|
|
30917
31200
|
try {
|
|
30918
|
-
lastLength =
|
|
31201
|
+
lastLength = readFileSync24(targetPath, "utf-8").length;
|
|
30919
31202
|
} catch {
|
|
30920
31203
|
}
|
|
30921
31204
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
30922
31205
|
try {
|
|
30923
|
-
const content =
|
|
31206
|
+
const content = readFileSync24(targetPath, "utf-8");
|
|
30924
31207
|
if (content.length > lastLength) {
|
|
30925
31208
|
process.stdout.write(content.slice(lastLength));
|
|
30926
31209
|
lastLength = content.length;
|
|
@@ -30964,11 +31247,11 @@ __export(gemini_exports, {
|
|
|
30964
31247
|
geminiReorder: () => geminiReorder,
|
|
30965
31248
|
geminiRotation: () => geminiRotation
|
|
30966
31249
|
});
|
|
30967
|
-
import { existsSync as
|
|
30968
|
-
import { join as
|
|
31250
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync25, chmodSync } from "fs";
|
|
31251
|
+
import { join as join34 } from "path";
|
|
30969
31252
|
import { createInterface as createInterface8 } from "readline";
|
|
30970
31253
|
function requireDb() {
|
|
30971
|
-
if (!
|
|
31254
|
+
if (!existsSync36(DB_PATH)) {
|
|
30972
31255
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30973
31256
|
process.exit(1);
|
|
30974
31257
|
}
|
|
@@ -30993,9 +31276,9 @@ async function resolveSlotId(idOrLabel) {
|
|
|
30993
31276
|
function resolveOAuthEmail(configHome) {
|
|
30994
31277
|
if (!configHome) return null;
|
|
30995
31278
|
try {
|
|
30996
|
-
const accountsPath =
|
|
30997
|
-
if (!
|
|
30998
|
-
const accounts = JSON.parse(
|
|
31279
|
+
const accountsPath = join34(configHome, ".gemini", "google_accounts.json");
|
|
31280
|
+
if (!existsSync36(accountsPath)) return null;
|
|
31281
|
+
const accounts = JSON.parse(readFileSync25(accountsPath, "utf-8"));
|
|
30999
31282
|
return accounts.active || null;
|
|
31000
31283
|
} catch {
|
|
31001
31284
|
return null;
|
|
@@ -31077,14 +31360,14 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
31077
31360
|
}
|
|
31078
31361
|
async function geminiAddAccount(globalOpts, opts) {
|
|
31079
31362
|
await requireWriteDb();
|
|
31080
|
-
const slotsDir =
|
|
31081
|
-
if (!
|
|
31363
|
+
const slotsDir = join34(CC_CLAW_HOME, "gemini-slots");
|
|
31364
|
+
if (!existsSync36(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
31082
31365
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31083
31366
|
const tempId = Date.now();
|
|
31084
|
-
const slotDir =
|
|
31367
|
+
const slotDir = join34(slotsDir, `slot-${tempId}`);
|
|
31085
31368
|
mkdirSync14(slotDir, { recursive: true, mode: 448 });
|
|
31086
|
-
mkdirSync14(
|
|
31087
|
-
writeFileSync10(
|
|
31369
|
+
mkdirSync14(join34(slotDir, ".gemini"), { recursive: true });
|
|
31370
|
+
writeFileSync10(join34(slotDir, ".gemini", "settings.json"), JSON.stringify({
|
|
31088
31371
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
31089
31372
|
}, null, 2));
|
|
31090
31373
|
console.log("");
|
|
@@ -31101,8 +31384,8 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
31101
31384
|
});
|
|
31102
31385
|
} catch {
|
|
31103
31386
|
}
|
|
31104
|
-
const oauthPath =
|
|
31105
|
-
if (!
|
|
31387
|
+
const oauthPath = join34(slotDir, ".gemini", "oauth_creds.json");
|
|
31388
|
+
if (!existsSync36(oauthPath)) {
|
|
31106
31389
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
31107
31390
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
31108
31391
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -31110,7 +31393,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
31110
31393
|
}
|
|
31111
31394
|
let accountEmail = "unknown";
|
|
31112
31395
|
try {
|
|
31113
|
-
const accounts = JSON.parse(__require("fs").readFileSync(
|
|
31396
|
+
const accounts = JSON.parse(__require("fs").readFileSync(join34(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
|
|
31114
31397
|
accountEmail = accounts.active || accountEmail;
|
|
31115
31398
|
} catch {
|
|
31116
31399
|
}
|
|
@@ -31221,9 +31504,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31221
31504
|
outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
|
|
31222
31505
|
return;
|
|
31223
31506
|
}
|
|
31224
|
-
const settingsPath =
|
|
31225
|
-
if (!
|
|
31226
|
-
mkdirSync14(
|
|
31507
|
+
const settingsPath = join34(slot.configHome, ".gemini", "settings.json");
|
|
31508
|
+
if (!existsSync36(settingsPath)) {
|
|
31509
|
+
mkdirSync14(join34(slot.configHome, ".gemini"), { recursive: true });
|
|
31227
31510
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
31228
31511
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
31229
31512
|
}, null, 2));
|
|
@@ -31247,8 +31530,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31247
31530
|
});
|
|
31248
31531
|
} catch {
|
|
31249
31532
|
}
|
|
31250
|
-
const oauthPath =
|
|
31251
|
-
if (!
|
|
31533
|
+
const oauthPath = join34(slot.configHome, ".gemini", "oauth_creds.json");
|
|
31534
|
+
if (!existsSync36(oauthPath)) {
|
|
31252
31535
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
31253
31536
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
31254
31537
|
`);
|
|
@@ -31258,7 +31541,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31258
31541
|
setGeminiSlotEnabled2(slotId, true);
|
|
31259
31542
|
let accountEmail = slot.label;
|
|
31260
31543
|
try {
|
|
31261
|
-
const accounts = JSON.parse(
|
|
31544
|
+
const accounts = JSON.parse(readFileSync25(join34(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
31262
31545
|
if (accounts.active) accountEmail = accounts.active;
|
|
31263
31546
|
} catch {
|
|
31264
31547
|
}
|
|
@@ -31317,11 +31600,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
31317
31600
|
makeReorder: () => makeReorder,
|
|
31318
31601
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
31319
31602
|
});
|
|
31320
|
-
import { existsSync as
|
|
31321
|
-
import { join as
|
|
31603
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync26 } from "fs";
|
|
31604
|
+
import { join as join35 } from "path";
|
|
31322
31605
|
import { createInterface as createInterface9 } from "readline";
|
|
31323
31606
|
function requireDb2() {
|
|
31324
|
-
if (!
|
|
31607
|
+
if (!existsSync37(DB_PATH)) {
|
|
31325
31608
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31326
31609
|
process.exit(1);
|
|
31327
31610
|
}
|
|
@@ -31410,10 +31693,10 @@ function makeAddAccount(backend2, displayName) {
|
|
|
31410
31693
|
process.exit(1);
|
|
31411
31694
|
}
|
|
31412
31695
|
await requireWriteDb2();
|
|
31413
|
-
const slotsDir =
|
|
31414
|
-
if (!
|
|
31696
|
+
const slotsDir = join35(CC_CLAW_HOME, config2.slotsSubdir);
|
|
31697
|
+
if (!existsSync37(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
31415
31698
|
const tempId = Date.now();
|
|
31416
|
-
const slotDir =
|
|
31699
|
+
const slotDir = join35(slotsDir, `slot-${tempId}`);
|
|
31417
31700
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
31418
31701
|
if (config2.preSetup) config2.preSetup(slotDir);
|
|
31419
31702
|
console.log("");
|
|
@@ -31664,22 +31947,22 @@ var init_backend_cmd_factory = __esm({
|
|
|
31664
31947
|
envValue: (slotDir) => slotDir,
|
|
31665
31948
|
envOverrides: { ANTHROPIC_API_KEY: void 0 },
|
|
31666
31949
|
preSetup: (slotDir) => {
|
|
31667
|
-
mkdirSync15(
|
|
31950
|
+
mkdirSync15(join35(slotDir, ".claude"), { recursive: true });
|
|
31668
31951
|
},
|
|
31669
31952
|
verifyCredentials: (slotDir) => {
|
|
31670
|
-
const claudeJson =
|
|
31671
|
-
const claudeJsonNested =
|
|
31672
|
-
if (
|
|
31953
|
+
const claudeJson = join35(slotDir, ".claude.json");
|
|
31954
|
+
const claudeJsonNested = join35(slotDir, ".claude", ".claude.json");
|
|
31955
|
+
if (existsSync37(claudeJson)) {
|
|
31673
31956
|
try {
|
|
31674
|
-
const data = JSON.parse(
|
|
31957
|
+
const data = JSON.parse(readFileSync26(claudeJson, "utf-8"));
|
|
31675
31958
|
return Boolean(data.oauthAccount);
|
|
31676
31959
|
} catch {
|
|
31677
31960
|
return false;
|
|
31678
31961
|
}
|
|
31679
31962
|
}
|
|
31680
|
-
if (
|
|
31963
|
+
if (existsSync37(claudeJsonNested)) {
|
|
31681
31964
|
try {
|
|
31682
|
-
const data = JSON.parse(
|
|
31965
|
+
const data = JSON.parse(readFileSync26(claudeJsonNested, "utf-8"));
|
|
31683
31966
|
return Boolean(data.oauthAccount);
|
|
31684
31967
|
} catch {
|
|
31685
31968
|
return false;
|
|
@@ -31700,9 +31983,9 @@ var init_backend_cmd_factory = __esm({
|
|
|
31700
31983
|
} catch {
|
|
31701
31984
|
}
|
|
31702
31985
|
try {
|
|
31703
|
-
const claudeJson =
|
|
31704
|
-
if (
|
|
31705
|
-
const data = JSON.parse(
|
|
31986
|
+
const claudeJson = join35(slotDir, ".claude.json");
|
|
31987
|
+
if (existsSync37(claudeJson)) {
|
|
31988
|
+
const data = JSON.parse(readFileSync26(claudeJson, "utf-8"));
|
|
31706
31989
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
31707
31990
|
}
|
|
31708
31991
|
} catch {
|
|
@@ -31717,11 +32000,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
31717
32000
|
envValue: (slotDir) => slotDir,
|
|
31718
32001
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
31719
32002
|
verifyCredentials: (slotDir) => {
|
|
31720
|
-
return
|
|
32003
|
+
return existsSync37(join35(slotDir, "auth.json"));
|
|
31721
32004
|
},
|
|
31722
32005
|
extractLabel: (slotDir) => {
|
|
31723
32006
|
try {
|
|
31724
|
-
const authData = JSON.parse(
|
|
32007
|
+
const authData = JSON.parse(readFileSync26(join35(slotDir, "auth.json"), "utf-8"));
|
|
31725
32008
|
if (authData.email) return authData.email;
|
|
31726
32009
|
if (authData.account_name) return authData.account_name;
|
|
31727
32010
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -31745,9 +32028,9 @@ __export(ollama_exports3, {
|
|
|
31745
32028
|
ollamaRemove: () => ollamaRemove,
|
|
31746
32029
|
ollamaTest: () => ollamaTest
|
|
31747
32030
|
});
|
|
31748
|
-
import { existsSync as
|
|
32031
|
+
import { existsSync as existsSync38 } from "fs";
|
|
31749
32032
|
function requireDb3() {
|
|
31750
|
-
if (!
|
|
32033
|
+
if (!existsSync38(DB_PATH)) {
|
|
31751
32034
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31752
32035
|
process.exit(1);
|
|
31753
32036
|
}
|
|
@@ -32006,12 +32289,12 @@ __export(backend_exports, {
|
|
|
32006
32289
|
backendList: () => backendList,
|
|
32007
32290
|
backendSet: () => backendSet
|
|
32008
32291
|
});
|
|
32009
|
-
import { existsSync as
|
|
32292
|
+
import { existsSync as existsSync39 } from "fs";
|
|
32010
32293
|
async function backendList(globalOpts) {
|
|
32011
32294
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
32012
32295
|
const chatId = resolveChatId2(globalOpts);
|
|
32013
32296
|
let activeBackend = null;
|
|
32014
|
-
if (
|
|
32297
|
+
if (existsSync39(DB_PATH)) {
|
|
32015
32298
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32016
32299
|
const readDb = openDatabaseReadOnly2();
|
|
32017
32300
|
try {
|
|
@@ -32042,7 +32325,7 @@ async function backendList(globalOpts) {
|
|
|
32042
32325
|
}
|
|
32043
32326
|
async function backendGet(globalOpts) {
|
|
32044
32327
|
const chatId = resolveChatId2(globalOpts);
|
|
32045
|
-
if (!
|
|
32328
|
+
if (!existsSync39(DB_PATH)) {
|
|
32046
32329
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32047
32330
|
process.exit(1);
|
|
32048
32331
|
}
|
|
@@ -32086,13 +32369,13 @@ __export(model_exports, {
|
|
|
32086
32369
|
modelList: () => modelList,
|
|
32087
32370
|
modelSet: () => modelSet
|
|
32088
32371
|
});
|
|
32089
|
-
import { existsSync as
|
|
32372
|
+
import { existsSync as existsSync40 } from "fs";
|
|
32090
32373
|
async function modelList(globalOpts) {
|
|
32091
32374
|
const chatId = resolveChatId2(globalOpts);
|
|
32092
32375
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32093
32376
|
const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
32094
32377
|
let backendId = "claude";
|
|
32095
|
-
if (
|
|
32378
|
+
if (existsSync40(DB_PATH)) {
|
|
32096
32379
|
const readDb = openDatabaseReadOnly2();
|
|
32097
32380
|
try {
|
|
32098
32381
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -32125,7 +32408,7 @@ async function modelList(globalOpts) {
|
|
|
32125
32408
|
}
|
|
32126
32409
|
async function modelGet(globalOpts) {
|
|
32127
32410
|
const chatId = resolveChatId2(globalOpts);
|
|
32128
|
-
if (!
|
|
32411
|
+
if (!existsSync40(DB_PATH)) {
|
|
32129
32412
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32130
32413
|
process.exit(1);
|
|
32131
32414
|
}
|
|
@@ -32169,9 +32452,9 @@ __export(memory_exports2, {
|
|
|
32169
32452
|
memoryList: () => memoryList,
|
|
32170
32453
|
memorySearch: () => memorySearch
|
|
32171
32454
|
});
|
|
32172
|
-
import { existsSync as
|
|
32455
|
+
import { existsSync as existsSync41 } from "fs";
|
|
32173
32456
|
async function memoryList(globalOpts) {
|
|
32174
|
-
if (!
|
|
32457
|
+
if (!existsSync41(DB_PATH)) {
|
|
32175
32458
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32176
32459
|
process.exit(1);
|
|
32177
32460
|
}
|
|
@@ -32195,7 +32478,7 @@ async function memoryList(globalOpts) {
|
|
|
32195
32478
|
});
|
|
32196
32479
|
}
|
|
32197
32480
|
async function memorySearch(globalOpts, query) {
|
|
32198
|
-
if (!
|
|
32481
|
+
if (!existsSync41(DB_PATH)) {
|
|
32199
32482
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32200
32483
|
process.exit(1);
|
|
32201
32484
|
}
|
|
@@ -32217,7 +32500,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
32217
32500
|
});
|
|
32218
32501
|
}
|
|
32219
32502
|
async function memoryHistory(globalOpts, opts) {
|
|
32220
|
-
if (!
|
|
32503
|
+
if (!existsSync41(DB_PATH)) {
|
|
32221
32504
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32222
32505
|
process.exit(1);
|
|
32223
32506
|
}
|
|
@@ -32265,7 +32548,7 @@ __export(cron_exports2, {
|
|
|
32265
32548
|
cronList: () => cronList,
|
|
32266
32549
|
cronRuns: () => cronRuns
|
|
32267
32550
|
});
|
|
32268
|
-
import { existsSync as
|
|
32551
|
+
import { existsSync as existsSync42 } from "fs";
|
|
32269
32552
|
function parseFallbacks(raw) {
|
|
32270
32553
|
return raw.slice(0, 3).map((f) => {
|
|
32271
32554
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -32286,7 +32569,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
32286
32569
|
return val;
|
|
32287
32570
|
}
|
|
32288
32571
|
async function cronList(globalOpts) {
|
|
32289
|
-
if (!
|
|
32572
|
+
if (!existsSync42(DB_PATH)) {
|
|
32290
32573
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32291
32574
|
process.exit(1);
|
|
32292
32575
|
}
|
|
@@ -32324,7 +32607,7 @@ async function cronList(globalOpts) {
|
|
|
32324
32607
|
});
|
|
32325
32608
|
}
|
|
32326
32609
|
async function cronHealth(globalOpts) {
|
|
32327
|
-
if (!
|
|
32610
|
+
if (!existsSync42(DB_PATH)) {
|
|
32328
32611
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32329
32612
|
process.exit(1);
|
|
32330
32613
|
}
|
|
@@ -32485,7 +32768,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
32485
32768
|
}
|
|
32486
32769
|
}
|
|
32487
32770
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
32488
|
-
if (!
|
|
32771
|
+
if (!existsSync42(DB_PATH)) {
|
|
32489
32772
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32490
32773
|
process.exit(1);
|
|
32491
32774
|
}
|
|
@@ -32532,9 +32815,9 @@ __export(agents_exports, {
|
|
|
32532
32815
|
runnersList: () => runnersList,
|
|
32533
32816
|
tasksList: () => tasksList
|
|
32534
32817
|
});
|
|
32535
|
-
import { existsSync as
|
|
32818
|
+
import { existsSync as existsSync43 } from "fs";
|
|
32536
32819
|
async function agentsList(globalOpts) {
|
|
32537
|
-
if (!
|
|
32820
|
+
if (!existsSync43(DB_PATH)) {
|
|
32538
32821
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32539
32822
|
process.exit(1);
|
|
32540
32823
|
}
|
|
@@ -32565,7 +32848,7 @@ async function agentsList(globalOpts) {
|
|
|
32565
32848
|
});
|
|
32566
32849
|
}
|
|
32567
32850
|
async function tasksList(globalOpts) {
|
|
32568
|
-
if (!
|
|
32851
|
+
if (!existsSync43(DB_PATH)) {
|
|
32569
32852
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32570
32853
|
process.exit(1);
|
|
32571
32854
|
}
|
|
@@ -32693,10 +32976,10 @@ __export(db_exports, {
|
|
|
32693
32976
|
dbPath: () => dbPath,
|
|
32694
32977
|
dbStats: () => dbStats
|
|
32695
32978
|
});
|
|
32696
|
-
import { existsSync as
|
|
32979
|
+
import { existsSync as existsSync44, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
32697
32980
|
import { dirname as dirname7 } from "path";
|
|
32698
32981
|
async function dbStats(globalOpts) {
|
|
32699
|
-
if (!
|
|
32982
|
+
if (!existsSync44(DB_PATH)) {
|
|
32700
32983
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
32701
32984
|
process.exit(1);
|
|
32702
32985
|
}
|
|
@@ -32704,7 +32987,7 @@ async function dbStats(globalOpts) {
|
|
|
32704
32987
|
const readDb = openDatabaseReadOnly2();
|
|
32705
32988
|
const mainSize = statSync11(DB_PATH).size;
|
|
32706
32989
|
const walPath = DB_PATH + "-wal";
|
|
32707
|
-
const walSize =
|
|
32990
|
+
const walSize = existsSync44(walPath) ? statSync11(walPath).size : 0;
|
|
32708
32991
|
const tableNames = readDb.prepare(
|
|
32709
32992
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
32710
32993
|
).all();
|
|
@@ -32738,7 +33021,7 @@ async function dbPath(globalOpts) {
|
|
|
32738
33021
|
output({ path: DB_PATH }, (d) => d.path);
|
|
32739
33022
|
}
|
|
32740
33023
|
async function dbBackup(globalOpts, destPath) {
|
|
32741
|
-
if (!
|
|
33024
|
+
if (!existsSync44(DB_PATH)) {
|
|
32742
33025
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
32743
33026
|
process.exit(1);
|
|
32744
33027
|
}
|
|
@@ -32747,7 +33030,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
32747
33030
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
32748
33031
|
copyFileSync3(DB_PATH, dest);
|
|
32749
33032
|
const walPath = DB_PATH + "-wal";
|
|
32750
|
-
if (
|
|
33033
|
+
if (existsSync44(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
32751
33034
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
32752
33035
|
const b = d;
|
|
32753
33036
|
return `
|
|
@@ -32776,9 +33059,9 @@ __export(usage_exports, {
|
|
|
32776
33059
|
usageCost: () => usageCost,
|
|
32777
33060
|
usageTokens: () => usageTokens
|
|
32778
33061
|
});
|
|
32779
|
-
import { existsSync as
|
|
33062
|
+
import { existsSync as existsSync45 } from "fs";
|
|
32780
33063
|
function ensureDb() {
|
|
32781
|
-
if (!
|
|
33064
|
+
if (!existsSync45(DB_PATH)) {
|
|
32782
33065
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32783
33066
|
process.exit(1);
|
|
32784
33067
|
}
|
|
@@ -32968,9 +33251,9 @@ __export(config_exports2, {
|
|
|
32968
33251
|
configList: () => configList,
|
|
32969
33252
|
configSet: () => configSet
|
|
32970
33253
|
});
|
|
32971
|
-
import { existsSync as
|
|
33254
|
+
import { existsSync as existsSync46, readFileSync as readFileSync27 } from "fs";
|
|
32972
33255
|
async function configList(globalOpts) {
|
|
32973
|
-
if (!
|
|
33256
|
+
if (!existsSync46(DB_PATH)) {
|
|
32974
33257
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32975
33258
|
process.exit(1);
|
|
32976
33259
|
}
|
|
@@ -33004,7 +33287,7 @@ async function configGet(globalOpts, key) {
|
|
|
33004
33287
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
33005
33288
|
process.exit(1);
|
|
33006
33289
|
}
|
|
33007
|
-
if (!
|
|
33290
|
+
if (!existsSync46(DB_PATH)) {
|
|
33008
33291
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33009
33292
|
process.exit(1);
|
|
33010
33293
|
}
|
|
@@ -33050,11 +33333,11 @@ async function configSet(globalOpts, key, value) {
|
|
|
33050
33333
|
}
|
|
33051
33334
|
}
|
|
33052
33335
|
async function configEnv(_globalOpts) {
|
|
33053
|
-
if (!
|
|
33336
|
+
if (!existsSync46(ENV_PATH)) {
|
|
33054
33337
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
33055
33338
|
process.exit(1);
|
|
33056
33339
|
}
|
|
33057
|
-
const content =
|
|
33340
|
+
const content = readFileSync27(ENV_PATH, "utf-8");
|
|
33058
33341
|
const entries = {};
|
|
33059
33342
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
33060
33343
|
for (const line of content.split("\n")) {
|
|
@@ -33104,9 +33387,9 @@ __export(session_exports, {
|
|
|
33104
33387
|
sessionGet: () => sessionGet,
|
|
33105
33388
|
sessionNew: () => sessionNew
|
|
33106
33389
|
});
|
|
33107
|
-
import { existsSync as
|
|
33390
|
+
import { existsSync as existsSync47 } from "fs";
|
|
33108
33391
|
async function sessionGet(globalOpts) {
|
|
33109
|
-
if (!
|
|
33392
|
+
if (!existsSync47(DB_PATH)) {
|
|
33110
33393
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33111
33394
|
process.exit(1);
|
|
33112
33395
|
}
|
|
@@ -33167,9 +33450,9 @@ __export(permissions_exports, {
|
|
|
33167
33450
|
verboseGet: () => verboseGet,
|
|
33168
33451
|
verboseSet: () => verboseSet
|
|
33169
33452
|
});
|
|
33170
|
-
import { existsSync as
|
|
33453
|
+
import { existsSync as existsSync48 } from "fs";
|
|
33171
33454
|
function ensureDb2() {
|
|
33172
|
-
if (!
|
|
33455
|
+
if (!existsSync48(DB_PATH)) {
|
|
33173
33456
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33174
33457
|
process.exit(1);
|
|
33175
33458
|
}
|
|
@@ -33316,9 +33599,9 @@ __export(cwd_exports, {
|
|
|
33316
33599
|
cwdGet: () => cwdGet,
|
|
33317
33600
|
cwdSet: () => cwdSet
|
|
33318
33601
|
});
|
|
33319
|
-
import { existsSync as
|
|
33602
|
+
import { existsSync as existsSync49 } from "fs";
|
|
33320
33603
|
async function cwdGet(globalOpts) {
|
|
33321
|
-
if (!
|
|
33604
|
+
if (!existsSync49(DB_PATH)) {
|
|
33322
33605
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33323
33606
|
process.exit(1);
|
|
33324
33607
|
}
|
|
@@ -33380,9 +33663,9 @@ __export(voice_exports, {
|
|
|
33380
33663
|
voiceGet: () => voiceGet,
|
|
33381
33664
|
voiceSet: () => voiceSet
|
|
33382
33665
|
});
|
|
33383
|
-
import { existsSync as
|
|
33666
|
+
import { existsSync as existsSync50 } from "fs";
|
|
33384
33667
|
async function voiceGet(globalOpts) {
|
|
33385
|
-
if (!
|
|
33668
|
+
if (!existsSync50(DB_PATH)) {
|
|
33386
33669
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33387
33670
|
process.exit(1);
|
|
33388
33671
|
}
|
|
@@ -33431,9 +33714,9 @@ __export(heartbeat_exports2, {
|
|
|
33431
33714
|
heartbeatGet: () => heartbeatGet,
|
|
33432
33715
|
heartbeatSet: () => heartbeatSet
|
|
33433
33716
|
});
|
|
33434
|
-
import { existsSync as
|
|
33717
|
+
import { existsSync as existsSync51 } from "fs";
|
|
33435
33718
|
async function heartbeatGet(globalOpts) {
|
|
33436
|
-
if (!
|
|
33719
|
+
if (!existsSync51(DB_PATH)) {
|
|
33437
33720
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33438
33721
|
process.exit(1);
|
|
33439
33722
|
}
|
|
@@ -33644,9 +33927,9 @@ __export(summarizer_exports, {
|
|
|
33644
33927
|
summarizerGet: () => summarizerGet,
|
|
33645
33928
|
summarizerSet: () => summarizerSet
|
|
33646
33929
|
});
|
|
33647
|
-
import { existsSync as
|
|
33930
|
+
import { existsSync as existsSync52 } from "fs";
|
|
33648
33931
|
async function summarizerGet(globalOpts) {
|
|
33649
|
-
if (!
|
|
33932
|
+
if (!existsSync52(DB_PATH)) {
|
|
33650
33933
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33651
33934
|
process.exit(1);
|
|
33652
33935
|
}
|
|
@@ -33690,9 +33973,9 @@ __export(thinking_exports, {
|
|
|
33690
33973
|
thinkingGet: () => thinkingGet,
|
|
33691
33974
|
thinkingSet: () => thinkingSet
|
|
33692
33975
|
});
|
|
33693
|
-
import { existsSync as
|
|
33976
|
+
import { existsSync as existsSync53 } from "fs";
|
|
33694
33977
|
async function thinkingGet(globalOpts) {
|
|
33695
|
-
if (!
|
|
33978
|
+
if (!existsSync53(DB_PATH)) {
|
|
33696
33979
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33697
33980
|
process.exit(1);
|
|
33698
33981
|
}
|
|
@@ -33736,9 +34019,9 @@ __export(chats_exports, {
|
|
|
33736
34019
|
chatsList: () => chatsList,
|
|
33737
34020
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
33738
34021
|
});
|
|
33739
|
-
import { existsSync as
|
|
34022
|
+
import { existsSync as existsSync54 } from "fs";
|
|
33740
34023
|
async function chatsList(_globalOpts) {
|
|
33741
|
-
if (!
|
|
34024
|
+
if (!existsSync54(DB_PATH)) {
|
|
33742
34025
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33743
34026
|
process.exit(1);
|
|
33744
34027
|
}
|
|
@@ -33866,9 +34149,9 @@ var mcps_exports2 = {};
|
|
|
33866
34149
|
__export(mcps_exports2, {
|
|
33867
34150
|
mcpsList: () => mcpsList
|
|
33868
34151
|
});
|
|
33869
|
-
import { existsSync as
|
|
34152
|
+
import { existsSync as existsSync55 } from "fs";
|
|
33870
34153
|
async function mcpsList(_globalOpts) {
|
|
33871
|
-
if (!
|
|
34154
|
+
if (!existsSync55(DB_PATH)) {
|
|
33872
34155
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33873
34156
|
process.exit(1);
|
|
33874
34157
|
}
|
|
@@ -33905,11 +34188,11 @@ __export(chat_exports2, {
|
|
|
33905
34188
|
chatSend: () => chatSend
|
|
33906
34189
|
});
|
|
33907
34190
|
import { request as httpRequest2 } from "http";
|
|
33908
|
-
import { readFileSync as
|
|
34191
|
+
import { readFileSync as readFileSync28, existsSync as existsSync56 } from "fs";
|
|
33909
34192
|
function getToken2() {
|
|
33910
34193
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
33911
34194
|
try {
|
|
33912
|
-
if (
|
|
34195
|
+
if (existsSync56(TOKEN_PATH2)) return readFileSync28(TOKEN_PATH2, "utf-8").trim();
|
|
33913
34196
|
} catch {
|
|
33914
34197
|
}
|
|
33915
34198
|
return null;
|
|
@@ -34397,7 +34680,7 @@ __export(completion_exports, {
|
|
|
34397
34680
|
completionCommand: () => completionCommand
|
|
34398
34681
|
});
|
|
34399
34682
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
|
|
34400
|
-
import { join as
|
|
34683
|
+
import { join as join36 } from "path";
|
|
34401
34684
|
import { homedir as homedir11 } from "os";
|
|
34402
34685
|
async function completionCommand(opts) {
|
|
34403
34686
|
const shell = opts.shell ?? detectShell();
|
|
@@ -34413,10 +34696,10 @@ async function completionCommand(opts) {
|
|
|
34413
34696
|
process.exit(1);
|
|
34414
34697
|
}
|
|
34415
34698
|
if (opts.install) {
|
|
34416
|
-
const dir =
|
|
34699
|
+
const dir = join36(homedir11(), ".config", "cc-claw", "completions");
|
|
34417
34700
|
mkdirSync17(dir, { recursive: true });
|
|
34418
34701
|
const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
|
|
34419
|
-
const filepath =
|
|
34702
|
+
const filepath = join36(dir, filename);
|
|
34420
34703
|
writeFileSync11(filepath, script, "utf-8");
|
|
34421
34704
|
console.log(`\u2713 Completion script written to ${filepath}
|
|
34422
34705
|
`);
|
|
@@ -34589,9 +34872,9 @@ __export(evolve_exports2, {
|
|
|
34589
34872
|
evolveStatus: () => evolveStatus,
|
|
34590
34873
|
evolveUndo: () => evolveUndo
|
|
34591
34874
|
});
|
|
34592
|
-
import { existsSync as
|
|
34875
|
+
import { existsSync as existsSync57 } from "fs";
|
|
34593
34876
|
function ensureDb3() {
|
|
34594
|
-
if (!
|
|
34877
|
+
if (!existsSync57(DB_PATH)) {
|
|
34595
34878
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
34596
34879
|
process.exit(1);
|
|
34597
34880
|
}
|
|
@@ -35065,10 +35348,10 @@ var init_optimize2 = __esm({
|
|
|
35065
35348
|
|
|
35066
35349
|
// src/setup.ts
|
|
35067
35350
|
var setup_exports = {};
|
|
35068
|
-
import { existsSync as
|
|
35351
|
+
import { existsSync as existsSync58, writeFileSync as writeFileSync12, readFileSync as readFileSync29, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
35069
35352
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
35070
35353
|
import { createInterface as createInterface11 } from "readline";
|
|
35071
|
-
import { join as
|
|
35354
|
+
import { join as join37 } from "path";
|
|
35072
35355
|
function divider2() {
|
|
35073
35356
|
console.log(dim("\u2500".repeat(55)));
|
|
35074
35357
|
}
|
|
@@ -35143,21 +35426,21 @@ async function setup() {
|
|
|
35143
35426
|
}
|
|
35144
35427
|
console.log("");
|
|
35145
35428
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
35146
|
-
if (!
|
|
35429
|
+
if (!existsSync58(dir)) mkdirSync18(dir, { recursive: true });
|
|
35147
35430
|
}
|
|
35148
35431
|
const env = {};
|
|
35149
|
-
const envSource =
|
|
35432
|
+
const envSource = existsSync58(ENV_PATH) ? ENV_PATH : existsSync58(".env") ? ".env" : null;
|
|
35150
35433
|
if (envSource) {
|
|
35151
35434
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
35152
35435
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
35153
|
-
const existing =
|
|
35436
|
+
const existing = readFileSync29(envSource, "utf-8");
|
|
35154
35437
|
for (const line of existing.split("\n")) {
|
|
35155
35438
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
35156
35439
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
35157
35440
|
}
|
|
35158
35441
|
}
|
|
35159
|
-
const cwdDb =
|
|
35160
|
-
if (
|
|
35442
|
+
const cwdDb = join37(process.cwd(), "cc-claw.db");
|
|
35443
|
+
if (existsSync58(cwdDb) && !existsSync58(DB_PATH)) {
|
|
35161
35444
|
const { size } = statSync12(cwdDb);
|
|
35162
35445
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
35163
35446
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
@@ -35297,14 +35580,44 @@ async function setup() {
|
|
|
35297
35580
|
}
|
|
35298
35581
|
header(4, TOTAL_STEPS, "Optional Features");
|
|
35299
35582
|
console.log(" These are optional \u2014 you can enable them later by editing .env\n");
|
|
35300
|
-
if (await confirm("Enable voice
|
|
35583
|
+
if (await confirm("Enable voice message transcription (speech-to-text)?")) {
|
|
35584
|
+
console.log("");
|
|
35585
|
+
console.log(dim(" Choose your transcription provider:\n"));
|
|
35586
|
+
console.log(" 1. Local Whisper \u2014 free, works offline, runs on your machine");
|
|
35587
|
+
console.log(" Requires: whisper-cli (brew install whisper-cpp on macOS)");
|
|
35588
|
+
console.log(" Models downloaded on first use (~75MB\u20131.5GB depending on quality)");
|
|
35589
|
+
console.log("");
|
|
35590
|
+
console.log(" 2. Groq \u2014 free cloud API, fast, no local install needed");
|
|
35591
|
+
console.log(" Requires: free Groq API key from https://console.groq.com/keys");
|
|
35301
35592
|
console.log("");
|
|
35302
|
-
console.log(
|
|
35303
|
-
|
|
35304
|
-
|
|
35305
|
-
|
|
35593
|
+
console.log(" 3. Skip");
|
|
35594
|
+
console.log("");
|
|
35595
|
+
const sttChoice = await requiredInput("Enter choice (1/2/3)", "1");
|
|
35596
|
+
if (sttChoice === "1") {
|
|
35597
|
+
console.log("");
|
|
35598
|
+
console.log(dim(" Local Whisper model to use (select quality vs download size):"));
|
|
35599
|
+
console.log(" 1. tiny.en 75MB English only ~0.3s Basic");
|
|
35600
|
+
console.log(" 2. base.en 150MB English only ~0.8s Good");
|
|
35601
|
+
console.log(" 3. small.en 500MB English only ~1.5s Very good \u2B50 Recommended");
|
|
35602
|
+
console.log(" 4. small 500MB Multilingual ~2s Very good");
|
|
35603
|
+
console.log(" 5. medium.en 1.5GB English only ~5s Excellent");
|
|
35604
|
+
console.log(" 6. medium 1.5GB Multilingual ~6s Excellent");
|
|
35605
|
+
console.log("");
|
|
35606
|
+
const modelChoice = await requiredInput("Enter choice (1\u20136)", "3");
|
|
35607
|
+
const modelMap = { "1": "tiny.en", "2": "base.en", "3": "small.en", "4": "small", "5": "medium.en", "6": "medium" };
|
|
35608
|
+
env.STT_PROVIDER = "local-whisper";
|
|
35609
|
+
env.STT_MODEL = modelMap[modelChoice] ?? "small.en";
|
|
35610
|
+
console.log(green(` Local Whisper selected (${env.STT_MODEL}). Model will download on first voice message.`));
|
|
35611
|
+
} else if (sttChoice === "2") {
|
|
35612
|
+
const groqKey = await requiredInput("Groq API key", env.GROQ_API_KEY);
|
|
35613
|
+
env.GROQ_API_KEY = groqKey;
|
|
35614
|
+
env.STT_PROVIDER = "groq";
|
|
35615
|
+
console.log(green(" Groq transcription enabled!"));
|
|
35616
|
+
} else {
|
|
35617
|
+
console.log(dim(" Voice transcription skipped. Configure later via /voice in Telegram."));
|
|
35618
|
+
}
|
|
35306
35619
|
console.log("");
|
|
35307
|
-
console.log(dim(" Choose a voice reply provider:"));
|
|
35620
|
+
console.log(dim(" Choose a voice reply provider (text-to-speech):"));
|
|
35308
35621
|
console.log(" 1. ElevenLabs (high-quality, requires API key)");
|
|
35309
35622
|
console.log(" 2. Grok / xAI (high-quality, requires API key)");
|
|
35310
35623
|
console.log(" 3. macOS (free, uses built-in voices \u2014 Samantha, Albert)");
|
|
@@ -35321,10 +35634,10 @@ async function setup() {
|
|
|
35321
35634
|
const xaiKey = await requiredInput("xAI API key", env.XAI_API_KEY);
|
|
35322
35635
|
env.XAI_API_KEY = xaiKey;
|
|
35323
35636
|
console.log(green(" Voice replies via Grok enabled!"));
|
|
35324
|
-
console.log(dim(" Use /
|
|
35637
|
+
console.log(dim(" Use /voice in Telegram to select a voice (Eve, Ara, Rex, Sal, Leo)."));
|
|
35325
35638
|
} else if (ttsChoice === "3") {
|
|
35326
35639
|
console.log(green(" Voice replies via macOS enabled!"));
|
|
35327
|
-
console.log(dim(" Use /
|
|
35640
|
+
console.log(dim(" Use /voice in Telegram to select a voice (Samantha or Albert)."));
|
|
35328
35641
|
} else {
|
|
35329
35642
|
console.log(dim(" Voice replies will use macOS text-to-speech as fallback."));
|
|
35330
35643
|
}
|
|
@@ -35358,14 +35671,14 @@ async function setup() {
|
|
|
35358
35671
|
`ANTHROPIC_VERTEX_PROJECT_ID=${env.ANTHROPIC_VERTEX_PROJECT_ID ?? ""}`
|
|
35359
35672
|
);
|
|
35360
35673
|
}
|
|
35361
|
-
|
|
35362
|
-
|
|
35363
|
-
|
|
35364
|
-
|
|
35365
|
-
}
|
|
35366
|
-
if (env.
|
|
35367
|
-
|
|
35368
|
-
}
|
|
35674
|
+
const hasVoice = env.GROQ_API_KEY || env.STT_PROVIDER || env.ELEVENLABS_API_KEY || env.XAI_API_KEY;
|
|
35675
|
+
if (hasVoice) {
|
|
35676
|
+
envLines.push("", "# Voice");
|
|
35677
|
+
if (env.STT_PROVIDER) envLines.push(`STT_PROVIDER=${env.STT_PROVIDER}`);
|
|
35678
|
+
if (env.STT_MODEL) envLines.push(`STT_MODEL=${env.STT_MODEL}`);
|
|
35679
|
+
if (env.GROQ_API_KEY) envLines.push(`GROQ_API_KEY=${env.GROQ_API_KEY}`);
|
|
35680
|
+
if (env.ELEVENLABS_API_KEY) envLines.push(`ELEVENLABS_API_KEY=${env.ELEVENLABS_API_KEY}`);
|
|
35681
|
+
if (env.XAI_API_KEY) envLines.push(`XAI_API_KEY=${env.XAI_API_KEY}`);
|
|
35369
35682
|
}
|
|
35370
35683
|
if (env.DASHBOARD_ENABLED) {
|
|
35371
35684
|
envLines.push("", "# Dashboard", `DASHBOARD_ENABLED=${env.DASHBOARD_ENABLED}`);
|
|
@@ -35409,7 +35722,9 @@ async function setup() {
|
|
|
35409
35722
|
console.log(" " + green("[ok]") + ` Telegram bot: @${env.TELEGRAM_BOT_TOKEN ? "configured" : "missing"}`);
|
|
35410
35723
|
console.log(" " + green("[ok]") + ` Chat ID: ${env.ALLOWED_CHAT_ID ?? "not set (anyone can message)"}`);
|
|
35411
35724
|
console.log(" " + green("[ok]") + ` Vertex AI: ${env.ANTHROPIC_VERTEX_PROJECT_ID ?? "not configured"}`);
|
|
35412
|
-
|
|
35725
|
+
const sttConfigured = env.STT_PROVIDER === "local-whisper" || !!env.GROQ_API_KEY;
|
|
35726
|
+
const sttLabel = env.STT_PROVIDER === "local-whisper" ? `Local Whisper (${env.STT_MODEL ?? "small.en"})` : env.GROQ_API_KEY ? "Groq" : "not configured";
|
|
35727
|
+
console.log(" " + (sttConfigured ? green("[ok]") : dim("[--]")) + ` Voice transcription: ${sttLabel}`);
|
|
35413
35728
|
console.log(" " + (env.ELEVENLABS_API_KEY ? green("[ok]") : dim("[--]")) + " Voice replies");
|
|
35414
35729
|
console.log(" " + (env.DASHBOARD_ENABLED ? green("[ok]") : dim("[--]")) + " Web dashboard");
|
|
35415
35730
|
console.log(" " + (env.GEMINI_API_KEY ? green("[ok]") : dim("[--]")) + " Video analysis");
|