cc-claw 0.20.21 → 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 +831 -508
- 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`;
|
|
@@ -13778,6 +13799,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13778
13799
|
const check = loopDetector.addCall(ev.toolName, ev.toolInput ?? {});
|
|
13779
13800
|
if (check.isLoop) {
|
|
13780
13801
|
warn(`[agent] Loop detected for ${adapter.id}: ${check.reason} \u2014 stopping`);
|
|
13802
|
+
loopKillReason = check.reason;
|
|
13781
13803
|
killProcessGroup(proc, "SIGTERM");
|
|
13782
13804
|
}
|
|
13783
13805
|
}
|
|
@@ -13902,8 +13924,12 @@ Partial output: ${accumulatedText.slice(-500)}`;
|
|
|
13902
13924
|
return;
|
|
13903
13925
|
}
|
|
13904
13926
|
if (code && code !== 0 && !cancelState.cancelled && !resultText) {
|
|
13905
|
-
|
|
13906
|
-
|
|
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
|
+
}
|
|
13907
13933
|
return;
|
|
13908
13934
|
}
|
|
13909
13935
|
const cleanedResult = stripThinkingContent(resultText || accumulatedText);
|
|
@@ -14454,7 +14480,7 @@ var init_agent = __esm({
|
|
|
14454
14480
|
chatLocks = /* @__PURE__ */ new Map();
|
|
14455
14481
|
SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
14456
14482
|
FIRST_RESPONSE_TIMEOUT_MS = parseInt(process.env.GEMINI_FIRST_RESPONSE_TIMEOUT_MS ?? "30000", 10);
|
|
14457
|
-
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);
|
|
14458
14484
|
CONTENT_SILENCE_TIMEOUT_ERROR = "CONTENT_SILENCE_TIMEOUT";
|
|
14459
14485
|
FIRST_RESPONSE_TIMEOUT_ERROR = "FIRST_RESPONSE_TIMEOUT";
|
|
14460
14486
|
FREE_SLOTS_EXHAUSTED = "FREE_SLOTS_EXHAUSTED";
|
|
@@ -14976,15 +15002,17 @@ var init_telegram_throttle = __esm({
|
|
|
14976
15002
|
* Used for cosmetic calls (typing indicators, reactions) that should count toward
|
|
14977
15003
|
* rate limits but must never queue up or amplify 429 spirals.
|
|
14978
15004
|
*/
|
|
14979
|
-
async tryBestEffort(chatId, label2, fn) {
|
|
15005
|
+
async tryBestEffort(chatId, label2, fn, opts) {
|
|
14980
15006
|
if (this.isPaused()) return void 0;
|
|
14981
15007
|
if (this.queue.length > 10) return void 0;
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
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
|
+
}
|
|
14985
15013
|
try {
|
|
14986
15014
|
const result = await fn();
|
|
14987
|
-
this.recordSend(chatId);
|
|
15015
|
+
if (!opts?.skipRecord) this.recordSend(chatId);
|
|
14988
15016
|
return result;
|
|
14989
15017
|
} catch (err) {
|
|
14990
15018
|
if (is429(err)) {
|
|
@@ -15126,11 +15154,11 @@ var init_telegram_throttle = __esm({
|
|
|
15126
15154
|
});
|
|
15127
15155
|
|
|
15128
15156
|
// src/health/checks.ts
|
|
15129
|
-
import { existsSync as existsSync15, statSync as statSync6, readFileSync as
|
|
15157
|
+
import { existsSync as existsSync15, statSync as statSync6, readFileSync as readFileSync9 } from "fs";
|
|
15130
15158
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
15131
15159
|
function getRecentErrors() {
|
|
15132
15160
|
if (!existsSync15(ERROR_LOG_PATH)) return null;
|
|
15133
|
-
const logContent =
|
|
15161
|
+
const logContent = readFileSync9(ERROR_LOG_PATH, "utf-8");
|
|
15134
15162
|
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
15135
15163
|
const last24h = Date.now() - 864e5;
|
|
15136
15164
|
const lines = allLines.filter((line) => {
|
|
@@ -15359,7 +15387,7 @@ __export(heartbeat_exports, {
|
|
|
15359
15387
|
stopHeartbeatForChat: () => stopHeartbeatForChat,
|
|
15360
15388
|
updateHeartbeatConfig: () => updateHeartbeatConfig
|
|
15361
15389
|
});
|
|
15362
|
-
import { readFileSync as
|
|
15390
|
+
import { readFileSync as readFileSync10, existsSync as existsSync16 } from "fs";
|
|
15363
15391
|
import { join as join15 } from "path";
|
|
15364
15392
|
function findHeartbeatJob() {
|
|
15365
15393
|
try {
|
|
@@ -15493,7 +15521,7 @@ ${watchLines.join("\n")}`);
|
|
|
15493
15521
|
}
|
|
15494
15522
|
if (existsSync16(HEARTBEAT_MD_PATH)) {
|
|
15495
15523
|
try {
|
|
15496
|
-
const custom =
|
|
15524
|
+
const custom = readFileSync10(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
15497
15525
|
if (custom) {
|
|
15498
15526
|
sections.push(`[Custom checks from HEARTBEAT.md]
|
|
15499
15527
|
${custom}`);
|
|
@@ -15562,7 +15590,7 @@ var init_heartbeat2 = __esm({
|
|
|
15562
15590
|
});
|
|
15563
15591
|
|
|
15564
15592
|
// src/bootstrap/profile.ts
|
|
15565
|
-
import { readFileSync as
|
|
15593
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync17 } from "fs";
|
|
15566
15594
|
import { join as join16 } from "path";
|
|
15567
15595
|
function hasActiveProfile(chatId) {
|
|
15568
15596
|
return activeProfiles.has(chatId);
|
|
@@ -15693,7 +15721,7 @@ function extractUserUpdates(text) {
|
|
|
15693
15721
|
}
|
|
15694
15722
|
function appendToUserProfile(key, value) {
|
|
15695
15723
|
if (!existsSync17(USER_PATH2)) return;
|
|
15696
|
-
const content =
|
|
15724
|
+
const content = readFileSync11(USER_PATH2, "utf-8");
|
|
15697
15725
|
const line = `- **${key}**: ${value}`;
|
|
15698
15726
|
if (content.includes(line)) return;
|
|
15699
15727
|
const updated = content.trimEnd() + `
|
|
@@ -15802,8 +15830,9 @@ async function classifyWithOllama(text) {
|
|
|
15802
15830
|
(a, b) => (a.sizeBytes ?? Infinity) - (b.sizeBytes ?? Infinity)
|
|
15803
15831
|
);
|
|
15804
15832
|
const model2 = sorted[0].name;
|
|
15833
|
+
const baseUrl = ollamaStore.getBaseUrl(onlineServer);
|
|
15805
15834
|
const result = await ollamaClient.chat(
|
|
15806
|
-
|
|
15835
|
+
baseUrl,
|
|
15807
15836
|
model2,
|
|
15808
15837
|
[{ role: "user", content: LLM_CLASSIFY_PROMPT + text.slice(0, 500) }],
|
|
15809
15838
|
{ timeoutMs: LLM_CLASSIFY_TIMEOUT_MS, maxTokens: 5, temperature: 0 }
|
|
@@ -15828,7 +15857,7 @@ async function classifyWithSummarizerCli(text) {
|
|
|
15828
15857
|
const model2 = modelName ?? adapter.summarizerModel;
|
|
15829
15858
|
const { spawn: spawn8 } = await import("child_process");
|
|
15830
15859
|
const { resolveExecutable: resolveExecutable4 } = await Promise.resolve().then(() => (init_resolve_executable(), resolve_executable_exports));
|
|
15831
|
-
const exe = resolveExecutable4(adapter.id);
|
|
15860
|
+
const exe = resolveExecutable4({ envVar: `${adapter.id.toUpperCase()}_EXECUTABLE`, binaryName: adapter.id, candidates: [] });
|
|
15832
15861
|
if (!exe) return null;
|
|
15833
15862
|
return new Promise((resolve) => {
|
|
15834
15863
|
const timeout = setTimeout(() => {
|
|
@@ -17701,7 +17730,9 @@ var init_gate = __esm({
|
|
|
17701
17730
|
// src/voice/stt.ts
|
|
17702
17731
|
import crypto from "crypto";
|
|
17703
17732
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
17704
|
-
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";
|
|
17705
17736
|
import { promisify as promisify2 } from "util";
|
|
17706
17737
|
function ensureFfmpeg() {
|
|
17707
17738
|
if (ffmpegAvailable === true) return;
|
|
@@ -17746,11 +17777,120 @@ function setVoiceProvider(chatId, provider, voiceId) {
|
|
|
17746
17777
|
ON CONFLICT(chat_id) DO UPDATE SET provider = ?, voice_id = ?, enabled = 1
|
|
17747
17778
|
`).run(chatId, provider, voiceId, provider, voiceId);
|
|
17748
17779
|
}
|
|
17749
|
-
|
|
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
|
+
}
|
|
17750
17890
|
const GROQ_API_KEY = process.env.GROQ_API_KEY;
|
|
17751
17891
|
if (!GROQ_API_KEY) return null;
|
|
17752
17892
|
const formData = new FormData();
|
|
17753
|
-
formData.append("file", new Blob([new Uint8Array(audioBuffer)], { type:
|
|
17893
|
+
formData.append("file", new Blob([new Uint8Array(audioBuffer)], { type: "audio/ogg" }), "voice.ogg");
|
|
17754
17894
|
formData.append("model", "whisper-large-v3");
|
|
17755
17895
|
formData.append("response_format", "text");
|
|
17756
17896
|
const response = await fetch("https://api.groq.com/openai/v1/audio/transcriptions", {
|
|
@@ -17851,8 +17991,8 @@ async function mp3ToOgg(mp3Buffer) {
|
|
|
17851
17991
|
const id = crypto.randomUUID();
|
|
17852
17992
|
const tmpMp3 = `/tmp/cc-claw-tts-${id}.mp3`;
|
|
17853
17993
|
const tmpOgg = `/tmp/cc-claw-tts-${id}.ogg`;
|
|
17854
|
-
const { writeFile:
|
|
17855
|
-
await
|
|
17994
|
+
const { writeFile: writeFile7 } = await import("fs/promises");
|
|
17995
|
+
await writeFile7(tmpMp3, mp3Buffer);
|
|
17856
17996
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17857
17997
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17858
17998
|
unlink2(tmpMp3).catch((err) => {
|
|
@@ -17879,14 +18019,24 @@ async function macOsTts(text, voice2 = "Samantha") {
|
|
|
17879
18019
|
});
|
|
17880
18020
|
return oggBuffer;
|
|
17881
18021
|
}
|
|
17882
|
-
var execFileAsync2, ffmpegAvailable, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES;
|
|
18022
|
+
var execFileAsync2, ffmpegAvailable, LOCAL_WHISPER_MODELS, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES, whisperCliAvailableCache;
|
|
17883
18023
|
var init_stt = __esm({
|
|
17884
18024
|
"src/voice/stt.ts"() {
|
|
17885
18025
|
"use strict";
|
|
17886
18026
|
init_log();
|
|
17887
18027
|
init_store5();
|
|
18028
|
+
init_paths();
|
|
17888
18029
|
execFileAsync2 = promisify2(execFile2);
|
|
17889
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
|
+
};
|
|
17890
18040
|
ELEVENLABS_VOICES = {
|
|
17891
18041
|
"21m00Tcm4TlvDq8ikWAM": { name: "Rachel", gender: "F" },
|
|
17892
18042
|
"EXAVITQu4vr4xnSDxMaL": { name: "Sarah", gender: "F" },
|
|
@@ -17902,13 +18052,14 @@ var init_stt = __esm({
|
|
|
17902
18052
|
"Samantha": { name: "Samantha", gender: "F" },
|
|
17903
18053
|
"Albert": { name: "Albert", gender: "M" }
|
|
17904
18054
|
};
|
|
18055
|
+
whisperCliAvailableCache = null;
|
|
17905
18056
|
}
|
|
17906
18057
|
});
|
|
17907
18058
|
|
|
17908
18059
|
// src/media/image-gen.ts
|
|
17909
|
-
import { mkdirSync as mkdirSync9, existsSync as
|
|
17910
|
-
import { writeFile } from "fs/promises";
|
|
17911
|
-
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";
|
|
17912
18063
|
async function generateImage(prompt) {
|
|
17913
18064
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
17914
18065
|
if (!apiKey) {
|
|
@@ -17955,14 +18106,14 @@ async function generateImage(prompt) {
|
|
|
17955
18106
|
if (!imageData) {
|
|
17956
18107
|
throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
|
|
17957
18108
|
}
|
|
17958
|
-
if (!
|
|
18109
|
+
if (!existsSync20(IMAGE_OUTPUT_DIR)) {
|
|
17959
18110
|
mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
|
|
17960
18111
|
}
|
|
17961
18112
|
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
17962
18113
|
const filename = `img_${Date.now()}.${ext}`;
|
|
17963
|
-
const filePath =
|
|
18114
|
+
const filePath = join19(IMAGE_OUTPUT_DIR, filename);
|
|
17964
18115
|
const buffer = Buffer.from(imageData, "base64");
|
|
17965
|
-
await
|
|
18116
|
+
await writeFile2(filePath, buffer);
|
|
17966
18117
|
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
17967
18118
|
return { filePath, text: textResponse, mimeType };
|
|
17968
18119
|
}
|
|
@@ -17978,7 +18129,7 @@ function cleanupGeneratedImage(filePath) {
|
|
|
17978
18129
|
function pruneImageCache() {
|
|
17979
18130
|
readdir2(IMAGE_OUTPUT_DIR, (err, files) => {
|
|
17980
18131
|
if (err || !files) return;
|
|
17981
|
-
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));
|
|
17982
18133
|
if (imageFiles.length === 0) return;
|
|
17983
18134
|
const now = Date.now();
|
|
17984
18135
|
let statsPending = imageFiles.length;
|
|
@@ -18010,8 +18161,8 @@ var init_image_gen = __esm({
|
|
|
18010
18161
|
MAX_GENERATED_IMAGES = 20;
|
|
18011
18162
|
IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
18012
18163
|
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
18013
|
-
IMAGE_OUTPUT_DIR =
|
|
18014
|
-
process.env.CC_CLAW_HOME ??
|
|
18164
|
+
IMAGE_OUTPUT_DIR = join19(
|
|
18165
|
+
process.env.CC_CLAW_HOME ?? join19(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
18015
18166
|
"data",
|
|
18016
18167
|
"images"
|
|
18017
18168
|
);
|
|
@@ -18449,22 +18600,22 @@ var init_video = __esm({
|
|
|
18449
18600
|
});
|
|
18450
18601
|
|
|
18451
18602
|
// src/router/media.ts
|
|
18452
|
-
import { join as
|
|
18453
|
-
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";
|
|
18454
18605
|
function getMediaRetentionMs() {
|
|
18455
18606
|
const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
|
|
18456
18607
|
return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
|
|
18457
18608
|
}
|
|
18458
18609
|
async function saveMedia(buffer, prefix, ext) {
|
|
18459
|
-
await
|
|
18610
|
+
await mkdir3(MEDIA_INCOMING_PATH, { recursive: true });
|
|
18460
18611
|
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
18461
|
-
const fullPath =
|
|
18462
|
-
await
|
|
18612
|
+
const fullPath = join20(MEDIA_INCOMING_PATH, filename);
|
|
18613
|
+
await writeFile3(fullPath, buffer);
|
|
18463
18614
|
return fullPath;
|
|
18464
18615
|
}
|
|
18465
18616
|
async function cleanupOldMedia() {
|
|
18466
18617
|
try {
|
|
18467
|
-
await
|
|
18618
|
+
await mkdir3(MEDIA_INCOMING_PATH, { recursive: true });
|
|
18468
18619
|
const retentionMs = getMediaRetentionMs();
|
|
18469
18620
|
const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
|
|
18470
18621
|
const files = await readdir3(MEDIA_INCOMING_PATH);
|
|
@@ -18472,7 +18623,7 @@ async function cleanupOldMedia() {
|
|
|
18472
18623
|
let removed = 0;
|
|
18473
18624
|
for (const file of files) {
|
|
18474
18625
|
try {
|
|
18475
|
-
const filePath =
|
|
18626
|
+
const filePath = join20(MEDIA_INCOMING_PATH, file);
|
|
18476
18627
|
const s = await stat3(filePath);
|
|
18477
18628
|
if (now - s.mtimeMs > retentionMs) {
|
|
18478
18629
|
await unlink4(filePath);
|
|
@@ -18494,9 +18645,11 @@ async function handleVoice(msg, channel) {
|
|
|
18494
18645
|
return;
|
|
18495
18646
|
}
|
|
18496
18647
|
const audioBuffer = await channel.downloadFile(fileName);
|
|
18497
|
-
const transcript = await transcribeAudio(audioBuffer)
|
|
18648
|
+
const transcript = await transcribeAudio(audioBuffer, chatId, async (msg2) => {
|
|
18649
|
+
await channel.sendText(chatId, msg2, { parseMode: "plain" });
|
|
18650
|
+
});
|
|
18498
18651
|
if (!transcript) {
|
|
18499
|
-
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" });
|
|
18500
18653
|
return;
|
|
18501
18654
|
}
|
|
18502
18655
|
const vBackendId = getBackend(chatId) ?? "claude";
|
|
@@ -18746,7 +18899,7 @@ var init_media = __esm({
|
|
|
18746
18899
|
init_helpers();
|
|
18747
18900
|
init_response();
|
|
18748
18901
|
init_live_status();
|
|
18749
|
-
MEDIA_INCOMING_PATH =
|
|
18902
|
+
MEDIA_INCOMING_PATH = join20(MEDIA_PATH, "incoming");
|
|
18750
18903
|
}
|
|
18751
18904
|
});
|
|
18752
18905
|
|
|
@@ -19058,9 +19211,9 @@ var install_exports = {};
|
|
|
19058
19211
|
__export(install_exports, {
|
|
19059
19212
|
installSkillFromGitHub: () => installSkillFromGitHub
|
|
19060
19213
|
});
|
|
19061
|
-
import { mkdir as
|
|
19062
|
-
import { existsSync as
|
|
19063
|
-
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";
|
|
19064
19217
|
import { execSync as execSync4 } from "child_process";
|
|
19065
19218
|
async function installSkillFromGitHub(urlOrShorthand) {
|
|
19066
19219
|
let repoUrl;
|
|
@@ -19071,36 +19224,36 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
19071
19224
|
}
|
|
19072
19225
|
repoUrl = parsed.cloneUrl;
|
|
19073
19226
|
subPath = parsed.subPath;
|
|
19074
|
-
const tmpDir =
|
|
19227
|
+
const tmpDir = join21("/tmp", `cc-claw-skill-${Date.now()}`);
|
|
19075
19228
|
try {
|
|
19076
19229
|
log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
|
|
19077
19230
|
execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
19078
19231
|
stdio: "pipe",
|
|
19079
19232
|
timeout: 3e4
|
|
19080
19233
|
});
|
|
19081
|
-
if (!
|
|
19234
|
+
if (!existsSync21(join21(tmpDir, ".git"))) {
|
|
19082
19235
|
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
19083
19236
|
}
|
|
19084
|
-
const searchRoot = subPath ?
|
|
19237
|
+
const searchRoot = subPath ? join21(tmpDir, subPath) : tmpDir;
|
|
19085
19238
|
const skillDir = await findSkillDir(searchRoot);
|
|
19086
19239
|
if (!skillDir) {
|
|
19087
19240
|
return { success: false, error: "No SKILL.md found in the repository." };
|
|
19088
19241
|
}
|
|
19089
19242
|
const skillFolderName = basename2(skillDir);
|
|
19090
|
-
const destDir =
|
|
19091
|
-
if (
|
|
19243
|
+
const destDir = join21(SKILLS_PATH, skillFolderName);
|
|
19244
|
+
if (existsSync21(destDir)) {
|
|
19092
19245
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
19093
19246
|
}
|
|
19094
|
-
await
|
|
19247
|
+
await mkdir4(destDir, { recursive: true });
|
|
19095
19248
|
await cp(skillDir, destDir, { recursive: true });
|
|
19096
19249
|
let skillName = skillFolderName;
|
|
19097
19250
|
try {
|
|
19098
|
-
const content = await readFile4(
|
|
19251
|
+
const content = await readFile4(join21(destDir, "SKILL.md"), "utf-8");
|
|
19099
19252
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
19100
19253
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
19101
19254
|
} catch {
|
|
19102
19255
|
try {
|
|
19103
|
-
const content = await readFile4(
|
|
19256
|
+
const content = await readFile4(join21(destDir, "skill.md"), "utf-8");
|
|
19104
19257
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
19105
19258
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
19106
19259
|
} catch {
|
|
@@ -19135,15 +19288,15 @@ function parseGitHubUrl(input) {
|
|
|
19135
19288
|
async function findSkillDir(root) {
|
|
19136
19289
|
const candidates = ["SKILL.md", "skill.md"];
|
|
19137
19290
|
for (const c of candidates) {
|
|
19138
|
-
if (
|
|
19291
|
+
if (existsSync21(join21(root, c))) return root;
|
|
19139
19292
|
}
|
|
19140
19293
|
try {
|
|
19141
19294
|
const entries = await readdir4(root, { withFileTypes: true });
|
|
19142
19295
|
for (const entry of entries) {
|
|
19143
19296
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19144
19297
|
for (const c of candidates) {
|
|
19145
|
-
if (
|
|
19146
|
-
return
|
|
19298
|
+
if (existsSync21(join21(root, entry.name, c))) {
|
|
19299
|
+
return join21(root, entry.name);
|
|
19147
19300
|
}
|
|
19148
19301
|
}
|
|
19149
19302
|
}
|
|
@@ -19155,15 +19308,15 @@ async function findSkillDir(root) {
|
|
|
19155
19308
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19156
19309
|
let subEntries;
|
|
19157
19310
|
try {
|
|
19158
|
-
subEntries = await readdir4(
|
|
19311
|
+
subEntries = await readdir4(join21(root, entry.name), { withFileTypes: true });
|
|
19159
19312
|
} catch {
|
|
19160
19313
|
continue;
|
|
19161
19314
|
}
|
|
19162
19315
|
for (const sub of subEntries) {
|
|
19163
19316
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
19164
19317
|
for (const c of candidates) {
|
|
19165
|
-
if (
|
|
19166
|
-
return
|
|
19318
|
+
if (existsSync21(join21(root, entry.name, sub.name, c))) {
|
|
19319
|
+
return join21(root, entry.name, sub.name);
|
|
19167
19320
|
}
|
|
19168
19321
|
}
|
|
19169
19322
|
}
|
|
@@ -19190,7 +19343,7 @@ __export(discover_exports, {
|
|
|
19190
19343
|
import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
|
|
19191
19344
|
import { createHash } from "crypto";
|
|
19192
19345
|
import { homedir as homedir5 } from "os";
|
|
19193
|
-
import { join as
|
|
19346
|
+
import { join as join22 } from "path";
|
|
19194
19347
|
function invalidateSkillCache() {
|
|
19195
19348
|
cachedSkills = null;
|
|
19196
19349
|
cacheTimestamp = 0;
|
|
@@ -19208,7 +19361,7 @@ async function discoverAllSkills() {
|
|
|
19208
19361
|
const rawSkills = [];
|
|
19209
19362
|
rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
|
|
19210
19363
|
for (const backendId of getAllBackendIds()) {
|
|
19211
|
-
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [
|
|
19364
|
+
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join22(homedir5(), `.${backendId}`, "skills")];
|
|
19212
19365
|
for (const dir of dirs) {
|
|
19213
19366
|
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
19214
19367
|
}
|
|
@@ -19236,7 +19389,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
19236
19389
|
let content;
|
|
19237
19390
|
let resolvedPath;
|
|
19238
19391
|
for (const candidate of SKILL_FILE_CANDIDATES) {
|
|
19239
|
-
const p =
|
|
19392
|
+
const p = join22(skillsDir, entry.name, candidate);
|
|
19240
19393
|
try {
|
|
19241
19394
|
content = await readFile5(p, "utf-8");
|
|
19242
19395
|
resolvedPath = p;
|
|
@@ -19330,15 +19483,15 @@ var init_discover = __esm({
|
|
|
19330
19483
|
init_backends();
|
|
19331
19484
|
SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
|
|
19332
19485
|
BACKEND_SKILL_DIRS = {
|
|
19333
|
-
claude: [
|
|
19334
|
-
gemini: [
|
|
19486
|
+
claude: [join22(homedir5(), ".claude", "skills")],
|
|
19487
|
+
gemini: [join22(homedir5(), ".gemini", "skills")],
|
|
19335
19488
|
codex: [
|
|
19336
|
-
|
|
19337
|
-
|
|
19489
|
+
join22(homedir5(), ".agents", "skills"),
|
|
19490
|
+
join22(homedir5(), ".codex", "skills")
|
|
19338
19491
|
],
|
|
19339
19492
|
cursor: [
|
|
19340
|
-
|
|
19341
|
-
|
|
19493
|
+
join22(homedir5(), ".cursor", "skills"),
|
|
19494
|
+
join22(homedir5(), ".cursor", "skills-cursor")
|
|
19342
19495
|
]
|
|
19343
19496
|
};
|
|
19344
19497
|
CACHE_TTL_MS2 = 3e5;
|
|
@@ -19696,43 +19849,76 @@ async function sendVoiceConfigKeyboard(chatId, channel) {
|
|
|
19696
19849
|
await channel.sendText(chatId, "Voice configuration requires an interactive channel (Telegram).", { parseMode: "plain" });
|
|
19697
19850
|
return;
|
|
19698
19851
|
}
|
|
19699
|
-
const
|
|
19700
|
-
const
|
|
19701
|
-
const
|
|
19702
|
-
const
|
|
19703
|
-
Provider: ${providerLabel}
|
|
19704
|
-
Voice: ${currentVoiceName}
|
|
19705
|
-
Status: ${config2.enabled ? "ON" : "OFF"}`;
|
|
19852
|
+
const ttsConfig = getVoiceConfig(chatId);
|
|
19853
|
+
const sttProvider = getSttProvider(chatId);
|
|
19854
|
+
const sttModel = getSttModel(chatId);
|
|
19855
|
+
const whisperAvailable = isWhisperCliAvailable();
|
|
19706
19856
|
const buttons = [];
|
|
19857
|
+
const groqAvailable = !!process.env.GROQ_API_KEY;
|
|
19707
19858
|
buttons.push([
|
|
19708
|
-
{
|
|
19709
|
-
|
|
19710
|
-
|
|
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
|
+
}
|
|
19711
19869
|
]);
|
|
19712
|
-
if (
|
|
19713
|
-
const
|
|
19714
|
-
|
|
19715
|
-
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19724
|
-
|
|
19725
|
-
|
|
19726
|
-
label: `${config2.voiceId === v ? "\u2713 " : ""}${capitalize(v)}`,
|
|
19727
|
-
data: `vcfg:v:${v}`
|
|
19728
|
-
})));
|
|
19729
|
-
} else {
|
|
19730
|
-
const entries = Object.entries(MACOS_VOICES);
|
|
19731
|
-
buttons.push(entries.map(([id, v]) => ({
|
|
19732
|
-
label: `${config2.voiceId === id ? "\u2713 " : ""}${v.name}`,
|
|
19733
|
-
data: `vcfg:v:${id}`
|
|
19734
|
-
})));
|
|
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
|
+
}
|
|
19735
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}`;
|
|
19736
19922
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
19737
19923
|
}
|
|
19738
19924
|
async function sendSkillsPage(chatId, channel, skills2, page, messageId) {
|
|
@@ -19829,28 +20015,14 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
19829
20015
|
const backendDisplay = config2?.backend ? capitalize(config2.backend) : "Default";
|
|
19830
20016
|
const modelDisplay = config2?.model ?? "default";
|
|
19831
20017
|
const thinkingDisplay = config2?.thinking ?? "off";
|
|
19832
|
-
const
|
|
19833
|
-
|
|
19834
|
-
|
|
19835
|
-
|
|
19836
|
-
"
|
|
19837
|
-
|
|
19838
|
-
|
|
19839
|
-
|
|
19840
|
-
`Active hours: ${activeStart}-${activeEnd}`,
|
|
19841
|
-
`Backend: ${backendDisplay} | Model: ${modelDisplay}`
|
|
19842
|
-
];
|
|
19843
|
-
if (fallbacks.length > 0) {
|
|
19844
|
-
lines.push(`Fallbacks: ${fallbacks.map((f) => `${capitalize(f.backend)}${f.model ? `:${f.model}` : ""}`).join(", ")}`);
|
|
19845
|
-
}
|
|
19846
|
-
if (config2?.target) {
|
|
19847
|
-
lines.push(`Target: ${config2.target}`);
|
|
19848
|
-
}
|
|
19849
|
-
if (watches.length > 0) {
|
|
19850
|
-
lines.push(`Active watches: ${watches.length}`);
|
|
19851
|
-
}
|
|
19852
|
-
const lastBeat = config2?.lastBeatAt ?? "never";
|
|
19853
|
-
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");
|
|
19854
20026
|
const buttons = [];
|
|
19855
20027
|
buttons.push([
|
|
19856
20028
|
{ label: `${enabled ? "\u2713 " : ""}On`, data: "hb:on", ...enabled ? { style: "success" } : {} },
|
|
@@ -19864,63 +20036,62 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
19864
20036
|
...m === intervalMin ? { style: "primary" } : {}
|
|
19865
20037
|
})));
|
|
19866
20038
|
const available = getAvailableBackendIds();
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
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
|
+
];
|
|
19876
20051
|
buttons.push(backendRow);
|
|
19877
20052
|
const targetBackend = config2?.backend ?? getBackend(chatId) ?? "claude";
|
|
19878
20053
|
try {
|
|
19879
20054
|
const adapter = getAdapter(targetBackend);
|
|
19880
20055
|
const models = Object.entries(adapter.availableModels);
|
|
19881
20056
|
if (models.length > 0) {
|
|
19882
|
-
const modelRow =
|
|
19883
|
-
|
|
19884
|
-
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
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
|
+
];
|
|
19890
20069
|
buttons.push(modelRow);
|
|
19891
20070
|
}
|
|
19892
20071
|
} catch {
|
|
19893
20072
|
}
|
|
19894
20073
|
const fbCandidates = available.filter((bid) => !fallbacks.some((f) => f.backend === bid) && bid !== config2?.backend);
|
|
19895
|
-
if (fallbacks.length > 0 || fbCandidates.length > 0) {
|
|
19896
|
-
buttons.push([{ label: "\u{1F504} Fallback Chain", data: "hb:noop" }]);
|
|
19897
|
-
}
|
|
19898
20074
|
if (fallbacks.length > 0) {
|
|
19899
20075
|
buttons.push([
|
|
19900
|
-
{ label: fallbacks.map((f) =>
|
|
19901
|
-
|
|
19902
|
-
return f.model ? `${name} (${shortModelName(f.model)})` : name;
|
|
19903
|
-
}).join(" \u2192 "), data: "hb:noop" },
|
|
19904
|
-
{ 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" }
|
|
19905
20078
|
]);
|
|
19906
20079
|
}
|
|
19907
20080
|
if (fbCandidates.length > 0) {
|
|
19908
20081
|
buttons.push(fbCandidates.slice(0, 4).map((bid) => ({
|
|
19909
|
-
label: `+ ${capitalize(bid)}`,
|
|
20082
|
+
label: `+ ${capitalize(bid)} fallback`,
|
|
19910
20083
|
data: `hb:fb:add:${bid}`
|
|
19911
20084
|
})));
|
|
19912
20085
|
}
|
|
19913
|
-
const optionsRow = [
|
|
19914
|
-
{ label: `\u{1F4AD} Thinking: ${thinkingDisplay}`, data: "hb:thinking" }
|
|
19915
|
-
];
|
|
20086
|
+
const optionsRow = [];
|
|
19916
20087
|
if (watches.length > 0) {
|
|
19917
20088
|
optionsRow.push({ label: `\u{1F441} Watches (${watches.length})`, data: "hb:watches" });
|
|
19918
20089
|
} else {
|
|
19919
|
-
optionsRow.push({ label: "
|
|
20090
|
+
optionsRow.push({ label: "\u{1F441} Add Watch", data: "hb:addwatch" });
|
|
19920
20091
|
}
|
|
20092
|
+
optionsRow.push({ label: `\u23F0 Hours: ${activeStart}\u2013${activeEnd}`, data: "hb:noop" });
|
|
19921
20093
|
buttons.push(optionsRow);
|
|
19922
|
-
|
|
19923
|
-
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
20094
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
19924
20095
|
}
|
|
19925
20096
|
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
19926
20097
|
const memories = listMemories();
|
|
@@ -20569,13 +20740,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
20569
20740
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
20570
20741
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
20571
20742
|
if (current === "frozen") {
|
|
20572
|
-
const { readFileSync:
|
|
20573
|
-
const { join:
|
|
20743
|
+
const { readFileSync: readFileSync30, existsSync: existsSync59 } = await import("fs");
|
|
20744
|
+
const { join: join38 } = await import("path");
|
|
20574
20745
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
20575
|
-
const soulPath =
|
|
20576
|
-
const userPath =
|
|
20577
|
-
const soul =
|
|
20578
|
-
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") : "";
|
|
20579
20750
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
20580
20751
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
20581
20752
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -20652,11 +20823,11 @@ var init_evolve2 = __esm({
|
|
|
20652
20823
|
});
|
|
20653
20824
|
|
|
20654
20825
|
// src/optimizer/identity-audit.ts
|
|
20655
|
-
import { readFileSync as
|
|
20656
|
-
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";
|
|
20657
20828
|
function readIdentityFile2(filename) {
|
|
20658
20829
|
try {
|
|
20659
|
-
return
|
|
20830
|
+
return readFileSync12(join23(IDENTITY_PATH, filename), "utf-8");
|
|
20660
20831
|
} catch {
|
|
20661
20832
|
return "";
|
|
20662
20833
|
}
|
|
@@ -20671,13 +20842,13 @@ function getMtime(filepath) {
|
|
|
20671
20842
|
function findBackupFiles() {
|
|
20672
20843
|
const backups = [];
|
|
20673
20844
|
const dirs = [IDENTITY_PATH];
|
|
20674
|
-
const contextDir =
|
|
20675
|
-
if (
|
|
20845
|
+
const contextDir = join23(IDENTITY_PATH, "..", "workspace", "context");
|
|
20846
|
+
if (existsSync22(contextDir)) dirs.push(contextDir);
|
|
20676
20847
|
for (const dir of dirs) {
|
|
20677
20848
|
try {
|
|
20678
20849
|
for (const entry of readdirSync10(dir)) {
|
|
20679
20850
|
if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
|
|
20680
|
-
backups.push(
|
|
20851
|
+
backups.push(join23(dir, entry));
|
|
20681
20852
|
}
|
|
20682
20853
|
}
|
|
20683
20854
|
} catch {
|
|
@@ -20698,9 +20869,9 @@ function computeIdentityStats(pendingProposals, driftPercent) {
|
|
|
20698
20869
|
userChars,
|
|
20699
20870
|
ccClawChars,
|
|
20700
20871
|
boilerplateChars,
|
|
20701
|
-
soulMtime: getMtime(
|
|
20702
|
-
userMtime: getMtime(
|
|
20703
|
-
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")),
|
|
20704
20875
|
backupFiles: findBackupFiles(),
|
|
20705
20876
|
estimatedTokens: Math.ceil(ccClawChars / 4),
|
|
20706
20877
|
pendingEvolveProposals: pendingProposals,
|
|
@@ -20809,8 +20980,8 @@ var init_identity_audit = __esm({
|
|
|
20809
20980
|
});
|
|
20810
20981
|
|
|
20811
20982
|
// src/optimizer/skill-audit.ts
|
|
20812
|
-
import { readFileSync as
|
|
20813
|
-
import { join as
|
|
20983
|
+
import { readFileSync as readFileSync13, existsSync as existsSync23 } from "fs";
|
|
20984
|
+
import { join as join24, basename as basename3 } from "path";
|
|
20814
20985
|
function parseFrontmatter3(content) {
|
|
20815
20986
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
20816
20987
|
if (!fmMatch) return {};
|
|
@@ -20846,10 +21017,10 @@ function detectDependentSkills(content) {
|
|
|
20846
21017
|
return Array.from(deps);
|
|
20847
21018
|
}
|
|
20848
21019
|
function computeSkillStats(skillPath) {
|
|
20849
|
-
const content =
|
|
21020
|
+
const content = readFileSync13(skillPath, "utf-8");
|
|
20850
21021
|
const lines = content.split("\n");
|
|
20851
21022
|
return {
|
|
20852
|
-
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(
|
|
21023
|
+
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join24(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
20853
21024
|
skillPath,
|
|
20854
21025
|
lineCount: lines.length,
|
|
20855
21026
|
charCount: content.length,
|
|
@@ -20869,13 +21040,13 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
20869
21040
|
const results = [];
|
|
20870
21041
|
for (const name of depNames) {
|
|
20871
21042
|
const candidates = [
|
|
20872
|
-
|
|
20873
|
-
|
|
21043
|
+
join24(ccClawSkillsDir, name, "SKILL.md"),
|
|
21044
|
+
join24(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
20874
21045
|
];
|
|
20875
21046
|
for (const candidate of candidates) {
|
|
20876
|
-
if (
|
|
21047
|
+
if (existsSync23(candidate)) {
|
|
20877
21048
|
try {
|
|
20878
|
-
const content =
|
|
21049
|
+
const content = readFileSync13(candidate, "utf-8");
|
|
20879
21050
|
results.push({
|
|
20880
21051
|
name,
|
|
20881
21052
|
content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
|
|
@@ -20987,8 +21158,8 @@ __export(analyze_exports2, {
|
|
|
20987
21158
|
});
|
|
20988
21159
|
import { spawn as spawn7 } from "child_process";
|
|
20989
21160
|
import { createInterface as createInterface7 } from "readline";
|
|
20990
|
-
import { readFileSync as
|
|
20991
|
-
import { join as
|
|
21161
|
+
import { readFileSync as readFileSync14, existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
|
|
21162
|
+
import { join as join25 } from "path";
|
|
20992
21163
|
import { homedir as homedir7 } from "os";
|
|
20993
21164
|
function parseOptimizeOutput(raw, validAreas) {
|
|
20994
21165
|
if (!raw || raw.includes("NO_FINDINGS")) return [];
|
|
@@ -21118,20 +21289,20 @@ function getModelDisplayInfo(chatId) {
|
|
|
21118
21289
|
}
|
|
21119
21290
|
function readIdentityFile3(filename) {
|
|
21120
21291
|
try {
|
|
21121
|
-
return
|
|
21292
|
+
return readFileSync14(join25(IDENTITY_PATH, filename), "utf-8");
|
|
21122
21293
|
} catch {
|
|
21123
21294
|
return "";
|
|
21124
21295
|
}
|
|
21125
21296
|
}
|
|
21126
21297
|
function loadContextFiles2() {
|
|
21127
|
-
const contextDir =
|
|
21298
|
+
const contextDir = join25(homedir7(), ".cc-claw", "workspace", "context");
|
|
21128
21299
|
const results = [];
|
|
21129
|
-
if (!
|
|
21300
|
+
if (!existsSync24(contextDir)) return results;
|
|
21130
21301
|
try {
|
|
21131
21302
|
for (const entry of readdirSync12(contextDir)) {
|
|
21132
21303
|
if (!entry.endsWith(".md")) continue;
|
|
21133
21304
|
try {
|
|
21134
|
-
const content =
|
|
21305
|
+
const content = readFileSync14(join25(contextDir, entry), "utf-8");
|
|
21135
21306
|
results.push({ name: entry, content });
|
|
21136
21307
|
} catch {
|
|
21137
21308
|
}
|
|
@@ -21182,8 +21353,8 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
21182
21353
|
const stats = computeSkillStats(skillPath);
|
|
21183
21354
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
21184
21355
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
21185
|
-
const ccClawSkillsDir =
|
|
21186
|
-
const skillContent =
|
|
21356
|
+
const ccClawSkillsDir = join25(homedir7(), ".cc-claw", "workspace", "skills");
|
|
21357
|
+
const skillContent = readFileSync14(skillPath, "utf-8");
|
|
21187
21358
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
21188
21359
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
21189
21360
|
const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
|
|
@@ -21197,16 +21368,16 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
21197
21368
|
};
|
|
21198
21369
|
}
|
|
21199
21370
|
function listCcClawSkills() {
|
|
21200
|
-
const skillsDir =
|
|
21371
|
+
const skillsDir = join25(homedir7(), ".cc-claw", "workspace", "skills");
|
|
21201
21372
|
const entries = [];
|
|
21202
|
-
if (!
|
|
21373
|
+
if (!existsSync24(skillsDir)) return entries;
|
|
21203
21374
|
try {
|
|
21204
21375
|
for (const dir of readdirSync12(skillsDir)) {
|
|
21205
|
-
const skillFile =
|
|
21206
|
-
if (!
|
|
21376
|
+
const skillFile = join25(skillsDir, dir, "SKILL.md");
|
|
21377
|
+
if (!existsSync24(skillFile)) continue;
|
|
21207
21378
|
let description = "skill";
|
|
21208
21379
|
try {
|
|
21209
|
-
const content =
|
|
21380
|
+
const content = readFileSync14(skillFile, "utf-8");
|
|
21210
21381
|
const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
|
|
21211
21382
|
if (descMatch) description = descMatch[1].trim().slice(0, 60);
|
|
21212
21383
|
} catch {
|
|
@@ -21526,8 +21697,8 @@ var init_ui2 = __esm({
|
|
|
21526
21697
|
});
|
|
21527
21698
|
|
|
21528
21699
|
// src/router/optimize.ts
|
|
21529
|
-
import { readFileSync as
|
|
21530
|
-
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";
|
|
21531
21702
|
import { homedir as homedir8 } from "os";
|
|
21532
21703
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
21533
21704
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
@@ -21708,7 +21879,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
21708
21879
|
} = await Promise.resolve().then(() => (init_ui2(), ui_exports));
|
|
21709
21880
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
21710
21881
|
if (!modelInfo) return;
|
|
21711
|
-
const skillPath =
|
|
21882
|
+
const skillPath = join26(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
|
|
21712
21883
|
const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
|
|
21713
21884
|
chatId,
|
|
21714
21885
|
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
@@ -21797,13 +21968,13 @@ async function applyFinding(chatId, channel, index) {
|
|
|
21797
21968
|
await showFinding(chatId, channel, index + 1);
|
|
21798
21969
|
return;
|
|
21799
21970
|
}
|
|
21800
|
-
if (!
|
|
21971
|
+
if (!existsSync25(targetPath)) {
|
|
21801
21972
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
21802
21973
|
session2.skipped.push(index);
|
|
21803
21974
|
await showFinding(chatId, channel, index + 1);
|
|
21804
21975
|
return;
|
|
21805
21976
|
}
|
|
21806
|
-
const original =
|
|
21977
|
+
const original = readFileSync15(targetPath, "utf-8");
|
|
21807
21978
|
const backupPath = targetPath + `.bak.${Date.now()}`;
|
|
21808
21979
|
writeFileSync7(backupPath, original, "utf-8");
|
|
21809
21980
|
pruneBackups2(targetPath);
|
|
@@ -21879,14 +22050,14 @@ async function finishReview(chatId, channel) {
|
|
|
21879
22050
|
activeSessions.delete(chatId);
|
|
21880
22051
|
}
|
|
21881
22052
|
function resolveTargetFile(location, auditTarget) {
|
|
21882
|
-
const ccClawHome =
|
|
22053
|
+
const ccClawHome = join26(homedir8(), ".cc-claw");
|
|
21883
22054
|
const filePart = location.split(":")[0]?.trim();
|
|
21884
22055
|
if (!filePart) return null;
|
|
21885
|
-
if (filePart === "SOUL.md") return
|
|
21886
|
-
if (filePart === "USER.md") return
|
|
21887
|
-
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");
|
|
21888
22059
|
if (filePart === "SKILL.md" && auditTarget !== "identity") {
|
|
21889
|
-
return
|
|
22060
|
+
return join26(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
|
|
21890
22061
|
}
|
|
21891
22062
|
return null;
|
|
21892
22063
|
}
|
|
@@ -21894,7 +22065,7 @@ function pruneBackups2(absolutePath) {
|
|
|
21894
22065
|
const dir = dirname4(absolutePath);
|
|
21895
22066
|
const baseName = absolutePath.split("/").pop() ?? "";
|
|
21896
22067
|
try {
|
|
21897
|
-
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));
|
|
21898
22069
|
while (backups.length > 3) {
|
|
21899
22070
|
const oldest = backups.shift();
|
|
21900
22071
|
try {
|
|
@@ -21928,8 +22099,8 @@ __export(auto_create_exports, {
|
|
|
21928
22099
|
saveSkill: () => saveSkill,
|
|
21929
22100
|
storePendingDraft: () => storePendingDraft
|
|
21930
22101
|
});
|
|
21931
|
-
import { join as
|
|
21932
|
-
import { writeFile as
|
|
22102
|
+
import { join as join27 } from "path";
|
|
22103
|
+
import { writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
21933
22104
|
function isSkillWorthy(signals) {
|
|
21934
22105
|
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
21935
22106
|
if (toolUseCount < 12) return false;
|
|
@@ -22075,10 +22246,10 @@ function parseExtractedSkill(llmResponse) {
|
|
|
22075
22246
|
return { name, content };
|
|
22076
22247
|
}
|
|
22077
22248
|
async function saveSkill(name, content) {
|
|
22078
|
-
const dir =
|
|
22079
|
-
await
|
|
22080
|
-
const filePath =
|
|
22081
|
-
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");
|
|
22082
22253
|
invalidateSkillCache();
|
|
22083
22254
|
log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
|
|
22084
22255
|
return { path: filePath };
|
|
@@ -22248,18 +22419,7 @@ async function handleStopCommand(chatId, commandArgs, msg, channel) {
|
|
|
22248
22419
|
}
|
|
22249
22420
|
}
|
|
22250
22421
|
async function handleVoiceCommand(chatId, commandArgs, msg, channel) {
|
|
22251
|
-
|
|
22252
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
22253
|
-
await channel.sendKeyboard(chatId, `\u{1F3A7} Voice responses: ${vcEnabled ? "ON" : "OFF"}`, [
|
|
22254
|
-
[
|
|
22255
|
-
{ label: `${vcEnabled ? "" : "\u2713 "}\u{1F507} Off`, data: "voice:off", ...!vcEnabled ? { style: "danger" } : {} },
|
|
22256
|
-
{ label: `${vcEnabled ? "\u2713 " : ""}\u{1F50A} On`, data: "voice:on", ...vcEnabled ? { style: "success" } : {} }
|
|
22257
|
-
]
|
|
22258
|
-
]);
|
|
22259
|
-
} else {
|
|
22260
|
-
const toggled = toggleVoice(chatId);
|
|
22261
|
-
await channel.sendText(chatId, toggled ? "Voice responses enabled." : "Voice responses disabled.", { parseMode: "plain" });
|
|
22262
|
-
}
|
|
22422
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
22263
22423
|
}
|
|
22264
22424
|
async function handleVoiceConfigCommand(chatId, commandArgs, msg, channel) {
|
|
22265
22425
|
await sendVoiceConfigKeyboard(chatId, channel);
|
|
@@ -22458,42 +22618,40 @@ Use /skills to see it.`, { parseMode: "plain" });
|
|
|
22458
22618
|
async function handleExtractSkillCommand(chatId, commandArgs, msg, channel) {
|
|
22459
22619
|
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
22460
22620
|
const sessionMessages = getLog2(chatId);
|
|
22621
|
+
const exchangeCount = Math.floor(sessionMessages.length / 2);
|
|
22461
22622
|
if (sessionMessages.length < 2) {
|
|
22462
22623
|
await channel.sendText(chatId, "No session history to extract from. Have a conversation first, then run /extract_skill.", { parseMode: "plain" });
|
|
22463
22624
|
return;
|
|
22464
22625
|
}
|
|
22465
|
-
await channel.sendText(chatId, `Reviewing session (${Math.floor(sessionMessages.length / 2)} exchanges)...`, { parseMode: "plain" });
|
|
22466
|
-
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22467
|
-
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
22468
|
-
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22469
|
-
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
22470
|
-
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
22471
|
-
const extracted = parseExtractedSkill2(response.text);
|
|
22472
|
-
if (!extracted) {
|
|
22473
|
-
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
22474
|
-
return;
|
|
22475
|
-
}
|
|
22476
|
-
storePendingDraft2(chatId, {
|
|
22477
|
-
name: extracted.name,
|
|
22478
|
-
content: extracted.content,
|
|
22479
|
-
userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
|
|
22480
|
-
assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
|
|
22481
|
-
});
|
|
22482
|
-
const preview = extracted.content.length > 3500 ? extracted.content.slice(0, 3500) + "\n\n[...truncated...]" : extracted.content;
|
|
22483
22626
|
if (typeof channel.sendKeyboard === "function") {
|
|
22484
|
-
await channel.sendText(chatId, `Extracted skill: "${extracted.name}"
|
|
22485
|
-
|
|
22486
|
-
${preview}`, { parseMode: "plain" });
|
|
22487
22627
|
await channel.sendKeyboard(
|
|
22488
22628
|
chatId,
|
|
22489
|
-
|
|
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?`,
|
|
22490
22638
|
[[
|
|
22491
|
-
{ label: "\
|
|
22492
|
-
{ label: "\u2715
|
|
22639
|
+
{ label: "\u26A1 Start Extraction", data: "skill:start-extract", style: "success" },
|
|
22640
|
+
{ label: "\u2715 Cancel", data: "skill:discard" }
|
|
22493
22641
|
]]
|
|
22494
22642
|
);
|
|
22495
22643
|
} else {
|
|
22496
|
-
|
|
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
|
+
}
|
|
22497
22655
|
const result = await saveSkill2(extracted.name, extracted.content);
|
|
22498
22656
|
await channel.sendText(chatId, `Skill "${extracted.name}" saved to ${result.path}`, { parseMode: "plain" });
|
|
22499
22657
|
}
|
|
@@ -22604,6 +22762,22 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22604
22762
|
stopAllSideQuests(chatId);
|
|
22605
22763
|
const oldSessionId = getSessionId(chatId);
|
|
22606
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
|
+
}
|
|
22607
22781
|
const summarized = await summarizeSession(chatId);
|
|
22608
22782
|
clearSession(chatId);
|
|
22609
22783
|
clearChatPaidSlots(chatId);
|
|
@@ -22622,6 +22796,7 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22622
22796
|
const text = `\u2705 New session started. Previous session archived${exchangeCount > 0 ? ` (${exchangeCount} exchanges)` : ""}.
|
|
22623
22797
|
|
|
22624
22798
|
\u{1F9E0} ${backendLabel} \xB7 ${modelLabel}`;
|
|
22799
|
+
if (ackMsgId) await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
22625
22800
|
const kbMsgId = await channel.sendKeyboard(chatId, text, [
|
|
22626
22801
|
[
|
|
22627
22802
|
{ label: "Switch Backend", data: "menu:backend", style: "primary" },
|
|
@@ -22640,7 +22815,11 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22640
22815
|
}
|
|
22641
22816
|
} else {
|
|
22642
22817
|
const text = summarized ? "Session summarized and saved. Fresh conversation started!" : "Fresh conversation started. What's on your mind?";
|
|
22643
|
-
|
|
22818
|
+
if (ackMsgId) {
|
|
22819
|
+
await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
22820
|
+
} else {
|
|
22821
|
+
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
22822
|
+
}
|
|
22644
22823
|
}
|
|
22645
22824
|
}
|
|
22646
22825
|
async function handleSummarizeCommand(chatId, commandArgs, msg, channel) {
|
|
@@ -24522,6 +24701,44 @@ ${plan.originalMessage}`;
|
|
|
24522
24701
|
if (current !== desired) toggleVoice(chatId);
|
|
24523
24702
|
await channel.sendText(chatId, desired ? "\u{1F50A} Voice responses enabled." : "\u{1F507} Voice responses disabled.", { parseMode: "plain" });
|
|
24524
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);
|
|
24525
24742
|
} else if (data.startsWith("vcfg:")) {
|
|
24526
24743
|
const parts = data.slice(5).split(":");
|
|
24527
24744
|
const action = parts[0];
|
|
@@ -25429,15 +25646,16 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
25429
25646
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25430
25647
|
} else if (rest.startsWith("backend:")) {
|
|
25431
25648
|
const bid = rest.slice(8);
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25435
|
-
|
|
25436
|
-
}
|
|
25649
|
+
const newBackend = bid === "default" ? null : bid;
|
|
25650
|
+
updateHeartbeatConfig2({ backend: newBackend, model: null });
|
|
25651
|
+
updateHeartbeatField(chatId, "backend", newBackend);
|
|
25652
|
+
updateHeartbeatField(chatId, "model", null);
|
|
25437
25653
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25438
25654
|
} else if (rest.startsWith("model:")) {
|
|
25439
25655
|
const model2 = rest.slice(6);
|
|
25440
|
-
|
|
25656
|
+
const newModel = model2 === "default" ? null : model2;
|
|
25657
|
+
updateHeartbeatConfig2({ model: newModel });
|
|
25658
|
+
updateHeartbeatField(chatId, "model", newModel);
|
|
25441
25659
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25442
25660
|
} else if (rest === "thinking") {
|
|
25443
25661
|
await channel.sendText(chatId, "Thinking level for heartbeat: use /editjob to configure.", { parseMode: "plain" });
|
|
@@ -25557,6 +25775,71 @@ Example: /limits ${bid} daily 500000`, { parseMode: "plain" });
|
|
|
25557
25775
|
const page = parseInt(data.slice(12), 10);
|
|
25558
25776
|
const skills2 = await discoverAllSkills();
|
|
25559
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
|
+
}
|
|
25560
25843
|
} else if (data === "skill:extract") {
|
|
25561
25844
|
const { getPendingDraft: getPendingDraft2, clearPendingDraft: clearPendingDraft2, buildSkillExtractionPrompt: buildSkillExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25562
25845
|
const draft = getPendingDraft2(chatId);
|
|
@@ -26432,7 +26715,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26432
26715
|
})) {
|
|
26433
26716
|
const planDirective = buildPlanningDirective();
|
|
26434
26717
|
let typingActive2 = true;
|
|
26435
|
-
const
|
|
26718
|
+
const typingLoop2 = async () => {
|
|
26436
26719
|
while (typingActive2) {
|
|
26437
26720
|
try {
|
|
26438
26721
|
await channel.sendTyping?.(chatId);
|
|
@@ -26441,7 +26724,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26441
26724
|
await new Promise((r) => setTimeout(r, 4e3));
|
|
26442
26725
|
}
|
|
26443
26726
|
};
|
|
26444
|
-
|
|
26727
|
+
typingLoop2().catch(() => {
|
|
26445
26728
|
});
|
|
26446
26729
|
try {
|
|
26447
26730
|
const planResponse = await askAgent(chatId, cleanText || text, {
|
|
@@ -26482,23 +26765,18 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26482
26765
|
}
|
|
26483
26766
|
return;
|
|
26484
26767
|
}
|
|
26485
|
-
|
|
26486
|
-
const
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
while (typingActive) {
|
|
26492
|
-
try {
|
|
26493
|
-
await channel.sendTyping?.(chatId);
|
|
26494
|
-
} catch {
|
|
26495
|
-
}
|
|
26496
|
-
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 {
|
|
26497
26774
|
}
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26775
|
+
await new Promise((r) => setTimeout(r, 4e3));
|
|
26776
|
+
}
|
|
26777
|
+
};
|
|
26778
|
+
typingLoop().catch(() => {
|
|
26779
|
+
});
|
|
26502
26780
|
try {
|
|
26503
26781
|
const tMode = settings.getMode();
|
|
26504
26782
|
const tVerbose = settings.getVerboseLevel();
|
|
@@ -26529,6 +26807,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26529
26807
|
}
|
|
26530
26808
|
};
|
|
26531
26809
|
await liveStatus.init();
|
|
26810
|
+
typingActive = false;
|
|
26532
26811
|
if (showThinkingUi && adapter.id !== "claude") {
|
|
26533
26812
|
liveStatus.addInfo(`\u{1F4AD} Thinking display not available for ${adapter.displayName}`);
|
|
26534
26813
|
}
|
|
@@ -26622,7 +26901,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26622
26901
|
}
|
|
26623
26902
|
responseText += `
|
|
26624
26903
|
|
|
26625
|
-
\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`;
|
|
26626
26905
|
}
|
|
26627
26906
|
if (observedSubagents.size > 0) {
|
|
26628
26907
|
const names = [...observedSubagents].join(", ");
|
|
@@ -27177,7 +27456,19 @@ function resolveJobBackendId(job) {
|
|
|
27177
27456
|
})();
|
|
27178
27457
|
}
|
|
27179
27458
|
function resolveJobModel(job) {
|
|
27180
|
-
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
|
+
}
|
|
27181
27472
|
const backendId = resolveJobBackendId(job);
|
|
27182
27473
|
try {
|
|
27183
27474
|
const adapter = getAdapter(backendId);
|
|
@@ -27211,7 +27502,7 @@ var init_cron = __esm({
|
|
|
27211
27502
|
});
|
|
27212
27503
|
|
|
27213
27504
|
// src/agents/runners/wrap-backend.ts
|
|
27214
|
-
import { join as
|
|
27505
|
+
import { join as join28 } from "path";
|
|
27215
27506
|
function buildMcpCommands(backendId) {
|
|
27216
27507
|
const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
|
|
27217
27508
|
return {
|
|
@@ -27305,7 +27596,7 @@ function wrapBackendAdapter(adapter) {
|
|
|
27305
27596
|
const configPath = writeMcpConfigFile(server);
|
|
27306
27597
|
return ["--mcp-config", configPath];
|
|
27307
27598
|
},
|
|
27308
|
-
getSkillPath: () =>
|
|
27599
|
+
getSkillPath: () => join28(SKILLS_PATH, `agent-${adapter.id}.md`)
|
|
27309
27600
|
};
|
|
27310
27601
|
}
|
|
27311
27602
|
var BACKEND_CAPABILITIES;
|
|
@@ -27367,18 +27658,18 @@ var init_wrap_backend = __esm({
|
|
|
27367
27658
|
});
|
|
27368
27659
|
|
|
27369
27660
|
// src/agents/runners/config-loader.ts
|
|
27370
|
-
import { readFileSync as
|
|
27371
|
-
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";
|
|
27372
27663
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
27373
27664
|
function resolveExecutable2(config2) {
|
|
27374
|
-
if (
|
|
27665
|
+
if (existsSync26(config2.executable)) return config2.executable;
|
|
27375
27666
|
try {
|
|
27376
27667
|
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
27377
27668
|
} catch {
|
|
27378
27669
|
}
|
|
27379
27670
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
27380
27671
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
27381
|
-
if (
|
|
27672
|
+
if (existsSync26(resolved)) return resolved;
|
|
27382
27673
|
}
|
|
27383
27674
|
return config2.executable;
|
|
27384
27675
|
}
|
|
@@ -27504,12 +27795,12 @@ function configToRunner(config2) {
|
|
|
27504
27795
|
prepareMcpInjection() {
|
|
27505
27796
|
return [];
|
|
27506
27797
|
},
|
|
27507
|
-
getSkillPath: () =>
|
|
27798
|
+
getSkillPath: () => join29(SKILLS_PATH, `agent-${config2.id}.md`)
|
|
27508
27799
|
};
|
|
27509
27800
|
}
|
|
27510
27801
|
function loadRunnerConfig(filePath) {
|
|
27511
27802
|
try {
|
|
27512
|
-
const content =
|
|
27803
|
+
const content = readFileSync16(filePath, "utf-8");
|
|
27513
27804
|
return JSON.parse(content);
|
|
27514
27805
|
} catch (err) {
|
|
27515
27806
|
warn(`[runners] Failed to load config ${filePath}: ${err}`);
|
|
@@ -27517,14 +27808,14 @@ function loadRunnerConfig(filePath) {
|
|
|
27517
27808
|
}
|
|
27518
27809
|
}
|
|
27519
27810
|
function loadAllRunnerConfigs() {
|
|
27520
|
-
if (!
|
|
27811
|
+
if (!existsSync26(RUNNERS_PATH)) {
|
|
27521
27812
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
27522
27813
|
return [];
|
|
27523
27814
|
}
|
|
27524
27815
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
27525
27816
|
const configs = [];
|
|
27526
27817
|
for (const file of files) {
|
|
27527
|
-
const config2 = loadRunnerConfig(
|
|
27818
|
+
const config2 = loadRunnerConfig(join29(RUNNERS_PATH, file));
|
|
27528
27819
|
if (config2) configs.push(config2);
|
|
27529
27820
|
}
|
|
27530
27821
|
return configs;
|
|
@@ -27545,16 +27836,16 @@ function registerConfigRunners() {
|
|
|
27545
27836
|
return count;
|
|
27546
27837
|
}
|
|
27547
27838
|
function watchRunnerConfigs(onChange) {
|
|
27548
|
-
if (!
|
|
27839
|
+
if (!existsSync26(RUNNERS_PATH)) return;
|
|
27549
27840
|
for (const prev of watchedFiles) {
|
|
27550
|
-
if (!
|
|
27841
|
+
if (!existsSync26(prev)) {
|
|
27551
27842
|
unwatchFile(prev);
|
|
27552
27843
|
watchedFiles.delete(prev);
|
|
27553
27844
|
}
|
|
27554
27845
|
}
|
|
27555
27846
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
27556
27847
|
for (const file of files) {
|
|
27557
|
-
const fullPath =
|
|
27848
|
+
const fullPath = join29(RUNNERS_PATH, file);
|
|
27558
27849
|
if (watchedFiles.has(fullPath)) continue;
|
|
27559
27850
|
watchedFiles.add(fullPath);
|
|
27560
27851
|
watchFile(fullPath, { interval: 5e3 }, () => {
|
|
@@ -28159,8 +28450,7 @@ var init_telegram2 = __esm({
|
|
|
28159
28450
|
{ command: "skills", description: "List and invoke skills" },
|
|
28160
28451
|
{ command: "extract_skill", description: "Extract a reusable skill from this session" },
|
|
28161
28452
|
{ command: "skill_install", description: "Install a skill from GitHub" },
|
|
28162
|
-
{ command: "voice", description: "
|
|
28163
|
-
{ command: "voice_config", description: "Configure voice provider and voice" },
|
|
28453
|
+
{ command: "voice", description: "Voice settings (STT transcription + TTS replies)" },
|
|
28164
28454
|
{ command: "response_style", description: "Set the AI response style (concise/normal/detailed)" },
|
|
28165
28455
|
{ command: "model_signature", description: "Toggle model+thinking signature on responses" },
|
|
28166
28456
|
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
@@ -28588,7 +28878,8 @@ var init_telegram2 = __esm({
|
|
|
28588
28878
|
"reaction",
|
|
28589
28879
|
() => this.bot.api.setMessageReaction(numericChatId(chatId), parseInt(messageId), [
|
|
28590
28880
|
{ type: "emoji", emoji }
|
|
28591
|
-
])
|
|
28881
|
+
]),
|
|
28882
|
+
{ skipRecord: true }
|
|
28592
28883
|
);
|
|
28593
28884
|
} catch (err) {
|
|
28594
28885
|
log(`[telegram] reactToMessage failed (chat=${chatId} msg=${messageId}): ${err}`);
|
|
@@ -28831,19 +29122,19 @@ var init_telegram2 = __esm({
|
|
|
28831
29122
|
});
|
|
28832
29123
|
|
|
28833
29124
|
// src/skills/bootstrap.ts
|
|
28834
|
-
import { existsSync as
|
|
28835
|
-
import { readdir as readdir6, readFile as readFile8, writeFile as
|
|
28836
|
-
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";
|
|
28837
29128
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
28838
29129
|
async function copyAgentManifestSkills() {
|
|
28839
|
-
if (!
|
|
29130
|
+
if (!existsSync27(PKG_SKILLS)) return;
|
|
28840
29131
|
try {
|
|
28841
29132
|
const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
|
|
28842
29133
|
for (const entry of entries) {
|
|
28843
29134
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
28844
|
-
const src =
|
|
28845
|
-
const dest =
|
|
28846
|
-
if (
|
|
29135
|
+
const src = join30(PKG_SKILLS, entry.name);
|
|
29136
|
+
const dest = join30(SKILLS_PATH, entry.name);
|
|
29137
|
+
if (existsSync27(dest)) continue;
|
|
28847
29138
|
await copyFile(src, dest);
|
|
28848
29139
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
28849
29140
|
}
|
|
@@ -28853,8 +29144,8 @@ async function copyAgentManifestSkills() {
|
|
|
28853
29144
|
}
|
|
28854
29145
|
async function bootstrapSkills() {
|
|
28855
29146
|
await copyAgentManifestSkills();
|
|
28856
|
-
const usmDir =
|
|
28857
|
-
if (
|
|
29147
|
+
const usmDir = join30(SKILLS_PATH, USM_DIR_NAME);
|
|
29148
|
+
if (existsSync27(usmDir)) return;
|
|
28858
29149
|
try {
|
|
28859
29150
|
const entries = await readdir6(SKILLS_PATH);
|
|
28860
29151
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
@@ -28877,8 +29168,8 @@ async function bootstrapSkills() {
|
|
|
28877
29168
|
}
|
|
28878
29169
|
}
|
|
28879
29170
|
async function patchUsmForCcClaw(usmDir) {
|
|
28880
|
-
const skillPath =
|
|
28881
|
-
if (!
|
|
29171
|
+
const skillPath = join30(usmDir, "SKILL.md");
|
|
29172
|
+
if (!existsSync27(skillPath)) return;
|
|
28882
29173
|
try {
|
|
28883
29174
|
let content = await readFile8(skillPath, "utf-8");
|
|
28884
29175
|
let patched = false;
|
|
@@ -28906,7 +29197,7 @@ async function patchUsmForCcClaw(usmDir) {
|
|
|
28906
29197
|
}
|
|
28907
29198
|
}
|
|
28908
29199
|
if (patched) {
|
|
28909
|
-
await
|
|
29200
|
+
await writeFile6(skillPath, content, "utf-8");
|
|
28910
29201
|
log("[skills] Patched USM SKILL.md with CC-Claw support");
|
|
28911
29202
|
}
|
|
28912
29203
|
} catch (err) {
|
|
@@ -28923,8 +29214,8 @@ var init_bootstrap = __esm({
|
|
|
28923
29214
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
28924
29215
|
USM_DIR_NAME = "universal-skills-manager";
|
|
28925
29216
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
28926
|
-
PKG_ROOT =
|
|
28927
|
-
PKG_SKILLS =
|
|
29217
|
+
PKG_ROOT = join30(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
|
|
29218
|
+
PKG_SKILLS = join30(PKG_ROOT, "skills");
|
|
28928
29219
|
}
|
|
28929
29220
|
});
|
|
28930
29221
|
|
|
@@ -29037,13 +29328,13 @@ __export(ai_skill_exports, {
|
|
|
29037
29328
|
generateAiSkill: () => generateAiSkill,
|
|
29038
29329
|
installAiSkill: () => installAiSkill
|
|
29039
29330
|
});
|
|
29040
|
-
import { existsSync as
|
|
29041
|
-
import { join as
|
|
29331
|
+
import { existsSync as existsSync28, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
29332
|
+
import { join as join31 } from "path";
|
|
29042
29333
|
import { homedir as homedir9 } from "os";
|
|
29043
29334
|
function generateAiSkill() {
|
|
29044
29335
|
const version = VERSION;
|
|
29045
29336
|
let systemState = "";
|
|
29046
|
-
if (
|
|
29337
|
+
if (existsSync28(DB_PATH)) {
|
|
29047
29338
|
try {
|
|
29048
29339
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
29049
29340
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -29483,8 +29774,8 @@ function installAiSkill() {
|
|
|
29483
29774
|
const failed = [];
|
|
29484
29775
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
29485
29776
|
for (const dir of dirs) {
|
|
29486
|
-
const skillDir =
|
|
29487
|
-
const skillPath =
|
|
29777
|
+
const skillDir = join31(dir, "cc-claw-cli");
|
|
29778
|
+
const skillPath = join31(skillDir, "SKILL.md");
|
|
29488
29779
|
try {
|
|
29489
29780
|
mkdirSync11(skillDir, { recursive: true });
|
|
29490
29781
|
writeFileSync8(skillPath, skill, "utf-8");
|
|
@@ -29503,11 +29794,11 @@ var init_ai_skill = __esm({
|
|
|
29503
29794
|
init_paths();
|
|
29504
29795
|
init_version();
|
|
29505
29796
|
BACKEND_SKILL_DIRS2 = {
|
|
29506
|
-
"cc-claw": [
|
|
29507
|
-
claude: [
|
|
29508
|
-
gemini: [
|
|
29509
|
-
codex: [
|
|
29510
|
-
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")]
|
|
29511
29802
|
};
|
|
29512
29803
|
}
|
|
29513
29804
|
});
|
|
@@ -29517,21 +29808,21 @@ var index_exports = {};
|
|
|
29517
29808
|
__export(index_exports, {
|
|
29518
29809
|
main: () => main
|
|
29519
29810
|
});
|
|
29520
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
29521
|
-
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";
|
|
29522
29813
|
import dotenv from "dotenv";
|
|
29523
29814
|
function migrateLayout() {
|
|
29524
29815
|
const moves = [
|
|
29525
|
-
[
|
|
29526
|
-
[
|
|
29527
|
-
[
|
|
29528
|
-
[
|
|
29529
|
-
[
|
|
29530
|
-
[
|
|
29531
|
-
[
|
|
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")]
|
|
29532
29823
|
];
|
|
29533
29824
|
for (const [from, to] of moves) {
|
|
29534
|
-
if (
|
|
29825
|
+
if (existsSync29(from) && !existsSync29(to)) {
|
|
29535
29826
|
try {
|
|
29536
29827
|
renameSync2(from, to);
|
|
29537
29828
|
} catch {
|
|
@@ -29560,7 +29851,7 @@ async function main() {
|
|
|
29560
29851
|
let version = "unknown";
|
|
29561
29852
|
try {
|
|
29562
29853
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
29563
|
-
version = JSON.parse(
|
|
29854
|
+
version = JSON.parse(readFileSync18(pkgPath, "utf-8")).version;
|
|
29564
29855
|
} catch {
|
|
29565
29856
|
}
|
|
29566
29857
|
log(`[cc-claw] Starting v${version}`);
|
|
@@ -29708,10 +29999,10 @@ async function main() {
|
|
|
29708
29999
|
try {
|
|
29709
30000
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
29710
30001
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
|
|
29711
|
-
const { join:
|
|
29712
|
-
const skillDir =
|
|
30002
|
+
const { join: join38 } = await import("path");
|
|
30003
|
+
const skillDir = join38(SKILLS_PATH, "cc-claw-cli");
|
|
29713
30004
|
mkdirSync19(skillDir, { recursive: true });
|
|
29714
|
-
writeFileSync13(
|
|
30005
|
+
writeFileSync13(join38(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
29715
30006
|
log("[cc-claw] AI skill updated");
|
|
29716
30007
|
} catch {
|
|
29717
30008
|
}
|
|
@@ -29811,10 +30102,10 @@ var init_index = __esm({
|
|
|
29811
30102
|
init_health3();
|
|
29812
30103
|
init_image_gen();
|
|
29813
30104
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
29814
|
-
if (!
|
|
30105
|
+
if (!existsSync29(dir)) mkdirSync12(dir, { recursive: true });
|
|
29815
30106
|
}
|
|
29816
30107
|
migrateLayout();
|
|
29817
|
-
if (
|
|
30108
|
+
if (existsSync29(ENV_PATH)) {
|
|
29818
30109
|
dotenv.config({ path: ENV_PATH });
|
|
29819
30110
|
} else {
|
|
29820
30111
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -29835,12 +30126,12 @@ __export(api_client_exports, {
|
|
|
29835
30126
|
apiPost: () => apiPost,
|
|
29836
30127
|
isDaemonRunning: () => isDaemonRunning
|
|
29837
30128
|
});
|
|
29838
|
-
import { readFileSync as
|
|
30129
|
+
import { readFileSync as readFileSync19, existsSync as existsSync30 } from "fs";
|
|
29839
30130
|
import { request as httpRequest, Agent } from "http";
|
|
29840
30131
|
function getToken() {
|
|
29841
30132
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
29842
30133
|
try {
|
|
29843
|
-
if (
|
|
30134
|
+
if (existsSync30(TOKEN_PATH)) return readFileSync19(TOKEN_PATH, "utf-8").trim();
|
|
29844
30135
|
} catch {
|
|
29845
30136
|
}
|
|
29846
30137
|
return null;
|
|
@@ -29939,10 +30230,10 @@ __export(service_exports2, {
|
|
|
29939
30230
|
serviceStatus: () => serviceStatus,
|
|
29940
30231
|
uninstallService: () => uninstallService
|
|
29941
30232
|
});
|
|
29942
|
-
import { existsSync as
|
|
30233
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
29943
30234
|
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
29944
30235
|
import { homedir as homedir10, platform } from "os";
|
|
29945
|
-
import { join as
|
|
30236
|
+
import { join as join33, dirname as dirname6 } from "path";
|
|
29946
30237
|
function xmlEscape(s) {
|
|
29947
30238
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
29948
30239
|
}
|
|
@@ -29951,7 +30242,7 @@ function resolveExecutable3(name) {
|
|
|
29951
30242
|
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
29952
30243
|
} catch {
|
|
29953
30244
|
const fallback = process.argv[1];
|
|
29954
|
-
if (fallback &&
|
|
30245
|
+
if (fallback && existsSync31(fallback)) return fallback;
|
|
29955
30246
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
29956
30247
|
}
|
|
29957
30248
|
}
|
|
@@ -29960,14 +30251,14 @@ function getPathDirs() {
|
|
|
29960
30251
|
const home = homedir10();
|
|
29961
30252
|
const dirs = /* @__PURE__ */ new Set([
|
|
29962
30253
|
nodeBin,
|
|
29963
|
-
|
|
30254
|
+
join33(home, ".local", "bin"),
|
|
29964
30255
|
"/usr/local/bin",
|
|
29965
30256
|
"/usr/bin",
|
|
29966
30257
|
"/bin"
|
|
29967
30258
|
]);
|
|
29968
30259
|
try {
|
|
29969
30260
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
29970
|
-
if (prefix) dirs.add(
|
|
30261
|
+
if (prefix) dirs.add(join33(prefix, "bin"));
|
|
29971
30262
|
} catch {
|
|
29972
30263
|
}
|
|
29973
30264
|
return [...dirs].join(":");
|
|
@@ -30026,9 +30317,9 @@ function generatePlist() {
|
|
|
30026
30317
|
}
|
|
30027
30318
|
function installMacOS() {
|
|
30028
30319
|
const agentsDir = dirname6(PLIST_PATH);
|
|
30029
|
-
if (!
|
|
30030
|
-
if (!
|
|
30031
|
-
if (
|
|
30320
|
+
if (!existsSync31(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
30321
|
+
if (!existsSync31(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
30322
|
+
if (existsSync31(PLIST_PATH)) {
|
|
30032
30323
|
try {
|
|
30033
30324
|
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
30034
30325
|
} catch {
|
|
@@ -30040,7 +30331,7 @@ function installMacOS() {
|
|
|
30040
30331
|
console.log(" Service loaded and starting.");
|
|
30041
30332
|
}
|
|
30042
30333
|
function uninstallMacOS() {
|
|
30043
|
-
if (!
|
|
30334
|
+
if (!existsSync31(PLIST_PATH)) {
|
|
30044
30335
|
console.log(" No service found to uninstall.");
|
|
30045
30336
|
return;
|
|
30046
30337
|
}
|
|
@@ -30115,8 +30406,8 @@ WantedBy=default.target
|
|
|
30115
30406
|
`;
|
|
30116
30407
|
}
|
|
30117
30408
|
function installLinux() {
|
|
30118
|
-
if (!
|
|
30119
|
-
if (!
|
|
30409
|
+
if (!existsSync31(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
30410
|
+
if (!existsSync31(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
30120
30411
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
30121
30412
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
30122
30413
|
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -30125,7 +30416,7 @@ function installLinux() {
|
|
|
30125
30416
|
console.log(" Service enabled and started.");
|
|
30126
30417
|
}
|
|
30127
30418
|
function uninstallLinux() {
|
|
30128
|
-
if (!
|
|
30419
|
+
if (!existsSync31(UNIT_PATH)) {
|
|
30129
30420
|
console.log(" No service found to uninstall.");
|
|
30130
30421
|
return;
|
|
30131
30422
|
}
|
|
@@ -30150,7 +30441,7 @@ function statusLinux() {
|
|
|
30150
30441
|
}
|
|
30151
30442
|
}
|
|
30152
30443
|
function installService() {
|
|
30153
|
-
if (!
|
|
30444
|
+
if (!existsSync31(join33(CC_CLAW_HOME, ".env"))) {
|
|
30154
30445
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
30155
30446
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
30156
30447
|
process.exitCode = 1;
|
|
@@ -30179,9 +30470,9 @@ var init_service2 = __esm({
|
|
|
30179
30470
|
"use strict";
|
|
30180
30471
|
init_paths();
|
|
30181
30472
|
PLIST_LABEL = "com.cc-claw";
|
|
30182
|
-
PLIST_PATH =
|
|
30183
|
-
SYSTEMD_DIR =
|
|
30184
|
-
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");
|
|
30185
30476
|
}
|
|
30186
30477
|
});
|
|
30187
30478
|
|
|
@@ -30348,13 +30639,13 @@ var init_daemon = __esm({
|
|
|
30348
30639
|
});
|
|
30349
30640
|
|
|
30350
30641
|
// src/cli/resolve-chat.ts
|
|
30351
|
-
import { readFileSync as
|
|
30642
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
30352
30643
|
function resolveChatId2(globalOpts) {
|
|
30353
30644
|
const explicit = globalOpts.chat;
|
|
30354
30645
|
if (explicit) return explicit;
|
|
30355
30646
|
if (_cachedDefault) return _cachedDefault;
|
|
30356
30647
|
try {
|
|
30357
|
-
const content =
|
|
30648
|
+
const content = readFileSync21(ENV_PATH, "utf-8");
|
|
30358
30649
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
30359
30650
|
if (match) {
|
|
30360
30651
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -30378,7 +30669,7 @@ var status_exports = {};
|
|
|
30378
30669
|
__export(status_exports, {
|
|
30379
30670
|
statusCommand: () => statusCommand
|
|
30380
30671
|
});
|
|
30381
|
-
import { existsSync as
|
|
30672
|
+
import { existsSync as existsSync32, statSync as statSync10 } from "fs";
|
|
30382
30673
|
async function statusCommand(globalOpts, localOpts) {
|
|
30383
30674
|
try {
|
|
30384
30675
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
@@ -30418,7 +30709,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
30418
30709
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
30419
30710
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
30420
30711
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
30421
|
-
const dbStat =
|
|
30712
|
+
const dbStat = existsSync32(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
30422
30713
|
let daemonRunning = false;
|
|
30423
30714
|
let daemonInfo = {};
|
|
30424
30715
|
try {
|
|
@@ -30530,13 +30821,13 @@ __export(doctor_exports, {
|
|
|
30530
30821
|
doctorCommand: () => doctorCommand,
|
|
30531
30822
|
doctorErrors: () => doctorErrors
|
|
30532
30823
|
});
|
|
30533
|
-
import { existsSync as
|
|
30824
|
+
import { existsSync as existsSync33, accessSync, constants } from "fs";
|
|
30534
30825
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
30535
30826
|
async function doctorCommand(globalOpts, localOpts) {
|
|
30536
30827
|
const checks = [];
|
|
30537
30828
|
const dbChecks = checkDatabase();
|
|
30538
30829
|
checks.push(...dbChecks);
|
|
30539
|
-
if (
|
|
30830
|
+
if (existsSync33(DB_PATH)) {
|
|
30540
30831
|
try {
|
|
30541
30832
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30542
30833
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -30562,7 +30853,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30562
30853
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
30563
30854
|
}
|
|
30564
30855
|
}
|
|
30565
|
-
if (
|
|
30856
|
+
if (existsSync33(ENV_PATH)) {
|
|
30566
30857
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
30567
30858
|
} else {
|
|
30568
30859
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
@@ -30605,7 +30896,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30605
30896
|
} catch {
|
|
30606
30897
|
}
|
|
30607
30898
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
30608
|
-
if (
|
|
30899
|
+
if (existsSync33(tokenPath)) {
|
|
30609
30900
|
try {
|
|
30610
30901
|
accessSync(tokenPath, constants.R_OK);
|
|
30611
30902
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -30671,7 +30962,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30671
30962
|
const errorChecks = checks.filter(
|
|
30672
30963
|
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
30673
30964
|
);
|
|
30674
|
-
if (errorChecks.length > 0 &&
|
|
30965
|
+
if (errorChecks.length > 0 && existsSync33(ERROR_LOG_PATH)) {
|
|
30675
30966
|
try {
|
|
30676
30967
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
30677
30968
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -30800,15 +31091,15 @@ var logs_exports = {};
|
|
|
30800
31091
|
__export(logs_exports, {
|
|
30801
31092
|
logsCommand: () => logsCommand
|
|
30802
31093
|
});
|
|
30803
|
-
import { existsSync as
|
|
31094
|
+
import { existsSync as existsSync34, readFileSync as readFileSync23, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
30804
31095
|
async function logsCommand(opts) {
|
|
30805
31096
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
30806
|
-
if (!
|
|
31097
|
+
if (!existsSync34(logFile)) {
|
|
30807
31098
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
30808
31099
|
process.exit(1);
|
|
30809
31100
|
}
|
|
30810
31101
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
30811
|
-
const content =
|
|
31102
|
+
const content = readFileSync23(logFile, "utf-8");
|
|
30812
31103
|
const allLines = content.split("\n");
|
|
30813
31104
|
const tailLines = allLines.slice(-maxLines);
|
|
30814
31105
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -30818,7 +31109,7 @@ async function logsCommand(opts) {
|
|
|
30818
31109
|
let lastLength = content.length;
|
|
30819
31110
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
30820
31111
|
try {
|
|
30821
|
-
const newContent =
|
|
31112
|
+
const newContent = readFileSync23(logFile, "utf-8");
|
|
30822
31113
|
if (newContent.length > lastLength) {
|
|
30823
31114
|
const newPart = newContent.slice(lastLength);
|
|
30824
31115
|
process.stdout.write(newPart);
|
|
@@ -30850,7 +31141,7 @@ __export(session_logs_exports, {
|
|
|
30850
31141
|
sessionLogsList: () => sessionLogsList,
|
|
30851
31142
|
sessionLogsTail: () => sessionLogsTail
|
|
30852
31143
|
});
|
|
30853
|
-
import { readFileSync as
|
|
31144
|
+
import { readFileSync as readFileSync24, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
30854
31145
|
async function sessionLogsList(opts) {
|
|
30855
31146
|
const logs = listSessionLogs();
|
|
30856
31147
|
if (logs.length === 0) {
|
|
@@ -30907,12 +31198,12 @@ async function sessionLogsTail(opts) {
|
|
|
30907
31198
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
30908
31199
|
let lastLength = 0;
|
|
30909
31200
|
try {
|
|
30910
|
-
lastLength =
|
|
31201
|
+
lastLength = readFileSync24(targetPath, "utf-8").length;
|
|
30911
31202
|
} catch {
|
|
30912
31203
|
}
|
|
30913
31204
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
30914
31205
|
try {
|
|
30915
|
-
const content =
|
|
31206
|
+
const content = readFileSync24(targetPath, "utf-8");
|
|
30916
31207
|
if (content.length > lastLength) {
|
|
30917
31208
|
process.stdout.write(content.slice(lastLength));
|
|
30918
31209
|
lastLength = content.length;
|
|
@@ -30956,11 +31247,11 @@ __export(gemini_exports, {
|
|
|
30956
31247
|
geminiReorder: () => geminiReorder,
|
|
30957
31248
|
geminiRotation: () => geminiRotation
|
|
30958
31249
|
});
|
|
30959
|
-
import { existsSync as
|
|
30960
|
-
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";
|
|
30961
31252
|
import { createInterface as createInterface8 } from "readline";
|
|
30962
31253
|
function requireDb() {
|
|
30963
|
-
if (!
|
|
31254
|
+
if (!existsSync36(DB_PATH)) {
|
|
30964
31255
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30965
31256
|
process.exit(1);
|
|
30966
31257
|
}
|
|
@@ -30985,9 +31276,9 @@ async function resolveSlotId(idOrLabel) {
|
|
|
30985
31276
|
function resolveOAuthEmail(configHome) {
|
|
30986
31277
|
if (!configHome) return null;
|
|
30987
31278
|
try {
|
|
30988
|
-
const accountsPath =
|
|
30989
|
-
if (!
|
|
30990
|
-
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"));
|
|
30991
31282
|
return accounts.active || null;
|
|
30992
31283
|
} catch {
|
|
30993
31284
|
return null;
|
|
@@ -31069,14 +31360,14 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
31069
31360
|
}
|
|
31070
31361
|
async function geminiAddAccount(globalOpts, opts) {
|
|
31071
31362
|
await requireWriteDb();
|
|
31072
|
-
const slotsDir =
|
|
31073
|
-
if (!
|
|
31363
|
+
const slotsDir = join34(CC_CLAW_HOME, "gemini-slots");
|
|
31364
|
+
if (!existsSync36(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
31074
31365
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31075
31366
|
const tempId = Date.now();
|
|
31076
|
-
const slotDir =
|
|
31367
|
+
const slotDir = join34(slotsDir, `slot-${tempId}`);
|
|
31077
31368
|
mkdirSync14(slotDir, { recursive: true, mode: 448 });
|
|
31078
|
-
mkdirSync14(
|
|
31079
|
-
writeFileSync10(
|
|
31369
|
+
mkdirSync14(join34(slotDir, ".gemini"), { recursive: true });
|
|
31370
|
+
writeFileSync10(join34(slotDir, ".gemini", "settings.json"), JSON.stringify({
|
|
31080
31371
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
31081
31372
|
}, null, 2));
|
|
31082
31373
|
console.log("");
|
|
@@ -31093,8 +31384,8 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
31093
31384
|
});
|
|
31094
31385
|
} catch {
|
|
31095
31386
|
}
|
|
31096
|
-
const oauthPath =
|
|
31097
|
-
if (!
|
|
31387
|
+
const oauthPath = join34(slotDir, ".gemini", "oauth_creds.json");
|
|
31388
|
+
if (!existsSync36(oauthPath)) {
|
|
31098
31389
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
31099
31390
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
31100
31391
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -31102,7 +31393,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
31102
31393
|
}
|
|
31103
31394
|
let accountEmail = "unknown";
|
|
31104
31395
|
try {
|
|
31105
|
-
const accounts = JSON.parse(__require("fs").readFileSync(
|
|
31396
|
+
const accounts = JSON.parse(__require("fs").readFileSync(join34(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
|
|
31106
31397
|
accountEmail = accounts.active || accountEmail;
|
|
31107
31398
|
} catch {
|
|
31108
31399
|
}
|
|
@@ -31213,9 +31504,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31213
31504
|
outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
|
|
31214
31505
|
return;
|
|
31215
31506
|
}
|
|
31216
|
-
const settingsPath =
|
|
31217
|
-
if (!
|
|
31218
|
-
mkdirSync14(
|
|
31507
|
+
const settingsPath = join34(slot.configHome, ".gemini", "settings.json");
|
|
31508
|
+
if (!existsSync36(settingsPath)) {
|
|
31509
|
+
mkdirSync14(join34(slot.configHome, ".gemini"), { recursive: true });
|
|
31219
31510
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
31220
31511
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
31221
31512
|
}, null, 2));
|
|
@@ -31239,8 +31530,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31239
31530
|
});
|
|
31240
31531
|
} catch {
|
|
31241
31532
|
}
|
|
31242
|
-
const oauthPath =
|
|
31243
|
-
if (!
|
|
31533
|
+
const oauthPath = join34(slot.configHome, ".gemini", "oauth_creds.json");
|
|
31534
|
+
if (!existsSync36(oauthPath)) {
|
|
31244
31535
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
31245
31536
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
31246
31537
|
`);
|
|
@@ -31250,7 +31541,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31250
31541
|
setGeminiSlotEnabled2(slotId, true);
|
|
31251
31542
|
let accountEmail = slot.label;
|
|
31252
31543
|
try {
|
|
31253
|
-
const accounts = JSON.parse(
|
|
31544
|
+
const accounts = JSON.parse(readFileSync25(join34(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
31254
31545
|
if (accounts.active) accountEmail = accounts.active;
|
|
31255
31546
|
} catch {
|
|
31256
31547
|
}
|
|
@@ -31309,11 +31600,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
31309
31600
|
makeReorder: () => makeReorder,
|
|
31310
31601
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
31311
31602
|
});
|
|
31312
|
-
import { existsSync as
|
|
31313
|
-
import { join as
|
|
31603
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync26 } from "fs";
|
|
31604
|
+
import { join as join35 } from "path";
|
|
31314
31605
|
import { createInterface as createInterface9 } from "readline";
|
|
31315
31606
|
function requireDb2() {
|
|
31316
|
-
if (!
|
|
31607
|
+
if (!existsSync37(DB_PATH)) {
|
|
31317
31608
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31318
31609
|
process.exit(1);
|
|
31319
31610
|
}
|
|
@@ -31402,10 +31693,10 @@ function makeAddAccount(backend2, displayName) {
|
|
|
31402
31693
|
process.exit(1);
|
|
31403
31694
|
}
|
|
31404
31695
|
await requireWriteDb2();
|
|
31405
|
-
const slotsDir =
|
|
31406
|
-
if (!
|
|
31696
|
+
const slotsDir = join35(CC_CLAW_HOME, config2.slotsSubdir);
|
|
31697
|
+
if (!existsSync37(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
31407
31698
|
const tempId = Date.now();
|
|
31408
|
-
const slotDir =
|
|
31699
|
+
const slotDir = join35(slotsDir, `slot-${tempId}`);
|
|
31409
31700
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
31410
31701
|
if (config2.preSetup) config2.preSetup(slotDir);
|
|
31411
31702
|
console.log("");
|
|
@@ -31656,22 +31947,22 @@ var init_backend_cmd_factory = __esm({
|
|
|
31656
31947
|
envValue: (slotDir) => slotDir,
|
|
31657
31948
|
envOverrides: { ANTHROPIC_API_KEY: void 0 },
|
|
31658
31949
|
preSetup: (slotDir) => {
|
|
31659
|
-
mkdirSync15(
|
|
31950
|
+
mkdirSync15(join35(slotDir, ".claude"), { recursive: true });
|
|
31660
31951
|
},
|
|
31661
31952
|
verifyCredentials: (slotDir) => {
|
|
31662
|
-
const claudeJson =
|
|
31663
|
-
const claudeJsonNested =
|
|
31664
|
-
if (
|
|
31953
|
+
const claudeJson = join35(slotDir, ".claude.json");
|
|
31954
|
+
const claudeJsonNested = join35(slotDir, ".claude", ".claude.json");
|
|
31955
|
+
if (existsSync37(claudeJson)) {
|
|
31665
31956
|
try {
|
|
31666
|
-
const data = JSON.parse(
|
|
31957
|
+
const data = JSON.parse(readFileSync26(claudeJson, "utf-8"));
|
|
31667
31958
|
return Boolean(data.oauthAccount);
|
|
31668
31959
|
} catch {
|
|
31669
31960
|
return false;
|
|
31670
31961
|
}
|
|
31671
31962
|
}
|
|
31672
|
-
if (
|
|
31963
|
+
if (existsSync37(claudeJsonNested)) {
|
|
31673
31964
|
try {
|
|
31674
|
-
const data = JSON.parse(
|
|
31965
|
+
const data = JSON.parse(readFileSync26(claudeJsonNested, "utf-8"));
|
|
31675
31966
|
return Boolean(data.oauthAccount);
|
|
31676
31967
|
} catch {
|
|
31677
31968
|
return false;
|
|
@@ -31692,9 +31983,9 @@ var init_backend_cmd_factory = __esm({
|
|
|
31692
31983
|
} catch {
|
|
31693
31984
|
}
|
|
31694
31985
|
try {
|
|
31695
|
-
const claudeJson =
|
|
31696
|
-
if (
|
|
31697
|
-
const data = JSON.parse(
|
|
31986
|
+
const claudeJson = join35(slotDir, ".claude.json");
|
|
31987
|
+
if (existsSync37(claudeJson)) {
|
|
31988
|
+
const data = JSON.parse(readFileSync26(claudeJson, "utf-8"));
|
|
31698
31989
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
31699
31990
|
}
|
|
31700
31991
|
} catch {
|
|
@@ -31709,11 +32000,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
31709
32000
|
envValue: (slotDir) => slotDir,
|
|
31710
32001
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
31711
32002
|
verifyCredentials: (slotDir) => {
|
|
31712
|
-
return
|
|
32003
|
+
return existsSync37(join35(slotDir, "auth.json"));
|
|
31713
32004
|
},
|
|
31714
32005
|
extractLabel: (slotDir) => {
|
|
31715
32006
|
try {
|
|
31716
|
-
const authData = JSON.parse(
|
|
32007
|
+
const authData = JSON.parse(readFileSync26(join35(slotDir, "auth.json"), "utf-8"));
|
|
31717
32008
|
if (authData.email) return authData.email;
|
|
31718
32009
|
if (authData.account_name) return authData.account_name;
|
|
31719
32010
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -31737,9 +32028,9 @@ __export(ollama_exports3, {
|
|
|
31737
32028
|
ollamaRemove: () => ollamaRemove,
|
|
31738
32029
|
ollamaTest: () => ollamaTest
|
|
31739
32030
|
});
|
|
31740
|
-
import { existsSync as
|
|
32031
|
+
import { existsSync as existsSync38 } from "fs";
|
|
31741
32032
|
function requireDb3() {
|
|
31742
|
-
if (!
|
|
32033
|
+
if (!existsSync38(DB_PATH)) {
|
|
31743
32034
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31744
32035
|
process.exit(1);
|
|
31745
32036
|
}
|
|
@@ -31998,12 +32289,12 @@ __export(backend_exports, {
|
|
|
31998
32289
|
backendList: () => backendList,
|
|
31999
32290
|
backendSet: () => backendSet
|
|
32000
32291
|
});
|
|
32001
|
-
import { existsSync as
|
|
32292
|
+
import { existsSync as existsSync39 } from "fs";
|
|
32002
32293
|
async function backendList(globalOpts) {
|
|
32003
32294
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
32004
32295
|
const chatId = resolveChatId2(globalOpts);
|
|
32005
32296
|
let activeBackend = null;
|
|
32006
|
-
if (
|
|
32297
|
+
if (existsSync39(DB_PATH)) {
|
|
32007
32298
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32008
32299
|
const readDb = openDatabaseReadOnly2();
|
|
32009
32300
|
try {
|
|
@@ -32034,7 +32325,7 @@ async function backendList(globalOpts) {
|
|
|
32034
32325
|
}
|
|
32035
32326
|
async function backendGet(globalOpts) {
|
|
32036
32327
|
const chatId = resolveChatId2(globalOpts);
|
|
32037
|
-
if (!
|
|
32328
|
+
if (!existsSync39(DB_PATH)) {
|
|
32038
32329
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32039
32330
|
process.exit(1);
|
|
32040
32331
|
}
|
|
@@ -32078,13 +32369,13 @@ __export(model_exports, {
|
|
|
32078
32369
|
modelList: () => modelList,
|
|
32079
32370
|
modelSet: () => modelSet
|
|
32080
32371
|
});
|
|
32081
|
-
import { existsSync as
|
|
32372
|
+
import { existsSync as existsSync40 } from "fs";
|
|
32082
32373
|
async function modelList(globalOpts) {
|
|
32083
32374
|
const chatId = resolveChatId2(globalOpts);
|
|
32084
32375
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32085
32376
|
const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
32086
32377
|
let backendId = "claude";
|
|
32087
|
-
if (
|
|
32378
|
+
if (existsSync40(DB_PATH)) {
|
|
32088
32379
|
const readDb = openDatabaseReadOnly2();
|
|
32089
32380
|
try {
|
|
32090
32381
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -32117,7 +32408,7 @@ async function modelList(globalOpts) {
|
|
|
32117
32408
|
}
|
|
32118
32409
|
async function modelGet(globalOpts) {
|
|
32119
32410
|
const chatId = resolveChatId2(globalOpts);
|
|
32120
|
-
if (!
|
|
32411
|
+
if (!existsSync40(DB_PATH)) {
|
|
32121
32412
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32122
32413
|
process.exit(1);
|
|
32123
32414
|
}
|
|
@@ -32161,9 +32452,9 @@ __export(memory_exports2, {
|
|
|
32161
32452
|
memoryList: () => memoryList,
|
|
32162
32453
|
memorySearch: () => memorySearch
|
|
32163
32454
|
});
|
|
32164
|
-
import { existsSync as
|
|
32455
|
+
import { existsSync as existsSync41 } from "fs";
|
|
32165
32456
|
async function memoryList(globalOpts) {
|
|
32166
|
-
if (!
|
|
32457
|
+
if (!existsSync41(DB_PATH)) {
|
|
32167
32458
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32168
32459
|
process.exit(1);
|
|
32169
32460
|
}
|
|
@@ -32187,7 +32478,7 @@ async function memoryList(globalOpts) {
|
|
|
32187
32478
|
});
|
|
32188
32479
|
}
|
|
32189
32480
|
async function memorySearch(globalOpts, query) {
|
|
32190
|
-
if (!
|
|
32481
|
+
if (!existsSync41(DB_PATH)) {
|
|
32191
32482
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32192
32483
|
process.exit(1);
|
|
32193
32484
|
}
|
|
@@ -32209,7 +32500,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
32209
32500
|
});
|
|
32210
32501
|
}
|
|
32211
32502
|
async function memoryHistory(globalOpts, opts) {
|
|
32212
|
-
if (!
|
|
32503
|
+
if (!existsSync41(DB_PATH)) {
|
|
32213
32504
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32214
32505
|
process.exit(1);
|
|
32215
32506
|
}
|
|
@@ -32257,7 +32548,7 @@ __export(cron_exports2, {
|
|
|
32257
32548
|
cronList: () => cronList,
|
|
32258
32549
|
cronRuns: () => cronRuns
|
|
32259
32550
|
});
|
|
32260
|
-
import { existsSync as
|
|
32551
|
+
import { existsSync as existsSync42 } from "fs";
|
|
32261
32552
|
function parseFallbacks(raw) {
|
|
32262
32553
|
return raw.slice(0, 3).map((f) => {
|
|
32263
32554
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -32278,7 +32569,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
32278
32569
|
return val;
|
|
32279
32570
|
}
|
|
32280
32571
|
async function cronList(globalOpts) {
|
|
32281
|
-
if (!
|
|
32572
|
+
if (!existsSync42(DB_PATH)) {
|
|
32282
32573
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32283
32574
|
process.exit(1);
|
|
32284
32575
|
}
|
|
@@ -32316,7 +32607,7 @@ async function cronList(globalOpts) {
|
|
|
32316
32607
|
});
|
|
32317
32608
|
}
|
|
32318
32609
|
async function cronHealth(globalOpts) {
|
|
32319
|
-
if (!
|
|
32610
|
+
if (!existsSync42(DB_PATH)) {
|
|
32320
32611
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32321
32612
|
process.exit(1);
|
|
32322
32613
|
}
|
|
@@ -32477,7 +32768,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
32477
32768
|
}
|
|
32478
32769
|
}
|
|
32479
32770
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
32480
|
-
if (!
|
|
32771
|
+
if (!existsSync42(DB_PATH)) {
|
|
32481
32772
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32482
32773
|
process.exit(1);
|
|
32483
32774
|
}
|
|
@@ -32524,9 +32815,9 @@ __export(agents_exports, {
|
|
|
32524
32815
|
runnersList: () => runnersList,
|
|
32525
32816
|
tasksList: () => tasksList
|
|
32526
32817
|
});
|
|
32527
|
-
import { existsSync as
|
|
32818
|
+
import { existsSync as existsSync43 } from "fs";
|
|
32528
32819
|
async function agentsList(globalOpts) {
|
|
32529
|
-
if (!
|
|
32820
|
+
if (!existsSync43(DB_PATH)) {
|
|
32530
32821
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32531
32822
|
process.exit(1);
|
|
32532
32823
|
}
|
|
@@ -32557,7 +32848,7 @@ async function agentsList(globalOpts) {
|
|
|
32557
32848
|
});
|
|
32558
32849
|
}
|
|
32559
32850
|
async function tasksList(globalOpts) {
|
|
32560
|
-
if (!
|
|
32851
|
+
if (!existsSync43(DB_PATH)) {
|
|
32561
32852
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32562
32853
|
process.exit(1);
|
|
32563
32854
|
}
|
|
@@ -32685,10 +32976,10 @@ __export(db_exports, {
|
|
|
32685
32976
|
dbPath: () => dbPath,
|
|
32686
32977
|
dbStats: () => dbStats
|
|
32687
32978
|
});
|
|
32688
|
-
import { existsSync as
|
|
32979
|
+
import { existsSync as existsSync44, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
32689
32980
|
import { dirname as dirname7 } from "path";
|
|
32690
32981
|
async function dbStats(globalOpts) {
|
|
32691
|
-
if (!
|
|
32982
|
+
if (!existsSync44(DB_PATH)) {
|
|
32692
32983
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
32693
32984
|
process.exit(1);
|
|
32694
32985
|
}
|
|
@@ -32696,7 +32987,7 @@ async function dbStats(globalOpts) {
|
|
|
32696
32987
|
const readDb = openDatabaseReadOnly2();
|
|
32697
32988
|
const mainSize = statSync11(DB_PATH).size;
|
|
32698
32989
|
const walPath = DB_PATH + "-wal";
|
|
32699
|
-
const walSize =
|
|
32990
|
+
const walSize = existsSync44(walPath) ? statSync11(walPath).size : 0;
|
|
32700
32991
|
const tableNames = readDb.prepare(
|
|
32701
32992
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
32702
32993
|
).all();
|
|
@@ -32730,7 +33021,7 @@ async function dbPath(globalOpts) {
|
|
|
32730
33021
|
output({ path: DB_PATH }, (d) => d.path);
|
|
32731
33022
|
}
|
|
32732
33023
|
async function dbBackup(globalOpts, destPath) {
|
|
32733
|
-
if (!
|
|
33024
|
+
if (!existsSync44(DB_PATH)) {
|
|
32734
33025
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
32735
33026
|
process.exit(1);
|
|
32736
33027
|
}
|
|
@@ -32739,7 +33030,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
32739
33030
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
32740
33031
|
copyFileSync3(DB_PATH, dest);
|
|
32741
33032
|
const walPath = DB_PATH + "-wal";
|
|
32742
|
-
if (
|
|
33033
|
+
if (existsSync44(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
32743
33034
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
32744
33035
|
const b = d;
|
|
32745
33036
|
return `
|
|
@@ -32768,9 +33059,9 @@ __export(usage_exports, {
|
|
|
32768
33059
|
usageCost: () => usageCost,
|
|
32769
33060
|
usageTokens: () => usageTokens
|
|
32770
33061
|
});
|
|
32771
|
-
import { existsSync as
|
|
33062
|
+
import { existsSync as existsSync45 } from "fs";
|
|
32772
33063
|
function ensureDb() {
|
|
32773
|
-
if (!
|
|
33064
|
+
if (!existsSync45(DB_PATH)) {
|
|
32774
33065
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32775
33066
|
process.exit(1);
|
|
32776
33067
|
}
|
|
@@ -32960,9 +33251,9 @@ __export(config_exports2, {
|
|
|
32960
33251
|
configList: () => configList,
|
|
32961
33252
|
configSet: () => configSet
|
|
32962
33253
|
});
|
|
32963
|
-
import { existsSync as
|
|
33254
|
+
import { existsSync as existsSync46, readFileSync as readFileSync27 } from "fs";
|
|
32964
33255
|
async function configList(globalOpts) {
|
|
32965
|
-
if (!
|
|
33256
|
+
if (!existsSync46(DB_PATH)) {
|
|
32966
33257
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32967
33258
|
process.exit(1);
|
|
32968
33259
|
}
|
|
@@ -32996,7 +33287,7 @@ async function configGet(globalOpts, key) {
|
|
|
32996
33287
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
32997
33288
|
process.exit(1);
|
|
32998
33289
|
}
|
|
32999
|
-
if (!
|
|
33290
|
+
if (!existsSync46(DB_PATH)) {
|
|
33000
33291
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33001
33292
|
process.exit(1);
|
|
33002
33293
|
}
|
|
@@ -33042,11 +33333,11 @@ async function configSet(globalOpts, key, value) {
|
|
|
33042
33333
|
}
|
|
33043
33334
|
}
|
|
33044
33335
|
async function configEnv(_globalOpts) {
|
|
33045
|
-
if (!
|
|
33336
|
+
if (!existsSync46(ENV_PATH)) {
|
|
33046
33337
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
33047
33338
|
process.exit(1);
|
|
33048
33339
|
}
|
|
33049
|
-
const content =
|
|
33340
|
+
const content = readFileSync27(ENV_PATH, "utf-8");
|
|
33050
33341
|
const entries = {};
|
|
33051
33342
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
33052
33343
|
for (const line of content.split("\n")) {
|
|
@@ -33096,9 +33387,9 @@ __export(session_exports, {
|
|
|
33096
33387
|
sessionGet: () => sessionGet,
|
|
33097
33388
|
sessionNew: () => sessionNew
|
|
33098
33389
|
});
|
|
33099
|
-
import { existsSync as
|
|
33390
|
+
import { existsSync as existsSync47 } from "fs";
|
|
33100
33391
|
async function sessionGet(globalOpts) {
|
|
33101
|
-
if (!
|
|
33392
|
+
if (!existsSync47(DB_PATH)) {
|
|
33102
33393
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33103
33394
|
process.exit(1);
|
|
33104
33395
|
}
|
|
@@ -33159,9 +33450,9 @@ __export(permissions_exports, {
|
|
|
33159
33450
|
verboseGet: () => verboseGet,
|
|
33160
33451
|
verboseSet: () => verboseSet
|
|
33161
33452
|
});
|
|
33162
|
-
import { existsSync as
|
|
33453
|
+
import { existsSync as existsSync48 } from "fs";
|
|
33163
33454
|
function ensureDb2() {
|
|
33164
|
-
if (!
|
|
33455
|
+
if (!existsSync48(DB_PATH)) {
|
|
33165
33456
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33166
33457
|
process.exit(1);
|
|
33167
33458
|
}
|
|
@@ -33308,9 +33599,9 @@ __export(cwd_exports, {
|
|
|
33308
33599
|
cwdGet: () => cwdGet,
|
|
33309
33600
|
cwdSet: () => cwdSet
|
|
33310
33601
|
});
|
|
33311
|
-
import { existsSync as
|
|
33602
|
+
import { existsSync as existsSync49 } from "fs";
|
|
33312
33603
|
async function cwdGet(globalOpts) {
|
|
33313
|
-
if (!
|
|
33604
|
+
if (!existsSync49(DB_PATH)) {
|
|
33314
33605
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33315
33606
|
process.exit(1);
|
|
33316
33607
|
}
|
|
@@ -33372,9 +33663,9 @@ __export(voice_exports, {
|
|
|
33372
33663
|
voiceGet: () => voiceGet,
|
|
33373
33664
|
voiceSet: () => voiceSet
|
|
33374
33665
|
});
|
|
33375
|
-
import { existsSync as
|
|
33666
|
+
import { existsSync as existsSync50 } from "fs";
|
|
33376
33667
|
async function voiceGet(globalOpts) {
|
|
33377
|
-
if (!
|
|
33668
|
+
if (!existsSync50(DB_PATH)) {
|
|
33378
33669
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33379
33670
|
process.exit(1);
|
|
33380
33671
|
}
|
|
@@ -33423,9 +33714,9 @@ __export(heartbeat_exports2, {
|
|
|
33423
33714
|
heartbeatGet: () => heartbeatGet,
|
|
33424
33715
|
heartbeatSet: () => heartbeatSet
|
|
33425
33716
|
});
|
|
33426
|
-
import { existsSync as
|
|
33717
|
+
import { existsSync as existsSync51 } from "fs";
|
|
33427
33718
|
async function heartbeatGet(globalOpts) {
|
|
33428
|
-
if (!
|
|
33719
|
+
if (!existsSync51(DB_PATH)) {
|
|
33429
33720
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33430
33721
|
process.exit(1);
|
|
33431
33722
|
}
|
|
@@ -33636,9 +33927,9 @@ __export(summarizer_exports, {
|
|
|
33636
33927
|
summarizerGet: () => summarizerGet,
|
|
33637
33928
|
summarizerSet: () => summarizerSet
|
|
33638
33929
|
});
|
|
33639
|
-
import { existsSync as
|
|
33930
|
+
import { existsSync as existsSync52 } from "fs";
|
|
33640
33931
|
async function summarizerGet(globalOpts) {
|
|
33641
|
-
if (!
|
|
33932
|
+
if (!existsSync52(DB_PATH)) {
|
|
33642
33933
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33643
33934
|
process.exit(1);
|
|
33644
33935
|
}
|
|
@@ -33682,9 +33973,9 @@ __export(thinking_exports, {
|
|
|
33682
33973
|
thinkingGet: () => thinkingGet,
|
|
33683
33974
|
thinkingSet: () => thinkingSet
|
|
33684
33975
|
});
|
|
33685
|
-
import { existsSync as
|
|
33976
|
+
import { existsSync as existsSync53 } from "fs";
|
|
33686
33977
|
async function thinkingGet(globalOpts) {
|
|
33687
|
-
if (!
|
|
33978
|
+
if (!existsSync53(DB_PATH)) {
|
|
33688
33979
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33689
33980
|
process.exit(1);
|
|
33690
33981
|
}
|
|
@@ -33728,9 +34019,9 @@ __export(chats_exports, {
|
|
|
33728
34019
|
chatsList: () => chatsList,
|
|
33729
34020
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
33730
34021
|
});
|
|
33731
|
-
import { existsSync as
|
|
34022
|
+
import { existsSync as existsSync54 } from "fs";
|
|
33732
34023
|
async function chatsList(_globalOpts) {
|
|
33733
|
-
if (!
|
|
34024
|
+
if (!existsSync54(DB_PATH)) {
|
|
33734
34025
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33735
34026
|
process.exit(1);
|
|
33736
34027
|
}
|
|
@@ -33858,9 +34149,9 @@ var mcps_exports2 = {};
|
|
|
33858
34149
|
__export(mcps_exports2, {
|
|
33859
34150
|
mcpsList: () => mcpsList
|
|
33860
34151
|
});
|
|
33861
|
-
import { existsSync as
|
|
34152
|
+
import { existsSync as existsSync55 } from "fs";
|
|
33862
34153
|
async function mcpsList(_globalOpts) {
|
|
33863
|
-
if (!
|
|
34154
|
+
if (!existsSync55(DB_PATH)) {
|
|
33864
34155
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33865
34156
|
process.exit(1);
|
|
33866
34157
|
}
|
|
@@ -33897,11 +34188,11 @@ __export(chat_exports2, {
|
|
|
33897
34188
|
chatSend: () => chatSend
|
|
33898
34189
|
});
|
|
33899
34190
|
import { request as httpRequest2 } from "http";
|
|
33900
|
-
import { readFileSync as
|
|
34191
|
+
import { readFileSync as readFileSync28, existsSync as existsSync56 } from "fs";
|
|
33901
34192
|
function getToken2() {
|
|
33902
34193
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
33903
34194
|
try {
|
|
33904
|
-
if (
|
|
34195
|
+
if (existsSync56(TOKEN_PATH2)) return readFileSync28(TOKEN_PATH2, "utf-8").trim();
|
|
33905
34196
|
} catch {
|
|
33906
34197
|
}
|
|
33907
34198
|
return null;
|
|
@@ -34389,7 +34680,7 @@ __export(completion_exports, {
|
|
|
34389
34680
|
completionCommand: () => completionCommand
|
|
34390
34681
|
});
|
|
34391
34682
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
|
|
34392
|
-
import { join as
|
|
34683
|
+
import { join as join36 } from "path";
|
|
34393
34684
|
import { homedir as homedir11 } from "os";
|
|
34394
34685
|
async function completionCommand(opts) {
|
|
34395
34686
|
const shell = opts.shell ?? detectShell();
|
|
@@ -34405,10 +34696,10 @@ async function completionCommand(opts) {
|
|
|
34405
34696
|
process.exit(1);
|
|
34406
34697
|
}
|
|
34407
34698
|
if (opts.install) {
|
|
34408
|
-
const dir =
|
|
34699
|
+
const dir = join36(homedir11(), ".config", "cc-claw", "completions");
|
|
34409
34700
|
mkdirSync17(dir, { recursive: true });
|
|
34410
34701
|
const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
|
|
34411
|
-
const filepath =
|
|
34702
|
+
const filepath = join36(dir, filename);
|
|
34412
34703
|
writeFileSync11(filepath, script, "utf-8");
|
|
34413
34704
|
console.log(`\u2713 Completion script written to ${filepath}
|
|
34414
34705
|
`);
|
|
@@ -34581,9 +34872,9 @@ __export(evolve_exports2, {
|
|
|
34581
34872
|
evolveStatus: () => evolveStatus,
|
|
34582
34873
|
evolveUndo: () => evolveUndo
|
|
34583
34874
|
});
|
|
34584
|
-
import { existsSync as
|
|
34875
|
+
import { existsSync as existsSync57 } from "fs";
|
|
34585
34876
|
function ensureDb3() {
|
|
34586
|
-
if (!
|
|
34877
|
+
if (!existsSync57(DB_PATH)) {
|
|
34587
34878
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
34588
34879
|
process.exit(1);
|
|
34589
34880
|
}
|
|
@@ -35057,10 +35348,10 @@ var init_optimize2 = __esm({
|
|
|
35057
35348
|
|
|
35058
35349
|
// src/setup.ts
|
|
35059
35350
|
var setup_exports = {};
|
|
35060
|
-
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";
|
|
35061
35352
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
35062
35353
|
import { createInterface as createInterface11 } from "readline";
|
|
35063
|
-
import { join as
|
|
35354
|
+
import { join as join37 } from "path";
|
|
35064
35355
|
function divider2() {
|
|
35065
35356
|
console.log(dim("\u2500".repeat(55)));
|
|
35066
35357
|
}
|
|
@@ -35135,21 +35426,21 @@ async function setup() {
|
|
|
35135
35426
|
}
|
|
35136
35427
|
console.log("");
|
|
35137
35428
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
35138
|
-
if (!
|
|
35429
|
+
if (!existsSync58(dir)) mkdirSync18(dir, { recursive: true });
|
|
35139
35430
|
}
|
|
35140
35431
|
const env = {};
|
|
35141
|
-
const envSource =
|
|
35432
|
+
const envSource = existsSync58(ENV_PATH) ? ENV_PATH : existsSync58(".env") ? ".env" : null;
|
|
35142
35433
|
if (envSource) {
|
|
35143
35434
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
35144
35435
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
35145
|
-
const existing =
|
|
35436
|
+
const existing = readFileSync29(envSource, "utf-8");
|
|
35146
35437
|
for (const line of existing.split("\n")) {
|
|
35147
35438
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
35148
35439
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
35149
35440
|
}
|
|
35150
35441
|
}
|
|
35151
|
-
const cwdDb =
|
|
35152
|
-
if (
|
|
35442
|
+
const cwdDb = join37(process.cwd(), "cc-claw.db");
|
|
35443
|
+
if (existsSync58(cwdDb) && !existsSync58(DB_PATH)) {
|
|
35153
35444
|
const { size } = statSync12(cwdDb);
|
|
35154
35445
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
35155
35446
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
@@ -35289,14 +35580,44 @@ async function setup() {
|
|
|
35289
35580
|
}
|
|
35290
35581
|
header(4, TOTAL_STEPS, "Optional Features");
|
|
35291
35582
|
console.log(" These are optional \u2014 you can enable them later by editing .env\n");
|
|
35292
|
-
if (await confirm("Enable voice
|
|
35583
|
+
if (await confirm("Enable voice message transcription (speech-to-text)?")) {
|
|
35293
35584
|
console.log("");
|
|
35294
|
-
console.log(dim("
|
|
35295
|
-
|
|
35296
|
-
|
|
35297
|
-
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)");
|
|
35298
35589
|
console.log("");
|
|
35299
|
-
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");
|
|
35592
|
+
console.log("");
|
|
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
|
+
}
|
|
35619
|
+
console.log("");
|
|
35620
|
+
console.log(dim(" Choose a voice reply provider (text-to-speech):"));
|
|
35300
35621
|
console.log(" 1. ElevenLabs (high-quality, requires API key)");
|
|
35301
35622
|
console.log(" 2. Grok / xAI (high-quality, requires API key)");
|
|
35302
35623
|
console.log(" 3. macOS (free, uses built-in voices \u2014 Samantha, Albert)");
|
|
@@ -35313,10 +35634,10 @@ async function setup() {
|
|
|
35313
35634
|
const xaiKey = await requiredInput("xAI API key", env.XAI_API_KEY);
|
|
35314
35635
|
env.XAI_API_KEY = xaiKey;
|
|
35315
35636
|
console.log(green(" Voice replies via Grok enabled!"));
|
|
35316
|
-
console.log(dim(" Use /
|
|
35637
|
+
console.log(dim(" Use /voice in Telegram to select a voice (Eve, Ara, Rex, Sal, Leo)."));
|
|
35317
35638
|
} else if (ttsChoice === "3") {
|
|
35318
35639
|
console.log(green(" Voice replies via macOS enabled!"));
|
|
35319
|
-
console.log(dim(" Use /
|
|
35640
|
+
console.log(dim(" Use /voice in Telegram to select a voice (Samantha or Albert)."));
|
|
35320
35641
|
} else {
|
|
35321
35642
|
console.log(dim(" Voice replies will use macOS text-to-speech as fallback."));
|
|
35322
35643
|
}
|
|
@@ -35350,14 +35671,14 @@ async function setup() {
|
|
|
35350
35671
|
`ANTHROPIC_VERTEX_PROJECT_ID=${env.ANTHROPIC_VERTEX_PROJECT_ID ?? ""}`
|
|
35351
35672
|
);
|
|
35352
35673
|
}
|
|
35353
|
-
|
|
35354
|
-
|
|
35355
|
-
|
|
35356
|
-
|
|
35357
|
-
}
|
|
35358
|
-
if (env.
|
|
35359
|
-
|
|
35360
|
-
}
|
|
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}`);
|
|
35361
35682
|
}
|
|
35362
35683
|
if (env.DASHBOARD_ENABLED) {
|
|
35363
35684
|
envLines.push("", "# Dashboard", `DASHBOARD_ENABLED=${env.DASHBOARD_ENABLED}`);
|
|
@@ -35401,7 +35722,9 @@ async function setup() {
|
|
|
35401
35722
|
console.log(" " + green("[ok]") + ` Telegram bot: @${env.TELEGRAM_BOT_TOKEN ? "configured" : "missing"}`);
|
|
35402
35723
|
console.log(" " + green("[ok]") + ` Chat ID: ${env.ALLOWED_CHAT_ID ?? "not set (anyone can message)"}`);
|
|
35403
35724
|
console.log(" " + green("[ok]") + ` Vertex AI: ${env.ANTHROPIC_VERTEX_PROJECT_ID ?? "not configured"}`);
|
|
35404
|
-
|
|
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}`);
|
|
35405
35728
|
console.log(" " + (env.ELEVENLABS_API_KEY ? green("[ok]") : dim("[--]")) + " Voice replies");
|
|
35406
35729
|
console.log(" " + (env.DASHBOARD_ENABLED ? green("[ok]") : dim("[--]")) + " Web dashboard");
|
|
35407
35730
|
console.log(" " + (env.GEMINI_API_KEY ? green("[ok]") : dim("[--]")) + " Video analysis");
|