cc-claw 0.20.11 → 0.20.13
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 +1035 -475
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.20.
|
|
36
|
+
VERSION = true ? "0.20.13" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -827,8 +827,8 @@ function cleanupBackupFiles(ccClawHome, retentionDays = 7) {
|
|
|
827
827
|
if (!file.includes(".bak.")) continue;
|
|
828
828
|
const fullPath = join3(dir, file);
|
|
829
829
|
try {
|
|
830
|
-
const
|
|
831
|
-
if (
|
|
830
|
+
const stat4 = statSync(fullPath);
|
|
831
|
+
if (stat4.mtimeMs < cutoffMs) unlinkSync(fullPath);
|
|
832
832
|
} catch {
|
|
833
833
|
}
|
|
834
834
|
}
|
|
@@ -2722,7 +2722,7 @@ function getSkillSuggestionsEnabled(chatId) {
|
|
|
2722
2722
|
const row = getDb().prepare(
|
|
2723
2723
|
"SELECT value FROM chat_skill_suggestions WHERE chat_id = ?"
|
|
2724
2724
|
).get(SKILL_SUGGESTIONS_GLOBAL_KEY);
|
|
2725
|
-
return (row?.value ??
|
|
2725
|
+
return (row?.value ?? 0) === 1;
|
|
2726
2726
|
}
|
|
2727
2727
|
function setSkillSuggestionsEnabled(chatIdOrEnabled, enabled) {
|
|
2728
2728
|
const value = typeof chatIdOrEnabled === "boolean" ? chatIdOrEnabled : enabled;
|
|
@@ -7501,8 +7501,21 @@ var init_loader = __esm({
|
|
|
7501
7501
|
function appendTextChunk(accumulated, chunk) {
|
|
7502
7502
|
if (!accumulated) return chunk;
|
|
7503
7503
|
if (!chunk) return accumulated;
|
|
7504
|
-
|
|
7505
|
-
|
|
7504
|
+
if (/\s$/.test(accumulated) || /^\s/.test(chunk)) {
|
|
7505
|
+
return accumulated + chunk;
|
|
7506
|
+
}
|
|
7507
|
+
const tailChar = accumulated[accumulated.length - 1];
|
|
7508
|
+
if ("/:@.=?&#%-_~+".includes(tailChar)) {
|
|
7509
|
+
return accumulated + chunk;
|
|
7510
|
+
}
|
|
7511
|
+
const headChar = chunk[0];
|
|
7512
|
+
if (`/:@.=?&#%-_~+,;)]}>'"`.includes(headChar)) {
|
|
7513
|
+
return accumulated + chunk;
|
|
7514
|
+
}
|
|
7515
|
+
if (/\d$/.test(accumulated) && /^\d/.test(chunk)) {
|
|
7516
|
+
return accumulated + chunk;
|
|
7517
|
+
}
|
|
7518
|
+
return accumulated + " " + chunk;
|
|
7506
7519
|
}
|
|
7507
7520
|
var init_text_utils = __esm({
|
|
7508
7521
|
"src/text-utils.ts"() {
|
|
@@ -9471,8 +9484,8 @@ function pruneAgentLogs() {
|
|
|
9471
9484
|
mkdirSync6(AGENTS_PATH, { recursive: true });
|
|
9472
9485
|
const files = readdirSync6(AGENTS_PATH).filter((f) => f.endsWith(".log")).map((f) => {
|
|
9473
9486
|
const fullPath = join11(AGENTS_PATH, f);
|
|
9474
|
-
const
|
|
9475
|
-
return { path: fullPath, mtime:
|
|
9487
|
+
const stat4 = statSync4(fullPath);
|
|
9488
|
+
return { path: fullPath, mtime: stat4.mtimeMs, size: stat4.size };
|
|
9476
9489
|
});
|
|
9477
9490
|
const now = Date.now();
|
|
9478
9491
|
const remaining = files.filter((f) => {
|
|
@@ -12720,12 +12733,12 @@ var init_evolve = __esm({
|
|
|
12720
12733
|
const body = JSON.parse(await readBody(req));
|
|
12721
12734
|
const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
12722
12735
|
const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
|
|
12723
|
-
const { join:
|
|
12736
|
+
const { join: join37 } = await import("path");
|
|
12724
12737
|
const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
12725
12738
|
const chatId = resolveChatId(body);
|
|
12726
12739
|
if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
|
|
12727
|
-
const soulPath =
|
|
12728
|
-
const userPath =
|
|
12740
|
+
const soulPath = join37(home, "identity/SOUL.md");
|
|
12741
|
+
const userPath = join37(home, "identity/USER.md");
|
|
12729
12742
|
const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
|
|
12730
12743
|
const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
|
|
12731
12744
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
@@ -12777,6 +12790,346 @@ var init_evolve = __esm({
|
|
|
12777
12790
|
}
|
|
12778
12791
|
});
|
|
12779
12792
|
|
|
12793
|
+
// src/dashboard/routes/files.ts
|
|
12794
|
+
import { createWriteStream, existsSync as existsSync14 } from "fs";
|
|
12795
|
+
import { readdir, stat, unlink, mkdir } from "fs/promises";
|
|
12796
|
+
import { join as join14, extname } from "path";
|
|
12797
|
+
function getUploadHtml(token, host) {
|
|
12798
|
+
return `<!DOCTYPE html>
|
|
12799
|
+
<html lang="en">
|
|
12800
|
+
<head>
|
|
12801
|
+
<meta charset="utf-8">
|
|
12802
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
12803
|
+
<title>CC-Claw File Upload</title>
|
|
12804
|
+
<style>
|
|
12805
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
12806
|
+
body { font-family: -apple-system, system-ui, sans-serif; background: #0d1117; color: #e6edf3; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
|
|
12807
|
+
.container { max-width: 520px; width: 100%; padding: 24px; }
|
|
12808
|
+
h1 { font-size: 1.4rem; margin-bottom: 8px; }
|
|
12809
|
+
.subtitle { color: #8b949e; margin-bottom: 24px; font-size: 0.9rem; }
|
|
12810
|
+
.drop-zone { border: 2px dashed #30363d; border-radius: 12px; padding: 48px 24px; text-align: center; cursor: pointer; transition: all 0.2s; }
|
|
12811
|
+
.drop-zone:hover, .drop-zone.drag-over { border-color: #58a6ff; background: rgba(88,166,255,0.05); }
|
|
12812
|
+
.drop-zone p { color: #8b949e; margin-top: 8px; font-size: 0.85rem; }
|
|
12813
|
+
.drop-zone .icon { font-size: 2.5rem; margin-bottom: 8px; }
|
|
12814
|
+
input[type="file"] { display: none; }
|
|
12815
|
+
.progress-wrap { margin-top: 16px; display: none; }
|
|
12816
|
+
.progress-bar { height: 6px; background: #21262d; border-radius: 3px; overflow: hidden; }
|
|
12817
|
+
.progress-fill { height: 100%; width: 0%; background: #58a6ff; transition: width 0.3s; }
|
|
12818
|
+
.progress-text { font-size: 0.8rem; color: #8b949e; margin-top: 6px; text-align: center; }
|
|
12819
|
+
.result { margin-top: 16px; padding: 12px; border-radius: 8px; font-size: 0.85rem; display: none; }
|
|
12820
|
+
.result.success { background: rgba(63,185,80,0.1); border: 1px solid #238636; color: #3fb950; }
|
|
12821
|
+
.result.error { background: rgba(248,81,73,0.1); border: 1px solid #da3633; color: #f85149; }
|
|
12822
|
+
.file-link { color: #58a6ff; text-decoration: none; word-break: break-all; }
|
|
12823
|
+
.file-link:hover { text-decoration: underline; }
|
|
12824
|
+
.files-list { margin-top: 24px; }
|
|
12825
|
+
.files-list h3 { font-size: 0.9rem; color: #8b949e; margin-bottom: 8px; }
|
|
12826
|
+
.files-list ul { list-style: none; }
|
|
12827
|
+
.files-list li { padding: 6px 0; border-bottom: 1px solid #21262d; font-size: 0.85rem; }
|
|
12828
|
+
.files-list li:last-child { border: none; }
|
|
12829
|
+
</style>
|
|
12830
|
+
</head>
|
|
12831
|
+
<body>
|
|
12832
|
+
<div class="container">
|
|
12833
|
+
<h1>CC-Claw Upload</h1>
|
|
12834
|
+
<p class="subtitle">Upload files for CC-Claw to use. Max 200MB.</p>
|
|
12835
|
+
<div class="drop-zone" id="dropZone">
|
|
12836
|
+
<div class="icon">\u{1F4C1}</div>
|
|
12837
|
+
<strong>Drop file here or click to browse</strong>
|
|
12838
|
+
<p>Videos, images, documents \u2014 anything the bot needs</p>
|
|
12839
|
+
</div>
|
|
12840
|
+
<input type="file" id="fileInput">
|
|
12841
|
+
<div class="progress-wrap" id="progressWrap">
|
|
12842
|
+
<div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
|
|
12843
|
+
<div class="progress-text" id="progressText">Uploading...</div>
|
|
12844
|
+
</div>
|
|
12845
|
+
<div class="result" id="result"></div>
|
|
12846
|
+
<div class="files-list" id="filesList"></div>
|
|
12847
|
+
</div>
|
|
12848
|
+
<script>
|
|
12849
|
+
const TOKEN = "${token}";
|
|
12850
|
+
const HOST = "${host}";
|
|
12851
|
+
const dropZone = document.getElementById("dropZone");
|
|
12852
|
+
const fileInput = document.getElementById("fileInput");
|
|
12853
|
+
const progressWrap = document.getElementById("progressWrap");
|
|
12854
|
+
const progressFill = document.getElementById("progressFill");
|
|
12855
|
+
const progressText = document.getElementById("progressText");
|
|
12856
|
+
const resultDiv = document.getElementById("result");
|
|
12857
|
+
|
|
12858
|
+
dropZone.addEventListener("click", () => fileInput.click());
|
|
12859
|
+
dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("drag-over"); });
|
|
12860
|
+
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("drag-over"));
|
|
12861
|
+
dropZone.addEventListener("drop", e => { e.preventDefault(); dropZone.classList.remove("drag-over"); if (e.dataTransfer.files.length) uploadFile(e.dataTransfer.files[0]); });
|
|
12862
|
+
fileInput.addEventListener("change", () => { if (fileInput.files.length) uploadFile(fileInput.files[0]); });
|
|
12863
|
+
|
|
12864
|
+
function uploadFile(file) {
|
|
12865
|
+
if (file.size > 200 * 1024 * 1024) { showResult("File too large (max 200MB)", true); return; }
|
|
12866
|
+
progressWrap.style.display = "block";
|
|
12867
|
+
resultDiv.style.display = "none";
|
|
12868
|
+
progressFill.style.width = "0%";
|
|
12869
|
+
progressText.textContent = "Uploading " + file.name + "...";
|
|
12870
|
+
|
|
12871
|
+
const xhr = new XMLHttpRequest();
|
|
12872
|
+
xhr.open("POST", HOST + "/upload");
|
|
12873
|
+
xhr.setRequestHeader("Authorization", "Bearer " + TOKEN);
|
|
12874
|
+
|
|
12875
|
+
xhr.upload.addEventListener("progress", e => {
|
|
12876
|
+
if (e.lengthComputable) {
|
|
12877
|
+
const pct = Math.round(e.loaded / e.total * 100);
|
|
12878
|
+
progressFill.style.width = pct + "%";
|
|
12879
|
+
progressText.textContent = pct + "% \u2014 " + (e.loaded / 1024 / 1024).toFixed(1) + " / " + (e.total / 1024 / 1024).toFixed(1) + " MB";
|
|
12880
|
+
}
|
|
12881
|
+
});
|
|
12882
|
+
xhr.addEventListener("load", () => {
|
|
12883
|
+
progressWrap.style.display = "none";
|
|
12884
|
+
try {
|
|
12885
|
+
const resp = JSON.parse(xhr.responseText);
|
|
12886
|
+
if (resp.ok && resp.data) {
|
|
12887
|
+
const link = HOST + "/files/" + resp.data.filename;
|
|
12888
|
+
showResult('Uploaded: <a class="file-link" href="' + link + '" target="_blank">' + resp.data.filename + '</a><br>Size: ' + resp.data.sizeMB + ' MB<br>Path: ' + resp.data.path, false);
|
|
12889
|
+
loadFileList();
|
|
12890
|
+
} else {
|
|
12891
|
+
showResult(resp.error || "Upload failed", true);
|
|
12892
|
+
}
|
|
12893
|
+
} catch { showResult("Upload failed: " + xhr.statusText, true); }
|
|
12894
|
+
});
|
|
12895
|
+
xhr.addEventListener("error", () => { progressWrap.style.display = "none"; showResult("Connection failed", true); });
|
|
12896
|
+
xhr.addEventListener("abort", () => { progressWrap.style.display = "none"; showResult("Upload cancelled", true); });
|
|
12897
|
+
|
|
12898
|
+
const formData = new FormData();
|
|
12899
|
+
formData.append("file", file);
|
|
12900
|
+
xhr.send(formData);
|
|
12901
|
+
}
|
|
12902
|
+
|
|
12903
|
+
function showResult(html, isError) {
|
|
12904
|
+
resultDiv.innerHTML = html;
|
|
12905
|
+
resultDiv.className = "result " + (isError ? "error" : "success");
|
|
12906
|
+
resultDiv.style.display = "block";
|
|
12907
|
+
}
|
|
12908
|
+
|
|
12909
|
+
function loadFileList() {
|
|
12910
|
+
fetch(HOST + "/files", { headers: { "Authorization": "Bearer " + TOKEN } })
|
|
12911
|
+
.then(r => r.json()).then(resp => {
|
|
12912
|
+
if (!resp.ok || !resp.data?.length) { document.getElementById("filesList").innerHTML = ""; return; }
|
|
12913
|
+
const items = resp.data.slice(0, 20).map(f =>
|
|
12914
|
+
'<li><a class="file-link" href="' + HOST + '/files/' + f.name + '" target="_blank">' + f.name + '</a> (' + f.sizeMB + ' MB)</li>'
|
|
12915
|
+
).join("");
|
|
12916
|
+
document.getElementById("filesList").innerHTML = "<h3>Recent Files</h3><ul>" + items + "</ul>";
|
|
12917
|
+
}).catch(() => {});
|
|
12918
|
+
}
|
|
12919
|
+
loadFileList();
|
|
12920
|
+
</script>
|
|
12921
|
+
</body>
|
|
12922
|
+
</html>`;
|
|
12923
|
+
}
|
|
12924
|
+
function handleUploadPage(req, res, _url, token) {
|
|
12925
|
+
const host = `http://${req.headers.host ?? "localhost:3141"}`;
|
|
12926
|
+
htmlResponse(res, getUploadHtml(token, host));
|
|
12927
|
+
}
|
|
12928
|
+
async function handleUpload(req, res) {
|
|
12929
|
+
const contentType = req.headers["content-type"] ?? "";
|
|
12930
|
+
const boundaryMatch = contentType.match(/boundary=(.+?)(?:;|$)/);
|
|
12931
|
+
if (!boundaryMatch) {
|
|
12932
|
+
return jsonResponse(res, { ok: false, error: "Missing multipart boundary" }, 400);
|
|
12933
|
+
}
|
|
12934
|
+
const boundary = boundaryMatch[1];
|
|
12935
|
+
const contentLength = parseInt(req.headers["content-length"] ?? "0", 10);
|
|
12936
|
+
if (contentLength > MAX_UPLOAD_BYTES) {
|
|
12937
|
+
return jsonResponse(res, { ok: false, error: `File too large (max ${MAX_UPLOAD_BYTES / 1024 / 1024}MB)` }, 413);
|
|
12938
|
+
}
|
|
12939
|
+
await mkdir(INCOMING_PATH, { recursive: true });
|
|
12940
|
+
try {
|
|
12941
|
+
const result = await parseMultipartUpload(req, boundary);
|
|
12942
|
+
if (!result) {
|
|
12943
|
+
return jsonResponse(res, { ok: false, error: "No file found in upload" }, 400);
|
|
12944
|
+
}
|
|
12945
|
+
const ext = extname(result.originalName).toLowerCase();
|
|
12946
|
+
if (BLOCKED_EXTENSIONS.has(ext)) {
|
|
12947
|
+
try {
|
|
12948
|
+
await unlink(result.path);
|
|
12949
|
+
} catch {
|
|
12950
|
+
}
|
|
12951
|
+
return jsonResponse(res, { ok: false, error: `File type ${ext} is not allowed` }, 400);
|
|
12952
|
+
}
|
|
12953
|
+
const fileInfo = await stat(result.path);
|
|
12954
|
+
const sizeMB = (fileInfo.size / 1024 / 1024).toFixed(1);
|
|
12955
|
+
log(`[upload] Saved ${result.filename} (${sizeMB} MB) from ${result.originalName}`);
|
|
12956
|
+
return jsonResponse(res, {
|
|
12957
|
+
ok: true,
|
|
12958
|
+
data: {
|
|
12959
|
+
filename: result.filename,
|
|
12960
|
+
originalName: result.originalName,
|
|
12961
|
+
path: result.path,
|
|
12962
|
+
sizeMB
|
|
12963
|
+
}
|
|
12964
|
+
});
|
|
12965
|
+
} catch (err) {
|
|
12966
|
+
warn(`[upload] Failed: ${err.message}`);
|
|
12967
|
+
return jsonResponse(res, { ok: false, error: `Upload failed: ${err.message}` }, 500);
|
|
12968
|
+
}
|
|
12969
|
+
}
|
|
12970
|
+
async function handleFileList(req, res) {
|
|
12971
|
+
await mkdir(INCOMING_PATH, { recursive: true });
|
|
12972
|
+
try {
|
|
12973
|
+
const entries = await readdir(INCOMING_PATH);
|
|
12974
|
+
const files = [];
|
|
12975
|
+
for (const name of entries) {
|
|
12976
|
+
try {
|
|
12977
|
+
const s = await stat(join14(INCOMING_PATH, name));
|
|
12978
|
+
if (!s.isFile()) continue;
|
|
12979
|
+
files.push({
|
|
12980
|
+
name,
|
|
12981
|
+
sizeMB: (s.size / 1024 / 1024).toFixed(1),
|
|
12982
|
+
modified: s.mtime.toISOString()
|
|
12983
|
+
});
|
|
12984
|
+
} catch {
|
|
12985
|
+
continue;
|
|
12986
|
+
}
|
|
12987
|
+
}
|
|
12988
|
+
files.sort((a, b) => b.modified.localeCompare(a.modified));
|
|
12989
|
+
return jsonResponse(res, { ok: true, data: files });
|
|
12990
|
+
} catch {
|
|
12991
|
+
return jsonResponse(res, { ok: true, data: [] });
|
|
12992
|
+
}
|
|
12993
|
+
}
|
|
12994
|
+
async function handleFileServe(req, res, url) {
|
|
12995
|
+
const filename = decodeURIComponent(url.pathname.slice("/files/".length));
|
|
12996
|
+
if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) {
|
|
12997
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
12998
|
+
res.end("Invalid filename");
|
|
12999
|
+
return;
|
|
13000
|
+
}
|
|
13001
|
+
const filePath = join14(INCOMING_PATH, filename);
|
|
13002
|
+
if (!existsSync14(filePath)) {
|
|
13003
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
13004
|
+
res.end("File not found");
|
|
13005
|
+
return;
|
|
13006
|
+
}
|
|
13007
|
+
const fileInfo = await stat(filePath);
|
|
13008
|
+
const ext = extname(filename).toLowerCase();
|
|
13009
|
+
const mimeTypes = {
|
|
13010
|
+
".mp4": "video/mp4",
|
|
13011
|
+
".mov": "video/quicktime",
|
|
13012
|
+
".avi": "video/x-msvideo",
|
|
13013
|
+
".webm": "video/webm",
|
|
13014
|
+
".jpg": "image/jpeg",
|
|
13015
|
+
".jpeg": "image/jpeg",
|
|
13016
|
+
".png": "image/png",
|
|
13017
|
+
".gif": "image/gif",
|
|
13018
|
+
".webp": "image/webp",
|
|
13019
|
+
".pdf": "application/pdf",
|
|
13020
|
+
".txt": "text/plain",
|
|
13021
|
+
".json": "application/json",
|
|
13022
|
+
".mp3": "audio/mpeg",
|
|
13023
|
+
".wav": "audio/wav",
|
|
13024
|
+
".ogg": "audio/ogg"
|
|
13025
|
+
};
|
|
13026
|
+
const mime = mimeTypes[ext] ?? "application/octet-stream";
|
|
13027
|
+
res.writeHead(200, {
|
|
13028
|
+
"Content-Type": mime,
|
|
13029
|
+
"Content-Length": fileInfo.size,
|
|
13030
|
+
"Content-Disposition": `inline; filename="${filename}"`,
|
|
13031
|
+
"Cache-Control": "public, max-age=3600"
|
|
13032
|
+
});
|
|
13033
|
+
const { createReadStream: createReadStream2 } = await import("fs");
|
|
13034
|
+
const stream = createReadStream2(filePath);
|
|
13035
|
+
stream.pipe(res);
|
|
13036
|
+
stream.on("error", () => {
|
|
13037
|
+
if (!res.headersSent) {
|
|
13038
|
+
res.writeHead(500);
|
|
13039
|
+
}
|
|
13040
|
+
res.end();
|
|
13041
|
+
});
|
|
13042
|
+
}
|
|
13043
|
+
function parseMultipartUpload(req, boundary) {
|
|
13044
|
+
return new Promise((resolve, reject) => {
|
|
13045
|
+
const chunks = [];
|
|
13046
|
+
let totalBytes = 0;
|
|
13047
|
+
let cleanupPath = null;
|
|
13048
|
+
req.on("data", (chunk) => {
|
|
13049
|
+
totalBytes += chunk.length;
|
|
13050
|
+
if (totalBytes > MAX_UPLOAD_BYTES) {
|
|
13051
|
+
req.destroy(new Error(`Upload exceeds ${MAX_UPLOAD_BYTES / 1024 / 1024}MB limit`));
|
|
13052
|
+
return;
|
|
13053
|
+
}
|
|
13054
|
+
chunks.push(chunk);
|
|
13055
|
+
});
|
|
13056
|
+
req.on("error", async (err) => {
|
|
13057
|
+
if (cleanupPath) {
|
|
13058
|
+
try {
|
|
13059
|
+
await unlink(cleanupPath);
|
|
13060
|
+
} catch {
|
|
13061
|
+
}
|
|
13062
|
+
}
|
|
13063
|
+
reject(err);
|
|
13064
|
+
});
|
|
13065
|
+
req.on("end", async () => {
|
|
13066
|
+
try {
|
|
13067
|
+
const body = Buffer.concat(chunks);
|
|
13068
|
+
const boundaryBuf = Buffer.from(`--${boundary}`);
|
|
13069
|
+
const headerEnd = body.indexOf(Buffer.from("\r\n\r\n"));
|
|
13070
|
+
if (headerEnd === -1) return resolve(null);
|
|
13071
|
+
const headerSection = body.subarray(0, headerEnd).toString("utf-8");
|
|
13072
|
+
const nameMatch = headerSection.match(/filename="(.+?)"/);
|
|
13073
|
+
if (!nameMatch) return resolve(null);
|
|
13074
|
+
const originalName = nameMatch[1].replace(/[^\w.-]/g, "_");
|
|
13075
|
+
const ext = extname(originalName) || ".bin";
|
|
13076
|
+
const filename = `upload-${Date.now()}${ext}`;
|
|
13077
|
+
const filePath = join14(INCOMING_PATH, filename);
|
|
13078
|
+
cleanupPath = filePath;
|
|
13079
|
+
const fileStart = headerEnd + 4;
|
|
13080
|
+
const closingBoundary = Buffer.from(`\r
|
|
13081
|
+
--${boundary}`);
|
|
13082
|
+
let fileEnd = body.indexOf(closingBoundary, fileStart);
|
|
13083
|
+
if (fileEnd === -1) fileEnd = body.length;
|
|
13084
|
+
const fileContent = body.subarray(fileStart, fileEnd);
|
|
13085
|
+
const ws = createWriteStream(filePath);
|
|
13086
|
+
await new Promise((res, rej) => {
|
|
13087
|
+
ws.write(fileContent, (err) => {
|
|
13088
|
+
if (err) return rej(err);
|
|
13089
|
+
ws.end(res);
|
|
13090
|
+
});
|
|
13091
|
+
});
|
|
13092
|
+
cleanupPath = null;
|
|
13093
|
+
resolve({ filename, originalName, path: filePath });
|
|
13094
|
+
} catch (err) {
|
|
13095
|
+
if (cleanupPath) {
|
|
13096
|
+
try {
|
|
13097
|
+
await unlink(cleanupPath);
|
|
13098
|
+
} catch {
|
|
13099
|
+
}
|
|
13100
|
+
}
|
|
13101
|
+
reject(err);
|
|
13102
|
+
}
|
|
13103
|
+
});
|
|
13104
|
+
});
|
|
13105
|
+
}
|
|
13106
|
+
var INCOMING_PATH, MAX_UPLOAD_BYTES, BLOCKED_EXTENSIONS;
|
|
13107
|
+
var init_files = __esm({
|
|
13108
|
+
"src/dashboard/routes/files.ts"() {
|
|
13109
|
+
"use strict";
|
|
13110
|
+
init_paths();
|
|
13111
|
+
init_middleware();
|
|
13112
|
+
init_log();
|
|
13113
|
+
INCOMING_PATH = join14(MEDIA_PATH, "incoming");
|
|
13114
|
+
MAX_UPLOAD_BYTES = 200 * 1024 * 1024;
|
|
13115
|
+
BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
13116
|
+
".exe",
|
|
13117
|
+
".bat",
|
|
13118
|
+
".cmd",
|
|
13119
|
+
".com",
|
|
13120
|
+
".scr",
|
|
13121
|
+
".pif",
|
|
13122
|
+
".msi",
|
|
13123
|
+
".sh",
|
|
13124
|
+
".bash",
|
|
13125
|
+
".csh",
|
|
13126
|
+
".ksh",
|
|
13127
|
+
".ps1",
|
|
13128
|
+
".psm1"
|
|
13129
|
+
]);
|
|
13130
|
+
}
|
|
13131
|
+
});
|
|
13132
|
+
|
|
12780
13133
|
// src/dashboard/server.ts
|
|
12781
13134
|
var server_exports = {};
|
|
12782
13135
|
__export(server_exports, {
|
|
@@ -12876,6 +13229,16 @@ function startDashboard() {
|
|
|
12876
13229
|
if (url.pathname === "/api/evolve/off" && req.method === "POST") return handleEvolveOff(req, res, url);
|
|
12877
13230
|
if (url.pathname === "/api/evolve/model" && req.method === "POST") return handleEvolveModel(req, res, url);
|
|
12878
13231
|
if (url.pathname === "/api/evolve/settings" && req.method === "POST") return handleEvolveSettings(req, res, url);
|
|
13232
|
+
if (url.pathname === "/upload" && req.method === "GET") return handleUploadPage(req, res, url, getDashboardToken());
|
|
13233
|
+
if (url.pathname === "/upload" && req.method === "POST") {
|
|
13234
|
+
await handleUpload(req, res);
|
|
13235
|
+
return;
|
|
13236
|
+
}
|
|
13237
|
+
if (url.pathname === "/files" && req.method === "GET") return handleFileList(req, res);
|
|
13238
|
+
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
13239
|
+
await handleFileServe(req, res, url);
|
|
13240
|
+
return;
|
|
13241
|
+
}
|
|
12879
13242
|
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
12880
13243
|
return htmlResponse(res, DASHBOARD_HTML.replace("__TOKEN__", getDashboardToken()));
|
|
12881
13244
|
}
|
|
@@ -12891,7 +13254,8 @@ function startDashboard() {
|
|
|
12891
13254
|
console.error(`[api] Server error:`, err);
|
|
12892
13255
|
}
|
|
12893
13256
|
});
|
|
12894
|
-
|
|
13257
|
+
const bindHost = process.env.CC_CLAW_DASHBOARD_HOST ?? "0.0.0.0";
|
|
13258
|
+
server.listen(PORT, bindHost);
|
|
12895
13259
|
persistToken();
|
|
12896
13260
|
return server;
|
|
12897
13261
|
}
|
|
@@ -12908,6 +13272,7 @@ var init_server = __esm({
|
|
|
12908
13272
|
init_scheduler();
|
|
12909
13273
|
init_config();
|
|
12910
13274
|
init_evolve();
|
|
13275
|
+
init_files();
|
|
12911
13276
|
}
|
|
12912
13277
|
});
|
|
12913
13278
|
|
|
@@ -13317,9 +13682,10 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13317
13682
|
const env = opts?.envOverride ? thinkingConfig?.envOverrides ? { ...opts.envOverride, ...thinkingConfig.envOverrides } : opts.envOverride : adapter.getEnv(thinkingConfig?.envOverrides);
|
|
13318
13683
|
const finalArgs = thinkingConfig?.extraArgs ? [...config2.args, ...thinkingConfig.extraArgs] : config2.args;
|
|
13319
13684
|
log(`[agent:spawn] backend=${adapter.id} exe=${config2.executable} model=${model2} timeout=${effectiveTimeout / 1e3}s cwd=${config2.cwd ?? "(inherited)"}`);
|
|
13685
|
+
const stdinMode = adapter.id === "codex" ? "pipe" : "ignore";
|
|
13320
13686
|
const proc = spawn6(config2.executable, finalArgs, {
|
|
13321
13687
|
env,
|
|
13322
|
-
stdio: [
|
|
13688
|
+
stdio: [stdinMode, "pipe", "pipe"],
|
|
13323
13689
|
detached: true,
|
|
13324
13690
|
...config2.cwd ? { cwd: config2.cwd } : {}
|
|
13325
13691
|
});
|
|
@@ -14696,6 +15062,7 @@ var init_telegram_throttle = __esm({
|
|
|
14696
15062
|
try {
|
|
14697
15063
|
const result = await this.execWithRetry(item.label, item.fn);
|
|
14698
15064
|
this.recordSend(item.chatId);
|
|
15065
|
+
this.pauseStartedAt = 0;
|
|
14699
15066
|
item.resolve(result);
|
|
14700
15067
|
} catch (err) {
|
|
14701
15068
|
if (is429(err)) {
|
|
@@ -14742,7 +15109,6 @@ var init_telegram_throttle = __esm({
|
|
|
14742
15109
|
this.chatsPendingNotification.clear();
|
|
14743
15110
|
if (!this.resumeNotifier) return;
|
|
14744
15111
|
const pausedSec = this.pauseStartedAt > 0 ? Math.round((Date.now() - this.pauseStartedAt) / 1e3) : 0;
|
|
14745
|
-
this.pauseStartedAt = 0;
|
|
14746
15112
|
for (const chatId of chats2) {
|
|
14747
15113
|
const queuedForChat = this.queue.filter((q) => q.chatId === chatId).length;
|
|
14748
15114
|
if (queuedForChat === 0) continue;
|
|
@@ -14753,14 +15119,13 @@ var init_telegram_throttle = __esm({
|
|
|
14753
15119
|
if (is429(err)) {
|
|
14754
15120
|
const retrySec = err.parameters?.retry_after ?? 10;
|
|
14755
15121
|
this.pausedUntil = Date.now() + retrySec * 1e3;
|
|
14756
|
-
|
|
14757
|
-
for (const c of chats2) this.chatsPendingNotification.add(c);
|
|
14758
|
-
warn(`[throttle] Resume notification hit 429, re-pausing for ${retrySec}s`);
|
|
15122
|
+
warn(`[throttle] Resume notification hit 429, re-pausing for ${retrySec}s (skipping further notifications)`);
|
|
14759
15123
|
return;
|
|
14760
15124
|
}
|
|
14761
15125
|
warn(`[throttle] Resume notification failed for chat ${chatId}: ${err}`);
|
|
14762
15126
|
}
|
|
14763
15127
|
}
|
|
15128
|
+
this.pauseStartedAt = 0;
|
|
14764
15129
|
}
|
|
14765
15130
|
// ── Helpers ─────────────────────────────────────────────────────────
|
|
14766
15131
|
recordSend(chatId) {
|
|
@@ -14779,10 +15144,10 @@ var init_telegram_throttle = __esm({
|
|
|
14779
15144
|
});
|
|
14780
15145
|
|
|
14781
15146
|
// src/health/checks.ts
|
|
14782
|
-
import { existsSync as
|
|
15147
|
+
import { existsSync as existsSync15, statSync as statSync6, readFileSync as readFileSync8 } from "fs";
|
|
14783
15148
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
14784
15149
|
function getRecentErrors() {
|
|
14785
|
-
if (!
|
|
15150
|
+
if (!existsSync15(ERROR_LOG_PATH)) return null;
|
|
14786
15151
|
const logContent = readFileSync8(ERROR_LOG_PATH, "utf-8");
|
|
14787
15152
|
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
14788
15153
|
const last24h = Date.now() - 864e5;
|
|
@@ -14804,7 +15169,7 @@ function getRecentErrors() {
|
|
|
14804
15169
|
}
|
|
14805
15170
|
function checkDatabase() {
|
|
14806
15171
|
const checks = [];
|
|
14807
|
-
if (
|
|
15172
|
+
if (existsSync15(DB_PATH)) {
|
|
14808
15173
|
const size = statSync6(DB_PATH).size;
|
|
14809
15174
|
checks.push({ name: "Database", status: "ok", message: `${(size / 1024).toFixed(0)}KB` });
|
|
14810
15175
|
} else {
|
|
@@ -14836,18 +15201,18 @@ function checkErrorLog() {
|
|
|
14836
15201
|
return checks;
|
|
14837
15202
|
}
|
|
14838
15203
|
if (errors.rate429.length > 10) {
|
|
14839
|
-
checks.push({ name: "Rate limits", status: "error", message: `${errors.rate429.length} rate-limit (429) errors in last 24h
|
|
15204
|
+
checks.push({ name: "Rate limits", status: "error", message: `${errors.rate429.length} rate-limit (429) errors in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw doctor --fix \u2014 clear log" });
|
|
14840
15205
|
} else if (errors.rate429.length > 0) {
|
|
14841
|
-
checks.push({ name: "Rate limits", status: "warning", message: `${errors.rate429.length} rate-limit errors in last 24h
|
|
15206
|
+
checks.push({ name: "Rate limits", status: "warning", message: `${errors.rate429.length} rate-limit errors in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw doctor --fix \u2014 clear log" });
|
|
14842
15207
|
}
|
|
14843
15208
|
if (errors.contentSilence.length > 0) {
|
|
14844
|
-
checks.push({ name: "Content silence", status: "warning", message: `${errors.contentSilence.length} silence timeout(s) in last 24h
|
|
15209
|
+
checks.push({ name: "Content silence", status: "warning", message: `${errors.contentSilence.length} silence timeout(s) in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw service restart \u2014 if persistent" });
|
|
14845
15210
|
}
|
|
14846
15211
|
if (errors.spawnTimeout.length > 0) {
|
|
14847
|
-
checks.push({ name: "Spawn timeouts", status: "warning", message: `${errors.spawnTimeout.length} backend timeout(s) in last 24h
|
|
15212
|
+
checks.push({ name: "Spawn timeouts", status: "warning", message: `${errors.spawnTimeout.length} backend timeout(s) in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw status \u2014 check backends" });
|
|
14848
15213
|
}
|
|
14849
15214
|
if (errors.other.length > 0) {
|
|
14850
|
-
checks.push({ name: "Other errors", status: "warning", message: `${errors.other.length} error(s) in last 24h
|
|
15215
|
+
checks.push({ name: "Other errors", status: "warning", message: `${errors.other.length} error(s) in last 24h`, fix: "cc-claw doctor errors \u2014 view details; cc-claw doctor --fix \u2014 clear log" });
|
|
14851
15216
|
}
|
|
14852
15217
|
if (checks.length === 0) {
|
|
14853
15218
|
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
@@ -15012,8 +15377,8 @@ __export(heartbeat_exports, {
|
|
|
15012
15377
|
stopHeartbeatForChat: () => stopHeartbeatForChat,
|
|
15013
15378
|
updateHeartbeatConfig: () => updateHeartbeatConfig
|
|
15014
15379
|
});
|
|
15015
|
-
import { readFileSync as readFileSync9, existsSync as
|
|
15016
|
-
import { join as
|
|
15380
|
+
import { readFileSync as readFileSync9, existsSync as existsSync16 } from "fs";
|
|
15381
|
+
import { join as join15 } from "path";
|
|
15017
15382
|
function findHeartbeatJob() {
|
|
15018
15383
|
try {
|
|
15019
15384
|
const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
@@ -15144,7 +15509,7 @@ ${healthText}`);
|
|
|
15144
15509
|
sections.push(`[Active Watches \u2014 execute these checks using your tools]
|
|
15145
15510
|
${watchLines.join("\n")}`);
|
|
15146
15511
|
}
|
|
15147
|
-
if (
|
|
15512
|
+
if (existsSync16(HEARTBEAT_MD_PATH)) {
|
|
15148
15513
|
try {
|
|
15149
15514
|
const custom = readFileSync9(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
15150
15515
|
if (custom) {
|
|
@@ -15206,7 +15571,7 @@ var init_heartbeat2 = __esm({
|
|
|
15206
15571
|
init_store5();
|
|
15207
15572
|
init_checks();
|
|
15208
15573
|
init_log();
|
|
15209
|
-
HEARTBEAT_MD_PATH =
|
|
15574
|
+
HEARTBEAT_MD_PATH = join15(WORKSPACE_PATH, "HEARTBEAT.md");
|
|
15210
15575
|
HEARTBEAT_OK = "HEARTBEAT_OK";
|
|
15211
15576
|
HEARTBEAT_JOB_TYPE = "heartbeat";
|
|
15212
15577
|
DEFAULT_INTERVAL_MS = 30 * 60 * 1e3;
|
|
@@ -15215,8 +15580,8 @@ var init_heartbeat2 = __esm({
|
|
|
15215
15580
|
});
|
|
15216
15581
|
|
|
15217
15582
|
// src/bootstrap/profile.ts
|
|
15218
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as
|
|
15219
|
-
import { join as
|
|
15583
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync17 } from "fs";
|
|
15584
|
+
import { join as join16 } from "path";
|
|
15220
15585
|
function hasActiveProfile(chatId) {
|
|
15221
15586
|
return activeProfiles.has(chatId);
|
|
15222
15587
|
}
|
|
@@ -15345,7 +15710,7 @@ function extractUserUpdates(text) {
|
|
|
15345
15710
|
return { cleanText, updates };
|
|
15346
15711
|
}
|
|
15347
15712
|
function appendToUserProfile(key, value) {
|
|
15348
|
-
if (!
|
|
15713
|
+
if (!existsSync17(USER_PATH2)) return;
|
|
15349
15714
|
const content = readFileSync10(USER_PATH2, "utf-8");
|
|
15350
15715
|
const line = `- **${key}**: ${value}`;
|
|
15351
15716
|
if (content.includes(line)) return;
|
|
@@ -15361,7 +15726,7 @@ var init_profile = __esm({
|
|
|
15361
15726
|
"use strict";
|
|
15362
15727
|
init_paths();
|
|
15363
15728
|
init_log();
|
|
15364
|
-
USER_PATH2 =
|
|
15729
|
+
USER_PATH2 = join16(IDENTITY_PATH, "USER.md");
|
|
15365
15730
|
activeProfiles = /* @__PURE__ */ new Map();
|
|
15366
15731
|
}
|
|
15367
15732
|
});
|
|
@@ -16658,8 +17023,8 @@ __export(session_log_exports2, {
|
|
|
16658
17023
|
startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
|
|
16659
17024
|
tailSessionLog: () => tailSessionLog
|
|
16660
17025
|
});
|
|
16661
|
-
import { existsSync as
|
|
16662
|
-
import { join as
|
|
17026
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
|
|
17027
|
+
import { join as join17, basename } from "path";
|
|
16663
17028
|
import { createInterface as createInterface6 } from "readline";
|
|
16664
17029
|
function getRetentionDays() {
|
|
16665
17030
|
const env = process.env.SESSION_LOG_RETENTION_DAYS;
|
|
@@ -16671,13 +17036,13 @@ function getRetentionDays() {
|
|
|
16671
17036
|
}
|
|
16672
17037
|
function cleanupSessionLogs(retentionDays) {
|
|
16673
17038
|
const days = retentionDays ?? getRetentionDays();
|
|
16674
|
-
if (!
|
|
17039
|
+
if (!existsSync18(SESSION_LOGS_PATH)) return 0;
|
|
16675
17040
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
16676
17041
|
let cleaned = 0;
|
|
16677
17042
|
try {
|
|
16678
17043
|
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
16679
17044
|
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
16680
|
-
const filePath =
|
|
17045
|
+
const filePath = join17(SESSION_LOGS_PATH, file);
|
|
16681
17046
|
try {
|
|
16682
17047
|
const { mtimeMs } = statSync7(filePath);
|
|
16683
17048
|
if (mtimeMs < cutoff) {
|
|
@@ -16703,21 +17068,21 @@ function startSessionLogCleanupTimer() {
|
|
|
16703
17068
|
return timer;
|
|
16704
17069
|
}
|
|
16705
17070
|
function listSessionLogs() {
|
|
16706
|
-
if (!
|
|
17071
|
+
if (!existsSync18(SESSION_LOGS_PATH)) return [];
|
|
16707
17072
|
const logs = [];
|
|
16708
17073
|
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
16709
17074
|
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
16710
|
-
const filePath =
|
|
17075
|
+
const filePath = join17(SESSION_LOGS_PATH, file);
|
|
16711
17076
|
try {
|
|
16712
|
-
const
|
|
17077
|
+
const stat4 = statSync7(filePath);
|
|
16713
17078
|
const match = file.match(/^session-(.+?)-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})\.log$/);
|
|
16714
17079
|
logs.push({
|
|
16715
17080
|
filename: file,
|
|
16716
17081
|
filePath,
|
|
16717
17082
|
chatId: match?.[1] ?? "unknown",
|
|
16718
17083
|
timestamp: match?.[2]?.replace(/-/g, (m, i) => i > 9 ? ":" : m) ?? "unknown",
|
|
16719
|
-
sizeBytes:
|
|
16720
|
-
modifiedAt:
|
|
17084
|
+
sizeBytes: stat4.size,
|
|
17085
|
+
modifiedAt: stat4.mtime
|
|
16721
17086
|
});
|
|
16722
17087
|
} catch {
|
|
16723
17088
|
}
|
|
@@ -16726,7 +17091,7 @@ function listSessionLogs() {
|
|
|
16726
17091
|
return logs;
|
|
16727
17092
|
}
|
|
16728
17093
|
async function* tailSessionLog(filePath, lines = 50) {
|
|
16729
|
-
if (!
|
|
17094
|
+
if (!existsSync18(filePath)) {
|
|
16730
17095
|
yield `File not found: ${filePath}`;
|
|
16731
17096
|
return;
|
|
16732
17097
|
}
|
|
@@ -16754,12 +17119,12 @@ var init_session_log2 = __esm({
|
|
|
16754
17119
|
constructor(chatId, backend2, model2) {
|
|
16755
17120
|
this.backend = backend2;
|
|
16756
17121
|
this.model = model2;
|
|
16757
|
-
if (!
|
|
17122
|
+
if (!existsSync18(SESSION_LOGS_PATH)) {
|
|
16758
17123
|
mkdirSync8(SESSION_LOGS_PATH, { recursive: true });
|
|
16759
17124
|
}
|
|
16760
17125
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
16761
17126
|
const sanitizedChatId = chatId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
16762
|
-
this.filePath =
|
|
17127
|
+
this.filePath = join17(SESSION_LOGS_PATH, `session-${sanitizedChatId}-${ts2}.log`);
|
|
16763
17128
|
const header2 = [
|
|
16764
17129
|
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
16765
17130
|
`CC-Claw Agent Session \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -17162,7 +17527,7 @@ var init_gate = __esm({
|
|
|
17162
17527
|
// src/voice/stt.ts
|
|
17163
17528
|
import crypto from "crypto";
|
|
17164
17529
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
17165
|
-
import { readFile as readFile2, unlink } from "fs/promises";
|
|
17530
|
+
import { readFile as readFile2, unlink as unlink2 } from "fs/promises";
|
|
17166
17531
|
import { promisify as promisify2 } from "util";
|
|
17167
17532
|
function ensureFfmpeg() {
|
|
17168
17533
|
if (ffmpegAvailable === true) return;
|
|
@@ -17316,10 +17681,10 @@ async function mp3ToOgg(mp3Buffer) {
|
|
|
17316
17681
|
await writeFile6(tmpMp3, mp3Buffer);
|
|
17317
17682
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17318
17683
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17319
|
-
|
|
17684
|
+
unlink2(tmpMp3).catch((err) => {
|
|
17320
17685
|
error("[tts] cleanup failed:", err);
|
|
17321
17686
|
});
|
|
17322
|
-
|
|
17687
|
+
unlink2(tmpOgg).catch((err) => {
|
|
17323
17688
|
error("[tts] cleanup failed:", err);
|
|
17324
17689
|
});
|
|
17325
17690
|
return oggBuffer;
|
|
@@ -17332,10 +17697,10 @@ async function macOsTts(text, voice2 = "Samantha") {
|
|
|
17332
17697
|
await execFileAsync2("say", ["-v", voice2, "-o", tmpAiff, text]);
|
|
17333
17698
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpAiff, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17334
17699
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17335
|
-
|
|
17700
|
+
unlink2(tmpAiff).catch((err) => {
|
|
17336
17701
|
error("[tts] cleanup failed:", err);
|
|
17337
17702
|
});
|
|
17338
|
-
|
|
17703
|
+
unlink2(tmpOgg).catch((err) => {
|
|
17339
17704
|
error("[tts] cleanup failed:", err);
|
|
17340
17705
|
});
|
|
17341
17706
|
return oggBuffer;
|
|
@@ -17367,9 +17732,9 @@ var init_stt = __esm({
|
|
|
17367
17732
|
});
|
|
17368
17733
|
|
|
17369
17734
|
// src/media/image-gen.ts
|
|
17370
|
-
import { mkdirSync as mkdirSync9, existsSync as
|
|
17735
|
+
import { mkdirSync as mkdirSync9, existsSync as existsSync19, unlink as unlink3, readdir as readdir2, stat as stat2 } from "fs";
|
|
17371
17736
|
import { writeFile } from "fs/promises";
|
|
17372
|
-
import { join as
|
|
17737
|
+
import { join as join18 } from "path";
|
|
17373
17738
|
async function generateImage(prompt) {
|
|
17374
17739
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
17375
17740
|
if (!apiKey) {
|
|
@@ -17416,12 +17781,12 @@ async function generateImage(prompt) {
|
|
|
17416
17781
|
if (!imageData) {
|
|
17417
17782
|
throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
|
|
17418
17783
|
}
|
|
17419
|
-
if (!
|
|
17784
|
+
if (!existsSync19(IMAGE_OUTPUT_DIR)) {
|
|
17420
17785
|
mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
|
|
17421
17786
|
}
|
|
17422
17787
|
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
17423
17788
|
const filename = `img_${Date.now()}.${ext}`;
|
|
17424
|
-
const filePath =
|
|
17789
|
+
const filePath = join18(IMAGE_OUTPUT_DIR, filename);
|
|
17425
17790
|
const buffer = Buffer.from(imageData, "base64");
|
|
17426
17791
|
await writeFile(filePath, buffer);
|
|
17427
17792
|
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
@@ -17431,21 +17796,21 @@ function isImageGenAvailable() {
|
|
|
17431
17796
|
return !!process.env.GEMINI_API_KEY;
|
|
17432
17797
|
}
|
|
17433
17798
|
function cleanupGeneratedImage(filePath) {
|
|
17434
|
-
|
|
17799
|
+
unlink3(filePath, (err) => {
|
|
17435
17800
|
if (err) warn(`[image-gen] cleanup failed for ${filePath}: ${err.message}`);
|
|
17436
17801
|
else log(`[image-gen] cleaned up ${filePath}`);
|
|
17437
17802
|
});
|
|
17438
17803
|
}
|
|
17439
17804
|
function pruneImageCache() {
|
|
17440
|
-
|
|
17805
|
+
readdir2(IMAGE_OUTPUT_DIR, (err, files) => {
|
|
17441
17806
|
if (err || !files) return;
|
|
17442
|
-
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) =>
|
|
17807
|
+
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) => join18(IMAGE_OUTPUT_DIR, f));
|
|
17443
17808
|
if (imageFiles.length === 0) return;
|
|
17444
17809
|
const now = Date.now();
|
|
17445
17810
|
let statsPending = imageFiles.length;
|
|
17446
17811
|
const fileStats = [];
|
|
17447
17812
|
for (const fp of imageFiles) {
|
|
17448
|
-
|
|
17813
|
+
stat2(fp, (serr, s) => {
|
|
17449
17814
|
statsPending--;
|
|
17450
17815
|
if (!serr && s) fileStats.push({ path: fp, mtime: s.mtimeMs });
|
|
17451
17816
|
if (statsPending === 0) {
|
|
@@ -17454,7 +17819,7 @@ function pruneImageCache() {
|
|
|
17454
17819
|
(f, idx) => idx >= MAX_GENERATED_IMAGES || now - f.mtime > IMAGE_MAX_AGE_MS
|
|
17455
17820
|
);
|
|
17456
17821
|
for (const { path } of toDelete) {
|
|
17457
|
-
|
|
17822
|
+
unlink3(path, (derr) => {
|
|
17458
17823
|
if (!derr) log(`[image-gen] pruned old image: ${path}`);
|
|
17459
17824
|
});
|
|
17460
17825
|
}
|
|
@@ -17471,8 +17836,8 @@ var init_image_gen = __esm({
|
|
|
17471
17836
|
MAX_GENERATED_IMAGES = 20;
|
|
17472
17837
|
IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
17473
17838
|
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
17474
|
-
IMAGE_OUTPUT_DIR =
|
|
17475
|
-
process.env.CC_CLAW_HOME ??
|
|
17839
|
+
IMAGE_OUTPUT_DIR = join18(
|
|
17840
|
+
process.env.CC_CLAW_HOME ?? join18(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
17476
17841
|
"data",
|
|
17477
17842
|
"images"
|
|
17478
17843
|
);
|
|
@@ -17929,33 +18294,33 @@ var init_video = __esm({
|
|
|
17929
18294
|
});
|
|
17930
18295
|
|
|
17931
18296
|
// src/router/media.ts
|
|
17932
|
-
import { join as
|
|
17933
|
-
import { mkdir, writeFile as writeFile2, readdir as
|
|
18297
|
+
import { join as join19 } from "path";
|
|
18298
|
+
import { mkdir as mkdir2, writeFile as writeFile2, readdir as readdir3, stat as stat3, unlink as unlink4 } from "fs/promises";
|
|
17934
18299
|
function getMediaRetentionMs() {
|
|
17935
18300
|
const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
|
|
17936
18301
|
return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
|
|
17937
18302
|
}
|
|
17938
18303
|
async function saveMedia(buffer, prefix, ext) {
|
|
17939
|
-
await
|
|
18304
|
+
await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
|
|
17940
18305
|
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
17941
|
-
const fullPath =
|
|
18306
|
+
const fullPath = join19(MEDIA_INCOMING_PATH, filename);
|
|
17942
18307
|
await writeFile2(fullPath, buffer);
|
|
17943
18308
|
return fullPath;
|
|
17944
18309
|
}
|
|
17945
18310
|
async function cleanupOldMedia() {
|
|
17946
18311
|
try {
|
|
17947
|
-
await
|
|
18312
|
+
await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
|
|
17948
18313
|
const retentionMs = getMediaRetentionMs();
|
|
17949
18314
|
const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
|
|
17950
|
-
const files = await
|
|
18315
|
+
const files = await readdir3(MEDIA_INCOMING_PATH);
|
|
17951
18316
|
const now = Date.now();
|
|
17952
18317
|
let removed = 0;
|
|
17953
18318
|
for (const file of files) {
|
|
17954
18319
|
try {
|
|
17955
|
-
const filePath =
|
|
17956
|
-
const s = await
|
|
18320
|
+
const filePath = join19(MEDIA_INCOMING_PATH, file);
|
|
18321
|
+
const s = await stat3(filePath);
|
|
17957
18322
|
if (now - s.mtimeMs > retentionMs) {
|
|
17958
|
-
await
|
|
18323
|
+
await unlink4(filePath);
|
|
17959
18324
|
removed++;
|
|
17960
18325
|
}
|
|
17961
18326
|
} catch {
|
|
@@ -18170,7 +18535,21 @@ ${content}
|
|
|
18170
18535
|
await sendResponse(chatId, channel, mediaResponse, msg.messageId);
|
|
18171
18536
|
} catch (err) {
|
|
18172
18537
|
error("[router] Media error:", err);
|
|
18173
|
-
|
|
18538
|
+
const errStr = errorMessage(err);
|
|
18539
|
+
if (errStr.includes("file is too big") || errStr.includes("too big")) {
|
|
18540
|
+
const host = process.env.CC_CLAW_API_HOST ?? "192.168.68.10";
|
|
18541
|
+
const port = process.env.CC_CLAW_PORT ?? "3141";
|
|
18542
|
+
await channel.sendText(
|
|
18543
|
+
chatId,
|
|
18544
|
+
`File too large for Telegram (max 20MB via Bot API).
|
|
18545
|
+
|
|
18546
|
+
Upload directly instead:
|
|
18547
|
+
http://${host}:${port}/upload`,
|
|
18548
|
+
{ parseMode: "plain" }
|
|
18549
|
+
);
|
|
18550
|
+
} else {
|
|
18551
|
+
await channel.sendText(chatId, `Error processing file: ${errStr}`, { parseMode: "plain" });
|
|
18552
|
+
}
|
|
18174
18553
|
}
|
|
18175
18554
|
}
|
|
18176
18555
|
var MEDIA_INCOMING_PATH;
|
|
@@ -18188,7 +18567,7 @@ var init_media = __esm({
|
|
|
18188
18567
|
init_helpers();
|
|
18189
18568
|
init_response();
|
|
18190
18569
|
init_live_status();
|
|
18191
|
-
MEDIA_INCOMING_PATH =
|
|
18570
|
+
MEDIA_INCOMING_PATH = join19(MEDIA_PATH, "incoming");
|
|
18192
18571
|
}
|
|
18193
18572
|
});
|
|
18194
18573
|
|
|
@@ -18500,9 +18879,9 @@ var install_exports = {};
|
|
|
18500
18879
|
__export(install_exports, {
|
|
18501
18880
|
installSkillFromGitHub: () => installSkillFromGitHub
|
|
18502
18881
|
});
|
|
18503
|
-
import { mkdir as
|
|
18504
|
-
import { existsSync as
|
|
18505
|
-
import { join as
|
|
18882
|
+
import { mkdir as mkdir3, readdir as readdir4, readFile as readFile4, cp } from "fs/promises";
|
|
18883
|
+
import { existsSync as existsSync20 } from "fs";
|
|
18884
|
+
import { join as join20, basename as basename2 } from "path";
|
|
18506
18885
|
import { execSync as execSync4 } from "child_process";
|
|
18507
18886
|
async function installSkillFromGitHub(urlOrShorthand) {
|
|
18508
18887
|
let repoUrl;
|
|
@@ -18513,36 +18892,36 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
18513
18892
|
}
|
|
18514
18893
|
repoUrl = parsed.cloneUrl;
|
|
18515
18894
|
subPath = parsed.subPath;
|
|
18516
|
-
const tmpDir =
|
|
18895
|
+
const tmpDir = join20("/tmp", `cc-claw-skill-${Date.now()}`);
|
|
18517
18896
|
try {
|
|
18518
18897
|
log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
|
|
18519
18898
|
execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
18520
18899
|
stdio: "pipe",
|
|
18521
18900
|
timeout: 3e4
|
|
18522
18901
|
});
|
|
18523
|
-
if (!
|
|
18902
|
+
if (!existsSync20(join20(tmpDir, ".git"))) {
|
|
18524
18903
|
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
18525
18904
|
}
|
|
18526
|
-
const searchRoot = subPath ?
|
|
18905
|
+
const searchRoot = subPath ? join20(tmpDir, subPath) : tmpDir;
|
|
18527
18906
|
const skillDir = await findSkillDir(searchRoot);
|
|
18528
18907
|
if (!skillDir) {
|
|
18529
18908
|
return { success: false, error: "No SKILL.md found in the repository." };
|
|
18530
18909
|
}
|
|
18531
18910
|
const skillFolderName = basename2(skillDir);
|
|
18532
|
-
const destDir =
|
|
18533
|
-
if (
|
|
18911
|
+
const destDir = join20(SKILLS_PATH, skillFolderName);
|
|
18912
|
+
if (existsSync20(destDir)) {
|
|
18534
18913
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
18535
18914
|
}
|
|
18536
|
-
await
|
|
18915
|
+
await mkdir3(destDir, { recursive: true });
|
|
18537
18916
|
await cp(skillDir, destDir, { recursive: true });
|
|
18538
18917
|
let skillName = skillFolderName;
|
|
18539
18918
|
try {
|
|
18540
|
-
const content = await readFile4(
|
|
18919
|
+
const content = await readFile4(join20(destDir, "SKILL.md"), "utf-8");
|
|
18541
18920
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
18542
18921
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
18543
18922
|
} catch {
|
|
18544
18923
|
try {
|
|
18545
|
-
const content = await readFile4(
|
|
18924
|
+
const content = await readFile4(join20(destDir, "skill.md"), "utf-8");
|
|
18546
18925
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
18547
18926
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
18548
18927
|
} catch {
|
|
@@ -18577,35 +18956,35 @@ function parseGitHubUrl(input) {
|
|
|
18577
18956
|
async function findSkillDir(root) {
|
|
18578
18957
|
const candidates = ["SKILL.md", "skill.md"];
|
|
18579
18958
|
for (const c of candidates) {
|
|
18580
|
-
if (
|
|
18959
|
+
if (existsSync20(join20(root, c))) return root;
|
|
18581
18960
|
}
|
|
18582
18961
|
try {
|
|
18583
|
-
const entries = await
|
|
18962
|
+
const entries = await readdir4(root, { withFileTypes: true });
|
|
18584
18963
|
for (const entry of entries) {
|
|
18585
18964
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
18586
18965
|
for (const c of candidates) {
|
|
18587
|
-
if (
|
|
18588
|
-
return
|
|
18966
|
+
if (existsSync20(join20(root, entry.name, c))) {
|
|
18967
|
+
return join20(root, entry.name);
|
|
18589
18968
|
}
|
|
18590
18969
|
}
|
|
18591
18970
|
}
|
|
18592
18971
|
} catch {
|
|
18593
18972
|
}
|
|
18594
18973
|
try {
|
|
18595
|
-
const entries = await
|
|
18974
|
+
const entries = await readdir4(root, { withFileTypes: true });
|
|
18596
18975
|
for (const entry of entries) {
|
|
18597
18976
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
18598
18977
|
let subEntries;
|
|
18599
18978
|
try {
|
|
18600
|
-
subEntries = await
|
|
18979
|
+
subEntries = await readdir4(join20(root, entry.name), { withFileTypes: true });
|
|
18601
18980
|
} catch {
|
|
18602
18981
|
continue;
|
|
18603
18982
|
}
|
|
18604
18983
|
for (const sub of subEntries) {
|
|
18605
18984
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
18606
18985
|
for (const c of candidates) {
|
|
18607
|
-
if (
|
|
18608
|
-
return
|
|
18986
|
+
if (existsSync20(join20(root, entry.name, sub.name, c))) {
|
|
18987
|
+
return join20(root, entry.name, sub.name);
|
|
18609
18988
|
}
|
|
18610
18989
|
}
|
|
18611
18990
|
}
|
|
@@ -18629,10 +19008,10 @@ __export(discover_exports, {
|
|
|
18629
19008
|
invalidateSkillCache: () => invalidateSkillCache,
|
|
18630
19009
|
stripFrontmatter: () => stripFrontmatter2
|
|
18631
19010
|
});
|
|
18632
|
-
import { readdir as
|
|
19011
|
+
import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
|
|
18633
19012
|
import { createHash } from "crypto";
|
|
18634
19013
|
import { homedir as homedir5 } from "os";
|
|
18635
|
-
import { join as
|
|
19014
|
+
import { join as join21 } from "path";
|
|
18636
19015
|
function invalidateSkillCache() {
|
|
18637
19016
|
cachedSkills = null;
|
|
18638
19017
|
cacheTimestamp = 0;
|
|
@@ -18650,7 +19029,7 @@ async function discoverAllSkills() {
|
|
|
18650
19029
|
const rawSkills = [];
|
|
18651
19030
|
rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
|
|
18652
19031
|
for (const backendId of getAllBackendIds()) {
|
|
18653
|
-
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [
|
|
19032
|
+
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join21(homedir5(), `.${backendId}`, "skills")];
|
|
18654
19033
|
for (const dir of dirs) {
|
|
18655
19034
|
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
18656
19035
|
}
|
|
@@ -18669,7 +19048,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
18669
19048
|
const results = [];
|
|
18670
19049
|
let entries;
|
|
18671
19050
|
try {
|
|
18672
|
-
entries = await
|
|
19051
|
+
entries = await readdir5(skillsDir, { withFileTypes: true });
|
|
18673
19052
|
} catch {
|
|
18674
19053
|
return results;
|
|
18675
19054
|
}
|
|
@@ -18678,7 +19057,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
18678
19057
|
let content;
|
|
18679
19058
|
let resolvedPath;
|
|
18680
19059
|
for (const candidate of SKILL_FILE_CANDIDATES) {
|
|
18681
|
-
const p =
|
|
19060
|
+
const p = join21(skillsDir, entry.name, candidate);
|
|
18682
19061
|
try {
|
|
18683
19062
|
content = await readFile5(p, "utf-8");
|
|
18684
19063
|
resolvedPath = p;
|
|
@@ -18695,9 +19074,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
18695
19074
|
description: frontmatter.description,
|
|
18696
19075
|
filePath: resolvedPath,
|
|
18697
19076
|
source,
|
|
18698
|
-
contentHash: hash
|
|
18699
|
-
compatibleBackends: frontmatter.compatibleBackends,
|
|
18700
|
-
recommendedModel: frontmatter.recommendedModel
|
|
19077
|
+
contentHash: hash
|
|
18701
19078
|
});
|
|
18702
19079
|
}
|
|
18703
19080
|
return results;
|
|
@@ -18726,9 +19103,7 @@ function mergeAndDeduplicate(raw) {
|
|
|
18726
19103
|
filePath: primary.filePath,
|
|
18727
19104
|
source: primary.source,
|
|
18728
19105
|
sources: [...new Set(allSources)],
|
|
18729
|
-
contentHash: primary.contentHash
|
|
18730
|
-
compatibleBackends: primary.compatibleBackends,
|
|
18731
|
-
recommendedModel: primary.recommendedModel
|
|
19106
|
+
contentHash: primary.contentHash
|
|
18732
19107
|
});
|
|
18733
19108
|
} else {
|
|
18734
19109
|
for (const [hash, copies] of byHash) {
|
|
@@ -18740,9 +19115,7 @@ function mergeAndDeduplicate(raw) {
|
|
|
18740
19115
|
filePath: primary.filePath,
|
|
18741
19116
|
source: primary.source,
|
|
18742
19117
|
sources: [...new Set(allSources)],
|
|
18743
|
-
contentHash: hash
|
|
18744
|
-
compatibleBackends: primary.compatibleBackends,
|
|
18745
|
-
recommendedModel: primary.recommendedModel
|
|
19118
|
+
contentHash: hash
|
|
18746
19119
|
});
|
|
18747
19120
|
}
|
|
18748
19121
|
}
|
|
@@ -18762,13 +19135,9 @@ function parseFrontmatter2(content, fallbackName) {
|
|
|
18762
19135
|
const fm = fmMatch[1];
|
|
18763
19136
|
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
18764
19137
|
const descMatch = fm.match(/^description:\s*(.+)$/m);
|
|
18765
|
-
const compatMatch = fm.match(/^compatible_backends:\s*(.+)$/m);
|
|
18766
|
-
const modelMatch = fm.match(/^recommended_model:\s*(.+)$/m);
|
|
18767
19138
|
return {
|
|
18768
19139
|
name: nameMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? fallbackName,
|
|
18769
|
-
description: descMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? ""
|
|
18770
|
-
compatibleBackends: compatMatch ? compatMatch[1].split(",").map((s) => s.trim().toLowerCase()) : void 0,
|
|
18771
|
-
recommendedModel: modelMatch?.[1]?.trim() ?? void 0
|
|
19140
|
+
description: descMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? ""
|
|
18772
19141
|
};
|
|
18773
19142
|
}
|
|
18774
19143
|
function stripFrontmatter2(content) {
|
|
@@ -18782,15 +19151,15 @@ var init_discover = __esm({
|
|
|
18782
19151
|
init_backends();
|
|
18783
19152
|
SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
|
|
18784
19153
|
BACKEND_SKILL_DIRS = {
|
|
18785
|
-
claude: [
|
|
18786
|
-
gemini: [
|
|
19154
|
+
claude: [join21(homedir5(), ".claude", "skills")],
|
|
19155
|
+
gemini: [join21(homedir5(), ".gemini", "skills")],
|
|
18787
19156
|
codex: [
|
|
18788
|
-
|
|
18789
|
-
|
|
19157
|
+
join21(homedir5(), ".agents", "skills"),
|
|
19158
|
+
join21(homedir5(), ".codex", "skills")
|
|
18790
19159
|
],
|
|
18791
19160
|
cursor: [
|
|
18792
|
-
|
|
18793
|
-
|
|
19161
|
+
join21(homedir5(), ".cursor", "skills"),
|
|
19162
|
+
join21(homedir5(), ".cursor", "skills-cursor")
|
|
18794
19163
|
]
|
|
18795
19164
|
};
|
|
18796
19165
|
CACHE_TTL_MS2 = 3e5;
|
|
@@ -20014,13 +20383,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
20014
20383
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
20015
20384
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
20016
20385
|
if (current === "frozen") {
|
|
20017
|
-
const { readFileSync: readFileSync29, existsSync:
|
|
20018
|
-
const { join:
|
|
20386
|
+
const { readFileSync: readFileSync29, existsSync: existsSync58 } = await import("fs");
|
|
20387
|
+
const { join: join37 } = await import("path");
|
|
20019
20388
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
20020
|
-
const soulPath =
|
|
20021
|
-
const userPath =
|
|
20022
|
-
const soul =
|
|
20023
|
-
const user =
|
|
20389
|
+
const soulPath = join37(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
20390
|
+
const userPath = join37(CC_CLAW_HOME3, "identity/USER.md");
|
|
20391
|
+
const soul = existsSync58(soulPath) ? readFileSync29(soulPath, "utf-8") : "";
|
|
20392
|
+
const user = existsSync58(userPath) ? readFileSync29(userPath, "utf-8") : "";
|
|
20024
20393
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
20025
20394
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
20026
20395
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -20097,11 +20466,11 @@ var init_evolve2 = __esm({
|
|
|
20097
20466
|
});
|
|
20098
20467
|
|
|
20099
20468
|
// src/optimizer/identity-audit.ts
|
|
20100
|
-
import { readFileSync as readFileSync11, existsSync as
|
|
20101
|
-
import { join as
|
|
20469
|
+
import { readFileSync as readFileSync11, existsSync as existsSync21, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
20470
|
+
import { join as join22 } from "path";
|
|
20102
20471
|
function readIdentityFile2(filename) {
|
|
20103
20472
|
try {
|
|
20104
|
-
return readFileSync11(
|
|
20473
|
+
return readFileSync11(join22(IDENTITY_PATH, filename), "utf-8");
|
|
20105
20474
|
} catch {
|
|
20106
20475
|
return "";
|
|
20107
20476
|
}
|
|
@@ -20116,13 +20485,13 @@ function getMtime(filepath) {
|
|
|
20116
20485
|
function findBackupFiles() {
|
|
20117
20486
|
const backups = [];
|
|
20118
20487
|
const dirs = [IDENTITY_PATH];
|
|
20119
|
-
const contextDir =
|
|
20120
|
-
if (
|
|
20488
|
+
const contextDir = join22(IDENTITY_PATH, "..", "workspace", "context");
|
|
20489
|
+
if (existsSync21(contextDir)) dirs.push(contextDir);
|
|
20121
20490
|
for (const dir of dirs) {
|
|
20122
20491
|
try {
|
|
20123
20492
|
for (const entry of readdirSync10(dir)) {
|
|
20124
20493
|
if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
|
|
20125
|
-
backups.push(
|
|
20494
|
+
backups.push(join22(dir, entry));
|
|
20126
20495
|
}
|
|
20127
20496
|
}
|
|
20128
20497
|
} catch {
|
|
@@ -20143,9 +20512,9 @@ function computeIdentityStats(pendingProposals, driftPercent) {
|
|
|
20143
20512
|
userChars,
|
|
20144
20513
|
ccClawChars,
|
|
20145
20514
|
boilerplateChars,
|
|
20146
|
-
soulMtime: getMtime(
|
|
20147
|
-
userMtime: getMtime(
|
|
20148
|
-
ccClawMtime: getMtime(
|
|
20515
|
+
soulMtime: getMtime(join22(IDENTITY_PATH, "SOUL.md")),
|
|
20516
|
+
userMtime: getMtime(join22(IDENTITY_PATH, "USER.md")),
|
|
20517
|
+
ccClawMtime: getMtime(join22(IDENTITY_PATH, "CC-CLAW.md")),
|
|
20149
20518
|
backupFiles: findBackupFiles(),
|
|
20150
20519
|
estimatedTokens: Math.ceil(ccClawChars / 4),
|
|
20151
20520
|
pendingEvolveProposals: pendingProposals,
|
|
@@ -20254,8 +20623,8 @@ var init_identity_audit = __esm({
|
|
|
20254
20623
|
});
|
|
20255
20624
|
|
|
20256
20625
|
// src/optimizer/skill-audit.ts
|
|
20257
|
-
import { readFileSync as readFileSync12, existsSync as
|
|
20258
|
-
import { join as
|
|
20626
|
+
import { readFileSync as readFileSync12, existsSync as existsSync22 } from "fs";
|
|
20627
|
+
import { join as join23, basename as basename3 } from "path";
|
|
20259
20628
|
function parseFrontmatter3(content) {
|
|
20260
20629
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
20261
20630
|
if (!fmMatch) return {};
|
|
@@ -20270,10 +20639,6 @@ function parseFrontmatter3(content) {
|
|
|
20270
20639
|
const singleDesc = fm.match(/^description:\s*(.+)/m);
|
|
20271
20640
|
if (singleDesc) result.description = singleDesc[1].trim().replace(/^["']|["']$/g, "");
|
|
20272
20641
|
}
|
|
20273
|
-
const backendsMatch = fm.match(/^compatible_backends:\s*(.+)/m);
|
|
20274
|
-
if (backendsMatch) result.compatibleBackends = backendsMatch[1].trim();
|
|
20275
|
-
const modelMatch = fm.match(/^recommended_model:\s*(.+)/m);
|
|
20276
|
-
if (modelMatch) result.recommendedModel = modelMatch[1].trim();
|
|
20277
20642
|
return result;
|
|
20278
20643
|
}
|
|
20279
20644
|
function detectDependentSkills(content) {
|
|
@@ -20298,7 +20663,7 @@ function computeSkillStats(skillPath) {
|
|
|
20298
20663
|
const content = readFileSync12(skillPath, "utf-8");
|
|
20299
20664
|
const lines = content.split("\n");
|
|
20300
20665
|
return {
|
|
20301
|
-
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(
|
|
20666
|
+
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join23(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
20302
20667
|
skillPath,
|
|
20303
20668
|
lineCount: lines.length,
|
|
20304
20669
|
charCount: content.length,
|
|
@@ -20318,11 +20683,11 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
20318
20683
|
const results = [];
|
|
20319
20684
|
for (const name of depNames) {
|
|
20320
20685
|
const candidates = [
|
|
20321
|
-
|
|
20322
|
-
|
|
20686
|
+
join23(ccClawSkillsDir, name, "SKILL.md"),
|
|
20687
|
+
join23(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
20323
20688
|
];
|
|
20324
20689
|
for (const candidate of candidates) {
|
|
20325
|
-
if (
|
|
20690
|
+
if (existsSync22(candidate)) {
|
|
20326
20691
|
try {
|
|
20327
20692
|
const content = readFileSync12(candidate, "utf-8");
|
|
20328
20693
|
results.push({
|
|
@@ -20353,8 +20718,6 @@ Lines: ${stats.lineCount} (cap: 500 \u2014 over 500 = progressive disclosure nee
|
|
|
20353
20718
|
Chars: ${stats.charCount} (~${stats.estimatedTokens} tokens)
|
|
20354
20719
|
Frontmatter name: ${stats.frontmatter.name || "(missing)"}
|
|
20355
20720
|
Frontmatter description: ${stats.frontmatter.description ? stats.frontmatter.description.slice(0, 200) : "(missing)"}
|
|
20356
|
-
Compatible backends: ${stats.frontmatter.compatibleBackends || "(not specified)"}
|
|
20357
|
-
Recommended model: ${stats.frontmatter.recommendedModel || "(not specified)"}
|
|
20358
20721
|
Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.join(", ") : "none"}`);
|
|
20359
20722
|
sections.push(`[Rubric \u2014 Evaluate Each Area]
|
|
20360
20723
|
|
|
@@ -20362,7 +20725,6 @@ Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.jo
|
|
|
20362
20725
|
- Is 'name' present and descriptive?
|
|
20363
20726
|
- Does 'description' include BOTH what the skill does AND trigger info (when to use it)?
|
|
20364
20727
|
- A good description tells the AI exactly when to activate this skill
|
|
20365
|
-
- Should 'compatible_backends' or 'recommended_model' be specified?
|
|
20366
20728
|
|
|
20367
20729
|
2. BODY STRUCTURE
|
|
20368
20730
|
- Over 500 lines? \u2192 needs progressive disclosure (essentials in body, details in references/)
|
|
@@ -20439,8 +20801,8 @@ __export(analyze_exports2, {
|
|
|
20439
20801
|
});
|
|
20440
20802
|
import { spawn as spawn7 } from "child_process";
|
|
20441
20803
|
import { createInterface as createInterface7 } from "readline";
|
|
20442
|
-
import { readFileSync as readFileSync13, existsSync as
|
|
20443
|
-
import { join as
|
|
20804
|
+
import { readFileSync as readFileSync13, existsSync as existsSync23, readdirSync as readdirSync12 } from "fs";
|
|
20805
|
+
import { join as join24 } from "path";
|
|
20444
20806
|
import { homedir as homedir7 } from "os";
|
|
20445
20807
|
function parseOptimizeOutput(raw, validAreas) {
|
|
20446
20808
|
if (!raw || raw.includes("NO_FINDINGS")) return [];
|
|
@@ -20570,20 +20932,20 @@ function getModelDisplayInfo(chatId) {
|
|
|
20570
20932
|
}
|
|
20571
20933
|
function readIdentityFile3(filename) {
|
|
20572
20934
|
try {
|
|
20573
|
-
return readFileSync13(
|
|
20935
|
+
return readFileSync13(join24(IDENTITY_PATH, filename), "utf-8");
|
|
20574
20936
|
} catch {
|
|
20575
20937
|
return "";
|
|
20576
20938
|
}
|
|
20577
20939
|
}
|
|
20578
20940
|
function loadContextFiles2() {
|
|
20579
|
-
const contextDir =
|
|
20941
|
+
const contextDir = join24(homedir7(), ".cc-claw", "workspace", "context");
|
|
20580
20942
|
const results = [];
|
|
20581
|
-
if (!
|
|
20943
|
+
if (!existsSync23(contextDir)) return results;
|
|
20582
20944
|
try {
|
|
20583
20945
|
for (const entry of readdirSync12(contextDir)) {
|
|
20584
20946
|
if (!entry.endsWith(".md")) continue;
|
|
20585
20947
|
try {
|
|
20586
|
-
const content = readFileSync13(
|
|
20948
|
+
const content = readFileSync13(join24(contextDir, entry), "utf-8");
|
|
20587
20949
|
results.push({ name: entry, content });
|
|
20588
20950
|
} catch {
|
|
20589
20951
|
}
|
|
@@ -20634,7 +20996,7 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20634
20996
|
const stats = computeSkillStats(skillPath);
|
|
20635
20997
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
20636
20998
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
20637
|
-
const ccClawSkillsDir =
|
|
20999
|
+
const ccClawSkillsDir = join24(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20638
21000
|
const skillContent = readFileSync13(skillPath, "utf-8");
|
|
20639
21001
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
20640
21002
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
@@ -20649,13 +21011,13 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20649
21011
|
};
|
|
20650
21012
|
}
|
|
20651
21013
|
function listCcClawSkills() {
|
|
20652
|
-
const skillsDir =
|
|
21014
|
+
const skillsDir = join24(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20653
21015
|
const entries = [];
|
|
20654
|
-
if (!
|
|
21016
|
+
if (!existsSync23(skillsDir)) return entries;
|
|
20655
21017
|
try {
|
|
20656
21018
|
for (const dir of readdirSync12(skillsDir)) {
|
|
20657
|
-
const skillFile =
|
|
20658
|
-
if (!
|
|
21019
|
+
const skillFile = join24(skillsDir, dir, "SKILL.md");
|
|
21020
|
+
if (!existsSync23(skillFile)) continue;
|
|
20659
21021
|
let description = "skill";
|
|
20660
21022
|
try {
|
|
20661
21023
|
const content = readFileSync13(skillFile, "utf-8");
|
|
@@ -20978,8 +21340,8 @@ var init_ui2 = __esm({
|
|
|
20978
21340
|
});
|
|
20979
21341
|
|
|
20980
21342
|
// src/router/optimize.ts
|
|
20981
|
-
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as
|
|
20982
|
-
import { join as
|
|
21343
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync24, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
21344
|
+
import { join as join25, dirname as dirname4 } from "path";
|
|
20983
21345
|
import { homedir as homedir8 } from "os";
|
|
20984
21346
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
20985
21347
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
@@ -21160,7 +21522,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
21160
21522
|
} = await Promise.resolve().then(() => (init_ui2(), ui_exports));
|
|
21161
21523
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
21162
21524
|
if (!modelInfo) return;
|
|
21163
|
-
const skillPath =
|
|
21525
|
+
const skillPath = join25(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
|
|
21164
21526
|
const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
|
|
21165
21527
|
chatId,
|
|
21166
21528
|
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
@@ -21249,7 +21611,7 @@ async function applyFinding(chatId, channel, index) {
|
|
|
21249
21611
|
await showFinding(chatId, channel, index + 1);
|
|
21250
21612
|
return;
|
|
21251
21613
|
}
|
|
21252
|
-
if (!
|
|
21614
|
+
if (!existsSync24(targetPath)) {
|
|
21253
21615
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
21254
21616
|
session2.skipped.push(index);
|
|
21255
21617
|
await showFinding(chatId, channel, index + 1);
|
|
@@ -21331,14 +21693,14 @@ async function finishReview(chatId, channel) {
|
|
|
21331
21693
|
activeSessions.delete(chatId);
|
|
21332
21694
|
}
|
|
21333
21695
|
function resolveTargetFile(location, auditTarget) {
|
|
21334
|
-
const ccClawHome =
|
|
21696
|
+
const ccClawHome = join25(homedir8(), ".cc-claw");
|
|
21335
21697
|
const filePart = location.split(":")[0]?.trim();
|
|
21336
21698
|
if (!filePart) return null;
|
|
21337
|
-
if (filePart === "SOUL.md") return
|
|
21338
|
-
if (filePart === "USER.md") return
|
|
21339
|
-
if (filePart === "CC-CLAW.md") return
|
|
21699
|
+
if (filePart === "SOUL.md") return join25(ccClawHome, "identity", "SOUL.md");
|
|
21700
|
+
if (filePart === "USER.md") return join25(ccClawHome, "identity", "USER.md");
|
|
21701
|
+
if (filePart === "CC-CLAW.md") return join25(ccClawHome, "identity", "CC-CLAW.md");
|
|
21340
21702
|
if (filePart === "SKILL.md" && auditTarget !== "identity") {
|
|
21341
|
-
return
|
|
21703
|
+
return join25(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
|
|
21342
21704
|
}
|
|
21343
21705
|
return null;
|
|
21344
21706
|
}
|
|
@@ -21346,7 +21708,7 @@ function pruneBackups2(absolutePath) {
|
|
|
21346
21708
|
const dir = dirname4(absolutePath);
|
|
21347
21709
|
const baseName = absolutePath.split("/").pop() ?? "";
|
|
21348
21710
|
try {
|
|
21349
|
-
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) =>
|
|
21711
|
+
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join25(dir, f));
|
|
21350
21712
|
while (backups.length > 3) {
|
|
21351
21713
|
const oldest = backups.shift();
|
|
21352
21714
|
try {
|
|
@@ -21368,6 +21730,193 @@ var init_optimize = __esm({
|
|
|
21368
21730
|
}
|
|
21369
21731
|
});
|
|
21370
21732
|
|
|
21733
|
+
// src/skills/auto-create.ts
|
|
21734
|
+
var auto_create_exports = {};
|
|
21735
|
+
__export(auto_create_exports, {
|
|
21736
|
+
buildSessionExtractionPrompt: () => buildSessionExtractionPrompt,
|
|
21737
|
+
buildSkillExtractionPrompt: () => buildSkillExtractionPrompt,
|
|
21738
|
+
clearPendingDraft: () => clearPendingDraft,
|
|
21739
|
+
getPendingDraft: () => getPendingDraft,
|
|
21740
|
+
isSkillWorthy: () => isSkillWorthy,
|
|
21741
|
+
parseExtractedSkill: () => parseExtractedSkill,
|
|
21742
|
+
saveSkill: () => saveSkill,
|
|
21743
|
+
storePendingDraft: () => storePendingDraft
|
|
21744
|
+
});
|
|
21745
|
+
import { join as join26 } from "path";
|
|
21746
|
+
import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
21747
|
+
function isSkillWorthy(signals) {
|
|
21748
|
+
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
21749
|
+
if (toolUseCount < 12) return false;
|
|
21750
|
+
if (elapsedMs < 3e5) return false;
|
|
21751
|
+
if (tokenOutput < 3e3) return false;
|
|
21752
|
+
const words = userMessage.split(/\s+/).length;
|
|
21753
|
+
if (words < 5) return false;
|
|
21754
|
+
const skillPatterns = /\b(use|using|run|with|the)\s+(the\s+)?[\w-]+\s+skill\b/i;
|
|
21755
|
+
if (skillPatterns.test(userMessage)) {
|
|
21756
|
+
log(`[auto-skill] Skipping \u2014 message references an existing skill`);
|
|
21757
|
+
return false;
|
|
21758
|
+
}
|
|
21759
|
+
log(`[auto-skill] Skill-worthy: tools=${toolUseCount}, tokens=${tokenOutput}, elapsed=${elapsedMs}ms`);
|
|
21760
|
+
return true;
|
|
21761
|
+
}
|
|
21762
|
+
function buildSkillExtractionPrompt(userMessage, assistantResponse) {
|
|
21763
|
+
const cappedResponse = assistantResponse.length > 8e3 ? assistantResponse.slice(0, 8e3) + "\n\n[...truncated...]" : assistantResponse;
|
|
21764
|
+
return [
|
|
21765
|
+
"You are a skill extraction assistant. Analyze the following completed task and create a reusable SKILL.md file.",
|
|
21766
|
+
"",
|
|
21767
|
+
"A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
|
|
21768
|
+
"Focus on the PROCESS and APPROACH, not the specific details of this particular task.",
|
|
21769
|
+
"",
|
|
21770
|
+
"## Completed Task",
|
|
21771
|
+
"",
|
|
21772
|
+
`**User request:** ${userMessage}`,
|
|
21773
|
+
"",
|
|
21774
|
+
`**Assistant response:**`,
|
|
21775
|
+
cappedResponse,
|
|
21776
|
+
"",
|
|
21777
|
+
"## Output Format",
|
|
21778
|
+
"",
|
|
21779
|
+
"Output ONLY the SKILL.md content with this exact format:",
|
|
21780
|
+
"",
|
|
21781
|
+
"```",
|
|
21782
|
+
"---",
|
|
21783
|
+
'name: "skill-name-in-kebab-case"',
|
|
21784
|
+
'description: "What this skill does and when to use it"',
|
|
21785
|
+
"---",
|
|
21786
|
+
"",
|
|
21787
|
+
"## [Skill Title]",
|
|
21788
|
+
"",
|
|
21789
|
+
"[Step-by-step instructions for accomplishing this type of task]",
|
|
21790
|
+
"[Include decision points, best practices, and common pitfalls]",
|
|
21791
|
+
"[Keep it general enough to be reusable, specific enough to be helpful]",
|
|
21792
|
+
"```",
|
|
21793
|
+
"",
|
|
21794
|
+
"Rules:",
|
|
21795
|
+
"- The skill name should be descriptive and kebab-case",
|
|
21796
|
+
"- The description should explain WHAT it does AND WHEN to use it",
|
|
21797
|
+
"- Instructions should be general (not tied to specific file names or projects)",
|
|
21798
|
+
"- Include any important caveats or prerequisites",
|
|
21799
|
+
"- Keep it under 100 lines"
|
|
21800
|
+
].join("\n");
|
|
21801
|
+
}
|
|
21802
|
+
function buildSessionExtractionPrompt(sessionMessages) {
|
|
21803
|
+
const transcript = [];
|
|
21804
|
+
let totalChars = 0;
|
|
21805
|
+
const CAP = 12e3;
|
|
21806
|
+
for (const msg of sessionMessages) {
|
|
21807
|
+
const prefix = msg.role === "user" ? "USER" : "ASSISTANT";
|
|
21808
|
+
const text = msg.text.length > 2e3 ? msg.text.slice(0, 2e3) + " [...]" : msg.text;
|
|
21809
|
+
if (totalChars + text.length > CAP) {
|
|
21810
|
+
transcript.push(`[...earlier messages truncated...]`);
|
|
21811
|
+
break;
|
|
21812
|
+
}
|
|
21813
|
+
transcript.push(`[${prefix}]: ${text}`);
|
|
21814
|
+
totalChars += text.length;
|
|
21815
|
+
}
|
|
21816
|
+
return [
|
|
21817
|
+
"You are a skill extraction assistant. Review the following session transcript and create a reusable SKILL.md file.",
|
|
21818
|
+
"",
|
|
21819
|
+
"A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
|
|
21820
|
+
"Analyze the ENTIRE session to understand:",
|
|
21821
|
+
"- What the user was trying to accomplish",
|
|
21822
|
+
"- What tools and approaches the AI used",
|
|
21823
|
+
"- Any feedback the user gave (corrections, preferences, refinements)",
|
|
21824
|
+
"- The overall workflow pattern",
|
|
21825
|
+
"",
|
|
21826
|
+
"Focus on the PROCESS and APPROACH, not the specific details of this session.",
|
|
21827
|
+
"Incorporate user feedback as best practices or decision points in the skill.",
|
|
21828
|
+
"",
|
|
21829
|
+
"## Session Transcript",
|
|
21830
|
+
"",
|
|
21831
|
+
...transcript,
|
|
21832
|
+
"",
|
|
21833
|
+
"## Output Format",
|
|
21834
|
+
"",
|
|
21835
|
+
"Output ONLY the SKILL.md content with this exact format:",
|
|
21836
|
+
"",
|
|
21837
|
+
"```",
|
|
21838
|
+
"---",
|
|
21839
|
+
'name: "skill-name-in-kebab-case"',
|
|
21840
|
+
'description: "What this skill does and when to use it"',
|
|
21841
|
+
"---",
|
|
21842
|
+
"",
|
|
21843
|
+
"## [Skill Title]",
|
|
21844
|
+
"",
|
|
21845
|
+
"[Step-by-step instructions for accomplishing this type of task]",
|
|
21846
|
+
"[Include decision points, best practices, and common pitfalls]",
|
|
21847
|
+
"[Incorporate any user preferences or corrections from the session]",
|
|
21848
|
+
"[Keep it general enough to be reusable, specific enough to be helpful]",
|
|
21849
|
+
"```",
|
|
21850
|
+
"",
|
|
21851
|
+
"Rules:",
|
|
21852
|
+
"- The skill name should be descriptive and kebab-case",
|
|
21853
|
+
"- The description should explain WHAT it does AND WHEN to use it",
|
|
21854
|
+
"- Instructions should be general (not tied to specific file names or projects)",
|
|
21855
|
+
"- If the user corrected the AI during the session, bake those corrections into the skill as best practices",
|
|
21856
|
+
"- Include any important caveats or prerequisites",
|
|
21857
|
+
"- Keep it under 100 lines"
|
|
21858
|
+
].join("\n");
|
|
21859
|
+
}
|
|
21860
|
+
function parseExtractedSkill(llmResponse) {
|
|
21861
|
+
let content = llmResponse;
|
|
21862
|
+
const fenceMatch = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
|
|
21863
|
+
if (fenceMatch) content = fenceMatch[1].trim();
|
|
21864
|
+
let fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
21865
|
+
if (!fmMatch) {
|
|
21866
|
+
const greedyFence = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*)```/);
|
|
21867
|
+
if (greedyFence) {
|
|
21868
|
+
content = greedyFence[1].trim();
|
|
21869
|
+
fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
21870
|
+
}
|
|
21871
|
+
}
|
|
21872
|
+
if (!fmMatch) {
|
|
21873
|
+
fmMatch = llmResponse.match(/---\s*\n([\s\S]*?)\n---/);
|
|
21874
|
+
if (fmMatch) {
|
|
21875
|
+
const fmStart = llmResponse.indexOf(fmMatch[0]);
|
|
21876
|
+
content = llmResponse.slice(fmStart).replace(/```\s*$/, "").trim();
|
|
21877
|
+
}
|
|
21878
|
+
}
|
|
21879
|
+
if (!fmMatch) {
|
|
21880
|
+
warn("[auto-skill] No frontmatter found in extracted skill");
|
|
21881
|
+
return null;
|
|
21882
|
+
}
|
|
21883
|
+
const nameMatch = fmMatch[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);
|
|
21884
|
+
if (!nameMatch) {
|
|
21885
|
+
warn("[auto-skill] No name field in skill frontmatter");
|
|
21886
|
+
return null;
|
|
21887
|
+
}
|
|
21888
|
+
const name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, "-");
|
|
21889
|
+
return { name, content };
|
|
21890
|
+
}
|
|
21891
|
+
async function saveSkill(name, content) {
|
|
21892
|
+
const dir = join26(SKILLS_PATH, name);
|
|
21893
|
+
await mkdir4(dir, { recursive: true });
|
|
21894
|
+
const filePath = join26(dir, "SKILL.md");
|
|
21895
|
+
await writeFile4(filePath, content, "utf-8");
|
|
21896
|
+
invalidateSkillCache();
|
|
21897
|
+
log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
|
|
21898
|
+
return { path: filePath };
|
|
21899
|
+
}
|
|
21900
|
+
function storePendingDraft(chatId, draft) {
|
|
21901
|
+
pendingDrafts.set(chatId, draft);
|
|
21902
|
+
}
|
|
21903
|
+
function getPendingDraft(chatId) {
|
|
21904
|
+
return pendingDrafts.get(chatId);
|
|
21905
|
+
}
|
|
21906
|
+
function clearPendingDraft(chatId) {
|
|
21907
|
+
pendingDrafts.delete(chatId);
|
|
21908
|
+
}
|
|
21909
|
+
var pendingDrafts;
|
|
21910
|
+
var init_auto_create = __esm({
|
|
21911
|
+
"src/skills/auto-create.ts"() {
|
|
21912
|
+
"use strict";
|
|
21913
|
+
init_paths();
|
|
21914
|
+
init_discover();
|
|
21915
|
+
init_log();
|
|
21916
|
+
pendingDrafts = /* @__PURE__ */ new Map();
|
|
21917
|
+
}
|
|
21918
|
+
});
|
|
21919
|
+
|
|
21371
21920
|
// src/council/types.ts
|
|
21372
21921
|
var COUNCIL_MIN_PARTICIPANTS, COUNCIL_MAX_ROUNDS, COUNCIL_WIZARD_TIMEOUT_MS;
|
|
21373
21922
|
var init_types4 = __esm({
|
|
@@ -21720,6 +22269,49 @@ Use /skills to see it.`, { parseMode: "plain" });
|
|
|
21720
22269
|
await channel.sendText(chatId, `Installation failed: ${result.error}`, { parseMode: "plain" });
|
|
21721
22270
|
}
|
|
21722
22271
|
}
|
|
22272
|
+
async function handleExtractSkillCommand(chatId, commandArgs, msg, channel) {
|
|
22273
|
+
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
22274
|
+
const sessionMessages = getLog2(chatId);
|
|
22275
|
+
if (sessionMessages.length < 2) {
|
|
22276
|
+
await channel.sendText(chatId, "No session history to extract from. Have a conversation first, then run /extract_skill.", { parseMode: "plain" });
|
|
22277
|
+
return;
|
|
22278
|
+
}
|
|
22279
|
+
await channel.sendText(chatId, `Reviewing session (${Math.floor(sessionMessages.length / 2)} exchanges)...`, { parseMode: "plain" });
|
|
22280
|
+
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22281
|
+
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
22282
|
+
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22283
|
+
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
22284
|
+
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
22285
|
+
const extracted = parseExtractedSkill2(response.text);
|
|
22286
|
+
if (!extracted) {
|
|
22287
|
+
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
22288
|
+
return;
|
|
22289
|
+
}
|
|
22290
|
+
storePendingDraft2(chatId, {
|
|
22291
|
+
name: extracted.name,
|
|
22292
|
+
content: extracted.content,
|
|
22293
|
+
userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
|
|
22294
|
+
assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
|
|
22295
|
+
});
|
|
22296
|
+
const preview = extracted.content.length > 3500 ? extracted.content.slice(0, 3500) + "\n\n[...truncated...]" : extracted.content;
|
|
22297
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
22298
|
+
await channel.sendText(chatId, `Extracted skill: "${extracted.name}"
|
|
22299
|
+
|
|
22300
|
+
${preview}`, { parseMode: "plain" });
|
|
22301
|
+
await channel.sendKeyboard(
|
|
22302
|
+
chatId,
|
|
22303
|
+
`Save this skill?`,
|
|
22304
|
+
[[
|
|
22305
|
+
{ label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
|
|
22306
|
+
{ label: "\u2715 Discard", data: "skill:discard" }
|
|
22307
|
+
]]
|
|
22308
|
+
);
|
|
22309
|
+
} else {
|
|
22310
|
+
const { saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22311
|
+
const result = await saveSkill2(extracted.name, extracted.content);
|
|
22312
|
+
await channel.sendText(chatId, `Skill "${extracted.name}" saved to ${result.path}`, { parseMode: "plain" });
|
|
22313
|
+
}
|
|
22314
|
+
}
|
|
21723
22315
|
async function handleSetupProfileCommand(chatId, commandArgs, msg, channel) {
|
|
21724
22316
|
await startProfileWizard(chatId, channel);
|
|
21725
22317
|
}
|
|
@@ -23364,6 +23956,10 @@ async function handleCommand(msg, channel) {
|
|
|
23364
23956
|
case "skill-install":
|
|
23365
23957
|
await handleSkillInstallCommand(chatId, commandArgs, msg, channel);
|
|
23366
23958
|
break;
|
|
23959
|
+
case "extract_skill":
|
|
23960
|
+
case "extractskill":
|
|
23961
|
+
await handleExtractSkillCommand(chatId, commandArgs, msg, channel);
|
|
23962
|
+
break;
|
|
23367
23963
|
case "setup-profile":
|
|
23368
23964
|
await handleSetupProfileCommand(chatId, commandArgs, msg, channel);
|
|
23369
23965
|
break;
|
|
@@ -23450,134 +24046,6 @@ var init_commands = __esm({
|
|
|
23450
24046
|
}
|
|
23451
24047
|
});
|
|
23452
24048
|
|
|
23453
|
-
// src/skills/auto-create.ts
|
|
23454
|
-
var auto_create_exports = {};
|
|
23455
|
-
__export(auto_create_exports, {
|
|
23456
|
-
buildSkillExtractionPrompt: () => buildSkillExtractionPrompt,
|
|
23457
|
-
clearPendingDraft: () => clearPendingDraft,
|
|
23458
|
-
getPendingDraft: () => getPendingDraft,
|
|
23459
|
-
isSkillWorthy: () => isSkillWorthy,
|
|
23460
|
-
parseExtractedSkill: () => parseExtractedSkill,
|
|
23461
|
-
saveSkill: () => saveSkill,
|
|
23462
|
-
storePendingDraft: () => storePendingDraft
|
|
23463
|
-
});
|
|
23464
|
-
import { join as join25 } from "path";
|
|
23465
|
-
import { writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
23466
|
-
function isSkillWorthy(signals) {
|
|
23467
|
-
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
23468
|
-
if (toolUseCount < 8) return false;
|
|
23469
|
-
const supplementary = [
|
|
23470
|
-
tokenOutput >= 3e3,
|
|
23471
|
-
// substantial output
|
|
23472
|
-
elapsedMs >= 45e3
|
|
23473
|
-
// took real effort (45s+)
|
|
23474
|
-
].filter(Boolean).length;
|
|
23475
|
-
if (supplementary < 1) return false;
|
|
23476
|
-
const words = userMessage.split(/\s+/).length;
|
|
23477
|
-
if (words < 5) return false;
|
|
23478
|
-
log(`[auto-skill] Skill-worthy: tools=${toolUseCount}, tokens=${tokenOutput}, elapsed=${elapsedMs}ms`);
|
|
23479
|
-
return true;
|
|
23480
|
-
}
|
|
23481
|
-
function buildSkillExtractionPrompt(userMessage, assistantResponse) {
|
|
23482
|
-
const cappedResponse = assistantResponse.length > 8e3 ? assistantResponse.slice(0, 8e3) + "\n\n[...truncated...]" : assistantResponse;
|
|
23483
|
-
return [
|
|
23484
|
-
"You are a skill extraction assistant. Analyze the following completed task and create a reusable SKILL.md file.",
|
|
23485
|
-
"",
|
|
23486
|
-
"A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
|
|
23487
|
-
"Focus on the PROCESS and APPROACH, not the specific details of this particular task.",
|
|
23488
|
-
"",
|
|
23489
|
-
"## Completed Task",
|
|
23490
|
-
"",
|
|
23491
|
-
`**User request:** ${userMessage}`,
|
|
23492
|
-
"",
|
|
23493
|
-
`**Assistant response:**`,
|
|
23494
|
-
cappedResponse,
|
|
23495
|
-
"",
|
|
23496
|
-
"## Output Format",
|
|
23497
|
-
"",
|
|
23498
|
-
"Output ONLY the SKILL.md content with this exact format:",
|
|
23499
|
-
"",
|
|
23500
|
-
"```",
|
|
23501
|
-
"---",
|
|
23502
|
-
'name: "skill-name-in-kebab-case"',
|
|
23503
|
-
'description: "What this skill does and when to use it"',
|
|
23504
|
-
"---",
|
|
23505
|
-
"",
|
|
23506
|
-
"## [Skill Title]",
|
|
23507
|
-
"",
|
|
23508
|
-
"[Step-by-step instructions for accomplishing this type of task]",
|
|
23509
|
-
"[Include decision points, best practices, and common pitfalls]",
|
|
23510
|
-
"[Keep it general enough to be reusable, specific enough to be helpful]",
|
|
23511
|
-
"```",
|
|
23512
|
-
"",
|
|
23513
|
-
"Rules:",
|
|
23514
|
-
"- The skill name should be descriptive and kebab-case",
|
|
23515
|
-
"- The description should explain WHAT it does AND WHEN to use it",
|
|
23516
|
-
"- Instructions should be general (not tied to specific file names or projects)",
|
|
23517
|
-
"- Include any important caveats or prerequisites",
|
|
23518
|
-
"- Keep it under 100 lines"
|
|
23519
|
-
].join("\n");
|
|
23520
|
-
}
|
|
23521
|
-
function parseExtractedSkill(llmResponse) {
|
|
23522
|
-
let content = llmResponse;
|
|
23523
|
-
const fenceMatch = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
|
|
23524
|
-
if (fenceMatch) content = fenceMatch[1].trim();
|
|
23525
|
-
let fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
23526
|
-
if (!fmMatch) {
|
|
23527
|
-
const greedyFence = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*)```/);
|
|
23528
|
-
if (greedyFence) {
|
|
23529
|
-
content = greedyFence[1].trim();
|
|
23530
|
-
fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
23531
|
-
}
|
|
23532
|
-
}
|
|
23533
|
-
if (!fmMatch) {
|
|
23534
|
-
fmMatch = llmResponse.match(/---\s*\n([\s\S]*?)\n---/);
|
|
23535
|
-
if (fmMatch) {
|
|
23536
|
-
const fmStart = llmResponse.indexOf(fmMatch[0]);
|
|
23537
|
-
content = llmResponse.slice(fmStart).replace(/```\s*$/, "").trim();
|
|
23538
|
-
}
|
|
23539
|
-
}
|
|
23540
|
-
if (!fmMatch) {
|
|
23541
|
-
warn("[auto-skill] No frontmatter found in extracted skill");
|
|
23542
|
-
return null;
|
|
23543
|
-
}
|
|
23544
|
-
const nameMatch = fmMatch[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);
|
|
23545
|
-
if (!nameMatch) {
|
|
23546
|
-
warn("[auto-skill] No name field in skill frontmatter");
|
|
23547
|
-
return null;
|
|
23548
|
-
}
|
|
23549
|
-
const name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, "-");
|
|
23550
|
-
return { name, content };
|
|
23551
|
-
}
|
|
23552
|
-
async function saveSkill(name, content) {
|
|
23553
|
-
const dir = join25(SKILLS_PATH, name);
|
|
23554
|
-
await mkdir3(dir, { recursive: true });
|
|
23555
|
-
const filePath = join25(dir, "SKILL.md");
|
|
23556
|
-
await writeFile4(filePath, content, "utf-8");
|
|
23557
|
-
invalidateSkillCache();
|
|
23558
|
-
log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
|
|
23559
|
-
return { path: filePath };
|
|
23560
|
-
}
|
|
23561
|
-
function storePendingDraft(chatId, draft) {
|
|
23562
|
-
pendingDrafts.set(chatId, draft);
|
|
23563
|
-
}
|
|
23564
|
-
function getPendingDraft(chatId) {
|
|
23565
|
-
return pendingDrafts.get(chatId);
|
|
23566
|
-
}
|
|
23567
|
-
function clearPendingDraft(chatId) {
|
|
23568
|
-
pendingDrafts.delete(chatId);
|
|
23569
|
-
}
|
|
23570
|
-
var pendingDrafts;
|
|
23571
|
-
var init_auto_create = __esm({
|
|
23572
|
-
"src/skills/auto-create.ts"() {
|
|
23573
|
-
"use strict";
|
|
23574
|
-
init_paths();
|
|
23575
|
-
init_discover();
|
|
23576
|
-
init_log();
|
|
23577
|
-
pendingDrafts = /* @__PURE__ */ new Map();
|
|
23578
|
-
}
|
|
23579
|
-
});
|
|
23580
|
-
|
|
23581
24049
|
// src/router/callbacks.ts
|
|
23582
24050
|
import { readFile as readFile7 } from "fs/promises";
|
|
23583
24051
|
async function handleCallback(chatId, data, channel, messageId) {
|
|
@@ -23980,7 +24448,107 @@ This cannot be undone.`,
|
|
|
23980
24448
|
}
|
|
23981
24449
|
} else if (rest.startsWith("edit:")) {
|
|
23982
24450
|
const id = parseInt(rest.slice(5), 10);
|
|
24451
|
+
const editJob = getJobById(id);
|
|
24452
|
+
if (!editJob) {
|
|
24453
|
+
await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
|
|
24454
|
+
return;
|
|
24455
|
+
}
|
|
24456
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24457
|
+
const backendLabel = editJob.backend ? `${editJob.backend}/${editJob.model ?? "default"}` : "chat default";
|
|
24458
|
+
await channel.sendKeyboard(
|
|
24459
|
+
chatId,
|
|
24460
|
+
`Edit Job #${id}
|
|
24461
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
24462
|
+
What do you want to change?`,
|
|
24463
|
+
[
|
|
24464
|
+
[
|
|
24465
|
+
{ label: "\u{1F4C5} Schedule", data: `job:editwiz:${id}` },
|
|
24466
|
+
{ label: "\u{1F527} Backend/Model", data: `job:editbackend:${id}` }
|
|
24467
|
+
],
|
|
24468
|
+
[
|
|
24469
|
+
{ label: "\u23F1 Timeout", data: `job:edittimeout:${id}` },
|
|
24470
|
+
{ label: "\u{1F4DD} Full Edit", data: `job:editwiz:${id}` }
|
|
24471
|
+
],
|
|
24472
|
+
[{ label: "\u2190 Back to Job", data: `job:view:${id}` }]
|
|
24473
|
+
]
|
|
24474
|
+
);
|
|
24475
|
+
} else {
|
|
24476
|
+
await startEditWizard(chatId, id, channel);
|
|
24477
|
+
}
|
|
24478
|
+
} else if (rest.startsWith("editwiz:")) {
|
|
24479
|
+
const id = parseInt(rest.slice(8), 10);
|
|
23983
24480
|
await startEditWizard(chatId, id, channel);
|
|
24481
|
+
} else if (rest.startsWith("editbackend:")) {
|
|
24482
|
+
const id = parseInt(rest.slice(12), 10);
|
|
24483
|
+
const { getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24484
|
+
const adapters2 = getAllAdapters5().filter((a) => a.id !== "ollama");
|
|
24485
|
+
const buttons = adapters2.map((a) => ({
|
|
24486
|
+
label: a.displayName,
|
|
24487
|
+
data: `job:setbackend:${id}:${a.id}`
|
|
24488
|
+
}));
|
|
24489
|
+
const rows = [];
|
|
24490
|
+
for (let i = 0; i < buttons.length; i += 2) {
|
|
24491
|
+
rows.push(buttons.slice(i, i + 2));
|
|
24492
|
+
}
|
|
24493
|
+
rows.push([{ label: "\u2190 Back", data: `job:edit:${id}` }]);
|
|
24494
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24495
|
+
await channel.sendKeyboard(chatId, `Pick a backend for Job #${id}:`, rows);
|
|
24496
|
+
}
|
|
24497
|
+
} else if (rest.startsWith("setbackend:")) {
|
|
24498
|
+
const parts = rest.slice(11).split(":");
|
|
24499
|
+
const id = parseInt(parts[0], 10);
|
|
24500
|
+
const backend2 = parts[1];
|
|
24501
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24502
|
+
const adapter = getAdapter4(backend2);
|
|
24503
|
+
const models = Object.entries(adapter.availableModels);
|
|
24504
|
+
const buttons = models.map(([modelId, info]) => ({
|
|
24505
|
+
label: info.label?.split("\u2014")[0]?.trim() ?? modelId,
|
|
24506
|
+
data: `job:setmodel:${id}:${backend2}:${modelId}`
|
|
24507
|
+
}));
|
|
24508
|
+
const rows = [];
|
|
24509
|
+
for (let i = 0; i < buttons.length; i += 2) {
|
|
24510
|
+
rows.push(buttons.slice(i, i + 2));
|
|
24511
|
+
}
|
|
24512
|
+
rows.push([{ label: "\u2190 Back", data: `job:editbackend:${id}` }]);
|
|
24513
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24514
|
+
await channel.sendKeyboard(chatId, `Pick a model for ${adapter.displayName}:`, rows);
|
|
24515
|
+
}
|
|
24516
|
+
} else if (rest.startsWith("setmodel:")) {
|
|
24517
|
+
const parts = rest.slice(9).split(":");
|
|
24518
|
+
const id = parseInt(parts[0], 10);
|
|
24519
|
+
const backend2 = parts[1];
|
|
24520
|
+
const model2 = parts.slice(2).join(":");
|
|
24521
|
+
const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
24522
|
+
updateJobFields(id, { backend: backend2, model: model2 });
|
|
24523
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24524
|
+
const adapter = getAdapter4(backend2);
|
|
24525
|
+
await channel.sendText(chatId, `Job #${id} updated: ${adapter.displayName} / ${model2}`, { parseMode: "plain" });
|
|
24526
|
+
await sendJobDetail(chatId, id, channel);
|
|
24527
|
+
} else if (rest.startsWith("edittimeout:")) {
|
|
24528
|
+
const id = parseInt(rest.slice(12), 10);
|
|
24529
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24530
|
+
await channel.sendKeyboard(chatId, `Set timeout for Job #${id}:`, [
|
|
24531
|
+
[
|
|
24532
|
+
{ label: "2 min", data: `job:settimeout:${id}:120` },
|
|
24533
|
+
{ label: "5 min", data: `job:settimeout:${id}:300` },
|
|
24534
|
+
{ label: "10 min", data: `job:settimeout:${id}:600` }
|
|
24535
|
+
],
|
|
24536
|
+
[
|
|
24537
|
+
{ label: "15 min", data: `job:settimeout:${id}:900` },
|
|
24538
|
+
{ label: "30 min", data: `job:settimeout:${id}:1800` },
|
|
24539
|
+
{ label: "60 min", data: `job:settimeout:${id}:3600` }
|
|
24540
|
+
],
|
|
24541
|
+
[{ label: "\u2190 Back", data: `job:edit:${id}` }]
|
|
24542
|
+
]);
|
|
24543
|
+
}
|
|
24544
|
+
} else if (rest.startsWith("settimeout:")) {
|
|
24545
|
+
const parts = rest.slice(11).split(":");
|
|
24546
|
+
const id = parseInt(parts[0], 10);
|
|
24547
|
+
const timeout = parseInt(parts[1], 10);
|
|
24548
|
+
const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
24549
|
+
updateJobFields(id, { timeout });
|
|
24550
|
+
await channel.sendText(chatId, `Job #${id} timeout updated: ${timeout}s (${Math.round(timeout / 60)} min)`, { parseMode: "plain" });
|
|
24551
|
+
await sendJobDetail(chatId, id, channel);
|
|
23984
24552
|
} else if (rest === "back") {
|
|
23985
24553
|
await sendJobsBoard(chatId, channel, 1, messageId);
|
|
23986
24554
|
} else {
|
|
@@ -24786,14 +25354,6 @@ Use /skills to see all available skills.`, { parseMode: "plain" });
|
|
|
24786
25354
|
await channel.sendText(chatId, `Skill "${skillName}" not found.`, { parseMode: "plain" });
|
|
24787
25355
|
return;
|
|
24788
25356
|
}
|
|
24789
|
-
const activeBackend = getBackend(chatId) ?? "claude";
|
|
24790
|
-
if (skill.compatibleBackends && !skill.compatibleBackends.includes(activeBackend)) {
|
|
24791
|
-
await channel.sendText(
|
|
24792
|
-
chatId,
|
|
24793
|
-
`Note: "${skillName}" lists compatible backends as [${skill.compatibleBackends.join(", ")}], but active backend is ${activeBackend}. Proceeding anyway.`,
|
|
24794
|
-
{ parseMode: "plain" }
|
|
24795
|
-
);
|
|
24796
|
-
}
|
|
24797
25357
|
const raw = await readFile7(skill.filePath, "utf-8");
|
|
24798
25358
|
const skillContent = stripFrontmatter2(raw);
|
|
24799
25359
|
const tags = skill.sources.join(", ");
|
|
@@ -26322,7 +26882,7 @@ var init_cron = __esm({
|
|
|
26322
26882
|
});
|
|
26323
26883
|
|
|
26324
26884
|
// src/agents/runners/wrap-backend.ts
|
|
26325
|
-
import { join as
|
|
26885
|
+
import { join as join27 } from "path";
|
|
26326
26886
|
function buildMcpCommands(backendId) {
|
|
26327
26887
|
const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
|
|
26328
26888
|
return {
|
|
@@ -26416,7 +26976,7 @@ function wrapBackendAdapter(adapter) {
|
|
|
26416
26976
|
const configPath = writeMcpConfigFile(server);
|
|
26417
26977
|
return ["--mcp-config", configPath];
|
|
26418
26978
|
},
|
|
26419
|
-
getSkillPath: () =>
|
|
26979
|
+
getSkillPath: () => join27(SKILLS_PATH, `agent-${adapter.id}.md`)
|
|
26420
26980
|
};
|
|
26421
26981
|
}
|
|
26422
26982
|
var BACKEND_CAPABILITIES;
|
|
@@ -26478,18 +27038,18 @@ var init_wrap_backend = __esm({
|
|
|
26478
27038
|
});
|
|
26479
27039
|
|
|
26480
27040
|
// src/agents/runners/config-loader.ts
|
|
26481
|
-
import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as
|
|
26482
|
-
import { join as
|
|
27041
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as existsSync25, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
27042
|
+
import { join as join28 } from "path";
|
|
26483
27043
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
26484
27044
|
function resolveExecutable2(config2) {
|
|
26485
|
-
if (
|
|
27045
|
+
if (existsSync25(config2.executable)) return config2.executable;
|
|
26486
27046
|
try {
|
|
26487
27047
|
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
26488
27048
|
} catch {
|
|
26489
27049
|
}
|
|
26490
27050
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
26491
27051
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
26492
|
-
if (
|
|
27052
|
+
if (existsSync25(resolved)) return resolved;
|
|
26493
27053
|
}
|
|
26494
27054
|
return config2.executable;
|
|
26495
27055
|
}
|
|
@@ -26615,7 +27175,7 @@ function configToRunner(config2) {
|
|
|
26615
27175
|
prepareMcpInjection() {
|
|
26616
27176
|
return [];
|
|
26617
27177
|
},
|
|
26618
|
-
getSkillPath: () =>
|
|
27178
|
+
getSkillPath: () => join28(SKILLS_PATH, `agent-${config2.id}.md`)
|
|
26619
27179
|
};
|
|
26620
27180
|
}
|
|
26621
27181
|
function loadRunnerConfig(filePath) {
|
|
@@ -26628,14 +27188,14 @@ function loadRunnerConfig(filePath) {
|
|
|
26628
27188
|
}
|
|
26629
27189
|
}
|
|
26630
27190
|
function loadAllRunnerConfigs() {
|
|
26631
|
-
if (!
|
|
27191
|
+
if (!existsSync25(RUNNERS_PATH)) {
|
|
26632
27192
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
26633
27193
|
return [];
|
|
26634
27194
|
}
|
|
26635
27195
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
26636
27196
|
const configs = [];
|
|
26637
27197
|
for (const file of files) {
|
|
26638
|
-
const config2 = loadRunnerConfig(
|
|
27198
|
+
const config2 = loadRunnerConfig(join28(RUNNERS_PATH, file));
|
|
26639
27199
|
if (config2) configs.push(config2);
|
|
26640
27200
|
}
|
|
26641
27201
|
return configs;
|
|
@@ -26656,16 +27216,16 @@ function registerConfigRunners() {
|
|
|
26656
27216
|
return count;
|
|
26657
27217
|
}
|
|
26658
27218
|
function watchRunnerConfigs(onChange) {
|
|
26659
|
-
if (!
|
|
27219
|
+
if (!existsSync25(RUNNERS_PATH)) return;
|
|
26660
27220
|
for (const prev of watchedFiles) {
|
|
26661
|
-
if (!
|
|
27221
|
+
if (!existsSync25(prev)) {
|
|
26662
27222
|
unwatchFile(prev);
|
|
26663
27223
|
watchedFiles.delete(prev);
|
|
26664
27224
|
}
|
|
26665
27225
|
}
|
|
26666
27226
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
26667
27227
|
for (const file of files) {
|
|
26668
|
-
const fullPath =
|
|
27228
|
+
const fullPath = join28(RUNNERS_PATH, file);
|
|
26669
27229
|
if (watchedFiles.has(fullPath)) continue;
|
|
26670
27230
|
watchedFiles.add(fullPath);
|
|
26671
27231
|
watchFile(fullPath, { interval: 5e3 }, () => {
|
|
@@ -27731,19 +28291,19 @@ var init_telegram2 = __esm({
|
|
|
27731
28291
|
});
|
|
27732
28292
|
|
|
27733
28293
|
// src/skills/bootstrap.ts
|
|
27734
|
-
import { existsSync as
|
|
27735
|
-
import { readdir as
|
|
27736
|
-
import { join as
|
|
28294
|
+
import { existsSync as existsSync26 } from "fs";
|
|
28295
|
+
import { readdir as readdir6, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
|
|
28296
|
+
import { join as join29, dirname as dirname5 } from "path";
|
|
27737
28297
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
27738
28298
|
async function copyAgentManifestSkills() {
|
|
27739
|
-
if (!
|
|
28299
|
+
if (!existsSync26(PKG_SKILLS)) return;
|
|
27740
28300
|
try {
|
|
27741
|
-
const entries = await
|
|
28301
|
+
const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
|
|
27742
28302
|
for (const entry of entries) {
|
|
27743
28303
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
27744
|
-
const src =
|
|
27745
|
-
const dest =
|
|
27746
|
-
if (
|
|
28304
|
+
const src = join29(PKG_SKILLS, entry.name);
|
|
28305
|
+
const dest = join29(SKILLS_PATH, entry.name);
|
|
28306
|
+
if (existsSync26(dest)) continue;
|
|
27747
28307
|
await copyFile(src, dest);
|
|
27748
28308
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
27749
28309
|
}
|
|
@@ -27753,10 +28313,10 @@ async function copyAgentManifestSkills() {
|
|
|
27753
28313
|
}
|
|
27754
28314
|
async function bootstrapSkills() {
|
|
27755
28315
|
await copyAgentManifestSkills();
|
|
27756
|
-
const usmDir =
|
|
27757
|
-
if (
|
|
28316
|
+
const usmDir = join29(SKILLS_PATH, USM_DIR_NAME);
|
|
28317
|
+
if (existsSync26(usmDir)) return;
|
|
27758
28318
|
try {
|
|
27759
|
-
const entries = await
|
|
28319
|
+
const entries = await readdir6(SKILLS_PATH);
|
|
27760
28320
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
27761
28321
|
if (dirs.length > 0) return;
|
|
27762
28322
|
} catch (err) {
|
|
@@ -27777,8 +28337,8 @@ async function bootstrapSkills() {
|
|
|
27777
28337
|
}
|
|
27778
28338
|
}
|
|
27779
28339
|
async function patchUsmForCcClaw(usmDir) {
|
|
27780
|
-
const skillPath =
|
|
27781
|
-
if (!
|
|
28340
|
+
const skillPath = join29(usmDir, "SKILL.md");
|
|
28341
|
+
if (!existsSync26(skillPath)) return;
|
|
27782
28342
|
try {
|
|
27783
28343
|
let content = await readFile8(skillPath, "utf-8");
|
|
27784
28344
|
let patched = false;
|
|
@@ -27823,8 +28383,8 @@ var init_bootstrap = __esm({
|
|
|
27823
28383
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
27824
28384
|
USM_DIR_NAME = "universal-skills-manager";
|
|
27825
28385
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
27826
|
-
PKG_ROOT =
|
|
27827
|
-
PKG_SKILLS =
|
|
28386
|
+
PKG_ROOT = join29(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
|
|
28387
|
+
PKG_SKILLS = join29(PKG_ROOT, "skills");
|
|
27828
28388
|
}
|
|
27829
28389
|
});
|
|
27830
28390
|
|
|
@@ -28046,13 +28606,13 @@ __export(ai_skill_exports, {
|
|
|
28046
28606
|
generateAiSkill: () => generateAiSkill,
|
|
28047
28607
|
installAiSkill: () => installAiSkill
|
|
28048
28608
|
});
|
|
28049
|
-
import { existsSync as
|
|
28050
|
-
import { join as
|
|
28609
|
+
import { existsSync as existsSync27, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
28610
|
+
import { join as join30 } from "path";
|
|
28051
28611
|
import { homedir as homedir9 } from "os";
|
|
28052
28612
|
function generateAiSkill() {
|
|
28053
28613
|
const version = VERSION;
|
|
28054
28614
|
let systemState = "";
|
|
28055
|
-
if (
|
|
28615
|
+
if (existsSync27(DB_PATH)) {
|
|
28056
28616
|
try {
|
|
28057
28617
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
28058
28618
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -28492,8 +29052,8 @@ function installAiSkill() {
|
|
|
28492
29052
|
const failed = [];
|
|
28493
29053
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
28494
29054
|
for (const dir of dirs) {
|
|
28495
|
-
const skillDir =
|
|
28496
|
-
const skillPath =
|
|
29055
|
+
const skillDir = join30(dir, "cc-claw-cli");
|
|
29056
|
+
const skillPath = join30(skillDir, "SKILL.md");
|
|
28497
29057
|
try {
|
|
28498
29058
|
mkdirSync11(skillDir, { recursive: true });
|
|
28499
29059
|
writeFileSync8(skillPath, skill, "utf-8");
|
|
@@ -28512,11 +29072,11 @@ var init_ai_skill = __esm({
|
|
|
28512
29072
|
init_paths();
|
|
28513
29073
|
init_version();
|
|
28514
29074
|
BACKEND_SKILL_DIRS2 = {
|
|
28515
|
-
"cc-claw": [
|
|
28516
|
-
claude: [
|
|
28517
|
-
gemini: [
|
|
28518
|
-
codex: [
|
|
28519
|
-
cursor: [
|
|
29075
|
+
"cc-claw": [join30(homedir9(), ".cc-claw", "workspace", "skills")],
|
|
29076
|
+
claude: [join30(homedir9(), ".claude", "skills")],
|
|
29077
|
+
gemini: [join30(homedir9(), ".gemini", "skills")],
|
|
29078
|
+
codex: [join30(homedir9(), ".agents", "skills")],
|
|
29079
|
+
cursor: [join30(homedir9(), ".cursor", "skills"), join30(homedir9(), ".cursor", "skills-cursor")]
|
|
28520
29080
|
};
|
|
28521
29081
|
}
|
|
28522
29082
|
});
|
|
@@ -28526,21 +29086,21 @@ var index_exports = {};
|
|
|
28526
29086
|
__export(index_exports, {
|
|
28527
29087
|
main: () => main
|
|
28528
29088
|
});
|
|
28529
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
28530
|
-
import { join as
|
|
29089
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync28, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync17 } from "fs";
|
|
29090
|
+
import { join as join31 } from "path";
|
|
28531
29091
|
import dotenv from "dotenv";
|
|
28532
29092
|
function migrateLayout() {
|
|
28533
29093
|
const moves = [
|
|
28534
|
-
[
|
|
28535
|
-
[
|
|
28536
|
-
[
|
|
28537
|
-
[
|
|
28538
|
-
[
|
|
28539
|
-
[
|
|
28540
|
-
[
|
|
29094
|
+
[join31(CC_CLAW_HOME, "cc-claw.db"), join31(DATA_PATH, "cc-claw.db")],
|
|
29095
|
+
[join31(CC_CLAW_HOME, "cc-claw.db-shm"), join31(DATA_PATH, "cc-claw.db-shm")],
|
|
29096
|
+
[join31(CC_CLAW_HOME, "cc-claw.db-wal"), join31(DATA_PATH, "cc-claw.db-wal")],
|
|
29097
|
+
[join31(CC_CLAW_HOME, "cc-claw.log"), join31(LOGS_PATH, "cc-claw.log")],
|
|
29098
|
+
[join31(CC_CLAW_HOME, "cc-claw.log.1"), join31(LOGS_PATH, "cc-claw.log.1")],
|
|
29099
|
+
[join31(CC_CLAW_HOME, "cc-claw.error.log"), join31(LOGS_PATH, "cc-claw.error.log")],
|
|
29100
|
+
[join31(CC_CLAW_HOME, "cc-claw.error.log.1"), join31(LOGS_PATH, "cc-claw.error.log.1")]
|
|
28541
29101
|
];
|
|
28542
29102
|
for (const [from, to] of moves) {
|
|
28543
|
-
if (
|
|
29103
|
+
if (existsSync28(from) && !existsSync28(to)) {
|
|
28544
29104
|
try {
|
|
28545
29105
|
renameSync2(from, to);
|
|
28546
29106
|
} catch {
|
|
@@ -28717,10 +29277,10 @@ async function main() {
|
|
|
28717
29277
|
try {
|
|
28718
29278
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
28719
29279
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
|
|
28720
|
-
const { join:
|
|
28721
|
-
const skillDir =
|
|
29280
|
+
const { join: join37 } = await import("path");
|
|
29281
|
+
const skillDir = join37(SKILLS_PATH, "cc-claw-cli");
|
|
28722
29282
|
mkdirSync19(skillDir, { recursive: true });
|
|
28723
|
-
writeFileSync13(
|
|
29283
|
+
writeFileSync13(join37(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
28724
29284
|
log("[cc-claw] AI skill updated");
|
|
28725
29285
|
} catch {
|
|
28726
29286
|
}
|
|
@@ -28820,10 +29380,10 @@ var init_index = __esm({
|
|
|
28820
29380
|
init_health3();
|
|
28821
29381
|
init_image_gen();
|
|
28822
29382
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
28823
|
-
if (!
|
|
29383
|
+
if (!existsSync28(dir)) mkdirSync12(dir, { recursive: true });
|
|
28824
29384
|
}
|
|
28825
29385
|
migrateLayout();
|
|
28826
|
-
if (
|
|
29386
|
+
if (existsSync28(ENV_PATH)) {
|
|
28827
29387
|
dotenv.config({ path: ENV_PATH });
|
|
28828
29388
|
} else {
|
|
28829
29389
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -28844,12 +29404,12 @@ __export(api_client_exports, {
|
|
|
28844
29404
|
apiPost: () => apiPost,
|
|
28845
29405
|
isDaemonRunning: () => isDaemonRunning
|
|
28846
29406
|
});
|
|
28847
|
-
import { readFileSync as readFileSync18, existsSync as
|
|
29407
|
+
import { readFileSync as readFileSync18, existsSync as existsSync29 } from "fs";
|
|
28848
29408
|
import { request as httpRequest, Agent } from "http";
|
|
28849
29409
|
function getToken() {
|
|
28850
29410
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
28851
29411
|
try {
|
|
28852
|
-
if (
|
|
29412
|
+
if (existsSync29(TOKEN_PATH)) return readFileSync18(TOKEN_PATH, "utf-8").trim();
|
|
28853
29413
|
} catch {
|
|
28854
29414
|
}
|
|
28855
29415
|
return null;
|
|
@@ -28948,10 +29508,10 @@ __export(service_exports2, {
|
|
|
28948
29508
|
serviceStatus: () => serviceStatus,
|
|
28949
29509
|
uninstallService: () => uninstallService
|
|
28950
29510
|
});
|
|
28951
|
-
import { existsSync as
|
|
29511
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
28952
29512
|
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
28953
29513
|
import { homedir as homedir10, platform } from "os";
|
|
28954
|
-
import { join as
|
|
29514
|
+
import { join as join32, dirname as dirname6 } from "path";
|
|
28955
29515
|
function xmlEscape(s) {
|
|
28956
29516
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
28957
29517
|
}
|
|
@@ -28960,7 +29520,7 @@ function resolveExecutable3(name) {
|
|
|
28960
29520
|
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
28961
29521
|
} catch {
|
|
28962
29522
|
const fallback = process.argv[1];
|
|
28963
|
-
if (fallback &&
|
|
29523
|
+
if (fallback && existsSync30(fallback)) return fallback;
|
|
28964
29524
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
28965
29525
|
}
|
|
28966
29526
|
}
|
|
@@ -28969,14 +29529,14 @@ function getPathDirs() {
|
|
|
28969
29529
|
const home = homedir10();
|
|
28970
29530
|
const dirs = /* @__PURE__ */ new Set([
|
|
28971
29531
|
nodeBin,
|
|
28972
|
-
|
|
29532
|
+
join32(home, ".local", "bin"),
|
|
28973
29533
|
"/usr/local/bin",
|
|
28974
29534
|
"/usr/bin",
|
|
28975
29535
|
"/bin"
|
|
28976
29536
|
]);
|
|
28977
29537
|
try {
|
|
28978
29538
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
28979
|
-
if (prefix) dirs.add(
|
|
29539
|
+
if (prefix) dirs.add(join32(prefix, "bin"));
|
|
28980
29540
|
} catch {
|
|
28981
29541
|
}
|
|
28982
29542
|
return [...dirs].join(":");
|
|
@@ -29035,9 +29595,9 @@ function generatePlist() {
|
|
|
29035
29595
|
}
|
|
29036
29596
|
function installMacOS() {
|
|
29037
29597
|
const agentsDir = dirname6(PLIST_PATH);
|
|
29038
|
-
if (!
|
|
29039
|
-
if (!
|
|
29040
|
-
if (
|
|
29598
|
+
if (!existsSync30(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
29599
|
+
if (!existsSync30(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
29600
|
+
if (existsSync30(PLIST_PATH)) {
|
|
29041
29601
|
try {
|
|
29042
29602
|
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
29043
29603
|
} catch {
|
|
@@ -29049,7 +29609,7 @@ function installMacOS() {
|
|
|
29049
29609
|
console.log(" Service loaded and starting.");
|
|
29050
29610
|
}
|
|
29051
29611
|
function uninstallMacOS() {
|
|
29052
|
-
if (!
|
|
29612
|
+
if (!existsSync30(PLIST_PATH)) {
|
|
29053
29613
|
console.log(" No service found to uninstall.");
|
|
29054
29614
|
return;
|
|
29055
29615
|
}
|
|
@@ -29124,8 +29684,8 @@ WantedBy=default.target
|
|
|
29124
29684
|
`;
|
|
29125
29685
|
}
|
|
29126
29686
|
function installLinux() {
|
|
29127
|
-
if (!
|
|
29128
|
-
if (!
|
|
29687
|
+
if (!existsSync30(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
29688
|
+
if (!existsSync30(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
29129
29689
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
29130
29690
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
29131
29691
|
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -29134,7 +29694,7 @@ function installLinux() {
|
|
|
29134
29694
|
console.log(" Service enabled and started.");
|
|
29135
29695
|
}
|
|
29136
29696
|
function uninstallLinux() {
|
|
29137
|
-
if (!
|
|
29697
|
+
if (!existsSync30(UNIT_PATH)) {
|
|
29138
29698
|
console.log(" No service found to uninstall.");
|
|
29139
29699
|
return;
|
|
29140
29700
|
}
|
|
@@ -29159,7 +29719,7 @@ function statusLinux() {
|
|
|
29159
29719
|
}
|
|
29160
29720
|
}
|
|
29161
29721
|
function installService() {
|
|
29162
|
-
if (!
|
|
29722
|
+
if (!existsSync30(join32(CC_CLAW_HOME, ".env"))) {
|
|
29163
29723
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
29164
29724
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
29165
29725
|
process.exitCode = 1;
|
|
@@ -29188,9 +29748,9 @@ var init_service2 = __esm({
|
|
|
29188
29748
|
"use strict";
|
|
29189
29749
|
init_paths();
|
|
29190
29750
|
PLIST_LABEL = "com.cc-claw";
|
|
29191
|
-
PLIST_PATH =
|
|
29192
|
-
SYSTEMD_DIR =
|
|
29193
|
-
UNIT_PATH =
|
|
29751
|
+
PLIST_PATH = join32(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
29752
|
+
SYSTEMD_DIR = join32(homedir10(), ".config", "systemd", "user");
|
|
29753
|
+
UNIT_PATH = join32(SYSTEMD_DIR, "cc-claw.service");
|
|
29194
29754
|
}
|
|
29195
29755
|
});
|
|
29196
29756
|
|
|
@@ -29387,7 +29947,7 @@ var status_exports = {};
|
|
|
29387
29947
|
__export(status_exports, {
|
|
29388
29948
|
statusCommand: () => statusCommand
|
|
29389
29949
|
});
|
|
29390
|
-
import { existsSync as
|
|
29950
|
+
import { existsSync as existsSync31, statSync as statSync10 } from "fs";
|
|
29391
29951
|
async function statusCommand(globalOpts, localOpts) {
|
|
29392
29952
|
try {
|
|
29393
29953
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
@@ -29427,7 +29987,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
29427
29987
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
29428
29988
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
29429
29989
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
29430
|
-
const dbStat =
|
|
29990
|
+
const dbStat = existsSync31(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
29431
29991
|
let daemonRunning = false;
|
|
29432
29992
|
let daemonInfo = {};
|
|
29433
29993
|
try {
|
|
@@ -29539,13 +30099,13 @@ __export(doctor_exports, {
|
|
|
29539
30099
|
doctorCommand: () => doctorCommand,
|
|
29540
30100
|
doctorErrors: () => doctorErrors
|
|
29541
30101
|
});
|
|
29542
|
-
import { existsSync as
|
|
30102
|
+
import { existsSync as existsSync32, accessSync, constants } from "fs";
|
|
29543
30103
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
29544
30104
|
async function doctorCommand(globalOpts, localOpts) {
|
|
29545
30105
|
const checks = [];
|
|
29546
30106
|
const dbChecks = checkDatabase();
|
|
29547
30107
|
checks.push(...dbChecks);
|
|
29548
|
-
if (
|
|
30108
|
+
if (existsSync32(DB_PATH)) {
|
|
29549
30109
|
try {
|
|
29550
30110
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
29551
30111
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -29571,7 +30131,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29571
30131
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
29572
30132
|
}
|
|
29573
30133
|
}
|
|
29574
|
-
if (
|
|
30134
|
+
if (existsSync32(ENV_PATH)) {
|
|
29575
30135
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
29576
30136
|
} else {
|
|
29577
30137
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
@@ -29614,7 +30174,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29614
30174
|
} catch {
|
|
29615
30175
|
}
|
|
29616
30176
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
29617
|
-
if (
|
|
30177
|
+
if (existsSync32(tokenPath)) {
|
|
29618
30178
|
try {
|
|
29619
30179
|
accessSync(tokenPath, constants.R_OK);
|
|
29620
30180
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -29680,7 +30240,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29680
30240
|
const errorChecks = checks.filter(
|
|
29681
30241
|
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
29682
30242
|
);
|
|
29683
|
-
if (errorChecks.length > 0 &&
|
|
30243
|
+
if (errorChecks.length > 0 && existsSync32(ERROR_LOG_PATH)) {
|
|
29684
30244
|
try {
|
|
29685
30245
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
29686
30246
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -29809,10 +30369,10 @@ var logs_exports = {};
|
|
|
29809
30369
|
__export(logs_exports, {
|
|
29810
30370
|
logsCommand: () => logsCommand
|
|
29811
30371
|
});
|
|
29812
|
-
import { existsSync as
|
|
30372
|
+
import { existsSync as existsSync33, readFileSync as readFileSync22, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
29813
30373
|
async function logsCommand(opts) {
|
|
29814
30374
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
29815
|
-
if (!
|
|
30375
|
+
if (!existsSync33(logFile)) {
|
|
29816
30376
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
29817
30377
|
process.exit(1);
|
|
29818
30378
|
}
|
|
@@ -29965,11 +30525,11 @@ __export(gemini_exports, {
|
|
|
29965
30525
|
geminiReorder: () => geminiReorder,
|
|
29966
30526
|
geminiRotation: () => geminiRotation
|
|
29967
30527
|
});
|
|
29968
|
-
import { existsSync as
|
|
29969
|
-
import { join as
|
|
30528
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync24, chmodSync } from "fs";
|
|
30529
|
+
import { join as join33 } from "path";
|
|
29970
30530
|
import { createInterface as createInterface8 } from "readline";
|
|
29971
30531
|
function requireDb() {
|
|
29972
|
-
if (!
|
|
30532
|
+
if (!existsSync35(DB_PATH)) {
|
|
29973
30533
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
29974
30534
|
process.exit(1);
|
|
29975
30535
|
}
|
|
@@ -29994,8 +30554,8 @@ async function resolveSlotId(idOrLabel) {
|
|
|
29994
30554
|
function resolveOAuthEmail(configHome) {
|
|
29995
30555
|
if (!configHome) return null;
|
|
29996
30556
|
try {
|
|
29997
|
-
const accountsPath =
|
|
29998
|
-
if (!
|
|
30557
|
+
const accountsPath = join33(configHome, ".gemini", "google_accounts.json");
|
|
30558
|
+
if (!existsSync35(accountsPath)) return null;
|
|
29999
30559
|
const accounts = JSON.parse(readFileSync24(accountsPath, "utf-8"));
|
|
30000
30560
|
return accounts.active || null;
|
|
30001
30561
|
} catch {
|
|
@@ -30078,14 +30638,14 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
30078
30638
|
}
|
|
30079
30639
|
async function geminiAddAccount(globalOpts, opts) {
|
|
30080
30640
|
await requireWriteDb();
|
|
30081
|
-
const slotsDir =
|
|
30082
|
-
if (!
|
|
30641
|
+
const slotsDir = join33(CC_CLAW_HOME, "gemini-slots");
|
|
30642
|
+
if (!existsSync35(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
30083
30643
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30084
30644
|
const tempId = Date.now();
|
|
30085
|
-
const slotDir =
|
|
30645
|
+
const slotDir = join33(slotsDir, `slot-${tempId}`);
|
|
30086
30646
|
mkdirSync14(slotDir, { recursive: true, mode: 448 });
|
|
30087
|
-
mkdirSync14(
|
|
30088
|
-
writeFileSync10(
|
|
30647
|
+
mkdirSync14(join33(slotDir, ".gemini"), { recursive: true });
|
|
30648
|
+
writeFileSync10(join33(slotDir, ".gemini", "settings.json"), JSON.stringify({
|
|
30089
30649
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
30090
30650
|
}, null, 2));
|
|
30091
30651
|
console.log("");
|
|
@@ -30102,8 +30662,8 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
30102
30662
|
});
|
|
30103
30663
|
} catch {
|
|
30104
30664
|
}
|
|
30105
|
-
const oauthPath =
|
|
30106
|
-
if (!
|
|
30665
|
+
const oauthPath = join33(slotDir, ".gemini", "oauth_creds.json");
|
|
30666
|
+
if (!existsSync35(oauthPath)) {
|
|
30107
30667
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
30108
30668
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
30109
30669
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -30111,7 +30671,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
30111
30671
|
}
|
|
30112
30672
|
let accountEmail = "unknown";
|
|
30113
30673
|
try {
|
|
30114
|
-
const accounts = JSON.parse(__require("fs").readFileSync(
|
|
30674
|
+
const accounts = JSON.parse(__require("fs").readFileSync(join33(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
|
|
30115
30675
|
accountEmail = accounts.active || accountEmail;
|
|
30116
30676
|
} catch {
|
|
30117
30677
|
}
|
|
@@ -30222,9 +30782,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
30222
30782
|
outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
|
|
30223
30783
|
return;
|
|
30224
30784
|
}
|
|
30225
|
-
const settingsPath =
|
|
30226
|
-
if (!
|
|
30227
|
-
mkdirSync14(
|
|
30785
|
+
const settingsPath = join33(slot.configHome, ".gemini", "settings.json");
|
|
30786
|
+
if (!existsSync35(settingsPath)) {
|
|
30787
|
+
mkdirSync14(join33(slot.configHome, ".gemini"), { recursive: true });
|
|
30228
30788
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
30229
30789
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
30230
30790
|
}, null, 2));
|
|
@@ -30248,8 +30808,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
30248
30808
|
});
|
|
30249
30809
|
} catch {
|
|
30250
30810
|
}
|
|
30251
|
-
const oauthPath =
|
|
30252
|
-
if (!
|
|
30811
|
+
const oauthPath = join33(slot.configHome, ".gemini", "oauth_creds.json");
|
|
30812
|
+
if (!existsSync35(oauthPath)) {
|
|
30253
30813
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
30254
30814
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
30255
30815
|
`);
|
|
@@ -30259,7 +30819,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
30259
30819
|
setGeminiSlotEnabled2(slotId, true);
|
|
30260
30820
|
let accountEmail = slot.label;
|
|
30261
30821
|
try {
|
|
30262
|
-
const accounts = JSON.parse(readFileSync24(
|
|
30822
|
+
const accounts = JSON.parse(readFileSync24(join33(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
30263
30823
|
if (accounts.active) accountEmail = accounts.active;
|
|
30264
30824
|
} catch {
|
|
30265
30825
|
}
|
|
@@ -30318,11 +30878,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
30318
30878
|
makeReorder: () => makeReorder,
|
|
30319
30879
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
30320
30880
|
});
|
|
30321
|
-
import { existsSync as
|
|
30322
|
-
import { join as
|
|
30881
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync15, readFileSync as readFileSync25 } from "fs";
|
|
30882
|
+
import { join as join34 } from "path";
|
|
30323
30883
|
import { createInterface as createInterface9 } from "readline";
|
|
30324
30884
|
function requireDb2() {
|
|
30325
|
-
if (!
|
|
30885
|
+
if (!existsSync36(DB_PATH)) {
|
|
30326
30886
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30327
30887
|
process.exit(1);
|
|
30328
30888
|
}
|
|
@@ -30411,10 +30971,10 @@ function makeAddAccount(backend2, displayName) {
|
|
|
30411
30971
|
process.exit(1);
|
|
30412
30972
|
}
|
|
30413
30973
|
await requireWriteDb2();
|
|
30414
|
-
const slotsDir =
|
|
30415
|
-
if (!
|
|
30974
|
+
const slotsDir = join34(CC_CLAW_HOME, config2.slotsSubdir);
|
|
30975
|
+
if (!existsSync36(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
30416
30976
|
const tempId = Date.now();
|
|
30417
|
-
const slotDir =
|
|
30977
|
+
const slotDir = join34(slotsDir, `slot-${tempId}`);
|
|
30418
30978
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
30419
30979
|
if (config2.preSetup) config2.preSetup(slotDir);
|
|
30420
30980
|
console.log("");
|
|
@@ -30665,12 +31225,12 @@ var init_backend_cmd_factory = __esm({
|
|
|
30665
31225
|
envValue: (slotDir) => slotDir,
|
|
30666
31226
|
envOverrides: { ANTHROPIC_API_KEY: void 0 },
|
|
30667
31227
|
preSetup: (slotDir) => {
|
|
30668
|
-
mkdirSync15(
|
|
31228
|
+
mkdirSync15(join34(slotDir, ".claude"), { recursive: true });
|
|
30669
31229
|
},
|
|
30670
31230
|
verifyCredentials: (slotDir) => {
|
|
30671
|
-
const claudeJson =
|
|
30672
|
-
const claudeJsonNested =
|
|
30673
|
-
if (
|
|
31231
|
+
const claudeJson = join34(slotDir, ".claude.json");
|
|
31232
|
+
const claudeJsonNested = join34(slotDir, ".claude", ".claude.json");
|
|
31233
|
+
if (existsSync36(claudeJson)) {
|
|
30674
31234
|
try {
|
|
30675
31235
|
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
30676
31236
|
return Boolean(data.oauthAccount);
|
|
@@ -30678,7 +31238,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
30678
31238
|
return false;
|
|
30679
31239
|
}
|
|
30680
31240
|
}
|
|
30681
|
-
if (
|
|
31241
|
+
if (existsSync36(claudeJsonNested)) {
|
|
30682
31242
|
try {
|
|
30683
31243
|
const data = JSON.parse(readFileSync25(claudeJsonNested, "utf-8"));
|
|
30684
31244
|
return Boolean(data.oauthAccount);
|
|
@@ -30701,8 +31261,8 @@ var init_backend_cmd_factory = __esm({
|
|
|
30701
31261
|
} catch {
|
|
30702
31262
|
}
|
|
30703
31263
|
try {
|
|
30704
|
-
const claudeJson =
|
|
30705
|
-
if (
|
|
31264
|
+
const claudeJson = join34(slotDir, ".claude.json");
|
|
31265
|
+
if (existsSync36(claudeJson)) {
|
|
30706
31266
|
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
30707
31267
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
30708
31268
|
}
|
|
@@ -30718,11 +31278,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
30718
31278
|
envValue: (slotDir) => slotDir,
|
|
30719
31279
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
30720
31280
|
verifyCredentials: (slotDir) => {
|
|
30721
|
-
return
|
|
31281
|
+
return existsSync36(join34(slotDir, "auth.json"));
|
|
30722
31282
|
},
|
|
30723
31283
|
extractLabel: (slotDir) => {
|
|
30724
31284
|
try {
|
|
30725
|
-
const authData = JSON.parse(readFileSync25(
|
|
31285
|
+
const authData = JSON.parse(readFileSync25(join34(slotDir, "auth.json"), "utf-8"));
|
|
30726
31286
|
if (authData.email) return authData.email;
|
|
30727
31287
|
if (authData.account_name) return authData.account_name;
|
|
30728
31288
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -30746,9 +31306,9 @@ __export(ollama_exports3, {
|
|
|
30746
31306
|
ollamaRemove: () => ollamaRemove,
|
|
30747
31307
|
ollamaTest: () => ollamaTest
|
|
30748
31308
|
});
|
|
30749
|
-
import { existsSync as
|
|
31309
|
+
import { existsSync as existsSync37 } from "fs";
|
|
30750
31310
|
function requireDb3() {
|
|
30751
|
-
if (!
|
|
31311
|
+
if (!existsSync37(DB_PATH)) {
|
|
30752
31312
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30753
31313
|
process.exit(1);
|
|
30754
31314
|
}
|
|
@@ -31007,12 +31567,12 @@ __export(backend_exports, {
|
|
|
31007
31567
|
backendList: () => backendList,
|
|
31008
31568
|
backendSet: () => backendSet
|
|
31009
31569
|
});
|
|
31010
|
-
import { existsSync as
|
|
31570
|
+
import { existsSync as existsSync38 } from "fs";
|
|
31011
31571
|
async function backendList(globalOpts) {
|
|
31012
31572
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
31013
31573
|
const chatId = resolveChatId2(globalOpts);
|
|
31014
31574
|
let activeBackend = null;
|
|
31015
|
-
if (
|
|
31575
|
+
if (existsSync38(DB_PATH)) {
|
|
31016
31576
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31017
31577
|
const readDb = openDatabaseReadOnly2();
|
|
31018
31578
|
try {
|
|
@@ -31043,7 +31603,7 @@ async function backendList(globalOpts) {
|
|
|
31043
31603
|
}
|
|
31044
31604
|
async function backendGet(globalOpts) {
|
|
31045
31605
|
const chatId = resolveChatId2(globalOpts);
|
|
31046
|
-
if (!
|
|
31606
|
+
if (!existsSync38(DB_PATH)) {
|
|
31047
31607
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31048
31608
|
process.exit(1);
|
|
31049
31609
|
}
|
|
@@ -31087,13 +31647,13 @@ __export(model_exports, {
|
|
|
31087
31647
|
modelList: () => modelList,
|
|
31088
31648
|
modelSet: () => modelSet
|
|
31089
31649
|
});
|
|
31090
|
-
import { existsSync as
|
|
31650
|
+
import { existsSync as existsSync39 } from "fs";
|
|
31091
31651
|
async function modelList(globalOpts) {
|
|
31092
31652
|
const chatId = resolveChatId2(globalOpts);
|
|
31093
31653
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31094
31654
|
const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
31095
31655
|
let backendId = "claude";
|
|
31096
|
-
if (
|
|
31656
|
+
if (existsSync39(DB_PATH)) {
|
|
31097
31657
|
const readDb = openDatabaseReadOnly2();
|
|
31098
31658
|
try {
|
|
31099
31659
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -31126,7 +31686,7 @@ async function modelList(globalOpts) {
|
|
|
31126
31686
|
}
|
|
31127
31687
|
async function modelGet(globalOpts) {
|
|
31128
31688
|
const chatId = resolveChatId2(globalOpts);
|
|
31129
|
-
if (!
|
|
31689
|
+
if (!existsSync39(DB_PATH)) {
|
|
31130
31690
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31131
31691
|
process.exit(1);
|
|
31132
31692
|
}
|
|
@@ -31170,9 +31730,9 @@ __export(memory_exports2, {
|
|
|
31170
31730
|
memoryList: () => memoryList,
|
|
31171
31731
|
memorySearch: () => memorySearch
|
|
31172
31732
|
});
|
|
31173
|
-
import { existsSync as
|
|
31733
|
+
import { existsSync as existsSync40 } from "fs";
|
|
31174
31734
|
async function memoryList(globalOpts) {
|
|
31175
|
-
if (!
|
|
31735
|
+
if (!existsSync40(DB_PATH)) {
|
|
31176
31736
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31177
31737
|
process.exit(1);
|
|
31178
31738
|
}
|
|
@@ -31196,7 +31756,7 @@ async function memoryList(globalOpts) {
|
|
|
31196
31756
|
});
|
|
31197
31757
|
}
|
|
31198
31758
|
async function memorySearch(globalOpts, query) {
|
|
31199
|
-
if (!
|
|
31759
|
+
if (!existsSync40(DB_PATH)) {
|
|
31200
31760
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31201
31761
|
process.exit(1);
|
|
31202
31762
|
}
|
|
@@ -31218,7 +31778,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
31218
31778
|
});
|
|
31219
31779
|
}
|
|
31220
31780
|
async function memoryHistory(globalOpts, opts) {
|
|
31221
|
-
if (!
|
|
31781
|
+
if (!existsSync40(DB_PATH)) {
|
|
31222
31782
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31223
31783
|
process.exit(1);
|
|
31224
31784
|
}
|
|
@@ -31266,7 +31826,7 @@ __export(cron_exports2, {
|
|
|
31266
31826
|
cronList: () => cronList,
|
|
31267
31827
|
cronRuns: () => cronRuns
|
|
31268
31828
|
});
|
|
31269
|
-
import { existsSync as
|
|
31829
|
+
import { existsSync as existsSync41 } from "fs";
|
|
31270
31830
|
function parseFallbacks(raw) {
|
|
31271
31831
|
return raw.slice(0, 3).map((f) => {
|
|
31272
31832
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -31287,7 +31847,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
31287
31847
|
return val;
|
|
31288
31848
|
}
|
|
31289
31849
|
async function cronList(globalOpts) {
|
|
31290
|
-
if (!
|
|
31850
|
+
if (!existsSync41(DB_PATH)) {
|
|
31291
31851
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31292
31852
|
process.exit(1);
|
|
31293
31853
|
}
|
|
@@ -31325,7 +31885,7 @@ async function cronList(globalOpts) {
|
|
|
31325
31885
|
});
|
|
31326
31886
|
}
|
|
31327
31887
|
async function cronHealth(globalOpts) {
|
|
31328
|
-
if (!
|
|
31888
|
+
if (!existsSync41(DB_PATH)) {
|
|
31329
31889
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31330
31890
|
process.exit(1);
|
|
31331
31891
|
}
|
|
@@ -31486,7 +32046,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
31486
32046
|
}
|
|
31487
32047
|
}
|
|
31488
32048
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
31489
|
-
if (!
|
|
32049
|
+
if (!existsSync41(DB_PATH)) {
|
|
31490
32050
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31491
32051
|
process.exit(1);
|
|
31492
32052
|
}
|
|
@@ -31533,9 +32093,9 @@ __export(agents_exports, {
|
|
|
31533
32093
|
runnersList: () => runnersList,
|
|
31534
32094
|
tasksList: () => tasksList
|
|
31535
32095
|
});
|
|
31536
|
-
import { existsSync as
|
|
32096
|
+
import { existsSync as existsSync42 } from "fs";
|
|
31537
32097
|
async function agentsList(globalOpts) {
|
|
31538
|
-
if (!
|
|
32098
|
+
if (!existsSync42(DB_PATH)) {
|
|
31539
32099
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31540
32100
|
process.exit(1);
|
|
31541
32101
|
}
|
|
@@ -31566,7 +32126,7 @@ async function agentsList(globalOpts) {
|
|
|
31566
32126
|
});
|
|
31567
32127
|
}
|
|
31568
32128
|
async function tasksList(globalOpts) {
|
|
31569
|
-
if (!
|
|
32129
|
+
if (!existsSync42(DB_PATH)) {
|
|
31570
32130
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31571
32131
|
process.exit(1);
|
|
31572
32132
|
}
|
|
@@ -31694,10 +32254,10 @@ __export(db_exports, {
|
|
|
31694
32254
|
dbPath: () => dbPath,
|
|
31695
32255
|
dbStats: () => dbStats
|
|
31696
32256
|
});
|
|
31697
|
-
import { existsSync as
|
|
32257
|
+
import { existsSync as existsSync43, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
31698
32258
|
import { dirname as dirname7 } from "path";
|
|
31699
32259
|
async function dbStats(globalOpts) {
|
|
31700
|
-
if (!
|
|
32260
|
+
if (!existsSync43(DB_PATH)) {
|
|
31701
32261
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
31702
32262
|
process.exit(1);
|
|
31703
32263
|
}
|
|
@@ -31705,7 +32265,7 @@ async function dbStats(globalOpts) {
|
|
|
31705
32265
|
const readDb = openDatabaseReadOnly2();
|
|
31706
32266
|
const mainSize = statSync11(DB_PATH).size;
|
|
31707
32267
|
const walPath = DB_PATH + "-wal";
|
|
31708
|
-
const walSize =
|
|
32268
|
+
const walSize = existsSync43(walPath) ? statSync11(walPath).size : 0;
|
|
31709
32269
|
const tableNames = readDb.prepare(
|
|
31710
32270
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
31711
32271
|
).all();
|
|
@@ -31739,7 +32299,7 @@ async function dbPath(globalOpts) {
|
|
|
31739
32299
|
output({ path: DB_PATH }, (d) => d.path);
|
|
31740
32300
|
}
|
|
31741
32301
|
async function dbBackup(globalOpts, destPath) {
|
|
31742
|
-
if (!
|
|
32302
|
+
if (!existsSync43(DB_PATH)) {
|
|
31743
32303
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
31744
32304
|
process.exit(1);
|
|
31745
32305
|
}
|
|
@@ -31748,7 +32308,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
31748
32308
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
31749
32309
|
copyFileSync3(DB_PATH, dest);
|
|
31750
32310
|
const walPath = DB_PATH + "-wal";
|
|
31751
|
-
if (
|
|
32311
|
+
if (existsSync43(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
31752
32312
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
31753
32313
|
const b = d;
|
|
31754
32314
|
return `
|
|
@@ -31777,9 +32337,9 @@ __export(usage_exports, {
|
|
|
31777
32337
|
usageCost: () => usageCost,
|
|
31778
32338
|
usageTokens: () => usageTokens
|
|
31779
32339
|
});
|
|
31780
|
-
import { existsSync as
|
|
32340
|
+
import { existsSync as existsSync44 } from "fs";
|
|
31781
32341
|
function ensureDb() {
|
|
31782
|
-
if (!
|
|
32342
|
+
if (!existsSync44(DB_PATH)) {
|
|
31783
32343
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31784
32344
|
process.exit(1);
|
|
31785
32345
|
}
|
|
@@ -31969,9 +32529,9 @@ __export(config_exports2, {
|
|
|
31969
32529
|
configList: () => configList,
|
|
31970
32530
|
configSet: () => configSet
|
|
31971
32531
|
});
|
|
31972
|
-
import { existsSync as
|
|
32532
|
+
import { existsSync as existsSync45, readFileSync as readFileSync26 } from "fs";
|
|
31973
32533
|
async function configList(globalOpts) {
|
|
31974
|
-
if (!
|
|
32534
|
+
if (!existsSync45(DB_PATH)) {
|
|
31975
32535
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31976
32536
|
process.exit(1);
|
|
31977
32537
|
}
|
|
@@ -32005,7 +32565,7 @@ async function configGet(globalOpts, key) {
|
|
|
32005
32565
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
32006
32566
|
process.exit(1);
|
|
32007
32567
|
}
|
|
32008
|
-
if (!
|
|
32568
|
+
if (!existsSync45(DB_PATH)) {
|
|
32009
32569
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32010
32570
|
process.exit(1);
|
|
32011
32571
|
}
|
|
@@ -32051,7 +32611,7 @@ async function configSet(globalOpts, key, value) {
|
|
|
32051
32611
|
}
|
|
32052
32612
|
}
|
|
32053
32613
|
async function configEnv(_globalOpts) {
|
|
32054
|
-
if (!
|
|
32614
|
+
if (!existsSync45(ENV_PATH)) {
|
|
32055
32615
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
32056
32616
|
process.exit(1);
|
|
32057
32617
|
}
|
|
@@ -32105,9 +32665,9 @@ __export(session_exports, {
|
|
|
32105
32665
|
sessionGet: () => sessionGet,
|
|
32106
32666
|
sessionNew: () => sessionNew
|
|
32107
32667
|
});
|
|
32108
|
-
import { existsSync as
|
|
32668
|
+
import { existsSync as existsSync46 } from "fs";
|
|
32109
32669
|
async function sessionGet(globalOpts) {
|
|
32110
|
-
if (!
|
|
32670
|
+
if (!existsSync46(DB_PATH)) {
|
|
32111
32671
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32112
32672
|
process.exit(1);
|
|
32113
32673
|
}
|
|
@@ -32168,9 +32728,9 @@ __export(permissions_exports, {
|
|
|
32168
32728
|
verboseGet: () => verboseGet,
|
|
32169
32729
|
verboseSet: () => verboseSet
|
|
32170
32730
|
});
|
|
32171
|
-
import { existsSync as
|
|
32731
|
+
import { existsSync as existsSync47 } from "fs";
|
|
32172
32732
|
function ensureDb2() {
|
|
32173
|
-
if (!
|
|
32733
|
+
if (!existsSync47(DB_PATH)) {
|
|
32174
32734
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32175
32735
|
process.exit(1);
|
|
32176
32736
|
}
|
|
@@ -32317,9 +32877,9 @@ __export(cwd_exports, {
|
|
|
32317
32877
|
cwdGet: () => cwdGet,
|
|
32318
32878
|
cwdSet: () => cwdSet
|
|
32319
32879
|
});
|
|
32320
|
-
import { existsSync as
|
|
32880
|
+
import { existsSync as existsSync48 } from "fs";
|
|
32321
32881
|
async function cwdGet(globalOpts) {
|
|
32322
|
-
if (!
|
|
32882
|
+
if (!existsSync48(DB_PATH)) {
|
|
32323
32883
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32324
32884
|
process.exit(1);
|
|
32325
32885
|
}
|
|
@@ -32381,9 +32941,9 @@ __export(voice_exports, {
|
|
|
32381
32941
|
voiceGet: () => voiceGet,
|
|
32382
32942
|
voiceSet: () => voiceSet
|
|
32383
32943
|
});
|
|
32384
|
-
import { existsSync as
|
|
32944
|
+
import { existsSync as existsSync49 } from "fs";
|
|
32385
32945
|
async function voiceGet(globalOpts) {
|
|
32386
|
-
if (!
|
|
32946
|
+
if (!existsSync49(DB_PATH)) {
|
|
32387
32947
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32388
32948
|
process.exit(1);
|
|
32389
32949
|
}
|
|
@@ -32432,9 +32992,9 @@ __export(heartbeat_exports2, {
|
|
|
32432
32992
|
heartbeatGet: () => heartbeatGet,
|
|
32433
32993
|
heartbeatSet: () => heartbeatSet
|
|
32434
32994
|
});
|
|
32435
|
-
import { existsSync as
|
|
32995
|
+
import { existsSync as existsSync50 } from "fs";
|
|
32436
32996
|
async function heartbeatGet(globalOpts) {
|
|
32437
|
-
if (!
|
|
32997
|
+
if (!existsSync50(DB_PATH)) {
|
|
32438
32998
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32439
32999
|
process.exit(1);
|
|
32440
33000
|
}
|
|
@@ -32645,9 +33205,9 @@ __export(summarizer_exports, {
|
|
|
32645
33205
|
summarizerGet: () => summarizerGet,
|
|
32646
33206
|
summarizerSet: () => summarizerSet
|
|
32647
33207
|
});
|
|
32648
|
-
import { existsSync as
|
|
33208
|
+
import { existsSync as existsSync51 } from "fs";
|
|
32649
33209
|
async function summarizerGet(globalOpts) {
|
|
32650
|
-
if (!
|
|
33210
|
+
if (!existsSync51(DB_PATH)) {
|
|
32651
33211
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32652
33212
|
process.exit(1);
|
|
32653
33213
|
}
|
|
@@ -32691,9 +33251,9 @@ __export(thinking_exports, {
|
|
|
32691
33251
|
thinkingGet: () => thinkingGet,
|
|
32692
33252
|
thinkingSet: () => thinkingSet
|
|
32693
33253
|
});
|
|
32694
|
-
import { existsSync as
|
|
33254
|
+
import { existsSync as existsSync52 } from "fs";
|
|
32695
33255
|
async function thinkingGet(globalOpts) {
|
|
32696
|
-
if (!
|
|
33256
|
+
if (!existsSync52(DB_PATH)) {
|
|
32697
33257
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32698
33258
|
process.exit(1);
|
|
32699
33259
|
}
|
|
@@ -32737,9 +33297,9 @@ __export(chats_exports, {
|
|
|
32737
33297
|
chatsList: () => chatsList,
|
|
32738
33298
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
32739
33299
|
});
|
|
32740
|
-
import { existsSync as
|
|
33300
|
+
import { existsSync as existsSync53 } from "fs";
|
|
32741
33301
|
async function chatsList(_globalOpts) {
|
|
32742
|
-
if (!
|
|
33302
|
+
if (!existsSync53(DB_PATH)) {
|
|
32743
33303
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32744
33304
|
process.exit(1);
|
|
32745
33305
|
}
|
|
@@ -32867,9 +33427,9 @@ var mcps_exports2 = {};
|
|
|
32867
33427
|
__export(mcps_exports2, {
|
|
32868
33428
|
mcpsList: () => mcpsList
|
|
32869
33429
|
});
|
|
32870
|
-
import { existsSync as
|
|
33430
|
+
import { existsSync as existsSync54 } from "fs";
|
|
32871
33431
|
async function mcpsList(_globalOpts) {
|
|
32872
|
-
if (!
|
|
33432
|
+
if (!existsSync54(DB_PATH)) {
|
|
32873
33433
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32874
33434
|
process.exit(1);
|
|
32875
33435
|
}
|
|
@@ -32906,11 +33466,11 @@ __export(chat_exports2, {
|
|
|
32906
33466
|
chatSend: () => chatSend
|
|
32907
33467
|
});
|
|
32908
33468
|
import { request as httpRequest2 } from "http";
|
|
32909
|
-
import { readFileSync as readFileSync27, existsSync as
|
|
33469
|
+
import { readFileSync as readFileSync27, existsSync as existsSync55 } from "fs";
|
|
32910
33470
|
function getToken2() {
|
|
32911
33471
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
32912
33472
|
try {
|
|
32913
|
-
if (
|
|
33473
|
+
if (existsSync55(TOKEN_PATH2)) return readFileSync27(TOKEN_PATH2, "utf-8").trim();
|
|
32914
33474
|
} catch {
|
|
32915
33475
|
}
|
|
32916
33476
|
return null;
|
|
@@ -33398,7 +33958,7 @@ __export(completion_exports, {
|
|
|
33398
33958
|
completionCommand: () => completionCommand
|
|
33399
33959
|
});
|
|
33400
33960
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
|
|
33401
|
-
import { join as
|
|
33961
|
+
import { join as join35 } from "path";
|
|
33402
33962
|
import { homedir as homedir11 } from "os";
|
|
33403
33963
|
async function completionCommand(opts) {
|
|
33404
33964
|
const shell = opts.shell ?? detectShell();
|
|
@@ -33414,10 +33974,10 @@ async function completionCommand(opts) {
|
|
|
33414
33974
|
process.exit(1);
|
|
33415
33975
|
}
|
|
33416
33976
|
if (opts.install) {
|
|
33417
|
-
const dir =
|
|
33977
|
+
const dir = join35(homedir11(), ".config", "cc-claw", "completions");
|
|
33418
33978
|
mkdirSync17(dir, { recursive: true });
|
|
33419
33979
|
const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
|
|
33420
|
-
const filepath =
|
|
33980
|
+
const filepath = join35(dir, filename);
|
|
33421
33981
|
writeFileSync11(filepath, script, "utf-8");
|
|
33422
33982
|
console.log(`\u2713 Completion script written to ${filepath}
|
|
33423
33983
|
`);
|
|
@@ -33590,9 +34150,9 @@ __export(evolve_exports2, {
|
|
|
33590
34150
|
evolveStatus: () => evolveStatus,
|
|
33591
34151
|
evolveUndo: () => evolveUndo
|
|
33592
34152
|
});
|
|
33593
|
-
import { existsSync as
|
|
34153
|
+
import { existsSync as existsSync56 } from "fs";
|
|
33594
34154
|
function ensureDb3() {
|
|
33595
|
-
if (!
|
|
34155
|
+
if (!existsSync56(DB_PATH)) {
|
|
33596
34156
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
33597
34157
|
process.exit(1);
|
|
33598
34158
|
}
|
|
@@ -34066,10 +34626,10 @@ var init_optimize2 = __esm({
|
|
|
34066
34626
|
|
|
34067
34627
|
// src/setup.ts
|
|
34068
34628
|
var setup_exports = {};
|
|
34069
|
-
import { existsSync as
|
|
34629
|
+
import { existsSync as existsSync57, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
34070
34630
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
34071
34631
|
import { createInterface as createInterface11 } from "readline";
|
|
34072
|
-
import { join as
|
|
34632
|
+
import { join as join36 } from "path";
|
|
34073
34633
|
function divider2() {
|
|
34074
34634
|
console.log(dim("\u2500".repeat(55)));
|
|
34075
34635
|
}
|
|
@@ -34144,10 +34704,10 @@ async function setup() {
|
|
|
34144
34704
|
}
|
|
34145
34705
|
console.log("");
|
|
34146
34706
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
34147
|
-
if (!
|
|
34707
|
+
if (!existsSync57(dir)) mkdirSync18(dir, { recursive: true });
|
|
34148
34708
|
}
|
|
34149
34709
|
const env = {};
|
|
34150
|
-
const envSource =
|
|
34710
|
+
const envSource = existsSync57(ENV_PATH) ? ENV_PATH : existsSync57(".env") ? ".env" : null;
|
|
34151
34711
|
if (envSource) {
|
|
34152
34712
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
34153
34713
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
@@ -34157,8 +34717,8 @@ async function setup() {
|
|
|
34157
34717
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
34158
34718
|
}
|
|
34159
34719
|
}
|
|
34160
|
-
const cwdDb =
|
|
34161
|
-
if (
|
|
34720
|
+
const cwdDb = join36(process.cwd(), "cc-claw.db");
|
|
34721
|
+
if (existsSync57(cwdDb) && !existsSync57(DB_PATH)) {
|
|
34162
34722
|
const { size } = statSync12(cwdDb);
|
|
34163
34723
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
34164
34724
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|