cc-claw 0.20.21 → 0.21.1
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 +859 -509
- 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.1" : (() => {
|
|
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 = {
|
|
@@ -11159,6 +11179,10 @@ var init_scheduler = __esm({
|
|
|
11159
11179
|
updates.push("delivery_mode = ?");
|
|
11160
11180
|
values.push(body.deliveryMode);
|
|
11161
11181
|
}
|
|
11182
|
+
if (body.credentialSlotId !== void 0) {
|
|
11183
|
+
updates.push("credential_slot_id = ?");
|
|
11184
|
+
values.push(body.credentialSlotId ?? null);
|
|
11185
|
+
}
|
|
11162
11186
|
if (updates.length === 0) {
|
|
11163
11187
|
return jsonResponse(res, { error: "No fields to update" }, 400);
|
|
11164
11188
|
}
|
|
@@ -11562,7 +11586,7 @@ __export(analyze_exports, {
|
|
|
11562
11586
|
});
|
|
11563
11587
|
import { spawn as spawn4 } from "child_process";
|
|
11564
11588
|
import { createInterface as createInterface3 } from "readline";
|
|
11565
|
-
import { readFileSync as
|
|
11589
|
+
import { readFileSync as readFileSync7, existsSync as existsSync12, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
|
|
11566
11590
|
import { join as join12 } from "path";
|
|
11567
11591
|
import { homedir as homedir4 } from "os";
|
|
11568
11592
|
function applySignalDecay(confidence, createdAt) {
|
|
@@ -11584,7 +11608,7 @@ function discoverReflectionTargets() {
|
|
|
11584
11608
|
if (!existsSync12(skillFile)) continue;
|
|
11585
11609
|
let desc = "skill";
|
|
11586
11610
|
try {
|
|
11587
|
-
const content =
|
|
11611
|
+
const content = readFileSync7(skillFile, "utf-8");
|
|
11588
11612
|
const descMatch = content.match(/description:\s*["']?([^"'\n]+)/);
|
|
11589
11613
|
if (descMatch) desc = descMatch[1].trim().slice(0, 80);
|
|
11590
11614
|
} catch {
|
|
@@ -11793,7 +11817,7 @@ function resolveReflectionAdapter(chatId) {
|
|
|
11793
11817
|
}
|
|
11794
11818
|
function readIdentityFile(filename) {
|
|
11795
11819
|
try {
|
|
11796
|
-
return
|
|
11820
|
+
return readFileSync7(join12(IDENTITY_PATH, filename), "utf-8");
|
|
11797
11821
|
} catch {
|
|
11798
11822
|
return "";
|
|
11799
11823
|
}
|
|
@@ -11952,7 +11976,7 @@ async function runAnalysisImpl(chatId, opts) {
|
|
|
11952
11976
|
try {
|
|
11953
11977
|
const fullPath = join12(ccClawHome, target.path);
|
|
11954
11978
|
if (existsSync12(fullPath)) {
|
|
11955
|
-
const content =
|
|
11979
|
+
const content = readFileSync7(fullPath, "utf-8");
|
|
11956
11980
|
if (totalSkillChars + content.length > SKILL_CONTENT_CAP) break;
|
|
11957
11981
|
skillContents.push({ path: target.path, content });
|
|
11958
11982
|
totalSkillChars += content.length;
|
|
@@ -12332,7 +12356,7 @@ __export(apply_exports, {
|
|
|
12332
12356
|
isTargetAllowed: () => isTargetAllowed,
|
|
12333
12357
|
rollbackInsight: () => rollbackInsight
|
|
12334
12358
|
});
|
|
12335
|
-
import { readFileSync as
|
|
12359
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
|
|
12336
12360
|
import { join as join13, dirname as dirname3 } from "path";
|
|
12337
12361
|
function isTargetAllowed(relativePath) {
|
|
12338
12362
|
if (relativePath.includes("..")) return false;
|
|
@@ -12416,7 +12440,7 @@ async function applyInsight(insightId) {
|
|
|
12416
12440
|
const absolutePath = join13(CC_CLAW_HOME, insight.targetFile);
|
|
12417
12441
|
if (insight.proposedAction === "append" && insight.targetFile === "identity/SOUL.md") {
|
|
12418
12442
|
if (existsSync13(absolutePath)) {
|
|
12419
|
-
const currentContent =
|
|
12443
|
+
const currentContent = readFileSync8(absolutePath, "utf-8");
|
|
12420
12444
|
const lineCount = currentContent.split("\n").length;
|
|
12421
12445
|
if (lineCount >= SOUL_LINE_CAP) {
|
|
12422
12446
|
return {
|
|
@@ -12438,7 +12462,7 @@ async function applyInsight(insightId) {
|
|
|
12438
12462
|
}
|
|
12439
12463
|
let original = "";
|
|
12440
12464
|
if (existsSync13(absolutePath)) {
|
|
12441
|
-
original =
|
|
12465
|
+
original = readFileSync8(absolutePath, "utf-8");
|
|
12442
12466
|
} else if (insight.proposedAction !== "create") {
|
|
12443
12467
|
return { success: false, message: `Target file "${insight.targetFile}" does not exist` };
|
|
12444
12468
|
}
|
|
@@ -12578,7 +12602,7 @@ function computeLineDrift(baseline, absolutePath) {
|
|
|
12578
12602
|
let current = "";
|
|
12579
12603
|
try {
|
|
12580
12604
|
if (existsSync13(absolutePath)) {
|
|
12581
|
-
current =
|
|
12605
|
+
current = readFileSync8(absolutePath, "utf-8");
|
|
12582
12606
|
}
|
|
12583
12607
|
} catch {
|
|
12584
12608
|
return 1;
|
|
@@ -12677,12 +12701,12 @@ var init_evolve = __esm({
|
|
|
12677
12701
|
const body = JSON.parse(await readBody(req));
|
|
12678
12702
|
const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
12679
12703
|
const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
|
|
12680
|
-
const { join:
|
|
12704
|
+
const { join: join38 } = await import("path");
|
|
12681
12705
|
const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
12682
12706
|
const chatId = resolveChatId(body);
|
|
12683
12707
|
if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
|
|
12684
|
-
const soulPath =
|
|
12685
|
-
const userPath =
|
|
12708
|
+
const soulPath = join38(home, "identity/SOUL.md");
|
|
12709
|
+
const userPath = join38(home, "identity/USER.md");
|
|
12686
12710
|
const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
|
|
12687
12711
|
const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
|
|
12688
12712
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
@@ -13658,6 +13682,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13658
13682
|
let sawToolEvents = false;
|
|
13659
13683
|
let sawResultEvent = false;
|
|
13660
13684
|
let toolTurnCount = 0;
|
|
13685
|
+
let loopKillReason;
|
|
13661
13686
|
const loopDetector = new ToolLoopDetector();
|
|
13662
13687
|
const t0 = Date.now();
|
|
13663
13688
|
const elapsed = () => `${((Date.now() - t0) / 1e3).toFixed(1)}s`;
|
|
@@ -13778,6 +13803,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13778
13803
|
const check = loopDetector.addCall(ev.toolName, ev.toolInput ?? {});
|
|
13779
13804
|
if (check.isLoop) {
|
|
13780
13805
|
warn(`[agent] Loop detected for ${adapter.id}: ${check.reason} \u2014 stopping`);
|
|
13806
|
+
loopKillReason = check.reason;
|
|
13781
13807
|
killProcessGroup(proc, "SIGTERM");
|
|
13782
13808
|
}
|
|
13783
13809
|
}
|
|
@@ -13902,8 +13928,12 @@ Partial output: ${accumulatedText.slice(-500)}`;
|
|
|
13902
13928
|
return;
|
|
13903
13929
|
}
|
|
13904
13930
|
if (code && code !== 0 && !cancelState.cancelled && !resultText) {
|
|
13905
|
-
|
|
13906
|
-
|
|
13931
|
+
if (code === 143 && loopKillReason) {
|
|
13932
|
+
reject(new Error(`Stopped: agent was repeating the same action (${loopKillReason}). Try rephrasing your request.`));
|
|
13933
|
+
} else {
|
|
13934
|
+
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
13935
|
+
reject(new Error(`CLI exited with code ${code}${stderr ? `: ${stderr.slice(0, 500)}` : ""}`));
|
|
13936
|
+
}
|
|
13907
13937
|
return;
|
|
13908
13938
|
}
|
|
13909
13939
|
const cleanedResult = stripThinkingContent(resultText || accumulatedText);
|
|
@@ -14454,7 +14484,7 @@ var init_agent = __esm({
|
|
|
14454
14484
|
chatLocks = /* @__PURE__ */ new Map();
|
|
14455
14485
|
SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
14456
14486
|
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 ?? "
|
|
14487
|
+
CONTENT_SILENCE_TIMEOUT_MS = parseInt(process.env.CONTENT_SILENCE_TIMEOUT_MS ?? "180000", 10);
|
|
14458
14488
|
CONTENT_SILENCE_TIMEOUT_ERROR = "CONTENT_SILENCE_TIMEOUT";
|
|
14459
14489
|
FIRST_RESPONSE_TIMEOUT_ERROR = "FIRST_RESPONSE_TIMEOUT";
|
|
14460
14490
|
FREE_SLOTS_EXHAUSTED = "FREE_SLOTS_EXHAUSTED";
|
|
@@ -14976,15 +15006,17 @@ var init_telegram_throttle = __esm({
|
|
|
14976
15006
|
* Used for cosmetic calls (typing indicators, reactions) that should count toward
|
|
14977
15007
|
* rate limits but must never queue up or amplify 429 spirals.
|
|
14978
15008
|
*/
|
|
14979
|
-
async tryBestEffort(chatId, label2, fn) {
|
|
15009
|
+
async tryBestEffort(chatId, label2, fn, opts) {
|
|
14980
15010
|
if (this.isPaused()) return void 0;
|
|
14981
15011
|
if (this.queue.length > 10) return void 0;
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
15012
|
+
if (!opts?.skipRecord) {
|
|
15013
|
+
const lastChat = this.lastSendPerChat.get(chatId) ?? 0;
|
|
15014
|
+
if (Date.now() - lastChat < PER_CHAT_INTERVAL_MS) return void 0;
|
|
15015
|
+
if (Date.now() - this.lastGlobalSend < GLOBAL_INTERVAL_MS) return void 0;
|
|
15016
|
+
}
|
|
14985
15017
|
try {
|
|
14986
15018
|
const result = await fn();
|
|
14987
|
-
this.recordSend(chatId);
|
|
15019
|
+
if (!opts?.skipRecord) this.recordSend(chatId);
|
|
14988
15020
|
return result;
|
|
14989
15021
|
} catch (err) {
|
|
14990
15022
|
if (is429(err)) {
|
|
@@ -15126,11 +15158,11 @@ var init_telegram_throttle = __esm({
|
|
|
15126
15158
|
});
|
|
15127
15159
|
|
|
15128
15160
|
// src/health/checks.ts
|
|
15129
|
-
import { existsSync as existsSync15, statSync as statSync6, readFileSync as
|
|
15161
|
+
import { existsSync as existsSync15, statSync as statSync6, readFileSync as readFileSync9 } from "fs";
|
|
15130
15162
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
15131
15163
|
function getRecentErrors() {
|
|
15132
15164
|
if (!existsSync15(ERROR_LOG_PATH)) return null;
|
|
15133
|
-
const logContent =
|
|
15165
|
+
const logContent = readFileSync9(ERROR_LOG_PATH, "utf-8");
|
|
15134
15166
|
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
15135
15167
|
const last24h = Date.now() - 864e5;
|
|
15136
15168
|
const lines = allLines.filter((line) => {
|
|
@@ -15359,7 +15391,7 @@ __export(heartbeat_exports, {
|
|
|
15359
15391
|
stopHeartbeatForChat: () => stopHeartbeatForChat,
|
|
15360
15392
|
updateHeartbeatConfig: () => updateHeartbeatConfig
|
|
15361
15393
|
});
|
|
15362
|
-
import { readFileSync as
|
|
15394
|
+
import { readFileSync as readFileSync10, existsSync as existsSync16 } from "fs";
|
|
15363
15395
|
import { join as join15 } from "path";
|
|
15364
15396
|
function findHeartbeatJob() {
|
|
15365
15397
|
try {
|
|
@@ -15493,7 +15525,7 @@ ${watchLines.join("\n")}`);
|
|
|
15493
15525
|
}
|
|
15494
15526
|
if (existsSync16(HEARTBEAT_MD_PATH)) {
|
|
15495
15527
|
try {
|
|
15496
|
-
const custom =
|
|
15528
|
+
const custom = readFileSync10(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
15497
15529
|
if (custom) {
|
|
15498
15530
|
sections.push(`[Custom checks from HEARTBEAT.md]
|
|
15499
15531
|
${custom}`);
|
|
@@ -15562,7 +15594,7 @@ var init_heartbeat2 = __esm({
|
|
|
15562
15594
|
});
|
|
15563
15595
|
|
|
15564
15596
|
// src/bootstrap/profile.ts
|
|
15565
|
-
import { readFileSync as
|
|
15597
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync17 } from "fs";
|
|
15566
15598
|
import { join as join16 } from "path";
|
|
15567
15599
|
function hasActiveProfile(chatId) {
|
|
15568
15600
|
return activeProfiles.has(chatId);
|
|
@@ -15693,7 +15725,7 @@ function extractUserUpdates(text) {
|
|
|
15693
15725
|
}
|
|
15694
15726
|
function appendToUserProfile(key, value) {
|
|
15695
15727
|
if (!existsSync17(USER_PATH2)) return;
|
|
15696
|
-
const content =
|
|
15728
|
+
const content = readFileSync11(USER_PATH2, "utf-8");
|
|
15697
15729
|
const line = `- **${key}**: ${value}`;
|
|
15698
15730
|
if (content.includes(line)) return;
|
|
15699
15731
|
const updated = content.trimEnd() + `
|
|
@@ -15802,8 +15834,9 @@ async function classifyWithOllama(text) {
|
|
|
15802
15834
|
(a, b) => (a.sizeBytes ?? Infinity) - (b.sizeBytes ?? Infinity)
|
|
15803
15835
|
);
|
|
15804
15836
|
const model2 = sorted[0].name;
|
|
15837
|
+
const baseUrl = ollamaStore.getBaseUrl(onlineServer);
|
|
15805
15838
|
const result = await ollamaClient.chat(
|
|
15806
|
-
|
|
15839
|
+
baseUrl,
|
|
15807
15840
|
model2,
|
|
15808
15841
|
[{ role: "user", content: LLM_CLASSIFY_PROMPT + text.slice(0, 500) }],
|
|
15809
15842
|
{ timeoutMs: LLM_CLASSIFY_TIMEOUT_MS, maxTokens: 5, temperature: 0 }
|
|
@@ -15828,7 +15861,7 @@ async function classifyWithSummarizerCli(text) {
|
|
|
15828
15861
|
const model2 = modelName ?? adapter.summarizerModel;
|
|
15829
15862
|
const { spawn: spawn8 } = await import("child_process");
|
|
15830
15863
|
const { resolveExecutable: resolveExecutable4 } = await Promise.resolve().then(() => (init_resolve_executable(), resolve_executable_exports));
|
|
15831
|
-
const exe = resolveExecutable4(adapter.id);
|
|
15864
|
+
const exe = resolveExecutable4({ envVar: `${adapter.id.toUpperCase()}_EXECUTABLE`, binaryName: adapter.id, candidates: [] });
|
|
15832
15865
|
if (!exe) return null;
|
|
15833
15866
|
return new Promise((resolve) => {
|
|
15834
15867
|
const timeout = setTimeout(() => {
|
|
@@ -17701,7 +17734,9 @@ var init_gate = __esm({
|
|
|
17701
17734
|
// src/voice/stt.ts
|
|
17702
17735
|
import crypto from "crypto";
|
|
17703
17736
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
17704
|
-
import { readFile as readFile2, unlink as unlink2 } from "fs/promises";
|
|
17737
|
+
import { readFile as readFile2, unlink as unlink2, mkdir as mkdir2, writeFile } from "fs/promises";
|
|
17738
|
+
import { existsSync as existsSync19 } from "fs";
|
|
17739
|
+
import { join as join18 } from "path";
|
|
17705
17740
|
import { promisify as promisify2 } from "util";
|
|
17706
17741
|
function ensureFfmpeg() {
|
|
17707
17742
|
if (ffmpegAvailable === true) return;
|
|
@@ -17746,11 +17781,120 @@ function setVoiceProvider(chatId, provider, voiceId) {
|
|
|
17746
17781
|
ON CONFLICT(chat_id) DO UPDATE SET provider = ?, voice_id = ?, enabled = 1
|
|
17747
17782
|
`).run(chatId, provider, voiceId, provider, voiceId);
|
|
17748
17783
|
}
|
|
17749
|
-
|
|
17784
|
+
function getSttProvider(chatId) {
|
|
17785
|
+
const db3 = getDb();
|
|
17786
|
+
const row = db3.prepare("SELECT stt_provider FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
17787
|
+
return row?.stt_provider ?? "groq";
|
|
17788
|
+
}
|
|
17789
|
+
function setSttProvider(chatId, provider) {
|
|
17790
|
+
const db3 = getDb();
|
|
17791
|
+
db3.prepare(`
|
|
17792
|
+
INSERT INTO chat_voice (chat_id, enabled, stt_provider) VALUES (?, 0, ?)
|
|
17793
|
+
ON CONFLICT(chat_id) DO UPDATE SET stt_provider = ?
|
|
17794
|
+
`).run(chatId, provider, provider);
|
|
17795
|
+
}
|
|
17796
|
+
function getSttModel(chatId) {
|
|
17797
|
+
const db3 = getDb();
|
|
17798
|
+
const row = db3.prepare("SELECT stt_model FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
17799
|
+
const model2 = row?.stt_model ?? "small.en";
|
|
17800
|
+
return model2 in LOCAL_WHISPER_MODELS ? model2 : "small.en";
|
|
17801
|
+
}
|
|
17802
|
+
function setSttModel(chatId, model2) {
|
|
17803
|
+
const db3 = getDb();
|
|
17804
|
+
db3.prepare(`
|
|
17805
|
+
INSERT INTO chat_voice (chat_id, enabled, stt_model) VALUES (?, 0, ?)
|
|
17806
|
+
ON CONFLICT(chat_id) DO UPDATE SET stt_model = ?
|
|
17807
|
+
`).run(chatId, model2, model2);
|
|
17808
|
+
}
|
|
17809
|
+
function isWhisperCliAvailable() {
|
|
17810
|
+
if (whisperCliAvailableCache !== null) return whisperCliAvailableCache;
|
|
17811
|
+
try {
|
|
17812
|
+
execFileSync2("whisper-cli", ["--help"], { stdio: "ignore" });
|
|
17813
|
+
whisperCliAvailableCache = true;
|
|
17814
|
+
} catch {
|
|
17815
|
+
try {
|
|
17816
|
+
execFileSync2("whisper", ["--help"], { stdio: "ignore" });
|
|
17817
|
+
whisperCliAvailableCache = true;
|
|
17818
|
+
} catch {
|
|
17819
|
+
whisperCliAvailableCache = false;
|
|
17820
|
+
}
|
|
17821
|
+
}
|
|
17822
|
+
return whisperCliAvailableCache;
|
|
17823
|
+
}
|
|
17824
|
+
function getWhisperBin() {
|
|
17825
|
+
try {
|
|
17826
|
+
execFileSync2("whisper-cli", ["--help"], { stdio: "ignore" });
|
|
17827
|
+
return "whisper-cli";
|
|
17828
|
+
} catch {
|
|
17829
|
+
}
|
|
17830
|
+
try {
|
|
17831
|
+
execFileSync2("whisper", ["--help"], { stdio: "ignore" });
|
|
17832
|
+
return "whisper";
|
|
17833
|
+
} catch {
|
|
17834
|
+
}
|
|
17835
|
+
return null;
|
|
17836
|
+
}
|
|
17837
|
+
function whisperModelPath(model2) {
|
|
17838
|
+
return join18(WHISPER_MODELS_PATH, `ggml-${model2}.bin`);
|
|
17839
|
+
}
|
|
17840
|
+
function isWhisperModelDownloaded(model2) {
|
|
17841
|
+
return existsSync19(whisperModelPath(model2));
|
|
17842
|
+
}
|
|
17843
|
+
async function downloadWhisperModel(model2, onProgress) {
|
|
17844
|
+
await mkdir2(WHISPER_MODELS_PATH, { recursive: true });
|
|
17845
|
+
const dest = whisperModelPath(model2);
|
|
17846
|
+
const url = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model2}.bin`;
|
|
17847
|
+
const info = LOCAL_WHISPER_MODELS[model2];
|
|
17848
|
+
onProgress?.(`\u2B07\uFE0F Downloading Whisper model ${model2} (${info.size})...`);
|
|
17849
|
+
log(`[stt] Downloading model ${model2} from ${url}`);
|
|
17850
|
+
const response = await fetch(url);
|
|
17851
|
+
if (!response.ok) throw new Error(`Failed to download model: ${response.status} ${response.statusText}`);
|
|
17852
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
17853
|
+
await writeFile(dest, Buffer.from(arrayBuffer));
|
|
17854
|
+
log(`[stt] Model ${model2} downloaded to ${dest}`);
|
|
17855
|
+
}
|
|
17856
|
+
async function transcribeWithLocalWhisper(audioBuffer, model2, onProgress) {
|
|
17857
|
+
ensureFfmpeg();
|
|
17858
|
+
const bin = getWhisperBin();
|
|
17859
|
+
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");
|
|
17860
|
+
if (!isWhisperModelDownloaded(model2)) {
|
|
17861
|
+
await downloadWhisperModel(model2, onProgress);
|
|
17862
|
+
}
|
|
17863
|
+
const id = crypto.randomUUID();
|
|
17864
|
+
const tmpOgg = `/tmp/cc-claw-stt-${id}.ogg`;
|
|
17865
|
+
const tmpWav = `/tmp/cc-claw-stt-${id}.wav`;
|
|
17866
|
+
try {
|
|
17867
|
+
await writeFile(tmpOgg, audioBuffer);
|
|
17868
|
+
await execFileAsync2("ffmpeg", ["-y", "-i", tmpOgg, "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le", tmpWav]);
|
|
17869
|
+
const modelFile = whisperModelPath(model2);
|
|
17870
|
+
const result = await execFileAsync2(bin, ["-m", modelFile, "-f", tmpWav, "-nt", "--output-txt", "-of", `/tmp/cc-claw-stt-${id}`]);
|
|
17871
|
+
const txtFile = `/tmp/cc-claw-stt-${id}.txt`;
|
|
17872
|
+
let transcript = "";
|
|
17873
|
+
if (existsSync19(txtFile)) {
|
|
17874
|
+
transcript = (await readFile2(txtFile, "utf-8")).trim();
|
|
17875
|
+
unlink2(txtFile).catch(() => {
|
|
17876
|
+
});
|
|
17877
|
+
} else {
|
|
17878
|
+
transcript = (result.stdout ?? "").trim();
|
|
17879
|
+
}
|
|
17880
|
+
return transcript;
|
|
17881
|
+
} finally {
|
|
17882
|
+
unlink2(tmpOgg).catch(() => {
|
|
17883
|
+
});
|
|
17884
|
+
unlink2(tmpWav).catch(() => {
|
|
17885
|
+
});
|
|
17886
|
+
}
|
|
17887
|
+
}
|
|
17888
|
+
async function transcribeAudio(audioBuffer, chatId, onProgress) {
|
|
17889
|
+
const provider = chatId ? getSttProvider(chatId) : "groq";
|
|
17890
|
+
if (provider === "local-whisper") {
|
|
17891
|
+
const model2 = chatId ? getSttModel(chatId) : "small.en";
|
|
17892
|
+
return await transcribeWithLocalWhisper(audioBuffer, model2, onProgress);
|
|
17893
|
+
}
|
|
17750
17894
|
const GROQ_API_KEY = process.env.GROQ_API_KEY;
|
|
17751
17895
|
if (!GROQ_API_KEY) return null;
|
|
17752
17896
|
const formData = new FormData();
|
|
17753
|
-
formData.append("file", new Blob([new Uint8Array(audioBuffer)], { type:
|
|
17897
|
+
formData.append("file", new Blob([new Uint8Array(audioBuffer)], { type: "audio/ogg" }), "voice.ogg");
|
|
17754
17898
|
formData.append("model", "whisper-large-v3");
|
|
17755
17899
|
formData.append("response_format", "text");
|
|
17756
17900
|
const response = await fetch("https://api.groq.com/openai/v1/audio/transcriptions", {
|
|
@@ -17851,8 +17995,8 @@ async function mp3ToOgg(mp3Buffer) {
|
|
|
17851
17995
|
const id = crypto.randomUUID();
|
|
17852
17996
|
const tmpMp3 = `/tmp/cc-claw-tts-${id}.mp3`;
|
|
17853
17997
|
const tmpOgg = `/tmp/cc-claw-tts-${id}.ogg`;
|
|
17854
|
-
const { writeFile:
|
|
17855
|
-
await
|
|
17998
|
+
const { writeFile: writeFile7 } = await import("fs/promises");
|
|
17999
|
+
await writeFile7(tmpMp3, mp3Buffer);
|
|
17856
18000
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17857
18001
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17858
18002
|
unlink2(tmpMp3).catch((err) => {
|
|
@@ -17879,14 +18023,24 @@ async function macOsTts(text, voice2 = "Samantha") {
|
|
|
17879
18023
|
});
|
|
17880
18024
|
return oggBuffer;
|
|
17881
18025
|
}
|
|
17882
|
-
var execFileAsync2, ffmpegAvailable, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES;
|
|
18026
|
+
var execFileAsync2, ffmpegAvailable, LOCAL_WHISPER_MODELS, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES, whisperCliAvailableCache;
|
|
17883
18027
|
var init_stt = __esm({
|
|
17884
18028
|
"src/voice/stt.ts"() {
|
|
17885
18029
|
"use strict";
|
|
17886
18030
|
init_log();
|
|
17887
18031
|
init_store5();
|
|
18032
|
+
init_paths();
|
|
17888
18033
|
execFileAsync2 = promisify2(execFile2);
|
|
17889
18034
|
ffmpegAvailable = null;
|
|
18035
|
+
LOCAL_WHISPER_MODELS = {
|
|
18036
|
+
"tiny.en": { size: "75MB", lang: "English only", speed: "~0.3s", quality: "Basic" },
|
|
18037
|
+
"base.en": { size: "150MB", lang: "English only", speed: "~0.8s", quality: "Good" },
|
|
18038
|
+
"small.en": { size: "500MB", lang: "English only", speed: "~1.5s", quality: "Very good \u2B50" },
|
|
18039
|
+
"small": { size: "500MB", lang: "Multilingual", speed: "~2s", quality: "Very good" },
|
|
18040
|
+
"medium.en": { size: "1.5GB", lang: "English only", speed: "~5s", quality: "Excellent" },
|
|
18041
|
+
"medium": { size: "1.5GB", lang: "Multilingual", speed: "~6s", quality: "Excellent" },
|
|
18042
|
+
"large-v3-turbo": { size: "1.5GB", lang: "Multilingual", speed: "~4s", quality: "Best" }
|
|
18043
|
+
};
|
|
17890
18044
|
ELEVENLABS_VOICES = {
|
|
17891
18045
|
"21m00Tcm4TlvDq8ikWAM": { name: "Rachel", gender: "F" },
|
|
17892
18046
|
"EXAVITQu4vr4xnSDxMaL": { name: "Sarah", gender: "F" },
|
|
@@ -17902,13 +18056,14 @@ var init_stt = __esm({
|
|
|
17902
18056
|
"Samantha": { name: "Samantha", gender: "F" },
|
|
17903
18057
|
"Albert": { name: "Albert", gender: "M" }
|
|
17904
18058
|
};
|
|
18059
|
+
whisperCliAvailableCache = null;
|
|
17905
18060
|
}
|
|
17906
18061
|
});
|
|
17907
18062
|
|
|
17908
18063
|
// src/media/image-gen.ts
|
|
17909
|
-
import { mkdirSync as mkdirSync9, existsSync as
|
|
17910
|
-
import { writeFile } from "fs/promises";
|
|
17911
|
-
import { join as
|
|
18064
|
+
import { mkdirSync as mkdirSync9, existsSync as existsSync20, unlink as unlink3, readdir as readdir2, stat as stat2 } from "fs";
|
|
18065
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
18066
|
+
import { join as join19 } from "path";
|
|
17912
18067
|
async function generateImage(prompt) {
|
|
17913
18068
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
17914
18069
|
if (!apiKey) {
|
|
@@ -17955,14 +18110,14 @@ async function generateImage(prompt) {
|
|
|
17955
18110
|
if (!imageData) {
|
|
17956
18111
|
throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
|
|
17957
18112
|
}
|
|
17958
|
-
if (!
|
|
18113
|
+
if (!existsSync20(IMAGE_OUTPUT_DIR)) {
|
|
17959
18114
|
mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
|
|
17960
18115
|
}
|
|
17961
18116
|
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
17962
18117
|
const filename = `img_${Date.now()}.${ext}`;
|
|
17963
|
-
const filePath =
|
|
18118
|
+
const filePath = join19(IMAGE_OUTPUT_DIR, filename);
|
|
17964
18119
|
const buffer = Buffer.from(imageData, "base64");
|
|
17965
|
-
await
|
|
18120
|
+
await writeFile2(filePath, buffer);
|
|
17966
18121
|
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
17967
18122
|
return { filePath, text: textResponse, mimeType };
|
|
17968
18123
|
}
|
|
@@ -17978,7 +18133,7 @@ function cleanupGeneratedImage(filePath) {
|
|
|
17978
18133
|
function pruneImageCache() {
|
|
17979
18134
|
readdir2(IMAGE_OUTPUT_DIR, (err, files) => {
|
|
17980
18135
|
if (err || !files) return;
|
|
17981
|
-
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) =>
|
|
18136
|
+
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) => join19(IMAGE_OUTPUT_DIR, f));
|
|
17982
18137
|
if (imageFiles.length === 0) return;
|
|
17983
18138
|
const now = Date.now();
|
|
17984
18139
|
let statsPending = imageFiles.length;
|
|
@@ -18010,8 +18165,8 @@ var init_image_gen = __esm({
|
|
|
18010
18165
|
MAX_GENERATED_IMAGES = 20;
|
|
18011
18166
|
IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
18012
18167
|
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
18013
|
-
IMAGE_OUTPUT_DIR =
|
|
18014
|
-
process.env.CC_CLAW_HOME ??
|
|
18168
|
+
IMAGE_OUTPUT_DIR = join19(
|
|
18169
|
+
process.env.CC_CLAW_HOME ?? join19(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
18015
18170
|
"data",
|
|
18016
18171
|
"images"
|
|
18017
18172
|
);
|
|
@@ -18449,22 +18604,22 @@ var init_video = __esm({
|
|
|
18449
18604
|
});
|
|
18450
18605
|
|
|
18451
18606
|
// src/router/media.ts
|
|
18452
|
-
import { join as
|
|
18453
|
-
import { mkdir as
|
|
18607
|
+
import { join as join20 } from "path";
|
|
18608
|
+
import { mkdir as mkdir3, writeFile as writeFile3, readdir as readdir3, stat as stat3, unlink as unlink4 } from "fs/promises";
|
|
18454
18609
|
function getMediaRetentionMs() {
|
|
18455
18610
|
const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
|
|
18456
18611
|
return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
|
|
18457
18612
|
}
|
|
18458
18613
|
async function saveMedia(buffer, prefix, ext) {
|
|
18459
|
-
await
|
|
18614
|
+
await mkdir3(MEDIA_INCOMING_PATH, { recursive: true });
|
|
18460
18615
|
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
18461
|
-
const fullPath =
|
|
18462
|
-
await
|
|
18616
|
+
const fullPath = join20(MEDIA_INCOMING_PATH, filename);
|
|
18617
|
+
await writeFile3(fullPath, buffer);
|
|
18463
18618
|
return fullPath;
|
|
18464
18619
|
}
|
|
18465
18620
|
async function cleanupOldMedia() {
|
|
18466
18621
|
try {
|
|
18467
|
-
await
|
|
18622
|
+
await mkdir3(MEDIA_INCOMING_PATH, { recursive: true });
|
|
18468
18623
|
const retentionMs = getMediaRetentionMs();
|
|
18469
18624
|
const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
|
|
18470
18625
|
const files = await readdir3(MEDIA_INCOMING_PATH);
|
|
@@ -18472,7 +18627,7 @@ async function cleanupOldMedia() {
|
|
|
18472
18627
|
let removed = 0;
|
|
18473
18628
|
for (const file of files) {
|
|
18474
18629
|
try {
|
|
18475
|
-
const filePath =
|
|
18630
|
+
const filePath = join20(MEDIA_INCOMING_PATH, file);
|
|
18476
18631
|
const s = await stat3(filePath);
|
|
18477
18632
|
if (now - s.mtimeMs > retentionMs) {
|
|
18478
18633
|
await unlink4(filePath);
|
|
@@ -18494,9 +18649,11 @@ async function handleVoice(msg, channel) {
|
|
|
18494
18649
|
return;
|
|
18495
18650
|
}
|
|
18496
18651
|
const audioBuffer = await channel.downloadFile(fileName);
|
|
18497
|
-
const transcript = await transcribeAudio(audioBuffer)
|
|
18652
|
+
const transcript = await transcribeAudio(audioBuffer, chatId, async (msg2) => {
|
|
18653
|
+
await channel.sendText(chatId, msg2, { parseMode: "plain" });
|
|
18654
|
+
});
|
|
18498
18655
|
if (!transcript) {
|
|
18499
|
-
await channel.sendText(chatId, "Couldn't transcribe the voice message.", { parseMode: "plain" });
|
|
18656
|
+
await channel.sendText(chatId, "Couldn't transcribe the voice message. Make sure a transcription provider is configured via /voice.", { parseMode: "plain" });
|
|
18500
18657
|
return;
|
|
18501
18658
|
}
|
|
18502
18659
|
const vBackendId = getBackend(chatId) ?? "claude";
|
|
@@ -18746,7 +18903,7 @@ var init_media = __esm({
|
|
|
18746
18903
|
init_helpers();
|
|
18747
18904
|
init_response();
|
|
18748
18905
|
init_live_status();
|
|
18749
|
-
MEDIA_INCOMING_PATH =
|
|
18906
|
+
MEDIA_INCOMING_PATH = join20(MEDIA_PATH, "incoming");
|
|
18750
18907
|
}
|
|
18751
18908
|
});
|
|
18752
18909
|
|
|
@@ -19058,9 +19215,9 @@ var install_exports = {};
|
|
|
19058
19215
|
__export(install_exports, {
|
|
19059
19216
|
installSkillFromGitHub: () => installSkillFromGitHub
|
|
19060
19217
|
});
|
|
19061
|
-
import { mkdir as
|
|
19062
|
-
import { existsSync as
|
|
19063
|
-
import { join as
|
|
19218
|
+
import { mkdir as mkdir4, readdir as readdir4, readFile as readFile4, cp } from "fs/promises";
|
|
19219
|
+
import { existsSync as existsSync21 } from "fs";
|
|
19220
|
+
import { join as join21, basename as basename2 } from "path";
|
|
19064
19221
|
import { execSync as execSync4 } from "child_process";
|
|
19065
19222
|
async function installSkillFromGitHub(urlOrShorthand) {
|
|
19066
19223
|
let repoUrl;
|
|
@@ -19071,36 +19228,36 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
19071
19228
|
}
|
|
19072
19229
|
repoUrl = parsed.cloneUrl;
|
|
19073
19230
|
subPath = parsed.subPath;
|
|
19074
|
-
const tmpDir =
|
|
19231
|
+
const tmpDir = join21("/tmp", `cc-claw-skill-${Date.now()}`);
|
|
19075
19232
|
try {
|
|
19076
19233
|
log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
|
|
19077
19234
|
execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
19078
19235
|
stdio: "pipe",
|
|
19079
19236
|
timeout: 3e4
|
|
19080
19237
|
});
|
|
19081
|
-
if (!
|
|
19238
|
+
if (!existsSync21(join21(tmpDir, ".git"))) {
|
|
19082
19239
|
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
19083
19240
|
}
|
|
19084
|
-
const searchRoot = subPath ?
|
|
19241
|
+
const searchRoot = subPath ? join21(tmpDir, subPath) : tmpDir;
|
|
19085
19242
|
const skillDir = await findSkillDir(searchRoot);
|
|
19086
19243
|
if (!skillDir) {
|
|
19087
19244
|
return { success: false, error: "No SKILL.md found in the repository." };
|
|
19088
19245
|
}
|
|
19089
19246
|
const skillFolderName = basename2(skillDir);
|
|
19090
|
-
const destDir =
|
|
19091
|
-
if (
|
|
19247
|
+
const destDir = join21(SKILLS_PATH, skillFolderName);
|
|
19248
|
+
if (existsSync21(destDir)) {
|
|
19092
19249
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
19093
19250
|
}
|
|
19094
|
-
await
|
|
19251
|
+
await mkdir4(destDir, { recursive: true });
|
|
19095
19252
|
await cp(skillDir, destDir, { recursive: true });
|
|
19096
19253
|
let skillName = skillFolderName;
|
|
19097
19254
|
try {
|
|
19098
|
-
const content = await readFile4(
|
|
19255
|
+
const content = await readFile4(join21(destDir, "SKILL.md"), "utf-8");
|
|
19099
19256
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
19100
19257
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
19101
19258
|
} catch {
|
|
19102
19259
|
try {
|
|
19103
|
-
const content = await readFile4(
|
|
19260
|
+
const content = await readFile4(join21(destDir, "skill.md"), "utf-8");
|
|
19104
19261
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
19105
19262
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
19106
19263
|
} catch {
|
|
@@ -19135,15 +19292,15 @@ function parseGitHubUrl(input) {
|
|
|
19135
19292
|
async function findSkillDir(root) {
|
|
19136
19293
|
const candidates = ["SKILL.md", "skill.md"];
|
|
19137
19294
|
for (const c of candidates) {
|
|
19138
|
-
if (
|
|
19295
|
+
if (existsSync21(join21(root, c))) return root;
|
|
19139
19296
|
}
|
|
19140
19297
|
try {
|
|
19141
19298
|
const entries = await readdir4(root, { withFileTypes: true });
|
|
19142
19299
|
for (const entry of entries) {
|
|
19143
19300
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19144
19301
|
for (const c of candidates) {
|
|
19145
|
-
if (
|
|
19146
|
-
return
|
|
19302
|
+
if (existsSync21(join21(root, entry.name, c))) {
|
|
19303
|
+
return join21(root, entry.name);
|
|
19147
19304
|
}
|
|
19148
19305
|
}
|
|
19149
19306
|
}
|
|
@@ -19155,15 +19312,15 @@ async function findSkillDir(root) {
|
|
|
19155
19312
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
19156
19313
|
let subEntries;
|
|
19157
19314
|
try {
|
|
19158
|
-
subEntries = await readdir4(
|
|
19315
|
+
subEntries = await readdir4(join21(root, entry.name), { withFileTypes: true });
|
|
19159
19316
|
} catch {
|
|
19160
19317
|
continue;
|
|
19161
19318
|
}
|
|
19162
19319
|
for (const sub of subEntries) {
|
|
19163
19320
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
19164
19321
|
for (const c of candidates) {
|
|
19165
|
-
if (
|
|
19166
|
-
return
|
|
19322
|
+
if (existsSync21(join21(root, entry.name, sub.name, c))) {
|
|
19323
|
+
return join21(root, entry.name, sub.name);
|
|
19167
19324
|
}
|
|
19168
19325
|
}
|
|
19169
19326
|
}
|
|
@@ -19190,7 +19347,7 @@ __export(discover_exports, {
|
|
|
19190
19347
|
import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
|
|
19191
19348
|
import { createHash } from "crypto";
|
|
19192
19349
|
import { homedir as homedir5 } from "os";
|
|
19193
|
-
import { join as
|
|
19350
|
+
import { join as join22 } from "path";
|
|
19194
19351
|
function invalidateSkillCache() {
|
|
19195
19352
|
cachedSkills = null;
|
|
19196
19353
|
cacheTimestamp = 0;
|
|
@@ -19208,7 +19365,7 @@ async function discoverAllSkills() {
|
|
|
19208
19365
|
const rawSkills = [];
|
|
19209
19366
|
rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
|
|
19210
19367
|
for (const backendId of getAllBackendIds()) {
|
|
19211
|
-
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [
|
|
19368
|
+
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join22(homedir5(), `.${backendId}`, "skills")];
|
|
19212
19369
|
for (const dir of dirs) {
|
|
19213
19370
|
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
19214
19371
|
}
|
|
@@ -19236,7 +19393,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
19236
19393
|
let content;
|
|
19237
19394
|
let resolvedPath;
|
|
19238
19395
|
for (const candidate of SKILL_FILE_CANDIDATES) {
|
|
19239
|
-
const p =
|
|
19396
|
+
const p = join22(skillsDir, entry.name, candidate);
|
|
19240
19397
|
try {
|
|
19241
19398
|
content = await readFile5(p, "utf-8");
|
|
19242
19399
|
resolvedPath = p;
|
|
@@ -19330,15 +19487,15 @@ var init_discover = __esm({
|
|
|
19330
19487
|
init_backends();
|
|
19331
19488
|
SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
|
|
19332
19489
|
BACKEND_SKILL_DIRS = {
|
|
19333
|
-
claude: [
|
|
19334
|
-
gemini: [
|
|
19490
|
+
claude: [join22(homedir5(), ".claude", "skills")],
|
|
19491
|
+
gemini: [join22(homedir5(), ".gemini", "skills")],
|
|
19335
19492
|
codex: [
|
|
19336
|
-
|
|
19337
|
-
|
|
19493
|
+
join22(homedir5(), ".agents", "skills"),
|
|
19494
|
+
join22(homedir5(), ".codex", "skills")
|
|
19338
19495
|
],
|
|
19339
19496
|
cursor: [
|
|
19340
|
-
|
|
19341
|
-
|
|
19497
|
+
join22(homedir5(), ".cursor", "skills"),
|
|
19498
|
+
join22(homedir5(), ".cursor", "skills-cursor")
|
|
19342
19499
|
]
|
|
19343
19500
|
};
|
|
19344
19501
|
CACHE_TTL_MS2 = 3e5;
|
|
@@ -19696,43 +19853,76 @@ async function sendVoiceConfigKeyboard(chatId, channel) {
|
|
|
19696
19853
|
await channel.sendText(chatId, "Voice configuration requires an interactive channel (Telegram).", { parseMode: "plain" });
|
|
19697
19854
|
return;
|
|
19698
19855
|
}
|
|
19699
|
-
const
|
|
19700
|
-
const
|
|
19701
|
-
const
|
|
19702
|
-
const
|
|
19703
|
-
Provider: ${providerLabel}
|
|
19704
|
-
Voice: ${currentVoiceName}
|
|
19705
|
-
Status: ${config2.enabled ? "ON" : "OFF"}`;
|
|
19856
|
+
const ttsConfig = getVoiceConfig(chatId);
|
|
19857
|
+
const sttProvider = getSttProvider(chatId);
|
|
19858
|
+
const sttModel = getSttModel(chatId);
|
|
19859
|
+
const whisperAvailable = isWhisperCliAvailable();
|
|
19706
19860
|
const buttons = [];
|
|
19861
|
+
const groqAvailable = !!process.env.GROQ_API_KEY;
|
|
19707
19862
|
buttons.push([
|
|
19708
|
-
{
|
|
19709
|
-
|
|
19710
|
-
|
|
19863
|
+
{
|
|
19864
|
+
label: `${sttProvider === "groq" ? "\u2713 " : ""}\u{1F310} Groq${!groqAvailable ? " (no key)" : ""}`,
|
|
19865
|
+
data: "vcfg:stt:groq",
|
|
19866
|
+
...sttProvider === "groq" ? { style: "primary" } : {}
|
|
19867
|
+
},
|
|
19868
|
+
{
|
|
19869
|
+
label: `${sttProvider === "local-whisper" ? "\u2713 " : ""}\u{1F4BB} Local Whisper${!whisperAvailable ? " (install first)" : ""}`,
|
|
19870
|
+
data: "vcfg:stt:local-whisper",
|
|
19871
|
+
...sttProvider === "local-whisper" ? { style: "primary" } : {}
|
|
19872
|
+
}
|
|
19711
19873
|
]);
|
|
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
|
-
})));
|
|
19874
|
+
if (sttProvider === "local-whisper" || whisperAvailable) {
|
|
19875
|
+
const modelEntries = Object.entries(LOCAL_WHISPER_MODELS);
|
|
19876
|
+
for (let i = 0; i < modelEntries.length; i += 2) {
|
|
19877
|
+
const row = modelEntries.slice(i, i + 2).map(([id, info]) => {
|
|
19878
|
+
const downloaded = isWhisperModelDownloaded(id);
|
|
19879
|
+
const active = sttModel === id;
|
|
19880
|
+
return {
|
|
19881
|
+
label: `${active ? "\u2713 " : ""}${id} ${downloaded ? "\u25CF" : "\u25CB"} ${info.size}`,
|
|
19882
|
+
data: `vcfg:stt-model:${id}`,
|
|
19883
|
+
...active ? { style: "primary" } : {}
|
|
19884
|
+
};
|
|
19885
|
+
});
|
|
19886
|
+
buttons.push(row);
|
|
19887
|
+
}
|
|
19735
19888
|
}
|
|
19889
|
+
buttons.push([
|
|
19890
|
+
{
|
|
19891
|
+
label: `${!ttsConfig.enabled ? "\u2713 " : ""}\u{1F507} Replies Off`,
|
|
19892
|
+
data: "voice:off",
|
|
19893
|
+
...!ttsConfig.enabled ? { style: "danger" } : {}
|
|
19894
|
+
},
|
|
19895
|
+
{
|
|
19896
|
+
label: `${ttsConfig.enabled ? "\u2713 " : ""}\u{1F50A} Replies On`,
|
|
19897
|
+
data: "voice:on",
|
|
19898
|
+
...ttsConfig.enabled ? { style: "success" } : {}
|
|
19899
|
+
}
|
|
19900
|
+
]);
|
|
19901
|
+
buttons.push([
|
|
19902
|
+
{ label: `${ttsConfig.provider === "elevenlabs" ? "\u2713 " : ""}ElevenLabs`, data: "vcfg:p:elevenlabs", ...ttsConfig.provider === "elevenlabs" ? { style: "primary" } : {} },
|
|
19903
|
+
{ label: `${ttsConfig.provider === "grok" ? "\u2713 " : ""}Grok`, data: "vcfg:p:grok", ...ttsConfig.provider === "grok" ? { style: "primary" } : {} },
|
|
19904
|
+
{ label: `${ttsConfig.provider === "macos" ? "\u2713 " : ""}macOS`, data: "vcfg:p:macos", ...ttsConfig.provider === "macos" ? { style: "primary" } : {} }
|
|
19905
|
+
]);
|
|
19906
|
+
if (ttsConfig.enabled) {
|
|
19907
|
+
if (ttsConfig.provider === "elevenlabs") {
|
|
19908
|
+
const entries = Object.entries(ELEVENLABS_VOICES);
|
|
19909
|
+
const female = entries.filter(([, v]) => v.gender === "F");
|
|
19910
|
+
const male = entries.filter(([, v]) => v.gender === "M");
|
|
19911
|
+
buttons.push(female.map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
19912
|
+
buttons.push(male.map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
19913
|
+
} else if (ttsConfig.provider === "grok") {
|
|
19914
|
+
buttons.push(GROK_VOICES.map((v) => ({ label: `${ttsConfig.voiceId === v ? "\u2713 " : ""}${capitalize(v)}`, data: `vcfg:v:${v}` })));
|
|
19915
|
+
} else {
|
|
19916
|
+
buttons.push(Object.entries(MACOS_VOICES).map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
19917
|
+
}
|
|
19918
|
+
}
|
|
19919
|
+
const sttLabel = sttProvider === "groq" ? "Groq (cloud)" : `Local Whisper \xB7 ${sttModel}`;
|
|
19920
|
+
const ttsLabel = ttsConfig.enabled ? `${ttsConfig.provider === "grok" ? "Grok" : ttsConfig.provider === "macos" ? "macOS" : "ElevenLabs"} replies ON` : "Replies OFF";
|
|
19921
|
+
const modelLegend = sttProvider === "local-whisper" || whisperAvailable ? "\n\u25CF = downloaded \u25CB = not yet downloaded" : "";
|
|
19922
|
+
const header2 = `\u{1F399}\uFE0F Voice Settings
|
|
19923
|
+
|
|
19924
|
+
\u{1F3A4} Transcription: ${sttLabel}
|
|
19925
|
+
\u{1F50A} Text-to-Speech: ${ttsLabel}${modelLegend}`;
|
|
19736
19926
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
19737
19927
|
}
|
|
19738
19928
|
async function sendSkillsPage(chatId, channel, skills2, page, messageId) {
|
|
@@ -19829,28 +20019,14 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
19829
20019
|
const backendDisplay = config2?.backend ? capitalize(config2.backend) : "Default";
|
|
19830
20020
|
const modelDisplay = config2?.model ?? "default";
|
|
19831
20021
|
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}`);
|
|
20022
|
+
const lastBeat = config2?.lastBeatAt ? config2.lastBeatAt.replace("T", " ").slice(0, 16) : "never";
|
|
20023
|
+
const watchNote = watches.length > 0 ? ` \xB7 ${watches.length} watch${watches.length !== 1 ? "es" : ""}` : "";
|
|
20024
|
+
const fallbackNote = fallbacks.length > 0 ? `Fallbacks: ${fallbacks.map((f) => `${capitalize(f.backend)}${f.model ? ` (${shortModelName(f.model)})` : ""}`).join(" \u2192 ")}` : "";
|
|
20025
|
+
const header2 = [
|
|
20026
|
+
`\u{1FAC0} Heartbeat \u2014 ${enabled ? "ON" : "OFF"} \xB7 Every ${intervalMin} min \xB7 ${activeStart}\u2013${activeEnd}${watchNote}`,
|
|
20027
|
+
`Backend: ${backendDisplay} \xB7 Model: ${modelDisplay} \xB7 Last: ${lastBeat}`,
|
|
20028
|
+
fallbackNote
|
|
20029
|
+
].filter(Boolean).join("\n");
|
|
19854
20030
|
const buttons = [];
|
|
19855
20031
|
buttons.push([
|
|
19856
20032
|
{ label: `${enabled ? "\u2713 " : ""}On`, data: "hb:on", ...enabled ? { style: "success" } : {} },
|
|
@@ -19864,63 +20040,62 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
19864
20040
|
...m === intervalMin ? { style: "primary" } : {}
|
|
19865
20041
|
})));
|
|
19866
20042
|
const available = getAvailableBackendIds();
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
20043
|
+
const backendRow = [
|
|
20044
|
+
{
|
|
20045
|
+
label: `${!config2?.backend ? "\u2713 " : ""}Default`,
|
|
20046
|
+
data: "hb:backend:default",
|
|
20047
|
+
...!config2?.backend ? { style: "primary" } : {}
|
|
20048
|
+
},
|
|
20049
|
+
...available.map((bid) => ({
|
|
20050
|
+
label: `${config2?.backend === bid ? "\u2713 " : ""}${capitalize(bid)}`,
|
|
20051
|
+
data: `hb:backend:${bid}`,
|
|
20052
|
+
...config2?.backend === bid ? { style: "primary" } : {}
|
|
20053
|
+
}))
|
|
20054
|
+
];
|
|
19876
20055
|
buttons.push(backendRow);
|
|
19877
20056
|
const targetBackend = config2?.backend ?? getBackend(chatId) ?? "claude";
|
|
19878
20057
|
try {
|
|
19879
20058
|
const adapter = getAdapter(targetBackend);
|
|
19880
20059
|
const models = Object.entries(adapter.availableModels);
|
|
19881
20060
|
if (models.length > 0) {
|
|
19882
|
-
const modelRow =
|
|
19883
|
-
|
|
19884
|
-
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
20061
|
+
const modelRow = [
|
|
20062
|
+
{
|
|
20063
|
+
label: `${!config2?.model ? "\u2713 " : ""}Default`,
|
|
20064
|
+
data: "hb:model:default",
|
|
20065
|
+
...!config2?.model ? { style: "primary" } : {}
|
|
20066
|
+
},
|
|
20067
|
+
...models.slice(0, 4).map(([key]) => ({
|
|
20068
|
+
label: `${config2?.model === key ? "\u2713 " : ""}${shortModelName(key)}`,
|
|
20069
|
+
data: `hb:model:${key}`,
|
|
20070
|
+
...config2?.model === key ? { style: "primary" } : {}
|
|
20071
|
+
}))
|
|
20072
|
+
];
|
|
19890
20073
|
buttons.push(modelRow);
|
|
19891
20074
|
}
|
|
19892
20075
|
} catch {
|
|
19893
20076
|
}
|
|
19894
20077
|
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
20078
|
if (fallbacks.length > 0) {
|
|
19899
20079
|
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" }
|
|
20080
|
+
{ label: `\u{1F504} ${fallbacks.map((f) => capitalize(f.backend)).join(" \u2192 ")}`, data: "hb:noop" },
|
|
20081
|
+
{ label: "\u2715 Clear", data: "hb:fb:clear", style: "danger" }
|
|
19905
20082
|
]);
|
|
19906
20083
|
}
|
|
19907
20084
|
if (fbCandidates.length > 0) {
|
|
19908
20085
|
buttons.push(fbCandidates.slice(0, 4).map((bid) => ({
|
|
19909
|
-
label: `+ ${capitalize(bid)}`,
|
|
20086
|
+
label: `+ ${capitalize(bid)} fallback`,
|
|
19910
20087
|
data: `hb:fb:add:${bid}`
|
|
19911
20088
|
})));
|
|
19912
20089
|
}
|
|
19913
|
-
const optionsRow = [
|
|
19914
|
-
{ label: `\u{1F4AD} Thinking: ${thinkingDisplay}`, data: "hb:thinking" }
|
|
19915
|
-
];
|
|
20090
|
+
const optionsRow = [];
|
|
19916
20091
|
if (watches.length > 0) {
|
|
19917
20092
|
optionsRow.push({ label: `\u{1F441} Watches (${watches.length})`, data: "hb:watches" });
|
|
19918
20093
|
} else {
|
|
19919
|
-
optionsRow.push({ label: "
|
|
20094
|
+
optionsRow.push({ label: "\u{1F441} Add Watch", data: "hb:addwatch" });
|
|
19920
20095
|
}
|
|
20096
|
+
optionsRow.push({ label: `\u23F0 Hours: ${activeStart}\u2013${activeEnd}`, data: "hb:noop" });
|
|
19921
20097
|
buttons.push(optionsRow);
|
|
19922
|
-
|
|
19923
|
-
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
20098
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
19924
20099
|
}
|
|
19925
20100
|
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
19926
20101
|
const memories = listMemories();
|
|
@@ -20569,13 +20744,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
20569
20744
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
20570
20745
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
20571
20746
|
if (current === "frozen") {
|
|
20572
|
-
const { readFileSync:
|
|
20573
|
-
const { join:
|
|
20747
|
+
const { readFileSync: readFileSync30, existsSync: existsSync59 } = await import("fs");
|
|
20748
|
+
const { join: join38 } = await import("path");
|
|
20574
20749
|
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 =
|
|
20750
|
+
const soulPath = join38(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
20751
|
+
const userPath = join38(CC_CLAW_HOME3, "identity/USER.md");
|
|
20752
|
+
const soul = existsSync59(soulPath) ? readFileSync30(soulPath, "utf-8") : "";
|
|
20753
|
+
const user = existsSync59(userPath) ? readFileSync30(userPath, "utf-8") : "";
|
|
20579
20754
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
20580
20755
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
20581
20756
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -20652,11 +20827,11 @@ var init_evolve2 = __esm({
|
|
|
20652
20827
|
});
|
|
20653
20828
|
|
|
20654
20829
|
// src/optimizer/identity-audit.ts
|
|
20655
|
-
import { readFileSync as
|
|
20656
|
-
import { join as
|
|
20830
|
+
import { readFileSync as readFileSync12, existsSync as existsSync22, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
20831
|
+
import { join as join23 } from "path";
|
|
20657
20832
|
function readIdentityFile2(filename) {
|
|
20658
20833
|
try {
|
|
20659
|
-
return
|
|
20834
|
+
return readFileSync12(join23(IDENTITY_PATH, filename), "utf-8");
|
|
20660
20835
|
} catch {
|
|
20661
20836
|
return "";
|
|
20662
20837
|
}
|
|
@@ -20671,13 +20846,13 @@ function getMtime(filepath) {
|
|
|
20671
20846
|
function findBackupFiles() {
|
|
20672
20847
|
const backups = [];
|
|
20673
20848
|
const dirs = [IDENTITY_PATH];
|
|
20674
|
-
const contextDir =
|
|
20675
|
-
if (
|
|
20849
|
+
const contextDir = join23(IDENTITY_PATH, "..", "workspace", "context");
|
|
20850
|
+
if (existsSync22(contextDir)) dirs.push(contextDir);
|
|
20676
20851
|
for (const dir of dirs) {
|
|
20677
20852
|
try {
|
|
20678
20853
|
for (const entry of readdirSync10(dir)) {
|
|
20679
20854
|
if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
|
|
20680
|
-
backups.push(
|
|
20855
|
+
backups.push(join23(dir, entry));
|
|
20681
20856
|
}
|
|
20682
20857
|
}
|
|
20683
20858
|
} catch {
|
|
@@ -20698,9 +20873,9 @@ function computeIdentityStats(pendingProposals, driftPercent) {
|
|
|
20698
20873
|
userChars,
|
|
20699
20874
|
ccClawChars,
|
|
20700
20875
|
boilerplateChars,
|
|
20701
|
-
soulMtime: getMtime(
|
|
20702
|
-
userMtime: getMtime(
|
|
20703
|
-
ccClawMtime: getMtime(
|
|
20876
|
+
soulMtime: getMtime(join23(IDENTITY_PATH, "SOUL.md")),
|
|
20877
|
+
userMtime: getMtime(join23(IDENTITY_PATH, "USER.md")),
|
|
20878
|
+
ccClawMtime: getMtime(join23(IDENTITY_PATH, "CC-CLAW.md")),
|
|
20704
20879
|
backupFiles: findBackupFiles(),
|
|
20705
20880
|
estimatedTokens: Math.ceil(ccClawChars / 4),
|
|
20706
20881
|
pendingEvolveProposals: pendingProposals,
|
|
@@ -20809,8 +20984,8 @@ var init_identity_audit = __esm({
|
|
|
20809
20984
|
});
|
|
20810
20985
|
|
|
20811
20986
|
// src/optimizer/skill-audit.ts
|
|
20812
|
-
import { readFileSync as
|
|
20813
|
-
import { join as
|
|
20987
|
+
import { readFileSync as readFileSync13, existsSync as existsSync23 } from "fs";
|
|
20988
|
+
import { join as join24, basename as basename3 } from "path";
|
|
20814
20989
|
function parseFrontmatter3(content) {
|
|
20815
20990
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
20816
20991
|
if (!fmMatch) return {};
|
|
@@ -20846,10 +21021,10 @@ function detectDependentSkills(content) {
|
|
|
20846
21021
|
return Array.from(deps);
|
|
20847
21022
|
}
|
|
20848
21023
|
function computeSkillStats(skillPath) {
|
|
20849
|
-
const content =
|
|
21024
|
+
const content = readFileSync13(skillPath, "utf-8");
|
|
20850
21025
|
const lines = content.split("\n");
|
|
20851
21026
|
return {
|
|
20852
|
-
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(
|
|
21027
|
+
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join24(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
20853
21028
|
skillPath,
|
|
20854
21029
|
lineCount: lines.length,
|
|
20855
21030
|
charCount: content.length,
|
|
@@ -20869,13 +21044,13 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
20869
21044
|
const results = [];
|
|
20870
21045
|
for (const name of depNames) {
|
|
20871
21046
|
const candidates = [
|
|
20872
|
-
|
|
20873
|
-
|
|
21047
|
+
join24(ccClawSkillsDir, name, "SKILL.md"),
|
|
21048
|
+
join24(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
20874
21049
|
];
|
|
20875
21050
|
for (const candidate of candidates) {
|
|
20876
|
-
if (
|
|
21051
|
+
if (existsSync23(candidate)) {
|
|
20877
21052
|
try {
|
|
20878
|
-
const content =
|
|
21053
|
+
const content = readFileSync13(candidate, "utf-8");
|
|
20879
21054
|
results.push({
|
|
20880
21055
|
name,
|
|
20881
21056
|
content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
|
|
@@ -20987,8 +21162,8 @@ __export(analyze_exports2, {
|
|
|
20987
21162
|
});
|
|
20988
21163
|
import { spawn as spawn7 } from "child_process";
|
|
20989
21164
|
import { createInterface as createInterface7 } from "readline";
|
|
20990
|
-
import { readFileSync as
|
|
20991
|
-
import { join as
|
|
21165
|
+
import { readFileSync as readFileSync14, existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
|
|
21166
|
+
import { join as join25 } from "path";
|
|
20992
21167
|
import { homedir as homedir7 } from "os";
|
|
20993
21168
|
function parseOptimizeOutput(raw, validAreas) {
|
|
20994
21169
|
if (!raw || raw.includes("NO_FINDINGS")) return [];
|
|
@@ -21118,20 +21293,20 @@ function getModelDisplayInfo(chatId) {
|
|
|
21118
21293
|
}
|
|
21119
21294
|
function readIdentityFile3(filename) {
|
|
21120
21295
|
try {
|
|
21121
|
-
return
|
|
21296
|
+
return readFileSync14(join25(IDENTITY_PATH, filename), "utf-8");
|
|
21122
21297
|
} catch {
|
|
21123
21298
|
return "";
|
|
21124
21299
|
}
|
|
21125
21300
|
}
|
|
21126
21301
|
function loadContextFiles2() {
|
|
21127
|
-
const contextDir =
|
|
21302
|
+
const contextDir = join25(homedir7(), ".cc-claw", "workspace", "context");
|
|
21128
21303
|
const results = [];
|
|
21129
|
-
if (!
|
|
21304
|
+
if (!existsSync24(contextDir)) return results;
|
|
21130
21305
|
try {
|
|
21131
21306
|
for (const entry of readdirSync12(contextDir)) {
|
|
21132
21307
|
if (!entry.endsWith(".md")) continue;
|
|
21133
21308
|
try {
|
|
21134
|
-
const content =
|
|
21309
|
+
const content = readFileSync14(join25(contextDir, entry), "utf-8");
|
|
21135
21310
|
results.push({ name: entry, content });
|
|
21136
21311
|
} catch {
|
|
21137
21312
|
}
|
|
@@ -21182,8 +21357,8 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
21182
21357
|
const stats = computeSkillStats(skillPath);
|
|
21183
21358
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
21184
21359
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
21185
|
-
const ccClawSkillsDir =
|
|
21186
|
-
const skillContent =
|
|
21360
|
+
const ccClawSkillsDir = join25(homedir7(), ".cc-claw", "workspace", "skills");
|
|
21361
|
+
const skillContent = readFileSync14(skillPath, "utf-8");
|
|
21187
21362
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
21188
21363
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
21189
21364
|
const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
|
|
@@ -21197,16 +21372,16 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
21197
21372
|
};
|
|
21198
21373
|
}
|
|
21199
21374
|
function listCcClawSkills() {
|
|
21200
|
-
const skillsDir =
|
|
21375
|
+
const skillsDir = join25(homedir7(), ".cc-claw", "workspace", "skills");
|
|
21201
21376
|
const entries = [];
|
|
21202
|
-
if (!
|
|
21377
|
+
if (!existsSync24(skillsDir)) return entries;
|
|
21203
21378
|
try {
|
|
21204
21379
|
for (const dir of readdirSync12(skillsDir)) {
|
|
21205
|
-
const skillFile =
|
|
21206
|
-
if (!
|
|
21380
|
+
const skillFile = join25(skillsDir, dir, "SKILL.md");
|
|
21381
|
+
if (!existsSync24(skillFile)) continue;
|
|
21207
21382
|
let description = "skill";
|
|
21208
21383
|
try {
|
|
21209
|
-
const content =
|
|
21384
|
+
const content = readFileSync14(skillFile, "utf-8");
|
|
21210
21385
|
const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
|
|
21211
21386
|
if (descMatch) description = descMatch[1].trim().slice(0, 60);
|
|
21212
21387
|
} catch {
|
|
@@ -21526,8 +21701,8 @@ var init_ui2 = __esm({
|
|
|
21526
21701
|
});
|
|
21527
21702
|
|
|
21528
21703
|
// src/router/optimize.ts
|
|
21529
|
-
import { readFileSync as
|
|
21530
|
-
import { join as
|
|
21704
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, existsSync as existsSync25, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
21705
|
+
import { join as join26, dirname as dirname4 } from "path";
|
|
21531
21706
|
import { homedir as homedir8 } from "os";
|
|
21532
21707
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
21533
21708
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
@@ -21708,7 +21883,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
21708
21883
|
} = await Promise.resolve().then(() => (init_ui2(), ui_exports));
|
|
21709
21884
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
21710
21885
|
if (!modelInfo) return;
|
|
21711
|
-
const skillPath =
|
|
21886
|
+
const skillPath = join26(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
|
|
21712
21887
|
const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
|
|
21713
21888
|
chatId,
|
|
21714
21889
|
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
@@ -21797,13 +21972,13 @@ async function applyFinding(chatId, channel, index) {
|
|
|
21797
21972
|
await showFinding(chatId, channel, index + 1);
|
|
21798
21973
|
return;
|
|
21799
21974
|
}
|
|
21800
|
-
if (!
|
|
21975
|
+
if (!existsSync25(targetPath)) {
|
|
21801
21976
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
21802
21977
|
session2.skipped.push(index);
|
|
21803
21978
|
await showFinding(chatId, channel, index + 1);
|
|
21804
21979
|
return;
|
|
21805
21980
|
}
|
|
21806
|
-
const original =
|
|
21981
|
+
const original = readFileSync15(targetPath, "utf-8");
|
|
21807
21982
|
const backupPath = targetPath + `.bak.${Date.now()}`;
|
|
21808
21983
|
writeFileSync7(backupPath, original, "utf-8");
|
|
21809
21984
|
pruneBackups2(targetPath);
|
|
@@ -21879,14 +22054,14 @@ async function finishReview(chatId, channel) {
|
|
|
21879
22054
|
activeSessions.delete(chatId);
|
|
21880
22055
|
}
|
|
21881
22056
|
function resolveTargetFile(location, auditTarget) {
|
|
21882
|
-
const ccClawHome =
|
|
22057
|
+
const ccClawHome = join26(homedir8(), ".cc-claw");
|
|
21883
22058
|
const filePart = location.split(":")[0]?.trim();
|
|
21884
22059
|
if (!filePart) return null;
|
|
21885
|
-
if (filePart === "SOUL.md") return
|
|
21886
|
-
if (filePart === "USER.md") return
|
|
21887
|
-
if (filePart === "CC-CLAW.md") return
|
|
22060
|
+
if (filePart === "SOUL.md") return join26(ccClawHome, "identity", "SOUL.md");
|
|
22061
|
+
if (filePart === "USER.md") return join26(ccClawHome, "identity", "USER.md");
|
|
22062
|
+
if (filePart === "CC-CLAW.md") return join26(ccClawHome, "identity", "CC-CLAW.md");
|
|
21888
22063
|
if (filePart === "SKILL.md" && auditTarget !== "identity") {
|
|
21889
|
-
return
|
|
22064
|
+
return join26(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
|
|
21890
22065
|
}
|
|
21891
22066
|
return null;
|
|
21892
22067
|
}
|
|
@@ -21894,7 +22069,7 @@ function pruneBackups2(absolutePath) {
|
|
|
21894
22069
|
const dir = dirname4(absolutePath);
|
|
21895
22070
|
const baseName = absolutePath.split("/").pop() ?? "";
|
|
21896
22071
|
try {
|
|
21897
|
-
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) =>
|
|
22072
|
+
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join26(dir, f));
|
|
21898
22073
|
while (backups.length > 3) {
|
|
21899
22074
|
const oldest = backups.shift();
|
|
21900
22075
|
try {
|
|
@@ -21928,8 +22103,8 @@ __export(auto_create_exports, {
|
|
|
21928
22103
|
saveSkill: () => saveSkill,
|
|
21929
22104
|
storePendingDraft: () => storePendingDraft
|
|
21930
22105
|
});
|
|
21931
|
-
import { join as
|
|
21932
|
-
import { writeFile as
|
|
22106
|
+
import { join as join27 } from "path";
|
|
22107
|
+
import { writeFile as writeFile5, mkdir as mkdir5 } from "fs/promises";
|
|
21933
22108
|
function isSkillWorthy(signals) {
|
|
21934
22109
|
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
21935
22110
|
if (toolUseCount < 12) return false;
|
|
@@ -22075,10 +22250,10 @@ function parseExtractedSkill(llmResponse) {
|
|
|
22075
22250
|
return { name, content };
|
|
22076
22251
|
}
|
|
22077
22252
|
async function saveSkill(name, content) {
|
|
22078
|
-
const dir =
|
|
22079
|
-
await
|
|
22080
|
-
const filePath =
|
|
22081
|
-
await
|
|
22253
|
+
const dir = join27(SKILLS_PATH, name);
|
|
22254
|
+
await mkdir5(dir, { recursive: true });
|
|
22255
|
+
const filePath = join27(dir, "SKILL.md");
|
|
22256
|
+
await writeFile5(filePath, content, "utf-8");
|
|
22082
22257
|
invalidateSkillCache();
|
|
22083
22258
|
log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
|
|
22084
22259
|
return { path: filePath };
|
|
@@ -22248,18 +22423,7 @@ async function handleStopCommand(chatId, commandArgs, msg, channel) {
|
|
|
22248
22423
|
}
|
|
22249
22424
|
}
|
|
22250
22425
|
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
|
-
}
|
|
22426
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
22263
22427
|
}
|
|
22264
22428
|
async function handleVoiceConfigCommand(chatId, commandArgs, msg, channel) {
|
|
22265
22429
|
await sendVoiceConfigKeyboard(chatId, channel);
|
|
@@ -22458,42 +22622,40 @@ Use /skills to see it.`, { parseMode: "plain" });
|
|
|
22458
22622
|
async function handleExtractSkillCommand(chatId, commandArgs, msg, channel) {
|
|
22459
22623
|
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
22460
22624
|
const sessionMessages = getLog2(chatId);
|
|
22625
|
+
const exchangeCount = Math.floor(sessionMessages.length / 2);
|
|
22461
22626
|
if (sessionMessages.length < 2) {
|
|
22462
22627
|
await channel.sendText(chatId, "No session history to extract from. Have a conversation first, then run /extract_skill.", { parseMode: "plain" });
|
|
22463
22628
|
return;
|
|
22464
22629
|
}
|
|
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
22630
|
if (typeof channel.sendKeyboard === "function") {
|
|
22484
|
-
await channel.sendText(chatId, `Extracted skill: "${extracted.name}"
|
|
22485
|
-
|
|
22486
|
-
${preview}`, { parseMode: "plain" });
|
|
22487
22631
|
await channel.sendKeyboard(
|
|
22488
22632
|
chatId,
|
|
22489
|
-
|
|
22633
|
+
`\u{1F9E0} Extract Reusable Skill
|
|
22634
|
+
|
|
22635
|
+
This will analyze your current session (${exchangeCount} exchange${exchangeCount !== 1 ? "s" : ""}) and generate a reusable skill file.
|
|
22636
|
+
|
|
22637
|
+
\u2022 The AI reviews your conversation to identify the workflow
|
|
22638
|
+
\u2022 Generates a structured SKILL.md you can reuse in future tasks
|
|
22639
|
+
\u2022 You'll preview it before anything is saved
|
|
22640
|
+
|
|
22641
|
+
Ready to start?`,
|
|
22490
22642
|
[[
|
|
22491
|
-
{ label: "\
|
|
22492
|
-
{ label: "\u2715
|
|
22643
|
+
{ label: "\u26A1 Start Extraction", data: "skill:start-extract", style: "success" },
|
|
22644
|
+
{ label: "\u2715 Cancel", data: "skill:discard" }
|
|
22493
22645
|
]]
|
|
22494
22646
|
);
|
|
22495
22647
|
} else {
|
|
22496
|
-
|
|
22648
|
+
await channel.sendText(chatId, `Reviewing session (${exchangeCount} exchanges)...`, { parseMode: "plain" });
|
|
22649
|
+
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22650
|
+
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
22651
|
+
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22652
|
+
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
22653
|
+
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
22654
|
+
const extracted = parseExtractedSkill2(response.text);
|
|
22655
|
+
if (!extracted) {
|
|
22656
|
+
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
22657
|
+
return;
|
|
22658
|
+
}
|
|
22497
22659
|
const result = await saveSkill2(extracted.name, extracted.content);
|
|
22498
22660
|
await channel.sendText(chatId, `Skill "${extracted.name}" saved to ${result.path}`, { parseMode: "plain" });
|
|
22499
22661
|
}
|
|
@@ -22604,6 +22766,22 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22604
22766
|
stopAllSideQuests(chatId);
|
|
22605
22767
|
const oldSessionId = getSessionId(chatId);
|
|
22606
22768
|
const exchangeCount = getMessagePairCount(chatId);
|
|
22769
|
+
const needsSummary = exchangeCount > 0;
|
|
22770
|
+
let ackMsgId;
|
|
22771
|
+
if (needsSummary) {
|
|
22772
|
+
ackMsgId = await channel.sendTextReturningId?.(
|
|
22773
|
+
chatId,
|
|
22774
|
+
`\u23F3 Archiving session (${exchangeCount} exchanges)...`,
|
|
22775
|
+
"plain"
|
|
22776
|
+
);
|
|
22777
|
+
if (!ackMsgId) {
|
|
22778
|
+
await channel.sendText(
|
|
22779
|
+
chatId,
|
|
22780
|
+
`\u23F3 Archiving session (${exchangeCount} exchanges)...`,
|
|
22781
|
+
{ parseMode: "plain" }
|
|
22782
|
+
);
|
|
22783
|
+
}
|
|
22784
|
+
}
|
|
22607
22785
|
const summarized = await summarizeSession(chatId);
|
|
22608
22786
|
clearSession(chatId);
|
|
22609
22787
|
clearChatPaidSlots(chatId);
|
|
@@ -22622,6 +22800,7 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22622
22800
|
const text = `\u2705 New session started. Previous session archived${exchangeCount > 0 ? ` (${exchangeCount} exchanges)` : ""}.
|
|
22623
22801
|
|
|
22624
22802
|
\u{1F9E0} ${backendLabel} \xB7 ${modelLabel}`;
|
|
22803
|
+
if (ackMsgId) await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
22625
22804
|
const kbMsgId = await channel.sendKeyboard(chatId, text, [
|
|
22626
22805
|
[
|
|
22627
22806
|
{ label: "Switch Backend", data: "menu:backend", style: "primary" },
|
|
@@ -22640,7 +22819,11 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
22640
22819
|
}
|
|
22641
22820
|
} else {
|
|
22642
22821
|
const text = summarized ? "Session summarized and saved. Fresh conversation started!" : "Fresh conversation started. What's on your mind?";
|
|
22643
|
-
|
|
22822
|
+
if (ackMsgId) {
|
|
22823
|
+
await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
22824
|
+
} else {
|
|
22825
|
+
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
22826
|
+
}
|
|
22644
22827
|
}
|
|
22645
22828
|
}
|
|
22646
22829
|
async function handleSummarizeCommand(chatId, commandArgs, msg, channel) {
|
|
@@ -24522,6 +24705,44 @@ ${plan.originalMessage}`;
|
|
|
24522
24705
|
if (current !== desired) toggleVoice(chatId);
|
|
24523
24706
|
await channel.sendText(chatId, desired ? "\u{1F50A} Voice responses enabled." : "\u{1F507} Voice responses disabled.", { parseMode: "plain" });
|
|
24524
24707
|
}
|
|
24708
|
+
} else if (data.startsWith("vcfg:stt-model:")) {
|
|
24709
|
+
const model2 = data.slice(15);
|
|
24710
|
+
if (!(model2 in LOCAL_WHISPER_MODELS)) {
|
|
24711
|
+
await channel.sendText(chatId, "Unknown model.", { parseMode: "plain" });
|
|
24712
|
+
return;
|
|
24713
|
+
}
|
|
24714
|
+
setSttProvider(chatId, "local-whisper");
|
|
24715
|
+
setSttModel(chatId, model2);
|
|
24716
|
+
const info = LOCAL_WHISPER_MODELS[model2];
|
|
24717
|
+
const downloaded = isWhisperModelDownloaded(model2);
|
|
24718
|
+
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}).
|
|
24719
|
+
|
|
24720
|
+
\u2B07\uFE0F Model will be downloaded on your first voice message (~${info.size}). This is a one-time download.`;
|
|
24721
|
+
await channel.sendText(chatId, notice, { parseMode: "plain" });
|
|
24722
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
24723
|
+
} else if (data.startsWith("vcfg:stt:")) {
|
|
24724
|
+
const provider = data.slice(9);
|
|
24725
|
+
if (provider === "local-whisper") {
|
|
24726
|
+
if (!isWhisperCliAvailable()) {
|
|
24727
|
+
await channel.sendText(
|
|
24728
|
+
chatId,
|
|
24729
|
+
"\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.",
|
|
24730
|
+
{ parseMode: "markdown" }
|
|
24731
|
+
);
|
|
24732
|
+
return;
|
|
24733
|
+
}
|
|
24734
|
+
}
|
|
24735
|
+
if (provider === "groq" && !process.env.GROQ_API_KEY) {
|
|
24736
|
+
await channel.sendText(
|
|
24737
|
+
chatId,
|
|
24738
|
+
"\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```",
|
|
24739
|
+
{ parseMode: "markdown" }
|
|
24740
|
+
);
|
|
24741
|
+
}
|
|
24742
|
+
setSttProvider(chatId, provider);
|
|
24743
|
+
const label2 = provider === "groq" ? "Groq (cloud)" : "Local Whisper";
|
|
24744
|
+
await channel.sendText(chatId, `\u2705 Transcription provider set to: ${label2}`, { parseMode: "plain" });
|
|
24745
|
+
await sendVoiceConfigKeyboard(chatId, channel);
|
|
24525
24746
|
} else if (data.startsWith("vcfg:")) {
|
|
24526
24747
|
const parts = data.slice(5).split(":");
|
|
24527
24748
|
const action = parts[0];
|
|
@@ -25429,15 +25650,16 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
25429
25650
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25430
25651
|
} else if (rest.startsWith("backend:")) {
|
|
25431
25652
|
const bid = rest.slice(8);
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25435
|
-
|
|
25436
|
-
}
|
|
25653
|
+
const newBackend = bid === "default" ? null : bid;
|
|
25654
|
+
updateHeartbeatConfig2({ backend: newBackend, model: null });
|
|
25655
|
+
updateHeartbeatField(chatId, "backend", newBackend);
|
|
25656
|
+
updateHeartbeatField(chatId, "model", null);
|
|
25437
25657
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25438
25658
|
} else if (rest.startsWith("model:")) {
|
|
25439
25659
|
const model2 = rest.slice(6);
|
|
25440
|
-
|
|
25660
|
+
const newModel = model2 === "default" ? null : model2;
|
|
25661
|
+
updateHeartbeatConfig2({ model: newModel });
|
|
25662
|
+
updateHeartbeatField(chatId, "model", newModel);
|
|
25441
25663
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
25442
25664
|
} else if (rest === "thinking") {
|
|
25443
25665
|
await channel.sendText(chatId, "Thinking level for heartbeat: use /editjob to configure.", { parseMode: "plain" });
|
|
@@ -25557,6 +25779,71 @@ Example: /limits ${bid} daily 500000`, { parseMode: "plain" });
|
|
|
25557
25779
|
const page = parseInt(data.slice(12), 10);
|
|
25558
25780
|
const skills2 = await discoverAllSkills();
|
|
25559
25781
|
await sendSkillsPage(chatId, channel, skills2, page, messageId);
|
|
25782
|
+
} else if (data === "skill:start-extract") {
|
|
25783
|
+
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
25784
|
+
const sessionMessages = getLog2(chatId);
|
|
25785
|
+
if (sessionMessages.length < 2) {
|
|
25786
|
+
if (messageId) await replaceWithText("No session history to extract from.");
|
|
25787
|
+
else await channel.sendText(chatId, "No session history to extract from.", { parseMode: "plain" });
|
|
25788
|
+
return;
|
|
25789
|
+
}
|
|
25790
|
+
const exchangeCount = Math.floor(sessionMessages.length / 2);
|
|
25791
|
+
if (messageId) await replaceWithText(`\u{1F50D} Reviewing session (${exchangeCount} exchanges)...`);
|
|
25792
|
+
else await channel.sendText(chatId, `\u{1F50D} Reviewing session (${exchangeCount} exchanges)...`, { parseMode: "plain" });
|
|
25793
|
+
try {
|
|
25794
|
+
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25795
|
+
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
25796
|
+
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
25797
|
+
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
25798
|
+
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
25799
|
+
const extracted = parseExtractedSkill2(response.text);
|
|
25800
|
+
if (!extracted) {
|
|
25801
|
+
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
25802
|
+
return;
|
|
25803
|
+
}
|
|
25804
|
+
storePendingDraft2(chatId, {
|
|
25805
|
+
name: extracted.name,
|
|
25806
|
+
content: extracted.content,
|
|
25807
|
+
userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
|
|
25808
|
+
assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
|
|
25809
|
+
});
|
|
25810
|
+
const escaped = extracted.content.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
25811
|
+
const header2 = `\u{1F4CB} <b>Skill Preview: "${extracted.name}"</b>
|
|
25812
|
+
|
|
25813
|
+
`;
|
|
25814
|
+
const MAX_PREVIEW = 3500 - header2.length;
|
|
25815
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
25816
|
+
if (escaped.length > MAX_PREVIEW) {
|
|
25817
|
+
await channel.sendText(chatId, `${header2}<pre>${escaped.slice(0, 3500)}</pre>
|
|
25818
|
+
|
|
25819
|
+
\u2026(truncated \u2014 full skill will be saved)`, { parseMode: "html" });
|
|
25820
|
+
await channel.sendKeyboard(
|
|
25821
|
+
chatId,
|
|
25822
|
+
`Save "${extracted.name}" as a reusable skill?`,
|
|
25823
|
+
[[
|
|
25824
|
+
{ label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
|
|
25825
|
+
{ label: "\u2715 Discard", data: "skill:discard" }
|
|
25826
|
+
]]
|
|
25827
|
+
);
|
|
25828
|
+
} else {
|
|
25829
|
+
await channel.sendKeyboard(
|
|
25830
|
+
chatId,
|
|
25831
|
+
`${header2}<pre>${escaped}</pre>`,
|
|
25832
|
+
[[
|
|
25833
|
+
{ label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
|
|
25834
|
+
{ label: "\u2715 Discard", data: "skill:discard" }
|
|
25835
|
+
]]
|
|
25836
|
+
);
|
|
25837
|
+
}
|
|
25838
|
+
} else {
|
|
25839
|
+
const { saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25840
|
+
const { path } = await saveSkill2(extracted.name, extracted.content);
|
|
25841
|
+
await channel.sendText(chatId, `\u2705 Skill "${extracted.name}" saved.
|
|
25842
|
+
Path: ${path}`, { parseMode: "plain" });
|
|
25843
|
+
}
|
|
25844
|
+
} catch (e) {
|
|
25845
|
+
await channel.sendText(chatId, `Skill extraction failed: ${e.message}`, { parseMode: "plain" });
|
|
25846
|
+
}
|
|
25560
25847
|
} else if (data === "skill:extract") {
|
|
25561
25848
|
const { getPendingDraft: getPendingDraft2, clearPendingDraft: clearPendingDraft2, buildSkillExtractionPrompt: buildSkillExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
25562
25849
|
const draft = getPendingDraft2(chatId);
|
|
@@ -26432,7 +26719,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26432
26719
|
})) {
|
|
26433
26720
|
const planDirective = buildPlanningDirective();
|
|
26434
26721
|
let typingActive2 = true;
|
|
26435
|
-
const
|
|
26722
|
+
const typingLoop2 = async () => {
|
|
26436
26723
|
while (typingActive2) {
|
|
26437
26724
|
try {
|
|
26438
26725
|
await channel.sendTyping?.(chatId);
|
|
@@ -26441,7 +26728,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26441
26728
|
await new Promise((r) => setTimeout(r, 4e3));
|
|
26442
26729
|
}
|
|
26443
26730
|
};
|
|
26444
|
-
|
|
26731
|
+
typingLoop2().catch(() => {
|
|
26445
26732
|
});
|
|
26446
26733
|
try {
|
|
26447
26734
|
const planResponse = await askAgent(chatId, cleanText || text, {
|
|
@@ -26482,23 +26769,18 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26482
26769
|
}
|
|
26483
26770
|
return;
|
|
26484
26771
|
}
|
|
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));
|
|
26772
|
+
let typingActive = true;
|
|
26773
|
+
const typingLoop = async () => {
|
|
26774
|
+
while (typingActive) {
|
|
26775
|
+
try {
|
|
26776
|
+
await channel.sendTyping?.(chatId);
|
|
26777
|
+
} catch {
|
|
26497
26778
|
}
|
|
26498
|
-
|
|
26499
|
-
|
|
26500
|
-
|
|
26501
|
-
|
|
26779
|
+
await new Promise((r) => setTimeout(r, 4e3));
|
|
26780
|
+
}
|
|
26781
|
+
};
|
|
26782
|
+
typingLoop().catch(() => {
|
|
26783
|
+
});
|
|
26502
26784
|
try {
|
|
26503
26785
|
const tMode = settings.getMode();
|
|
26504
26786
|
const tVerbose = settings.getVerboseLevel();
|
|
@@ -26529,6 +26811,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26529
26811
|
}
|
|
26530
26812
|
};
|
|
26531
26813
|
await liveStatus.init();
|
|
26814
|
+
typingActive = false;
|
|
26532
26815
|
if (showThinkingUi && adapter.id !== "claude") {
|
|
26533
26816
|
liveStatus.addInfo(`\u{1F4AD} Thinking display not available for ${adapter.displayName}`);
|
|
26534
26817
|
}
|
|
@@ -26622,7 +26905,7 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
26622
26905
|
}
|
|
26623
26906
|
responseText += `
|
|
26624
26907
|
|
|
26625
|
-
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}${slotTag}] \u23F1\uFE0F ${elapsedSec}s`;
|
|
26908
|
+
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}${slotTag}] \u23F1\uFE0F ${elapsedSec}s \xB7 \u{1F195}/new`;
|
|
26626
26909
|
}
|
|
26627
26910
|
if (observedSubagents.size > 0) {
|
|
26628
26911
|
const names = [...observedSubagents].join(", ");
|
|
@@ -27177,7 +27460,19 @@ function resolveJobBackendId(job) {
|
|
|
27177
27460
|
})();
|
|
27178
27461
|
}
|
|
27179
27462
|
function resolveJobModel(job) {
|
|
27180
|
-
if (job.model)
|
|
27463
|
+
if (job.model) {
|
|
27464
|
+
if (job.backend) {
|
|
27465
|
+
try {
|
|
27466
|
+
const adapter = getAdapter(job.backend);
|
|
27467
|
+
if (!(job.model in adapter.availableModels)) {
|
|
27468
|
+
warn(`[scheduler] job #${job.id}: model "${job.model}" not valid for backend "${job.backend}" \u2014 using default`);
|
|
27469
|
+
return adapter.defaultModel;
|
|
27470
|
+
}
|
|
27471
|
+
} catch {
|
|
27472
|
+
}
|
|
27473
|
+
}
|
|
27474
|
+
return job.model;
|
|
27475
|
+
}
|
|
27181
27476
|
const backendId = resolveJobBackendId(job);
|
|
27182
27477
|
try {
|
|
27183
27478
|
const adapter = getAdapter(backendId);
|
|
@@ -27211,7 +27506,7 @@ var init_cron = __esm({
|
|
|
27211
27506
|
});
|
|
27212
27507
|
|
|
27213
27508
|
// src/agents/runners/wrap-backend.ts
|
|
27214
|
-
import { join as
|
|
27509
|
+
import { join as join28 } from "path";
|
|
27215
27510
|
function buildMcpCommands(backendId) {
|
|
27216
27511
|
const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
|
|
27217
27512
|
return {
|
|
@@ -27305,7 +27600,7 @@ function wrapBackendAdapter(adapter) {
|
|
|
27305
27600
|
const configPath = writeMcpConfigFile(server);
|
|
27306
27601
|
return ["--mcp-config", configPath];
|
|
27307
27602
|
},
|
|
27308
|
-
getSkillPath: () =>
|
|
27603
|
+
getSkillPath: () => join28(SKILLS_PATH, `agent-${adapter.id}.md`)
|
|
27309
27604
|
};
|
|
27310
27605
|
}
|
|
27311
27606
|
var BACKEND_CAPABILITIES;
|
|
@@ -27367,18 +27662,18 @@ var init_wrap_backend = __esm({
|
|
|
27367
27662
|
});
|
|
27368
27663
|
|
|
27369
27664
|
// src/agents/runners/config-loader.ts
|
|
27370
|
-
import { readFileSync as
|
|
27371
|
-
import { join as
|
|
27665
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync14, existsSync as existsSync26, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
27666
|
+
import { join as join29 } from "path";
|
|
27372
27667
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
27373
27668
|
function resolveExecutable2(config2) {
|
|
27374
|
-
if (
|
|
27669
|
+
if (existsSync26(config2.executable)) return config2.executable;
|
|
27375
27670
|
try {
|
|
27376
27671
|
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
27377
27672
|
} catch {
|
|
27378
27673
|
}
|
|
27379
27674
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
27380
27675
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
27381
|
-
if (
|
|
27676
|
+
if (existsSync26(resolved)) return resolved;
|
|
27382
27677
|
}
|
|
27383
27678
|
return config2.executable;
|
|
27384
27679
|
}
|
|
@@ -27504,12 +27799,12 @@ function configToRunner(config2) {
|
|
|
27504
27799
|
prepareMcpInjection() {
|
|
27505
27800
|
return [];
|
|
27506
27801
|
},
|
|
27507
|
-
getSkillPath: () =>
|
|
27802
|
+
getSkillPath: () => join29(SKILLS_PATH, `agent-${config2.id}.md`)
|
|
27508
27803
|
};
|
|
27509
27804
|
}
|
|
27510
27805
|
function loadRunnerConfig(filePath) {
|
|
27511
27806
|
try {
|
|
27512
|
-
const content =
|
|
27807
|
+
const content = readFileSync16(filePath, "utf-8");
|
|
27513
27808
|
return JSON.parse(content);
|
|
27514
27809
|
} catch (err) {
|
|
27515
27810
|
warn(`[runners] Failed to load config ${filePath}: ${err}`);
|
|
@@ -27517,14 +27812,14 @@ function loadRunnerConfig(filePath) {
|
|
|
27517
27812
|
}
|
|
27518
27813
|
}
|
|
27519
27814
|
function loadAllRunnerConfigs() {
|
|
27520
|
-
if (!
|
|
27815
|
+
if (!existsSync26(RUNNERS_PATH)) {
|
|
27521
27816
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
27522
27817
|
return [];
|
|
27523
27818
|
}
|
|
27524
27819
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
27525
27820
|
const configs = [];
|
|
27526
27821
|
for (const file of files) {
|
|
27527
|
-
const config2 = loadRunnerConfig(
|
|
27822
|
+
const config2 = loadRunnerConfig(join29(RUNNERS_PATH, file));
|
|
27528
27823
|
if (config2) configs.push(config2);
|
|
27529
27824
|
}
|
|
27530
27825
|
return configs;
|
|
@@ -27545,16 +27840,16 @@ function registerConfigRunners() {
|
|
|
27545
27840
|
return count;
|
|
27546
27841
|
}
|
|
27547
27842
|
function watchRunnerConfigs(onChange) {
|
|
27548
|
-
if (!
|
|
27843
|
+
if (!existsSync26(RUNNERS_PATH)) return;
|
|
27549
27844
|
for (const prev of watchedFiles) {
|
|
27550
|
-
if (!
|
|
27845
|
+
if (!existsSync26(prev)) {
|
|
27551
27846
|
unwatchFile(prev);
|
|
27552
27847
|
watchedFiles.delete(prev);
|
|
27553
27848
|
}
|
|
27554
27849
|
}
|
|
27555
27850
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
27556
27851
|
for (const file of files) {
|
|
27557
|
-
const fullPath =
|
|
27852
|
+
const fullPath = join29(RUNNERS_PATH, file);
|
|
27558
27853
|
if (watchedFiles.has(fullPath)) continue;
|
|
27559
27854
|
watchedFiles.add(fullPath);
|
|
27560
27855
|
watchFile(fullPath, { interval: 5e3 }, () => {
|
|
@@ -28159,8 +28454,7 @@ var init_telegram2 = __esm({
|
|
|
28159
28454
|
{ command: "skills", description: "List and invoke skills" },
|
|
28160
28455
|
{ command: "extract_skill", description: "Extract a reusable skill from this session" },
|
|
28161
28456
|
{ command: "skill_install", description: "Install a skill from GitHub" },
|
|
28162
|
-
{ command: "voice", description: "
|
|
28163
|
-
{ command: "voice_config", description: "Configure voice provider and voice" },
|
|
28457
|
+
{ command: "voice", description: "Voice settings (STT transcription + TTS replies)" },
|
|
28164
28458
|
{ command: "response_style", description: "Set the AI response style (concise/normal/detailed)" },
|
|
28165
28459
|
{ command: "model_signature", description: "Toggle model+thinking signature on responses" },
|
|
28166
28460
|
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
@@ -28588,7 +28882,8 @@ var init_telegram2 = __esm({
|
|
|
28588
28882
|
"reaction",
|
|
28589
28883
|
() => this.bot.api.setMessageReaction(numericChatId(chatId), parseInt(messageId), [
|
|
28590
28884
|
{ type: "emoji", emoji }
|
|
28591
|
-
])
|
|
28885
|
+
]),
|
|
28886
|
+
{ skipRecord: true }
|
|
28592
28887
|
);
|
|
28593
28888
|
} catch (err) {
|
|
28594
28889
|
log(`[telegram] reactToMessage failed (chat=${chatId} msg=${messageId}): ${err}`);
|
|
@@ -28831,19 +29126,19 @@ var init_telegram2 = __esm({
|
|
|
28831
29126
|
});
|
|
28832
29127
|
|
|
28833
29128
|
// src/skills/bootstrap.ts
|
|
28834
|
-
import { existsSync as
|
|
28835
|
-
import { readdir as readdir6, readFile as readFile8, writeFile as
|
|
28836
|
-
import { join as
|
|
29129
|
+
import { existsSync as existsSync27 } from "fs";
|
|
29130
|
+
import { readdir as readdir6, readFile as readFile8, writeFile as writeFile6, copyFile } from "fs/promises";
|
|
29131
|
+
import { join as join30, dirname as dirname5 } from "path";
|
|
28837
29132
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
28838
29133
|
async function copyAgentManifestSkills() {
|
|
28839
|
-
if (!
|
|
29134
|
+
if (!existsSync27(PKG_SKILLS)) return;
|
|
28840
29135
|
try {
|
|
28841
29136
|
const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
|
|
28842
29137
|
for (const entry of entries) {
|
|
28843
29138
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
28844
|
-
const src =
|
|
28845
|
-
const dest =
|
|
28846
|
-
if (
|
|
29139
|
+
const src = join30(PKG_SKILLS, entry.name);
|
|
29140
|
+
const dest = join30(SKILLS_PATH, entry.name);
|
|
29141
|
+
if (existsSync27(dest)) continue;
|
|
28847
29142
|
await copyFile(src, dest);
|
|
28848
29143
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
28849
29144
|
}
|
|
@@ -28853,8 +29148,8 @@ async function copyAgentManifestSkills() {
|
|
|
28853
29148
|
}
|
|
28854
29149
|
async function bootstrapSkills() {
|
|
28855
29150
|
await copyAgentManifestSkills();
|
|
28856
|
-
const usmDir =
|
|
28857
|
-
if (
|
|
29151
|
+
const usmDir = join30(SKILLS_PATH, USM_DIR_NAME);
|
|
29152
|
+
if (existsSync27(usmDir)) return;
|
|
28858
29153
|
try {
|
|
28859
29154
|
const entries = await readdir6(SKILLS_PATH);
|
|
28860
29155
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
@@ -28877,8 +29172,8 @@ async function bootstrapSkills() {
|
|
|
28877
29172
|
}
|
|
28878
29173
|
}
|
|
28879
29174
|
async function patchUsmForCcClaw(usmDir) {
|
|
28880
|
-
const skillPath =
|
|
28881
|
-
if (!
|
|
29175
|
+
const skillPath = join30(usmDir, "SKILL.md");
|
|
29176
|
+
if (!existsSync27(skillPath)) return;
|
|
28882
29177
|
try {
|
|
28883
29178
|
let content = await readFile8(skillPath, "utf-8");
|
|
28884
29179
|
let patched = false;
|
|
@@ -28906,7 +29201,7 @@ async function patchUsmForCcClaw(usmDir) {
|
|
|
28906
29201
|
}
|
|
28907
29202
|
}
|
|
28908
29203
|
if (patched) {
|
|
28909
|
-
await
|
|
29204
|
+
await writeFile6(skillPath, content, "utf-8");
|
|
28910
29205
|
log("[skills] Patched USM SKILL.md with CC-Claw support");
|
|
28911
29206
|
}
|
|
28912
29207
|
} catch (err) {
|
|
@@ -28923,8 +29218,8 @@ var init_bootstrap = __esm({
|
|
|
28923
29218
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
28924
29219
|
USM_DIR_NAME = "universal-skills-manager";
|
|
28925
29220
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
28926
|
-
PKG_ROOT =
|
|
28927
|
-
PKG_SKILLS =
|
|
29221
|
+
PKG_ROOT = join30(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
|
|
29222
|
+
PKG_SKILLS = join30(PKG_ROOT, "skills");
|
|
28928
29223
|
}
|
|
28929
29224
|
});
|
|
28930
29225
|
|
|
@@ -29037,13 +29332,13 @@ __export(ai_skill_exports, {
|
|
|
29037
29332
|
generateAiSkill: () => generateAiSkill,
|
|
29038
29333
|
installAiSkill: () => installAiSkill
|
|
29039
29334
|
});
|
|
29040
|
-
import { existsSync as
|
|
29041
|
-
import { join as
|
|
29335
|
+
import { existsSync as existsSync28, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
29336
|
+
import { join as join31 } from "path";
|
|
29042
29337
|
import { homedir as homedir9 } from "os";
|
|
29043
29338
|
function generateAiSkill() {
|
|
29044
29339
|
const version = VERSION;
|
|
29045
29340
|
let systemState = "";
|
|
29046
|
-
if (
|
|
29341
|
+
if (existsSync28(DB_PATH)) {
|
|
29047
29342
|
try {
|
|
29048
29343
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
29049
29344
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -29483,8 +29778,8 @@ function installAiSkill() {
|
|
|
29483
29778
|
const failed = [];
|
|
29484
29779
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
29485
29780
|
for (const dir of dirs) {
|
|
29486
|
-
const skillDir =
|
|
29487
|
-
const skillPath =
|
|
29781
|
+
const skillDir = join31(dir, "cc-claw-cli");
|
|
29782
|
+
const skillPath = join31(skillDir, "SKILL.md");
|
|
29488
29783
|
try {
|
|
29489
29784
|
mkdirSync11(skillDir, { recursive: true });
|
|
29490
29785
|
writeFileSync8(skillPath, skill, "utf-8");
|
|
@@ -29503,11 +29798,11 @@ var init_ai_skill = __esm({
|
|
|
29503
29798
|
init_paths();
|
|
29504
29799
|
init_version();
|
|
29505
29800
|
BACKEND_SKILL_DIRS2 = {
|
|
29506
|
-
"cc-claw": [
|
|
29507
|
-
claude: [
|
|
29508
|
-
gemini: [
|
|
29509
|
-
codex: [
|
|
29510
|
-
cursor: [
|
|
29801
|
+
"cc-claw": [join31(homedir9(), ".cc-claw", "workspace", "skills")],
|
|
29802
|
+
claude: [join31(homedir9(), ".claude", "skills")],
|
|
29803
|
+
gemini: [join31(homedir9(), ".gemini", "skills")],
|
|
29804
|
+
codex: [join31(homedir9(), ".agents", "skills")],
|
|
29805
|
+
cursor: [join31(homedir9(), ".cursor", "skills"), join31(homedir9(), ".cursor", "skills-cursor")]
|
|
29511
29806
|
};
|
|
29512
29807
|
}
|
|
29513
29808
|
});
|
|
@@ -29517,21 +29812,21 @@ var index_exports = {};
|
|
|
29517
29812
|
__export(index_exports, {
|
|
29518
29813
|
main: () => main
|
|
29519
29814
|
});
|
|
29520
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
29521
|
-
import { join as
|
|
29815
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync29, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync18 } from "fs";
|
|
29816
|
+
import { join as join32 } from "path";
|
|
29522
29817
|
import dotenv from "dotenv";
|
|
29523
29818
|
function migrateLayout() {
|
|
29524
29819
|
const moves = [
|
|
29525
|
-
[
|
|
29526
|
-
[
|
|
29527
|
-
[
|
|
29528
|
-
[
|
|
29529
|
-
[
|
|
29530
|
-
[
|
|
29531
|
-
[
|
|
29820
|
+
[join32(CC_CLAW_HOME, "cc-claw.db"), join32(DATA_PATH, "cc-claw.db")],
|
|
29821
|
+
[join32(CC_CLAW_HOME, "cc-claw.db-shm"), join32(DATA_PATH, "cc-claw.db-shm")],
|
|
29822
|
+
[join32(CC_CLAW_HOME, "cc-claw.db-wal"), join32(DATA_PATH, "cc-claw.db-wal")],
|
|
29823
|
+
[join32(CC_CLAW_HOME, "cc-claw.log"), join32(LOGS_PATH, "cc-claw.log")],
|
|
29824
|
+
[join32(CC_CLAW_HOME, "cc-claw.log.1"), join32(LOGS_PATH, "cc-claw.log.1")],
|
|
29825
|
+
[join32(CC_CLAW_HOME, "cc-claw.error.log"), join32(LOGS_PATH, "cc-claw.error.log")],
|
|
29826
|
+
[join32(CC_CLAW_HOME, "cc-claw.error.log.1"), join32(LOGS_PATH, "cc-claw.error.log.1")]
|
|
29532
29827
|
];
|
|
29533
29828
|
for (const [from, to] of moves) {
|
|
29534
|
-
if (
|
|
29829
|
+
if (existsSync29(from) && !existsSync29(to)) {
|
|
29535
29830
|
try {
|
|
29536
29831
|
renameSync2(from, to);
|
|
29537
29832
|
} catch {
|
|
@@ -29560,7 +29855,7 @@ async function main() {
|
|
|
29560
29855
|
let version = "unknown";
|
|
29561
29856
|
try {
|
|
29562
29857
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
29563
|
-
version = JSON.parse(
|
|
29858
|
+
version = JSON.parse(readFileSync18(pkgPath, "utf-8")).version;
|
|
29564
29859
|
} catch {
|
|
29565
29860
|
}
|
|
29566
29861
|
log(`[cc-claw] Starting v${version}`);
|
|
@@ -29708,10 +30003,10 @@ async function main() {
|
|
|
29708
30003
|
try {
|
|
29709
30004
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
29710
30005
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
|
|
29711
|
-
const { join:
|
|
29712
|
-
const skillDir =
|
|
30006
|
+
const { join: join38 } = await import("path");
|
|
30007
|
+
const skillDir = join38(SKILLS_PATH, "cc-claw-cli");
|
|
29713
30008
|
mkdirSync19(skillDir, { recursive: true });
|
|
29714
|
-
writeFileSync13(
|
|
30009
|
+
writeFileSync13(join38(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
29715
30010
|
log("[cc-claw] AI skill updated");
|
|
29716
30011
|
} catch {
|
|
29717
30012
|
}
|
|
@@ -29811,10 +30106,10 @@ var init_index = __esm({
|
|
|
29811
30106
|
init_health3();
|
|
29812
30107
|
init_image_gen();
|
|
29813
30108
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
29814
|
-
if (!
|
|
30109
|
+
if (!existsSync29(dir)) mkdirSync12(dir, { recursive: true });
|
|
29815
30110
|
}
|
|
29816
30111
|
migrateLayout();
|
|
29817
|
-
if (
|
|
30112
|
+
if (existsSync29(ENV_PATH)) {
|
|
29818
30113
|
dotenv.config({ path: ENV_PATH });
|
|
29819
30114
|
} else {
|
|
29820
30115
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -29835,12 +30130,12 @@ __export(api_client_exports, {
|
|
|
29835
30130
|
apiPost: () => apiPost,
|
|
29836
30131
|
isDaemonRunning: () => isDaemonRunning
|
|
29837
30132
|
});
|
|
29838
|
-
import { readFileSync as
|
|
30133
|
+
import { readFileSync as readFileSync19, existsSync as existsSync30 } from "fs";
|
|
29839
30134
|
import { request as httpRequest, Agent } from "http";
|
|
29840
30135
|
function getToken() {
|
|
29841
30136
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
29842
30137
|
try {
|
|
29843
|
-
if (
|
|
30138
|
+
if (existsSync30(TOKEN_PATH)) return readFileSync19(TOKEN_PATH, "utf-8").trim();
|
|
29844
30139
|
} catch {
|
|
29845
30140
|
}
|
|
29846
30141
|
return null;
|
|
@@ -29939,10 +30234,10 @@ __export(service_exports2, {
|
|
|
29939
30234
|
serviceStatus: () => serviceStatus,
|
|
29940
30235
|
uninstallService: () => uninstallService
|
|
29941
30236
|
});
|
|
29942
|
-
import { existsSync as
|
|
30237
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
29943
30238
|
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
29944
30239
|
import { homedir as homedir10, platform } from "os";
|
|
29945
|
-
import { join as
|
|
30240
|
+
import { join as join33, dirname as dirname6 } from "path";
|
|
29946
30241
|
function xmlEscape(s) {
|
|
29947
30242
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
29948
30243
|
}
|
|
@@ -29951,7 +30246,7 @@ function resolveExecutable3(name) {
|
|
|
29951
30246
|
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
29952
30247
|
} catch {
|
|
29953
30248
|
const fallback = process.argv[1];
|
|
29954
|
-
if (fallback &&
|
|
30249
|
+
if (fallback && existsSync31(fallback)) return fallback;
|
|
29955
30250
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
29956
30251
|
}
|
|
29957
30252
|
}
|
|
@@ -29960,14 +30255,14 @@ function getPathDirs() {
|
|
|
29960
30255
|
const home = homedir10();
|
|
29961
30256
|
const dirs = /* @__PURE__ */ new Set([
|
|
29962
30257
|
nodeBin,
|
|
29963
|
-
|
|
30258
|
+
join33(home, ".local", "bin"),
|
|
29964
30259
|
"/usr/local/bin",
|
|
29965
30260
|
"/usr/bin",
|
|
29966
30261
|
"/bin"
|
|
29967
30262
|
]);
|
|
29968
30263
|
try {
|
|
29969
30264
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
29970
|
-
if (prefix) dirs.add(
|
|
30265
|
+
if (prefix) dirs.add(join33(prefix, "bin"));
|
|
29971
30266
|
} catch {
|
|
29972
30267
|
}
|
|
29973
30268
|
return [...dirs].join(":");
|
|
@@ -30026,9 +30321,9 @@ function generatePlist() {
|
|
|
30026
30321
|
}
|
|
30027
30322
|
function installMacOS() {
|
|
30028
30323
|
const agentsDir = dirname6(PLIST_PATH);
|
|
30029
|
-
if (!
|
|
30030
|
-
if (!
|
|
30031
|
-
if (
|
|
30324
|
+
if (!existsSync31(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
30325
|
+
if (!existsSync31(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
30326
|
+
if (existsSync31(PLIST_PATH)) {
|
|
30032
30327
|
try {
|
|
30033
30328
|
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
30034
30329
|
} catch {
|
|
@@ -30040,7 +30335,7 @@ function installMacOS() {
|
|
|
30040
30335
|
console.log(" Service loaded and starting.");
|
|
30041
30336
|
}
|
|
30042
30337
|
function uninstallMacOS() {
|
|
30043
|
-
if (!
|
|
30338
|
+
if (!existsSync31(PLIST_PATH)) {
|
|
30044
30339
|
console.log(" No service found to uninstall.");
|
|
30045
30340
|
return;
|
|
30046
30341
|
}
|
|
@@ -30115,8 +30410,8 @@ WantedBy=default.target
|
|
|
30115
30410
|
`;
|
|
30116
30411
|
}
|
|
30117
30412
|
function installLinux() {
|
|
30118
|
-
if (!
|
|
30119
|
-
if (!
|
|
30413
|
+
if (!existsSync31(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
30414
|
+
if (!existsSync31(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
30120
30415
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
30121
30416
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
30122
30417
|
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -30125,7 +30420,7 @@ function installLinux() {
|
|
|
30125
30420
|
console.log(" Service enabled and started.");
|
|
30126
30421
|
}
|
|
30127
30422
|
function uninstallLinux() {
|
|
30128
|
-
if (!
|
|
30423
|
+
if (!existsSync31(UNIT_PATH)) {
|
|
30129
30424
|
console.log(" No service found to uninstall.");
|
|
30130
30425
|
return;
|
|
30131
30426
|
}
|
|
@@ -30150,7 +30445,7 @@ function statusLinux() {
|
|
|
30150
30445
|
}
|
|
30151
30446
|
}
|
|
30152
30447
|
function installService() {
|
|
30153
|
-
if (!
|
|
30448
|
+
if (!existsSync31(join33(CC_CLAW_HOME, ".env"))) {
|
|
30154
30449
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
30155
30450
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
30156
30451
|
process.exitCode = 1;
|
|
@@ -30179,9 +30474,9 @@ var init_service2 = __esm({
|
|
|
30179
30474
|
"use strict";
|
|
30180
30475
|
init_paths();
|
|
30181
30476
|
PLIST_LABEL = "com.cc-claw";
|
|
30182
|
-
PLIST_PATH =
|
|
30183
|
-
SYSTEMD_DIR =
|
|
30184
|
-
UNIT_PATH =
|
|
30477
|
+
PLIST_PATH = join33(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
30478
|
+
SYSTEMD_DIR = join33(homedir10(), ".config", "systemd", "user");
|
|
30479
|
+
UNIT_PATH = join33(SYSTEMD_DIR, "cc-claw.service");
|
|
30185
30480
|
}
|
|
30186
30481
|
});
|
|
30187
30482
|
|
|
@@ -30348,13 +30643,13 @@ var init_daemon = __esm({
|
|
|
30348
30643
|
});
|
|
30349
30644
|
|
|
30350
30645
|
// src/cli/resolve-chat.ts
|
|
30351
|
-
import { readFileSync as
|
|
30646
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
30352
30647
|
function resolveChatId2(globalOpts) {
|
|
30353
30648
|
const explicit = globalOpts.chat;
|
|
30354
30649
|
if (explicit) return explicit;
|
|
30355
30650
|
if (_cachedDefault) return _cachedDefault;
|
|
30356
30651
|
try {
|
|
30357
|
-
const content =
|
|
30652
|
+
const content = readFileSync21(ENV_PATH, "utf-8");
|
|
30358
30653
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
30359
30654
|
if (match) {
|
|
30360
30655
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -30378,7 +30673,7 @@ var status_exports = {};
|
|
|
30378
30673
|
__export(status_exports, {
|
|
30379
30674
|
statusCommand: () => statusCommand
|
|
30380
30675
|
});
|
|
30381
|
-
import { existsSync as
|
|
30676
|
+
import { existsSync as existsSync32, statSync as statSync10 } from "fs";
|
|
30382
30677
|
async function statusCommand(globalOpts, localOpts) {
|
|
30383
30678
|
try {
|
|
30384
30679
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
@@ -30418,7 +30713,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
30418
30713
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
30419
30714
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
30420
30715
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
30421
|
-
const dbStat =
|
|
30716
|
+
const dbStat = existsSync32(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
30422
30717
|
let daemonRunning = false;
|
|
30423
30718
|
let daemonInfo = {};
|
|
30424
30719
|
try {
|
|
@@ -30530,13 +30825,13 @@ __export(doctor_exports, {
|
|
|
30530
30825
|
doctorCommand: () => doctorCommand,
|
|
30531
30826
|
doctorErrors: () => doctorErrors
|
|
30532
30827
|
});
|
|
30533
|
-
import { existsSync as
|
|
30828
|
+
import { existsSync as existsSync33, accessSync, constants } from "fs";
|
|
30534
30829
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
30535
30830
|
async function doctorCommand(globalOpts, localOpts) {
|
|
30536
30831
|
const checks = [];
|
|
30537
30832
|
const dbChecks = checkDatabase();
|
|
30538
30833
|
checks.push(...dbChecks);
|
|
30539
|
-
if (
|
|
30834
|
+
if (existsSync33(DB_PATH)) {
|
|
30540
30835
|
try {
|
|
30541
30836
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30542
30837
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -30562,7 +30857,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30562
30857
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
30563
30858
|
}
|
|
30564
30859
|
}
|
|
30565
|
-
if (
|
|
30860
|
+
if (existsSync33(ENV_PATH)) {
|
|
30566
30861
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
30567
30862
|
} else {
|
|
30568
30863
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
@@ -30605,7 +30900,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30605
30900
|
} catch {
|
|
30606
30901
|
}
|
|
30607
30902
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
30608
|
-
if (
|
|
30903
|
+
if (existsSync33(tokenPath)) {
|
|
30609
30904
|
try {
|
|
30610
30905
|
accessSync(tokenPath, constants.R_OK);
|
|
30611
30906
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -30671,7 +30966,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
30671
30966
|
const errorChecks = checks.filter(
|
|
30672
30967
|
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
30673
30968
|
);
|
|
30674
|
-
if (errorChecks.length > 0 &&
|
|
30969
|
+
if (errorChecks.length > 0 && existsSync33(ERROR_LOG_PATH)) {
|
|
30675
30970
|
try {
|
|
30676
30971
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
30677
30972
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -30800,15 +31095,15 @@ var logs_exports = {};
|
|
|
30800
31095
|
__export(logs_exports, {
|
|
30801
31096
|
logsCommand: () => logsCommand
|
|
30802
31097
|
});
|
|
30803
|
-
import { existsSync as
|
|
31098
|
+
import { existsSync as existsSync34, readFileSync as readFileSync23, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
30804
31099
|
async function logsCommand(opts) {
|
|
30805
31100
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
30806
|
-
if (!
|
|
31101
|
+
if (!existsSync34(logFile)) {
|
|
30807
31102
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
30808
31103
|
process.exit(1);
|
|
30809
31104
|
}
|
|
30810
31105
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
30811
|
-
const content =
|
|
31106
|
+
const content = readFileSync23(logFile, "utf-8");
|
|
30812
31107
|
const allLines = content.split("\n");
|
|
30813
31108
|
const tailLines = allLines.slice(-maxLines);
|
|
30814
31109
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -30818,7 +31113,7 @@ async function logsCommand(opts) {
|
|
|
30818
31113
|
let lastLength = content.length;
|
|
30819
31114
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
30820
31115
|
try {
|
|
30821
|
-
const newContent =
|
|
31116
|
+
const newContent = readFileSync23(logFile, "utf-8");
|
|
30822
31117
|
if (newContent.length > lastLength) {
|
|
30823
31118
|
const newPart = newContent.slice(lastLength);
|
|
30824
31119
|
process.stdout.write(newPart);
|
|
@@ -30850,7 +31145,7 @@ __export(session_logs_exports, {
|
|
|
30850
31145
|
sessionLogsList: () => sessionLogsList,
|
|
30851
31146
|
sessionLogsTail: () => sessionLogsTail
|
|
30852
31147
|
});
|
|
30853
|
-
import { readFileSync as
|
|
31148
|
+
import { readFileSync as readFileSync24, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
30854
31149
|
async function sessionLogsList(opts) {
|
|
30855
31150
|
const logs = listSessionLogs();
|
|
30856
31151
|
if (logs.length === 0) {
|
|
@@ -30907,12 +31202,12 @@ async function sessionLogsTail(opts) {
|
|
|
30907
31202
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
30908
31203
|
let lastLength = 0;
|
|
30909
31204
|
try {
|
|
30910
|
-
lastLength =
|
|
31205
|
+
lastLength = readFileSync24(targetPath, "utf-8").length;
|
|
30911
31206
|
} catch {
|
|
30912
31207
|
}
|
|
30913
31208
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
30914
31209
|
try {
|
|
30915
|
-
const content =
|
|
31210
|
+
const content = readFileSync24(targetPath, "utf-8");
|
|
30916
31211
|
if (content.length > lastLength) {
|
|
30917
31212
|
process.stdout.write(content.slice(lastLength));
|
|
30918
31213
|
lastLength = content.length;
|
|
@@ -30956,11 +31251,11 @@ __export(gemini_exports, {
|
|
|
30956
31251
|
geminiReorder: () => geminiReorder,
|
|
30957
31252
|
geminiRotation: () => geminiRotation
|
|
30958
31253
|
});
|
|
30959
|
-
import { existsSync as
|
|
30960
|
-
import { join as
|
|
31254
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync25, chmodSync } from "fs";
|
|
31255
|
+
import { join as join34 } from "path";
|
|
30961
31256
|
import { createInterface as createInterface8 } from "readline";
|
|
30962
31257
|
function requireDb() {
|
|
30963
|
-
if (!
|
|
31258
|
+
if (!existsSync36(DB_PATH)) {
|
|
30964
31259
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30965
31260
|
process.exit(1);
|
|
30966
31261
|
}
|
|
@@ -30985,9 +31280,9 @@ async function resolveSlotId(idOrLabel) {
|
|
|
30985
31280
|
function resolveOAuthEmail(configHome) {
|
|
30986
31281
|
if (!configHome) return null;
|
|
30987
31282
|
try {
|
|
30988
|
-
const accountsPath =
|
|
30989
|
-
if (!
|
|
30990
|
-
const accounts = JSON.parse(
|
|
31283
|
+
const accountsPath = join34(configHome, ".gemini", "google_accounts.json");
|
|
31284
|
+
if (!existsSync36(accountsPath)) return null;
|
|
31285
|
+
const accounts = JSON.parse(readFileSync25(accountsPath, "utf-8"));
|
|
30991
31286
|
return accounts.active || null;
|
|
30992
31287
|
} catch {
|
|
30993
31288
|
return null;
|
|
@@ -31069,14 +31364,14 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
31069
31364
|
}
|
|
31070
31365
|
async function geminiAddAccount(globalOpts, opts) {
|
|
31071
31366
|
await requireWriteDb();
|
|
31072
|
-
const slotsDir =
|
|
31073
|
-
if (!
|
|
31367
|
+
const slotsDir = join34(CC_CLAW_HOME, "gemini-slots");
|
|
31368
|
+
if (!existsSync36(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
31074
31369
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31075
31370
|
const tempId = Date.now();
|
|
31076
|
-
const slotDir =
|
|
31371
|
+
const slotDir = join34(slotsDir, `slot-${tempId}`);
|
|
31077
31372
|
mkdirSync14(slotDir, { recursive: true, mode: 448 });
|
|
31078
|
-
mkdirSync14(
|
|
31079
|
-
writeFileSync10(
|
|
31373
|
+
mkdirSync14(join34(slotDir, ".gemini"), { recursive: true });
|
|
31374
|
+
writeFileSync10(join34(slotDir, ".gemini", "settings.json"), JSON.stringify({
|
|
31080
31375
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
31081
31376
|
}, null, 2));
|
|
31082
31377
|
console.log("");
|
|
@@ -31093,8 +31388,8 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
31093
31388
|
});
|
|
31094
31389
|
} catch {
|
|
31095
31390
|
}
|
|
31096
|
-
const oauthPath =
|
|
31097
|
-
if (!
|
|
31391
|
+
const oauthPath = join34(slotDir, ".gemini", "oauth_creds.json");
|
|
31392
|
+
if (!existsSync36(oauthPath)) {
|
|
31098
31393
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
31099
31394
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
31100
31395
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -31102,7 +31397,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
31102
31397
|
}
|
|
31103
31398
|
let accountEmail = "unknown";
|
|
31104
31399
|
try {
|
|
31105
|
-
const accounts = JSON.parse(__require("fs").readFileSync(
|
|
31400
|
+
const accounts = JSON.parse(__require("fs").readFileSync(join34(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
|
|
31106
31401
|
accountEmail = accounts.active || accountEmail;
|
|
31107
31402
|
} catch {
|
|
31108
31403
|
}
|
|
@@ -31213,9 +31508,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31213
31508
|
outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
|
|
31214
31509
|
return;
|
|
31215
31510
|
}
|
|
31216
|
-
const settingsPath =
|
|
31217
|
-
if (!
|
|
31218
|
-
mkdirSync14(
|
|
31511
|
+
const settingsPath = join34(slot.configHome, ".gemini", "settings.json");
|
|
31512
|
+
if (!existsSync36(settingsPath)) {
|
|
31513
|
+
mkdirSync14(join34(slot.configHome, ".gemini"), { recursive: true });
|
|
31219
31514
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
31220
31515
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
31221
31516
|
}, null, 2));
|
|
@@ -31239,8 +31534,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31239
31534
|
});
|
|
31240
31535
|
} catch {
|
|
31241
31536
|
}
|
|
31242
|
-
const oauthPath =
|
|
31243
|
-
if (!
|
|
31537
|
+
const oauthPath = join34(slot.configHome, ".gemini", "oauth_creds.json");
|
|
31538
|
+
if (!existsSync36(oauthPath)) {
|
|
31244
31539
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
31245
31540
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
31246
31541
|
`);
|
|
@@ -31250,7 +31545,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
31250
31545
|
setGeminiSlotEnabled2(slotId, true);
|
|
31251
31546
|
let accountEmail = slot.label;
|
|
31252
31547
|
try {
|
|
31253
|
-
const accounts = JSON.parse(
|
|
31548
|
+
const accounts = JSON.parse(readFileSync25(join34(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
31254
31549
|
if (accounts.active) accountEmail = accounts.active;
|
|
31255
31550
|
} catch {
|
|
31256
31551
|
}
|
|
@@ -31309,11 +31604,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
31309
31604
|
makeReorder: () => makeReorder,
|
|
31310
31605
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
31311
31606
|
});
|
|
31312
|
-
import { existsSync as
|
|
31313
|
-
import { join as
|
|
31607
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync15, readFileSync as readFileSync26 } from "fs";
|
|
31608
|
+
import { join as join35 } from "path";
|
|
31314
31609
|
import { createInterface as createInterface9 } from "readline";
|
|
31315
31610
|
function requireDb2() {
|
|
31316
|
-
if (!
|
|
31611
|
+
if (!existsSync37(DB_PATH)) {
|
|
31317
31612
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31318
31613
|
process.exit(1);
|
|
31319
31614
|
}
|
|
@@ -31402,10 +31697,10 @@ function makeAddAccount(backend2, displayName) {
|
|
|
31402
31697
|
process.exit(1);
|
|
31403
31698
|
}
|
|
31404
31699
|
await requireWriteDb2();
|
|
31405
|
-
const slotsDir =
|
|
31406
|
-
if (!
|
|
31700
|
+
const slotsDir = join35(CC_CLAW_HOME, config2.slotsSubdir);
|
|
31701
|
+
if (!existsSync37(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
31407
31702
|
const tempId = Date.now();
|
|
31408
|
-
const slotDir =
|
|
31703
|
+
const slotDir = join35(slotsDir, `slot-${tempId}`);
|
|
31409
31704
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
31410
31705
|
if (config2.preSetup) config2.preSetup(slotDir);
|
|
31411
31706
|
console.log("");
|
|
@@ -31656,22 +31951,22 @@ var init_backend_cmd_factory = __esm({
|
|
|
31656
31951
|
envValue: (slotDir) => slotDir,
|
|
31657
31952
|
envOverrides: { ANTHROPIC_API_KEY: void 0 },
|
|
31658
31953
|
preSetup: (slotDir) => {
|
|
31659
|
-
mkdirSync15(
|
|
31954
|
+
mkdirSync15(join35(slotDir, ".claude"), { recursive: true });
|
|
31660
31955
|
},
|
|
31661
31956
|
verifyCredentials: (slotDir) => {
|
|
31662
|
-
const claudeJson =
|
|
31663
|
-
const claudeJsonNested =
|
|
31664
|
-
if (
|
|
31957
|
+
const claudeJson = join35(slotDir, ".claude.json");
|
|
31958
|
+
const claudeJsonNested = join35(slotDir, ".claude", ".claude.json");
|
|
31959
|
+
if (existsSync37(claudeJson)) {
|
|
31665
31960
|
try {
|
|
31666
|
-
const data = JSON.parse(
|
|
31961
|
+
const data = JSON.parse(readFileSync26(claudeJson, "utf-8"));
|
|
31667
31962
|
return Boolean(data.oauthAccount);
|
|
31668
31963
|
} catch {
|
|
31669
31964
|
return false;
|
|
31670
31965
|
}
|
|
31671
31966
|
}
|
|
31672
|
-
if (
|
|
31967
|
+
if (existsSync37(claudeJsonNested)) {
|
|
31673
31968
|
try {
|
|
31674
|
-
const data = JSON.parse(
|
|
31969
|
+
const data = JSON.parse(readFileSync26(claudeJsonNested, "utf-8"));
|
|
31675
31970
|
return Boolean(data.oauthAccount);
|
|
31676
31971
|
} catch {
|
|
31677
31972
|
return false;
|
|
@@ -31692,9 +31987,9 @@ var init_backend_cmd_factory = __esm({
|
|
|
31692
31987
|
} catch {
|
|
31693
31988
|
}
|
|
31694
31989
|
try {
|
|
31695
|
-
const claudeJson =
|
|
31696
|
-
if (
|
|
31697
|
-
const data = JSON.parse(
|
|
31990
|
+
const claudeJson = join35(slotDir, ".claude.json");
|
|
31991
|
+
if (existsSync37(claudeJson)) {
|
|
31992
|
+
const data = JSON.parse(readFileSync26(claudeJson, "utf-8"));
|
|
31698
31993
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
31699
31994
|
}
|
|
31700
31995
|
} catch {
|
|
@@ -31709,11 +32004,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
31709
32004
|
envValue: (slotDir) => slotDir,
|
|
31710
32005
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
31711
32006
|
verifyCredentials: (slotDir) => {
|
|
31712
|
-
return
|
|
32007
|
+
return existsSync37(join35(slotDir, "auth.json"));
|
|
31713
32008
|
},
|
|
31714
32009
|
extractLabel: (slotDir) => {
|
|
31715
32010
|
try {
|
|
31716
|
-
const authData = JSON.parse(
|
|
32011
|
+
const authData = JSON.parse(readFileSync26(join35(slotDir, "auth.json"), "utf-8"));
|
|
31717
32012
|
if (authData.email) return authData.email;
|
|
31718
32013
|
if (authData.account_name) return authData.account_name;
|
|
31719
32014
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -31737,9 +32032,9 @@ __export(ollama_exports3, {
|
|
|
31737
32032
|
ollamaRemove: () => ollamaRemove,
|
|
31738
32033
|
ollamaTest: () => ollamaTest
|
|
31739
32034
|
});
|
|
31740
|
-
import { existsSync as
|
|
32035
|
+
import { existsSync as existsSync38 } from "fs";
|
|
31741
32036
|
function requireDb3() {
|
|
31742
|
-
if (!
|
|
32037
|
+
if (!existsSync38(DB_PATH)) {
|
|
31743
32038
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31744
32039
|
process.exit(1);
|
|
31745
32040
|
}
|
|
@@ -31998,12 +32293,12 @@ __export(backend_exports, {
|
|
|
31998
32293
|
backendList: () => backendList,
|
|
31999
32294
|
backendSet: () => backendSet
|
|
32000
32295
|
});
|
|
32001
|
-
import { existsSync as
|
|
32296
|
+
import { existsSync as existsSync39 } from "fs";
|
|
32002
32297
|
async function backendList(globalOpts) {
|
|
32003
32298
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
32004
32299
|
const chatId = resolveChatId2(globalOpts);
|
|
32005
32300
|
let activeBackend = null;
|
|
32006
|
-
if (
|
|
32301
|
+
if (existsSync39(DB_PATH)) {
|
|
32007
32302
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32008
32303
|
const readDb = openDatabaseReadOnly2();
|
|
32009
32304
|
try {
|
|
@@ -32034,7 +32329,7 @@ async function backendList(globalOpts) {
|
|
|
32034
32329
|
}
|
|
32035
32330
|
async function backendGet(globalOpts) {
|
|
32036
32331
|
const chatId = resolveChatId2(globalOpts);
|
|
32037
|
-
if (!
|
|
32332
|
+
if (!existsSync39(DB_PATH)) {
|
|
32038
32333
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32039
32334
|
process.exit(1);
|
|
32040
32335
|
}
|
|
@@ -32078,13 +32373,13 @@ __export(model_exports, {
|
|
|
32078
32373
|
modelList: () => modelList,
|
|
32079
32374
|
modelSet: () => modelSet
|
|
32080
32375
|
});
|
|
32081
|
-
import { existsSync as
|
|
32376
|
+
import { existsSync as existsSync40 } from "fs";
|
|
32082
32377
|
async function modelList(globalOpts) {
|
|
32083
32378
|
const chatId = resolveChatId2(globalOpts);
|
|
32084
32379
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32085
32380
|
const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
32086
32381
|
let backendId = "claude";
|
|
32087
|
-
if (
|
|
32382
|
+
if (existsSync40(DB_PATH)) {
|
|
32088
32383
|
const readDb = openDatabaseReadOnly2();
|
|
32089
32384
|
try {
|
|
32090
32385
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -32117,7 +32412,7 @@ async function modelList(globalOpts) {
|
|
|
32117
32412
|
}
|
|
32118
32413
|
async function modelGet(globalOpts) {
|
|
32119
32414
|
const chatId = resolveChatId2(globalOpts);
|
|
32120
|
-
if (!
|
|
32415
|
+
if (!existsSync40(DB_PATH)) {
|
|
32121
32416
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32122
32417
|
process.exit(1);
|
|
32123
32418
|
}
|
|
@@ -32161,9 +32456,9 @@ __export(memory_exports2, {
|
|
|
32161
32456
|
memoryList: () => memoryList,
|
|
32162
32457
|
memorySearch: () => memorySearch
|
|
32163
32458
|
});
|
|
32164
|
-
import { existsSync as
|
|
32459
|
+
import { existsSync as existsSync41 } from "fs";
|
|
32165
32460
|
async function memoryList(globalOpts) {
|
|
32166
|
-
if (!
|
|
32461
|
+
if (!existsSync41(DB_PATH)) {
|
|
32167
32462
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32168
32463
|
process.exit(1);
|
|
32169
32464
|
}
|
|
@@ -32187,7 +32482,7 @@ async function memoryList(globalOpts) {
|
|
|
32187
32482
|
});
|
|
32188
32483
|
}
|
|
32189
32484
|
async function memorySearch(globalOpts, query) {
|
|
32190
|
-
if (!
|
|
32485
|
+
if (!existsSync41(DB_PATH)) {
|
|
32191
32486
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32192
32487
|
process.exit(1);
|
|
32193
32488
|
}
|
|
@@ -32209,7 +32504,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
32209
32504
|
});
|
|
32210
32505
|
}
|
|
32211
32506
|
async function memoryHistory(globalOpts, opts) {
|
|
32212
|
-
if (!
|
|
32507
|
+
if (!existsSync41(DB_PATH)) {
|
|
32213
32508
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32214
32509
|
process.exit(1);
|
|
32215
32510
|
}
|
|
@@ -32257,7 +32552,7 @@ __export(cron_exports2, {
|
|
|
32257
32552
|
cronList: () => cronList,
|
|
32258
32553
|
cronRuns: () => cronRuns
|
|
32259
32554
|
});
|
|
32260
|
-
import { existsSync as
|
|
32555
|
+
import { existsSync as existsSync42 } from "fs";
|
|
32261
32556
|
function parseFallbacks(raw) {
|
|
32262
32557
|
return raw.slice(0, 3).map((f) => {
|
|
32263
32558
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -32278,7 +32573,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
32278
32573
|
return val;
|
|
32279
32574
|
}
|
|
32280
32575
|
async function cronList(globalOpts) {
|
|
32281
|
-
if (!
|
|
32576
|
+
if (!existsSync42(DB_PATH)) {
|
|
32282
32577
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32283
32578
|
process.exit(1);
|
|
32284
32579
|
}
|
|
@@ -32316,7 +32611,7 @@ async function cronList(globalOpts) {
|
|
|
32316
32611
|
});
|
|
32317
32612
|
}
|
|
32318
32613
|
async function cronHealth(globalOpts) {
|
|
32319
|
-
if (!
|
|
32614
|
+
if (!existsSync42(DB_PATH)) {
|
|
32320
32615
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32321
32616
|
process.exit(1);
|
|
32322
32617
|
}
|
|
@@ -32457,6 +32752,29 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
32457
32752
|
if (opts.fallback?.length) {
|
|
32458
32753
|
payload.fallbacks = parseFallbacks(opts.fallback);
|
|
32459
32754
|
}
|
|
32755
|
+
if (opts.account !== void 0) {
|
|
32756
|
+
const accountVal = opts.account;
|
|
32757
|
+
if (accountVal === "auto" || accountVal === "none" || accountVal === "0") {
|
|
32758
|
+
payload.credentialSlotId = null;
|
|
32759
|
+
} else if (/^\d+$/.test(accountVal)) {
|
|
32760
|
+
payload.credentialSlotId = parseInt(accountVal, 10);
|
|
32761
|
+
} else {
|
|
32762
|
+
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
32763
|
+
const readDb = openDatabaseReadOnly2();
|
|
32764
|
+
const geminiSlot = readDb.prepare(
|
|
32765
|
+
"SELECT id, label FROM gemini_credentials WHERE label = ? OR CAST(id AS TEXT) = ?"
|
|
32766
|
+
).get(accountVal, accountVal);
|
|
32767
|
+
const backendSlot = !geminiSlot ? readDb.prepare(
|
|
32768
|
+
"SELECT id, label FROM backend_credentials WHERE (label = ? OR CAST(id AS TEXT) = ?)"
|
|
32769
|
+
).get(accountVal, accountVal) : void 0;
|
|
32770
|
+
const resolved = geminiSlot ?? backendSlot;
|
|
32771
|
+
if (!resolved) {
|
|
32772
|
+
outputError("ACCOUNT_NOT_FOUND", `No credential slot found with label or ID "${accountVal}". Run cc-claw gemini list or cc-claw claude list to see available slots.`);
|
|
32773
|
+
process.exit(1);
|
|
32774
|
+
}
|
|
32775
|
+
payload.credentialSlotId = resolved.id;
|
|
32776
|
+
}
|
|
32777
|
+
}
|
|
32460
32778
|
const fieldCount = Object.keys(payload).length - 1;
|
|
32461
32779
|
if (fieldCount === 0) {
|
|
32462
32780
|
outputError("NO_CHANGES", "No fields to update. Specify fields with flags (e.g. --description, --cron).");
|
|
@@ -32477,7 +32795,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
32477
32795
|
}
|
|
32478
32796
|
}
|
|
32479
32797
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
32480
|
-
if (!
|
|
32798
|
+
if (!existsSync42(DB_PATH)) {
|
|
32481
32799
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32482
32800
|
process.exit(1);
|
|
32483
32801
|
}
|
|
@@ -32524,9 +32842,9 @@ __export(agents_exports, {
|
|
|
32524
32842
|
runnersList: () => runnersList,
|
|
32525
32843
|
tasksList: () => tasksList
|
|
32526
32844
|
});
|
|
32527
|
-
import { existsSync as
|
|
32845
|
+
import { existsSync as existsSync43 } from "fs";
|
|
32528
32846
|
async function agentsList(globalOpts) {
|
|
32529
|
-
if (!
|
|
32847
|
+
if (!existsSync43(DB_PATH)) {
|
|
32530
32848
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32531
32849
|
process.exit(1);
|
|
32532
32850
|
}
|
|
@@ -32557,7 +32875,7 @@ async function agentsList(globalOpts) {
|
|
|
32557
32875
|
});
|
|
32558
32876
|
}
|
|
32559
32877
|
async function tasksList(globalOpts) {
|
|
32560
|
-
if (!
|
|
32878
|
+
if (!existsSync43(DB_PATH)) {
|
|
32561
32879
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32562
32880
|
process.exit(1);
|
|
32563
32881
|
}
|
|
@@ -32685,10 +33003,10 @@ __export(db_exports, {
|
|
|
32685
33003
|
dbPath: () => dbPath,
|
|
32686
33004
|
dbStats: () => dbStats
|
|
32687
33005
|
});
|
|
32688
|
-
import { existsSync as
|
|
33006
|
+
import { existsSync as existsSync44, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
32689
33007
|
import { dirname as dirname7 } from "path";
|
|
32690
33008
|
async function dbStats(globalOpts) {
|
|
32691
|
-
if (!
|
|
33009
|
+
if (!existsSync44(DB_PATH)) {
|
|
32692
33010
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
32693
33011
|
process.exit(1);
|
|
32694
33012
|
}
|
|
@@ -32696,7 +33014,7 @@ async function dbStats(globalOpts) {
|
|
|
32696
33014
|
const readDb = openDatabaseReadOnly2();
|
|
32697
33015
|
const mainSize = statSync11(DB_PATH).size;
|
|
32698
33016
|
const walPath = DB_PATH + "-wal";
|
|
32699
|
-
const walSize =
|
|
33017
|
+
const walSize = existsSync44(walPath) ? statSync11(walPath).size : 0;
|
|
32700
33018
|
const tableNames = readDb.prepare(
|
|
32701
33019
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
32702
33020
|
).all();
|
|
@@ -32730,7 +33048,7 @@ async function dbPath(globalOpts) {
|
|
|
32730
33048
|
output({ path: DB_PATH }, (d) => d.path);
|
|
32731
33049
|
}
|
|
32732
33050
|
async function dbBackup(globalOpts, destPath) {
|
|
32733
|
-
if (!
|
|
33051
|
+
if (!existsSync44(DB_PATH)) {
|
|
32734
33052
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
32735
33053
|
process.exit(1);
|
|
32736
33054
|
}
|
|
@@ -32739,7 +33057,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
32739
33057
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
32740
33058
|
copyFileSync3(DB_PATH, dest);
|
|
32741
33059
|
const walPath = DB_PATH + "-wal";
|
|
32742
|
-
if (
|
|
33060
|
+
if (existsSync44(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
32743
33061
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
32744
33062
|
const b = d;
|
|
32745
33063
|
return `
|
|
@@ -32768,9 +33086,9 @@ __export(usage_exports, {
|
|
|
32768
33086
|
usageCost: () => usageCost,
|
|
32769
33087
|
usageTokens: () => usageTokens
|
|
32770
33088
|
});
|
|
32771
|
-
import { existsSync as
|
|
33089
|
+
import { existsSync as existsSync45 } from "fs";
|
|
32772
33090
|
function ensureDb() {
|
|
32773
|
-
if (!
|
|
33091
|
+
if (!existsSync45(DB_PATH)) {
|
|
32774
33092
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
32775
33093
|
process.exit(1);
|
|
32776
33094
|
}
|
|
@@ -32960,9 +33278,9 @@ __export(config_exports2, {
|
|
|
32960
33278
|
configList: () => configList,
|
|
32961
33279
|
configSet: () => configSet
|
|
32962
33280
|
});
|
|
32963
|
-
import { existsSync as
|
|
33281
|
+
import { existsSync as existsSync46, readFileSync as readFileSync27 } from "fs";
|
|
32964
33282
|
async function configList(globalOpts) {
|
|
32965
|
-
if (!
|
|
33283
|
+
if (!existsSync46(DB_PATH)) {
|
|
32966
33284
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32967
33285
|
process.exit(1);
|
|
32968
33286
|
}
|
|
@@ -32996,7 +33314,7 @@ async function configGet(globalOpts, key) {
|
|
|
32996
33314
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
32997
33315
|
process.exit(1);
|
|
32998
33316
|
}
|
|
32999
|
-
if (!
|
|
33317
|
+
if (!existsSync46(DB_PATH)) {
|
|
33000
33318
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33001
33319
|
process.exit(1);
|
|
33002
33320
|
}
|
|
@@ -33042,11 +33360,11 @@ async function configSet(globalOpts, key, value) {
|
|
|
33042
33360
|
}
|
|
33043
33361
|
}
|
|
33044
33362
|
async function configEnv(_globalOpts) {
|
|
33045
|
-
if (!
|
|
33363
|
+
if (!existsSync46(ENV_PATH)) {
|
|
33046
33364
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
33047
33365
|
process.exit(1);
|
|
33048
33366
|
}
|
|
33049
|
-
const content =
|
|
33367
|
+
const content = readFileSync27(ENV_PATH, "utf-8");
|
|
33050
33368
|
const entries = {};
|
|
33051
33369
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
33052
33370
|
for (const line of content.split("\n")) {
|
|
@@ -33096,9 +33414,9 @@ __export(session_exports, {
|
|
|
33096
33414
|
sessionGet: () => sessionGet,
|
|
33097
33415
|
sessionNew: () => sessionNew
|
|
33098
33416
|
});
|
|
33099
|
-
import { existsSync as
|
|
33417
|
+
import { existsSync as existsSync47 } from "fs";
|
|
33100
33418
|
async function sessionGet(globalOpts) {
|
|
33101
|
-
if (!
|
|
33419
|
+
if (!existsSync47(DB_PATH)) {
|
|
33102
33420
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33103
33421
|
process.exit(1);
|
|
33104
33422
|
}
|
|
@@ -33159,9 +33477,9 @@ __export(permissions_exports, {
|
|
|
33159
33477
|
verboseGet: () => verboseGet,
|
|
33160
33478
|
verboseSet: () => verboseSet
|
|
33161
33479
|
});
|
|
33162
|
-
import { existsSync as
|
|
33480
|
+
import { existsSync as existsSync48 } from "fs";
|
|
33163
33481
|
function ensureDb2() {
|
|
33164
|
-
if (!
|
|
33482
|
+
if (!existsSync48(DB_PATH)) {
|
|
33165
33483
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33166
33484
|
process.exit(1);
|
|
33167
33485
|
}
|
|
@@ -33308,9 +33626,9 @@ __export(cwd_exports, {
|
|
|
33308
33626
|
cwdGet: () => cwdGet,
|
|
33309
33627
|
cwdSet: () => cwdSet
|
|
33310
33628
|
});
|
|
33311
|
-
import { existsSync as
|
|
33629
|
+
import { existsSync as existsSync49 } from "fs";
|
|
33312
33630
|
async function cwdGet(globalOpts) {
|
|
33313
|
-
if (!
|
|
33631
|
+
if (!existsSync49(DB_PATH)) {
|
|
33314
33632
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33315
33633
|
process.exit(1);
|
|
33316
33634
|
}
|
|
@@ -33372,9 +33690,9 @@ __export(voice_exports, {
|
|
|
33372
33690
|
voiceGet: () => voiceGet,
|
|
33373
33691
|
voiceSet: () => voiceSet
|
|
33374
33692
|
});
|
|
33375
|
-
import { existsSync as
|
|
33693
|
+
import { existsSync as existsSync50 } from "fs";
|
|
33376
33694
|
async function voiceGet(globalOpts) {
|
|
33377
|
-
if (!
|
|
33695
|
+
if (!existsSync50(DB_PATH)) {
|
|
33378
33696
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33379
33697
|
process.exit(1);
|
|
33380
33698
|
}
|
|
@@ -33423,9 +33741,9 @@ __export(heartbeat_exports2, {
|
|
|
33423
33741
|
heartbeatGet: () => heartbeatGet,
|
|
33424
33742
|
heartbeatSet: () => heartbeatSet
|
|
33425
33743
|
});
|
|
33426
|
-
import { existsSync as
|
|
33744
|
+
import { existsSync as existsSync51 } from "fs";
|
|
33427
33745
|
async function heartbeatGet(globalOpts) {
|
|
33428
|
-
if (!
|
|
33746
|
+
if (!existsSync51(DB_PATH)) {
|
|
33429
33747
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33430
33748
|
process.exit(1);
|
|
33431
33749
|
}
|
|
@@ -33636,9 +33954,9 @@ __export(summarizer_exports, {
|
|
|
33636
33954
|
summarizerGet: () => summarizerGet,
|
|
33637
33955
|
summarizerSet: () => summarizerSet
|
|
33638
33956
|
});
|
|
33639
|
-
import { existsSync as
|
|
33957
|
+
import { existsSync as existsSync52 } from "fs";
|
|
33640
33958
|
async function summarizerGet(globalOpts) {
|
|
33641
|
-
if (!
|
|
33959
|
+
if (!existsSync52(DB_PATH)) {
|
|
33642
33960
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33643
33961
|
process.exit(1);
|
|
33644
33962
|
}
|
|
@@ -33682,9 +34000,9 @@ __export(thinking_exports, {
|
|
|
33682
34000
|
thinkingGet: () => thinkingGet,
|
|
33683
34001
|
thinkingSet: () => thinkingSet
|
|
33684
34002
|
});
|
|
33685
|
-
import { existsSync as
|
|
34003
|
+
import { existsSync as existsSync53 } from "fs";
|
|
33686
34004
|
async function thinkingGet(globalOpts) {
|
|
33687
|
-
if (!
|
|
34005
|
+
if (!existsSync53(DB_PATH)) {
|
|
33688
34006
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33689
34007
|
process.exit(1);
|
|
33690
34008
|
}
|
|
@@ -33728,9 +34046,9 @@ __export(chats_exports, {
|
|
|
33728
34046
|
chatsList: () => chatsList,
|
|
33729
34047
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
33730
34048
|
});
|
|
33731
|
-
import { existsSync as
|
|
34049
|
+
import { existsSync as existsSync54 } from "fs";
|
|
33732
34050
|
async function chatsList(_globalOpts) {
|
|
33733
|
-
if (!
|
|
34051
|
+
if (!existsSync54(DB_PATH)) {
|
|
33734
34052
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33735
34053
|
process.exit(1);
|
|
33736
34054
|
}
|
|
@@ -33858,9 +34176,9 @@ var mcps_exports2 = {};
|
|
|
33858
34176
|
__export(mcps_exports2, {
|
|
33859
34177
|
mcpsList: () => mcpsList
|
|
33860
34178
|
});
|
|
33861
|
-
import { existsSync as
|
|
34179
|
+
import { existsSync as existsSync55 } from "fs";
|
|
33862
34180
|
async function mcpsList(_globalOpts) {
|
|
33863
|
-
if (!
|
|
34181
|
+
if (!existsSync55(DB_PATH)) {
|
|
33864
34182
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
33865
34183
|
process.exit(1);
|
|
33866
34184
|
}
|
|
@@ -33897,11 +34215,11 @@ __export(chat_exports2, {
|
|
|
33897
34215
|
chatSend: () => chatSend
|
|
33898
34216
|
});
|
|
33899
34217
|
import { request as httpRequest2 } from "http";
|
|
33900
|
-
import { readFileSync as
|
|
34218
|
+
import { readFileSync as readFileSync28, existsSync as existsSync56 } from "fs";
|
|
33901
34219
|
function getToken2() {
|
|
33902
34220
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
33903
34221
|
try {
|
|
33904
|
-
if (
|
|
34222
|
+
if (existsSync56(TOKEN_PATH2)) return readFileSync28(TOKEN_PATH2, "utf-8").trim();
|
|
33905
34223
|
} catch {
|
|
33906
34224
|
}
|
|
33907
34225
|
return null;
|
|
@@ -34389,7 +34707,7 @@ __export(completion_exports, {
|
|
|
34389
34707
|
completionCommand: () => completionCommand
|
|
34390
34708
|
});
|
|
34391
34709
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
|
|
34392
|
-
import { join as
|
|
34710
|
+
import { join as join36 } from "path";
|
|
34393
34711
|
import { homedir as homedir11 } from "os";
|
|
34394
34712
|
async function completionCommand(opts) {
|
|
34395
34713
|
const shell = opts.shell ?? detectShell();
|
|
@@ -34405,10 +34723,10 @@ async function completionCommand(opts) {
|
|
|
34405
34723
|
process.exit(1);
|
|
34406
34724
|
}
|
|
34407
34725
|
if (opts.install) {
|
|
34408
|
-
const dir =
|
|
34726
|
+
const dir = join36(homedir11(), ".config", "cc-claw", "completions");
|
|
34409
34727
|
mkdirSync17(dir, { recursive: true });
|
|
34410
34728
|
const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
|
|
34411
|
-
const filepath =
|
|
34729
|
+
const filepath = join36(dir, filename);
|
|
34412
34730
|
writeFileSync11(filepath, script, "utf-8");
|
|
34413
34731
|
console.log(`\u2713 Completion script written to ${filepath}
|
|
34414
34732
|
`);
|
|
@@ -34581,9 +34899,9 @@ __export(evolve_exports2, {
|
|
|
34581
34899
|
evolveStatus: () => evolveStatus,
|
|
34582
34900
|
evolveUndo: () => evolveUndo
|
|
34583
34901
|
});
|
|
34584
|
-
import { existsSync as
|
|
34902
|
+
import { existsSync as existsSync57 } from "fs";
|
|
34585
34903
|
function ensureDb3() {
|
|
34586
|
-
if (!
|
|
34904
|
+
if (!existsSync57(DB_PATH)) {
|
|
34587
34905
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
34588
34906
|
process.exit(1);
|
|
34589
34907
|
}
|
|
@@ -35057,10 +35375,10 @@ var init_optimize2 = __esm({
|
|
|
35057
35375
|
|
|
35058
35376
|
// src/setup.ts
|
|
35059
35377
|
var setup_exports = {};
|
|
35060
|
-
import { existsSync as
|
|
35378
|
+
import { existsSync as existsSync58, writeFileSync as writeFileSync12, readFileSync as readFileSync29, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
35061
35379
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
35062
35380
|
import { createInterface as createInterface11 } from "readline";
|
|
35063
|
-
import { join as
|
|
35381
|
+
import { join as join37 } from "path";
|
|
35064
35382
|
function divider2() {
|
|
35065
35383
|
console.log(dim("\u2500".repeat(55)));
|
|
35066
35384
|
}
|
|
@@ -35135,21 +35453,21 @@ async function setup() {
|
|
|
35135
35453
|
}
|
|
35136
35454
|
console.log("");
|
|
35137
35455
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
35138
|
-
if (!
|
|
35456
|
+
if (!existsSync58(dir)) mkdirSync18(dir, { recursive: true });
|
|
35139
35457
|
}
|
|
35140
35458
|
const env = {};
|
|
35141
|
-
const envSource =
|
|
35459
|
+
const envSource = existsSync58(ENV_PATH) ? ENV_PATH : existsSync58(".env") ? ".env" : null;
|
|
35142
35460
|
if (envSource) {
|
|
35143
35461
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
35144
35462
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
35145
|
-
const existing =
|
|
35463
|
+
const existing = readFileSync29(envSource, "utf-8");
|
|
35146
35464
|
for (const line of existing.split("\n")) {
|
|
35147
35465
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
35148
35466
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
35149
35467
|
}
|
|
35150
35468
|
}
|
|
35151
|
-
const cwdDb =
|
|
35152
|
-
if (
|
|
35469
|
+
const cwdDb = join37(process.cwd(), "cc-claw.db");
|
|
35470
|
+
if (existsSync58(cwdDb) && !existsSync58(DB_PATH)) {
|
|
35153
35471
|
const { size } = statSync12(cwdDb);
|
|
35154
35472
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
35155
35473
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
@@ -35289,14 +35607,44 @@ async function setup() {
|
|
|
35289
35607
|
}
|
|
35290
35608
|
header(4, TOTAL_STEPS, "Optional Features");
|
|
35291
35609
|
console.log(" These are optional \u2014 you can enable them later by editing .env\n");
|
|
35292
|
-
if (await confirm("Enable voice
|
|
35610
|
+
if (await confirm("Enable voice message transcription (speech-to-text)?")) {
|
|
35293
35611
|
console.log("");
|
|
35294
|
-
console.log(dim("
|
|
35295
|
-
|
|
35296
|
-
|
|
35297
|
-
console.log(
|
|
35612
|
+
console.log(dim(" Choose your transcription provider:\n"));
|
|
35613
|
+
console.log(" 1. Local Whisper \u2014 free, works offline, runs on your machine");
|
|
35614
|
+
console.log(" Requires: whisper-cli (brew install whisper-cpp on macOS)");
|
|
35615
|
+
console.log(" Models downloaded on first use (~75MB\u20131.5GB depending on quality)");
|
|
35298
35616
|
console.log("");
|
|
35299
|
-
console.log(
|
|
35617
|
+
console.log(" 2. Groq \u2014 free cloud API, fast, no local install needed");
|
|
35618
|
+
console.log(" Requires: free Groq API key from https://console.groq.com/keys");
|
|
35619
|
+
console.log("");
|
|
35620
|
+
console.log(" 3. Skip");
|
|
35621
|
+
console.log("");
|
|
35622
|
+
const sttChoice = await requiredInput("Enter choice (1/2/3)", "1");
|
|
35623
|
+
if (sttChoice === "1") {
|
|
35624
|
+
console.log("");
|
|
35625
|
+
console.log(dim(" Local Whisper model to use (select quality vs download size):"));
|
|
35626
|
+
console.log(" 1. tiny.en 75MB English only ~0.3s Basic");
|
|
35627
|
+
console.log(" 2. base.en 150MB English only ~0.8s Good");
|
|
35628
|
+
console.log(" 3. small.en 500MB English only ~1.5s Very good \u2B50 Recommended");
|
|
35629
|
+
console.log(" 4. small 500MB Multilingual ~2s Very good");
|
|
35630
|
+
console.log(" 5. medium.en 1.5GB English only ~5s Excellent");
|
|
35631
|
+
console.log(" 6. medium 1.5GB Multilingual ~6s Excellent");
|
|
35632
|
+
console.log("");
|
|
35633
|
+
const modelChoice = await requiredInput("Enter choice (1\u20136)", "3");
|
|
35634
|
+
const modelMap = { "1": "tiny.en", "2": "base.en", "3": "small.en", "4": "small", "5": "medium.en", "6": "medium" };
|
|
35635
|
+
env.STT_PROVIDER = "local-whisper";
|
|
35636
|
+
env.STT_MODEL = modelMap[modelChoice] ?? "small.en";
|
|
35637
|
+
console.log(green(` Local Whisper selected (${env.STT_MODEL}). Model will download on first voice message.`));
|
|
35638
|
+
} else if (sttChoice === "2") {
|
|
35639
|
+
const groqKey = await requiredInput("Groq API key", env.GROQ_API_KEY);
|
|
35640
|
+
env.GROQ_API_KEY = groqKey;
|
|
35641
|
+
env.STT_PROVIDER = "groq";
|
|
35642
|
+
console.log(green(" Groq transcription enabled!"));
|
|
35643
|
+
} else {
|
|
35644
|
+
console.log(dim(" Voice transcription skipped. Configure later via /voice in Telegram."));
|
|
35645
|
+
}
|
|
35646
|
+
console.log("");
|
|
35647
|
+
console.log(dim(" Choose a voice reply provider (text-to-speech):"));
|
|
35300
35648
|
console.log(" 1. ElevenLabs (high-quality, requires API key)");
|
|
35301
35649
|
console.log(" 2. Grok / xAI (high-quality, requires API key)");
|
|
35302
35650
|
console.log(" 3. macOS (free, uses built-in voices \u2014 Samantha, Albert)");
|
|
@@ -35313,10 +35661,10 @@ async function setup() {
|
|
|
35313
35661
|
const xaiKey = await requiredInput("xAI API key", env.XAI_API_KEY);
|
|
35314
35662
|
env.XAI_API_KEY = xaiKey;
|
|
35315
35663
|
console.log(green(" Voice replies via Grok enabled!"));
|
|
35316
|
-
console.log(dim(" Use /
|
|
35664
|
+
console.log(dim(" Use /voice in Telegram to select a voice (Eve, Ara, Rex, Sal, Leo)."));
|
|
35317
35665
|
} else if (ttsChoice === "3") {
|
|
35318
35666
|
console.log(green(" Voice replies via macOS enabled!"));
|
|
35319
|
-
console.log(dim(" Use /
|
|
35667
|
+
console.log(dim(" Use /voice in Telegram to select a voice (Samantha or Albert)."));
|
|
35320
35668
|
} else {
|
|
35321
35669
|
console.log(dim(" Voice replies will use macOS text-to-speech as fallback."));
|
|
35322
35670
|
}
|
|
@@ -35350,14 +35698,14 @@ async function setup() {
|
|
|
35350
35698
|
`ANTHROPIC_VERTEX_PROJECT_ID=${env.ANTHROPIC_VERTEX_PROJECT_ID ?? ""}`
|
|
35351
35699
|
);
|
|
35352
35700
|
}
|
|
35353
|
-
|
|
35354
|
-
|
|
35355
|
-
|
|
35356
|
-
|
|
35357
|
-
}
|
|
35358
|
-
if (env.
|
|
35359
|
-
|
|
35360
|
-
}
|
|
35701
|
+
const hasVoice = env.GROQ_API_KEY || env.STT_PROVIDER || env.ELEVENLABS_API_KEY || env.XAI_API_KEY;
|
|
35702
|
+
if (hasVoice) {
|
|
35703
|
+
envLines.push("", "# Voice");
|
|
35704
|
+
if (env.STT_PROVIDER) envLines.push(`STT_PROVIDER=${env.STT_PROVIDER}`);
|
|
35705
|
+
if (env.STT_MODEL) envLines.push(`STT_MODEL=${env.STT_MODEL}`);
|
|
35706
|
+
if (env.GROQ_API_KEY) envLines.push(`GROQ_API_KEY=${env.GROQ_API_KEY}`);
|
|
35707
|
+
if (env.ELEVENLABS_API_KEY) envLines.push(`ELEVENLABS_API_KEY=${env.ELEVENLABS_API_KEY}`);
|
|
35708
|
+
if (env.XAI_API_KEY) envLines.push(`XAI_API_KEY=${env.XAI_API_KEY}`);
|
|
35361
35709
|
}
|
|
35362
35710
|
if (env.DASHBOARD_ENABLED) {
|
|
35363
35711
|
envLines.push("", "# Dashboard", `DASHBOARD_ENABLED=${env.DASHBOARD_ENABLED}`);
|
|
@@ -35401,7 +35749,9 @@ async function setup() {
|
|
|
35401
35749
|
console.log(" " + green("[ok]") + ` Telegram bot: @${env.TELEGRAM_BOT_TOKEN ? "configured" : "missing"}`);
|
|
35402
35750
|
console.log(" " + green("[ok]") + ` Chat ID: ${env.ALLOWED_CHAT_ID ?? "not set (anyone can message)"}`);
|
|
35403
35751
|
console.log(" " + green("[ok]") + ` Vertex AI: ${env.ANTHROPIC_VERTEX_PROJECT_ID ?? "not configured"}`);
|
|
35404
|
-
|
|
35752
|
+
const sttConfigured = env.STT_PROVIDER === "local-whisper" || !!env.GROQ_API_KEY;
|
|
35753
|
+
const sttLabel = env.STT_PROVIDER === "local-whisper" ? `Local Whisper (${env.STT_MODEL ?? "small.en"})` : env.GROQ_API_KEY ? "Groq" : "not configured";
|
|
35754
|
+
console.log(" " + (sttConfigured ? green("[ok]") : dim("[--]")) + ` Voice transcription: ${sttLabel}`);
|
|
35405
35755
|
console.log(" " + (env.ELEVENLABS_API_KEY ? green("[ok]") : dim("[--]")) + " Voice replies");
|
|
35406
35756
|
console.log(" " + (env.DASHBOARD_ENABLED ? green("[ok]") : dim("[--]")) + " Web dashboard");
|
|
35407
35757
|
console.log(" " + (env.GEMINI_API_KEY ? green("[ok]") : dim("[--]")) + " Video analysis");
|
|
@@ -35688,7 +36038,7 @@ function registerCronCommands(cmd) {
|
|
|
35688
36038
|
const { cronAction: cronAction2 } = await Promise.resolve().then(() => (init_cron2(), cron_exports2));
|
|
35689
36039
|
await cronAction2(program.opts(), "run", id);
|
|
35690
36040
|
});
|
|
35691
|
-
cmd.command("edit <id>").description("Edit a job (same flags as create)").option("--title <text>", "Short title for job list").option("--description <text>").option("--cron <expr>").option("--at <iso8601>").option("--every <interval>").option("--backend <name>").option("--model <name>").option("--thinking <level>").option("--timeout <seconds>", "Job timeout in seconds (30-3600)").option("--fallback <backend:model>", "Fallback backend:model (repeatable, max 3)", (val, prev) => [...prev, val], []).option("--timezone <tz>").option("--target <id>", "Delivery target (chat ID, or chatId:topicId for forum topics)").option("--delivery <mode>", "Delivery mode (announce/webhook/none)").action(async (id, opts) => {
|
|
36041
|
+
cmd.command("edit <id>").description("Edit a job (same flags as create)").option("--title <text>", "Short title for job list").option("--description <text>").option("--cron <expr>").option("--at <iso8601>").option("--every <interval>").option("--backend <name>").option("--model <name>").option("--thinking <level>").option("--timeout <seconds>", "Job timeout in seconds (30-3600)").option("--fallback <backend:model>", "Fallback backend:model (repeatable, max 3)", (val, prev) => [...prev, val], []).option("--timezone <tz>").option("--target <id>", "Delivery target (chat ID, or chatId:topicId for forum topics)").option("--delivery <mode>", "Delivery mode (announce/webhook/none)").option("--account <id-or-label>", "Pin a credential slot by ID or label (use 'auto' to clear)").action(async (id, opts) => {
|
|
35692
36042
|
const { cronEdit: cronEdit2 } = await Promise.resolve().then(() => (init_cron2(), cron_exports2));
|
|
35693
36043
|
await cronEdit2(program.opts(), id, opts);
|
|
35694
36044
|
});
|