cc-claw 0.20.12 → 0.20.14
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 +1047 -479
- 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.14" : (() => {
|
|
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"() {
|
|
@@ -8075,8 +8088,11 @@ function revokeSubAgentToken(agentId) {
|
|
|
8075
8088
|
function authenticateRequest(req, url) {
|
|
8076
8089
|
const authHeader = req.headers.authorization ?? "";
|
|
8077
8090
|
const bearerToken = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
8078
|
-
const
|
|
8079
|
-
const
|
|
8091
|
+
const queryToken = url.searchParams.get("token") ?? "";
|
|
8092
|
+
const isBrowserRoute = BROWSER_ROUTES.has(url.pathname) || url.pathname.startsWith("/files");
|
|
8093
|
+
const effectiveToken = bearerToken || (isBrowserRoute ? queryToken : "");
|
|
8094
|
+
const isMainToken = effectiveToken === DASHBOARD_TOKEN;
|
|
8095
|
+
const subEntry = subAgentTokens.get(effectiveToken);
|
|
8080
8096
|
const isSubAgentToken = !!subEntry && subEntry.expiresAt > Date.now();
|
|
8081
8097
|
if (!isMainToken && !isSubAgentToken) {
|
|
8082
8098
|
return { authenticated: false, isSubAgent: false };
|
|
@@ -8123,7 +8139,7 @@ function validateAgentIdentity(req, body) {
|
|
|
8123
8139
|
throw new Error(`IDENTITY_MISMATCH: Token bound to agent ${boundAgentId}, but request claims ${callerAgentId}`);
|
|
8124
8140
|
}
|
|
8125
8141
|
}
|
|
8126
|
-
var PORT, DASHBOARD_TOKEN, SUB_AGENT_TOKEN_TTL_MS, subAgentTokens, SUB_AGENT_ALLOWED_PATHS, MAX_BODY_BYTES;
|
|
8142
|
+
var PORT, DASHBOARD_TOKEN, SUB_AGENT_TOKEN_TTL_MS, subAgentTokens, SUB_AGENT_ALLOWED_PATHS, BROWSER_ROUTES, MAX_BODY_BYTES;
|
|
8127
8143
|
var init_middleware = __esm({
|
|
8128
8144
|
"src/dashboard/middleware.ts"() {
|
|
8129
8145
|
"use strict";
|
|
@@ -8159,6 +8175,7 @@ var init_middleware = __esm({
|
|
|
8159
8175
|
"/api/memory/history",
|
|
8160
8176
|
"/api/memory/summaries"
|
|
8161
8177
|
]);
|
|
8178
|
+
BROWSER_ROUTES = /* @__PURE__ */ new Set(["/", "/index.html", "/upload"]);
|
|
8162
8179
|
MAX_BODY_BYTES = 1048576;
|
|
8163
8180
|
}
|
|
8164
8181
|
});
|
|
@@ -9471,8 +9488,8 @@ function pruneAgentLogs() {
|
|
|
9471
9488
|
mkdirSync6(AGENTS_PATH, { recursive: true });
|
|
9472
9489
|
const files = readdirSync6(AGENTS_PATH).filter((f) => f.endsWith(".log")).map((f) => {
|
|
9473
9490
|
const fullPath = join11(AGENTS_PATH, f);
|
|
9474
|
-
const
|
|
9475
|
-
return { path: fullPath, mtime:
|
|
9491
|
+
const stat4 = statSync4(fullPath);
|
|
9492
|
+
return { path: fullPath, mtime: stat4.mtimeMs, size: stat4.size };
|
|
9476
9493
|
});
|
|
9477
9494
|
const now = Date.now();
|
|
9478
9495
|
const remaining = files.filter((f) => {
|
|
@@ -12720,12 +12737,12 @@ var init_evolve = __esm({
|
|
|
12720
12737
|
const body = JSON.parse(await readBody(req));
|
|
12721
12738
|
const { setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
12722
12739
|
const { existsSync: fileExists, readFileSync: fileRead } = await import("fs");
|
|
12723
|
-
const { join:
|
|
12740
|
+
const { join: join37 } = await import("path");
|
|
12724
12741
|
const { CC_CLAW_HOME: home } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
12725
12742
|
const chatId = resolveChatId(body);
|
|
12726
12743
|
if (!chatId) return jsonResponse(res, { error: "No chatId provided and ALLOWED_CHAT_ID is not set" }, 400);
|
|
12727
|
-
const soulPath =
|
|
12728
|
-
const userPath =
|
|
12744
|
+
const soulPath = join37(home, "identity/SOUL.md");
|
|
12745
|
+
const userPath = join37(home, "identity/USER.md");
|
|
12729
12746
|
const soul = fileExists(soulPath) ? fileRead(soulPath, "utf-8") : "";
|
|
12730
12747
|
const user = fileExists(userPath) ? fileRead(userPath, "utf-8") : "";
|
|
12731
12748
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
@@ -12777,6 +12794,346 @@ var init_evolve = __esm({
|
|
|
12777
12794
|
}
|
|
12778
12795
|
});
|
|
12779
12796
|
|
|
12797
|
+
// src/dashboard/routes/files.ts
|
|
12798
|
+
import { createWriteStream, existsSync as existsSync14 } from "fs";
|
|
12799
|
+
import { readdir, stat, unlink, mkdir } from "fs/promises";
|
|
12800
|
+
import { join as join14, extname } from "path";
|
|
12801
|
+
function getUploadHtml(token, host) {
|
|
12802
|
+
return `<!DOCTYPE html>
|
|
12803
|
+
<html lang="en">
|
|
12804
|
+
<head>
|
|
12805
|
+
<meta charset="utf-8">
|
|
12806
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
12807
|
+
<title>CC-Claw File Upload</title>
|
|
12808
|
+
<style>
|
|
12809
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
12810
|
+
body { font-family: -apple-system, system-ui, sans-serif; background: #0d1117; color: #e6edf3; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
|
|
12811
|
+
.container { max-width: 520px; width: 100%; padding: 24px; }
|
|
12812
|
+
h1 { font-size: 1.4rem; margin-bottom: 8px; }
|
|
12813
|
+
.subtitle { color: #8b949e; margin-bottom: 24px; font-size: 0.9rem; }
|
|
12814
|
+
.drop-zone { border: 2px dashed #30363d; border-radius: 12px; padding: 48px 24px; text-align: center; cursor: pointer; transition: all 0.2s; }
|
|
12815
|
+
.drop-zone:hover, .drop-zone.drag-over { border-color: #58a6ff; background: rgba(88,166,255,0.05); }
|
|
12816
|
+
.drop-zone p { color: #8b949e; margin-top: 8px; font-size: 0.85rem; }
|
|
12817
|
+
.drop-zone .icon { font-size: 2.5rem; margin-bottom: 8px; }
|
|
12818
|
+
input[type="file"] { display: none; }
|
|
12819
|
+
.progress-wrap { margin-top: 16px; display: none; }
|
|
12820
|
+
.progress-bar { height: 6px; background: #21262d; border-radius: 3px; overflow: hidden; }
|
|
12821
|
+
.progress-fill { height: 100%; width: 0%; background: #58a6ff; transition: width 0.3s; }
|
|
12822
|
+
.progress-text { font-size: 0.8rem; color: #8b949e; margin-top: 6px; text-align: center; }
|
|
12823
|
+
.result { margin-top: 16px; padding: 12px; border-radius: 8px; font-size: 0.85rem; display: none; }
|
|
12824
|
+
.result.success { background: rgba(63,185,80,0.1); border: 1px solid #238636; color: #3fb950; }
|
|
12825
|
+
.result.error { background: rgba(248,81,73,0.1); border: 1px solid #da3633; color: #f85149; }
|
|
12826
|
+
.file-link { color: #58a6ff; text-decoration: none; word-break: break-all; }
|
|
12827
|
+
.file-link:hover { text-decoration: underline; }
|
|
12828
|
+
.files-list { margin-top: 24px; }
|
|
12829
|
+
.files-list h3 { font-size: 0.9rem; color: #8b949e; margin-bottom: 8px; }
|
|
12830
|
+
.files-list ul { list-style: none; }
|
|
12831
|
+
.files-list li { padding: 6px 0; border-bottom: 1px solid #21262d; font-size: 0.85rem; }
|
|
12832
|
+
.files-list li:last-child { border: none; }
|
|
12833
|
+
</style>
|
|
12834
|
+
</head>
|
|
12835
|
+
<body>
|
|
12836
|
+
<div class="container">
|
|
12837
|
+
<h1>CC-Claw Upload</h1>
|
|
12838
|
+
<p class="subtitle">Upload files for CC-Claw to use. Max 200MB.</p>
|
|
12839
|
+
<div class="drop-zone" id="dropZone">
|
|
12840
|
+
<div class="icon">\u{1F4C1}</div>
|
|
12841
|
+
<strong>Drop file here or click to browse</strong>
|
|
12842
|
+
<p>Videos, images, documents \u2014 anything the bot needs</p>
|
|
12843
|
+
</div>
|
|
12844
|
+
<input type="file" id="fileInput">
|
|
12845
|
+
<div class="progress-wrap" id="progressWrap">
|
|
12846
|
+
<div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
|
|
12847
|
+
<div class="progress-text" id="progressText">Uploading...</div>
|
|
12848
|
+
</div>
|
|
12849
|
+
<div class="result" id="result"></div>
|
|
12850
|
+
<div class="files-list" id="filesList"></div>
|
|
12851
|
+
</div>
|
|
12852
|
+
<script>
|
|
12853
|
+
const TOKEN = "${token}";
|
|
12854
|
+
const HOST = "${host}";
|
|
12855
|
+
const dropZone = document.getElementById("dropZone");
|
|
12856
|
+
const fileInput = document.getElementById("fileInput");
|
|
12857
|
+
const progressWrap = document.getElementById("progressWrap");
|
|
12858
|
+
const progressFill = document.getElementById("progressFill");
|
|
12859
|
+
const progressText = document.getElementById("progressText");
|
|
12860
|
+
const resultDiv = document.getElementById("result");
|
|
12861
|
+
|
|
12862
|
+
dropZone.addEventListener("click", () => fileInput.click());
|
|
12863
|
+
dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("drag-over"); });
|
|
12864
|
+
dropZone.addEventListener("dragleave", () => dropZone.classList.remove("drag-over"));
|
|
12865
|
+
dropZone.addEventListener("drop", e => { e.preventDefault(); dropZone.classList.remove("drag-over"); if (e.dataTransfer.files.length) uploadFile(e.dataTransfer.files[0]); });
|
|
12866
|
+
fileInput.addEventListener("change", () => { if (fileInput.files.length) uploadFile(fileInput.files[0]); });
|
|
12867
|
+
|
|
12868
|
+
function uploadFile(file) {
|
|
12869
|
+
if (file.size > 200 * 1024 * 1024) { showResult("File too large (max 200MB)", true); return; }
|
|
12870
|
+
progressWrap.style.display = "block";
|
|
12871
|
+
resultDiv.style.display = "none";
|
|
12872
|
+
progressFill.style.width = "0%";
|
|
12873
|
+
progressText.textContent = "Uploading " + file.name + "...";
|
|
12874
|
+
|
|
12875
|
+
const xhr = new XMLHttpRequest();
|
|
12876
|
+
xhr.open("POST", HOST + "/upload");
|
|
12877
|
+
xhr.setRequestHeader("Authorization", "Bearer " + TOKEN);
|
|
12878
|
+
|
|
12879
|
+
xhr.upload.addEventListener("progress", e => {
|
|
12880
|
+
if (e.lengthComputable) {
|
|
12881
|
+
const pct = Math.round(e.loaded / e.total * 100);
|
|
12882
|
+
progressFill.style.width = pct + "%";
|
|
12883
|
+
progressText.textContent = pct + "% \u2014 " + (e.loaded / 1024 / 1024).toFixed(1) + " / " + (e.total / 1024 / 1024).toFixed(1) + " MB";
|
|
12884
|
+
}
|
|
12885
|
+
});
|
|
12886
|
+
xhr.addEventListener("load", () => {
|
|
12887
|
+
progressWrap.style.display = "none";
|
|
12888
|
+
try {
|
|
12889
|
+
const resp = JSON.parse(xhr.responseText);
|
|
12890
|
+
if (resp.ok && resp.data) {
|
|
12891
|
+
const link = HOST + "/files/" + resp.data.filename;
|
|
12892
|
+
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);
|
|
12893
|
+
loadFileList();
|
|
12894
|
+
} else {
|
|
12895
|
+
showResult(resp.error || "Upload failed", true);
|
|
12896
|
+
}
|
|
12897
|
+
} catch { showResult("Upload failed: " + xhr.statusText, true); }
|
|
12898
|
+
});
|
|
12899
|
+
xhr.addEventListener("error", () => { progressWrap.style.display = "none"; showResult("Connection failed", true); });
|
|
12900
|
+
xhr.addEventListener("abort", () => { progressWrap.style.display = "none"; showResult("Upload cancelled", true); });
|
|
12901
|
+
|
|
12902
|
+
const formData = new FormData();
|
|
12903
|
+
formData.append("file", file);
|
|
12904
|
+
xhr.send(formData);
|
|
12905
|
+
}
|
|
12906
|
+
|
|
12907
|
+
function showResult(html, isError) {
|
|
12908
|
+
resultDiv.innerHTML = html;
|
|
12909
|
+
resultDiv.className = "result " + (isError ? "error" : "success");
|
|
12910
|
+
resultDiv.style.display = "block";
|
|
12911
|
+
}
|
|
12912
|
+
|
|
12913
|
+
function loadFileList() {
|
|
12914
|
+
fetch(HOST + "/files", { headers: { "Authorization": "Bearer " + TOKEN } })
|
|
12915
|
+
.then(r => r.json()).then(resp => {
|
|
12916
|
+
if (!resp.ok || !resp.data?.length) { document.getElementById("filesList").innerHTML = ""; return; }
|
|
12917
|
+
const items = resp.data.slice(0, 20).map(f =>
|
|
12918
|
+
'<li><a class="file-link" href="' + HOST + '/files/' + f.name + '" target="_blank">' + f.name + '</a> (' + f.sizeMB + ' MB)</li>'
|
|
12919
|
+
).join("");
|
|
12920
|
+
document.getElementById("filesList").innerHTML = "<h3>Recent Files</h3><ul>" + items + "</ul>";
|
|
12921
|
+
}).catch(() => {});
|
|
12922
|
+
}
|
|
12923
|
+
loadFileList();
|
|
12924
|
+
</script>
|
|
12925
|
+
</body>
|
|
12926
|
+
</html>`;
|
|
12927
|
+
}
|
|
12928
|
+
function handleUploadPage(req, res, _url, token) {
|
|
12929
|
+
const host = `http://${req.headers.host ?? "localhost:3141"}`;
|
|
12930
|
+
htmlResponse(res, getUploadHtml(token, host));
|
|
12931
|
+
}
|
|
12932
|
+
async function handleUpload(req, res) {
|
|
12933
|
+
const contentType = req.headers["content-type"] ?? "";
|
|
12934
|
+
const boundaryMatch = contentType.match(/boundary=(.+?)(?:;|$)/);
|
|
12935
|
+
if (!boundaryMatch) {
|
|
12936
|
+
return jsonResponse(res, { ok: false, error: "Missing multipart boundary" }, 400);
|
|
12937
|
+
}
|
|
12938
|
+
const boundary = boundaryMatch[1];
|
|
12939
|
+
const contentLength = parseInt(req.headers["content-length"] ?? "0", 10);
|
|
12940
|
+
if (contentLength > MAX_UPLOAD_BYTES) {
|
|
12941
|
+
return jsonResponse(res, { ok: false, error: `File too large (max ${MAX_UPLOAD_BYTES / 1024 / 1024}MB)` }, 413);
|
|
12942
|
+
}
|
|
12943
|
+
await mkdir(INCOMING_PATH, { recursive: true });
|
|
12944
|
+
try {
|
|
12945
|
+
const result = await parseMultipartUpload(req, boundary);
|
|
12946
|
+
if (!result) {
|
|
12947
|
+
return jsonResponse(res, { ok: false, error: "No file found in upload" }, 400);
|
|
12948
|
+
}
|
|
12949
|
+
const ext = extname(result.originalName).toLowerCase();
|
|
12950
|
+
if (BLOCKED_EXTENSIONS.has(ext)) {
|
|
12951
|
+
try {
|
|
12952
|
+
await unlink(result.path);
|
|
12953
|
+
} catch {
|
|
12954
|
+
}
|
|
12955
|
+
return jsonResponse(res, { ok: false, error: `File type ${ext} is not allowed` }, 400);
|
|
12956
|
+
}
|
|
12957
|
+
const fileInfo = await stat(result.path);
|
|
12958
|
+
const sizeMB = (fileInfo.size / 1024 / 1024).toFixed(1);
|
|
12959
|
+
log(`[upload] Saved ${result.filename} (${sizeMB} MB) from ${result.originalName}`);
|
|
12960
|
+
return jsonResponse(res, {
|
|
12961
|
+
ok: true,
|
|
12962
|
+
data: {
|
|
12963
|
+
filename: result.filename,
|
|
12964
|
+
originalName: result.originalName,
|
|
12965
|
+
path: result.path,
|
|
12966
|
+
sizeMB
|
|
12967
|
+
}
|
|
12968
|
+
});
|
|
12969
|
+
} catch (err) {
|
|
12970
|
+
warn(`[upload] Failed: ${err.message}`);
|
|
12971
|
+
return jsonResponse(res, { ok: false, error: `Upload failed: ${err.message}` }, 500);
|
|
12972
|
+
}
|
|
12973
|
+
}
|
|
12974
|
+
async function handleFileList(req, res) {
|
|
12975
|
+
await mkdir(INCOMING_PATH, { recursive: true });
|
|
12976
|
+
try {
|
|
12977
|
+
const entries = await readdir(INCOMING_PATH);
|
|
12978
|
+
const files = [];
|
|
12979
|
+
for (const name of entries) {
|
|
12980
|
+
try {
|
|
12981
|
+
const s = await stat(join14(INCOMING_PATH, name));
|
|
12982
|
+
if (!s.isFile()) continue;
|
|
12983
|
+
files.push({
|
|
12984
|
+
name,
|
|
12985
|
+
sizeMB: (s.size / 1024 / 1024).toFixed(1),
|
|
12986
|
+
modified: s.mtime.toISOString()
|
|
12987
|
+
});
|
|
12988
|
+
} catch {
|
|
12989
|
+
continue;
|
|
12990
|
+
}
|
|
12991
|
+
}
|
|
12992
|
+
files.sort((a, b) => b.modified.localeCompare(a.modified));
|
|
12993
|
+
return jsonResponse(res, { ok: true, data: files });
|
|
12994
|
+
} catch {
|
|
12995
|
+
return jsonResponse(res, { ok: true, data: [] });
|
|
12996
|
+
}
|
|
12997
|
+
}
|
|
12998
|
+
async function handleFileServe(req, res, url) {
|
|
12999
|
+
const filename = decodeURIComponent(url.pathname.slice("/files/".length));
|
|
13000
|
+
if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) {
|
|
13001
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
13002
|
+
res.end("Invalid filename");
|
|
13003
|
+
return;
|
|
13004
|
+
}
|
|
13005
|
+
const filePath = join14(INCOMING_PATH, filename);
|
|
13006
|
+
if (!existsSync14(filePath)) {
|
|
13007
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
13008
|
+
res.end("File not found");
|
|
13009
|
+
return;
|
|
13010
|
+
}
|
|
13011
|
+
const fileInfo = await stat(filePath);
|
|
13012
|
+
const ext = extname(filename).toLowerCase();
|
|
13013
|
+
const mimeTypes = {
|
|
13014
|
+
".mp4": "video/mp4",
|
|
13015
|
+
".mov": "video/quicktime",
|
|
13016
|
+
".avi": "video/x-msvideo",
|
|
13017
|
+
".webm": "video/webm",
|
|
13018
|
+
".jpg": "image/jpeg",
|
|
13019
|
+
".jpeg": "image/jpeg",
|
|
13020
|
+
".png": "image/png",
|
|
13021
|
+
".gif": "image/gif",
|
|
13022
|
+
".webp": "image/webp",
|
|
13023
|
+
".pdf": "application/pdf",
|
|
13024
|
+
".txt": "text/plain",
|
|
13025
|
+
".json": "application/json",
|
|
13026
|
+
".mp3": "audio/mpeg",
|
|
13027
|
+
".wav": "audio/wav",
|
|
13028
|
+
".ogg": "audio/ogg"
|
|
13029
|
+
};
|
|
13030
|
+
const mime = mimeTypes[ext] ?? "application/octet-stream";
|
|
13031
|
+
res.writeHead(200, {
|
|
13032
|
+
"Content-Type": mime,
|
|
13033
|
+
"Content-Length": fileInfo.size,
|
|
13034
|
+
"Content-Disposition": `inline; filename="${filename}"`,
|
|
13035
|
+
"Cache-Control": "public, max-age=3600"
|
|
13036
|
+
});
|
|
13037
|
+
const { createReadStream: createReadStream2 } = await import("fs");
|
|
13038
|
+
const stream = createReadStream2(filePath);
|
|
13039
|
+
stream.pipe(res);
|
|
13040
|
+
stream.on("error", () => {
|
|
13041
|
+
if (!res.headersSent) {
|
|
13042
|
+
res.writeHead(500);
|
|
13043
|
+
}
|
|
13044
|
+
res.end();
|
|
13045
|
+
});
|
|
13046
|
+
}
|
|
13047
|
+
function parseMultipartUpload(req, boundary) {
|
|
13048
|
+
return new Promise((resolve, reject) => {
|
|
13049
|
+
const chunks = [];
|
|
13050
|
+
let totalBytes = 0;
|
|
13051
|
+
let cleanupPath = null;
|
|
13052
|
+
req.on("data", (chunk) => {
|
|
13053
|
+
totalBytes += chunk.length;
|
|
13054
|
+
if (totalBytes > MAX_UPLOAD_BYTES) {
|
|
13055
|
+
req.destroy(new Error(`Upload exceeds ${MAX_UPLOAD_BYTES / 1024 / 1024}MB limit`));
|
|
13056
|
+
return;
|
|
13057
|
+
}
|
|
13058
|
+
chunks.push(chunk);
|
|
13059
|
+
});
|
|
13060
|
+
req.on("error", async (err) => {
|
|
13061
|
+
if (cleanupPath) {
|
|
13062
|
+
try {
|
|
13063
|
+
await unlink(cleanupPath);
|
|
13064
|
+
} catch {
|
|
13065
|
+
}
|
|
13066
|
+
}
|
|
13067
|
+
reject(err);
|
|
13068
|
+
});
|
|
13069
|
+
req.on("end", async () => {
|
|
13070
|
+
try {
|
|
13071
|
+
const body = Buffer.concat(chunks);
|
|
13072
|
+
const boundaryBuf = Buffer.from(`--${boundary}`);
|
|
13073
|
+
const headerEnd = body.indexOf(Buffer.from("\r\n\r\n"));
|
|
13074
|
+
if (headerEnd === -1) return resolve(null);
|
|
13075
|
+
const headerSection = body.subarray(0, headerEnd).toString("utf-8");
|
|
13076
|
+
const nameMatch = headerSection.match(/filename="(.+?)"/);
|
|
13077
|
+
if (!nameMatch) return resolve(null);
|
|
13078
|
+
const originalName = nameMatch[1].replace(/[^\w.-]/g, "_");
|
|
13079
|
+
const ext = extname(originalName) || ".bin";
|
|
13080
|
+
const filename = `upload-${Date.now()}${ext}`;
|
|
13081
|
+
const filePath = join14(INCOMING_PATH, filename);
|
|
13082
|
+
cleanupPath = filePath;
|
|
13083
|
+
const fileStart = headerEnd + 4;
|
|
13084
|
+
const closingBoundary = Buffer.from(`\r
|
|
13085
|
+
--${boundary}`);
|
|
13086
|
+
let fileEnd = body.indexOf(closingBoundary, fileStart);
|
|
13087
|
+
if (fileEnd === -1) fileEnd = body.length;
|
|
13088
|
+
const fileContent = body.subarray(fileStart, fileEnd);
|
|
13089
|
+
const ws = createWriteStream(filePath);
|
|
13090
|
+
await new Promise((res, rej) => {
|
|
13091
|
+
ws.write(fileContent, (err) => {
|
|
13092
|
+
if (err) return rej(err);
|
|
13093
|
+
ws.end(res);
|
|
13094
|
+
});
|
|
13095
|
+
});
|
|
13096
|
+
cleanupPath = null;
|
|
13097
|
+
resolve({ filename, originalName, path: filePath });
|
|
13098
|
+
} catch (err) {
|
|
13099
|
+
if (cleanupPath) {
|
|
13100
|
+
try {
|
|
13101
|
+
await unlink(cleanupPath);
|
|
13102
|
+
} catch {
|
|
13103
|
+
}
|
|
13104
|
+
}
|
|
13105
|
+
reject(err);
|
|
13106
|
+
}
|
|
13107
|
+
});
|
|
13108
|
+
});
|
|
13109
|
+
}
|
|
13110
|
+
var INCOMING_PATH, MAX_UPLOAD_BYTES, BLOCKED_EXTENSIONS;
|
|
13111
|
+
var init_files = __esm({
|
|
13112
|
+
"src/dashboard/routes/files.ts"() {
|
|
13113
|
+
"use strict";
|
|
13114
|
+
init_paths();
|
|
13115
|
+
init_middleware();
|
|
13116
|
+
init_log();
|
|
13117
|
+
INCOMING_PATH = join14(MEDIA_PATH, "incoming");
|
|
13118
|
+
MAX_UPLOAD_BYTES = 200 * 1024 * 1024;
|
|
13119
|
+
BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
13120
|
+
".exe",
|
|
13121
|
+
".bat",
|
|
13122
|
+
".cmd",
|
|
13123
|
+
".com",
|
|
13124
|
+
".scr",
|
|
13125
|
+
".pif",
|
|
13126
|
+
".msi",
|
|
13127
|
+
".sh",
|
|
13128
|
+
".bash",
|
|
13129
|
+
".csh",
|
|
13130
|
+
".ksh",
|
|
13131
|
+
".ps1",
|
|
13132
|
+
".psm1"
|
|
13133
|
+
]);
|
|
13134
|
+
}
|
|
13135
|
+
});
|
|
13136
|
+
|
|
12780
13137
|
// src/dashboard/server.ts
|
|
12781
13138
|
var server_exports = {};
|
|
12782
13139
|
__export(server_exports, {
|
|
@@ -12876,6 +13233,16 @@ function startDashboard() {
|
|
|
12876
13233
|
if (url.pathname === "/api/evolve/off" && req.method === "POST") return handleEvolveOff(req, res, url);
|
|
12877
13234
|
if (url.pathname === "/api/evolve/model" && req.method === "POST") return handleEvolveModel(req, res, url);
|
|
12878
13235
|
if (url.pathname === "/api/evolve/settings" && req.method === "POST") return handleEvolveSettings(req, res, url);
|
|
13236
|
+
if (url.pathname === "/upload" && req.method === "GET") return handleUploadPage(req, res, url, getDashboardToken());
|
|
13237
|
+
if (url.pathname === "/upload" && req.method === "POST") {
|
|
13238
|
+
await handleUpload(req, res);
|
|
13239
|
+
return;
|
|
13240
|
+
}
|
|
13241
|
+
if (url.pathname === "/files" && req.method === "GET") return handleFileList(req, res);
|
|
13242
|
+
if (url.pathname.startsWith("/files/") && req.method === "GET") {
|
|
13243
|
+
await handleFileServe(req, res, url);
|
|
13244
|
+
return;
|
|
13245
|
+
}
|
|
12879
13246
|
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
12880
13247
|
return htmlResponse(res, DASHBOARD_HTML.replace("__TOKEN__", getDashboardToken()));
|
|
12881
13248
|
}
|
|
@@ -12891,7 +13258,8 @@ function startDashboard() {
|
|
|
12891
13258
|
console.error(`[api] Server error:`, err);
|
|
12892
13259
|
}
|
|
12893
13260
|
});
|
|
12894
|
-
|
|
13261
|
+
const bindHost = process.env.CC_CLAW_DASHBOARD_HOST ?? "0.0.0.0";
|
|
13262
|
+
server.listen(PORT, bindHost);
|
|
12895
13263
|
persistToken();
|
|
12896
13264
|
return server;
|
|
12897
13265
|
}
|
|
@@ -12908,6 +13276,7 @@ var init_server = __esm({
|
|
|
12908
13276
|
init_scheduler();
|
|
12909
13277
|
init_config();
|
|
12910
13278
|
init_evolve();
|
|
13279
|
+
init_files();
|
|
12911
13280
|
}
|
|
12912
13281
|
});
|
|
12913
13282
|
|
|
@@ -14779,10 +15148,10 @@ var init_telegram_throttle = __esm({
|
|
|
14779
15148
|
});
|
|
14780
15149
|
|
|
14781
15150
|
// src/health/checks.ts
|
|
14782
|
-
import { existsSync as
|
|
15151
|
+
import { existsSync as existsSync15, statSync as statSync6, readFileSync as readFileSync8 } from "fs";
|
|
14783
15152
|
import { execFileSync, execSync as execSync3 } from "child_process";
|
|
14784
15153
|
function getRecentErrors() {
|
|
14785
|
-
if (!
|
|
15154
|
+
if (!existsSync15(ERROR_LOG_PATH)) return null;
|
|
14786
15155
|
const logContent = readFileSync8(ERROR_LOG_PATH, "utf-8");
|
|
14787
15156
|
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
14788
15157
|
const last24h = Date.now() - 864e5;
|
|
@@ -14804,7 +15173,7 @@ function getRecentErrors() {
|
|
|
14804
15173
|
}
|
|
14805
15174
|
function checkDatabase() {
|
|
14806
15175
|
const checks = [];
|
|
14807
|
-
if (
|
|
15176
|
+
if (existsSync15(DB_PATH)) {
|
|
14808
15177
|
const size = statSync6(DB_PATH).size;
|
|
14809
15178
|
checks.push({ name: "Database", status: "ok", message: `${(size / 1024).toFixed(0)}KB` });
|
|
14810
15179
|
} else {
|
|
@@ -14836,18 +15205,18 @@ function checkErrorLog() {
|
|
|
14836
15205
|
return checks;
|
|
14837
15206
|
}
|
|
14838
15207
|
if (errors.rate429.length > 10) {
|
|
14839
|
-
checks.push({ name: "Rate limits", status: "error", message: `${errors.rate429.length} rate-limit (429) errors in last 24h
|
|
15208
|
+
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
15209
|
} else if (errors.rate429.length > 0) {
|
|
14841
|
-
checks.push({ name: "Rate limits", status: "warning", message: `${errors.rate429.length} rate-limit errors in last 24h
|
|
15210
|
+
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
15211
|
}
|
|
14843
15212
|
if (errors.contentSilence.length > 0) {
|
|
14844
|
-
checks.push({ name: "Content silence", status: "warning", message: `${errors.contentSilence.length} silence timeout(s) in last 24h
|
|
15213
|
+
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
15214
|
}
|
|
14846
15215
|
if (errors.spawnTimeout.length > 0) {
|
|
14847
|
-
checks.push({ name: "Spawn timeouts", status: "warning", message: `${errors.spawnTimeout.length} backend timeout(s) in last 24h
|
|
15216
|
+
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
15217
|
}
|
|
14849
15218
|
if (errors.other.length > 0) {
|
|
14850
|
-
checks.push({ name: "Other errors", status: "warning", message: `${errors.other.length} error(s) in last 24h
|
|
15219
|
+
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
15220
|
}
|
|
14852
15221
|
if (checks.length === 0) {
|
|
14853
15222
|
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
@@ -15012,8 +15381,8 @@ __export(heartbeat_exports, {
|
|
|
15012
15381
|
stopHeartbeatForChat: () => stopHeartbeatForChat,
|
|
15013
15382
|
updateHeartbeatConfig: () => updateHeartbeatConfig
|
|
15014
15383
|
});
|
|
15015
|
-
import { readFileSync as readFileSync9, existsSync as
|
|
15016
|
-
import { join as
|
|
15384
|
+
import { readFileSync as readFileSync9, existsSync as existsSync16 } from "fs";
|
|
15385
|
+
import { join as join15 } from "path";
|
|
15017
15386
|
function findHeartbeatJob() {
|
|
15018
15387
|
try {
|
|
15019
15388
|
const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
@@ -15144,7 +15513,7 @@ ${healthText}`);
|
|
|
15144
15513
|
sections.push(`[Active Watches \u2014 execute these checks using your tools]
|
|
15145
15514
|
${watchLines.join("\n")}`);
|
|
15146
15515
|
}
|
|
15147
|
-
if (
|
|
15516
|
+
if (existsSync16(HEARTBEAT_MD_PATH)) {
|
|
15148
15517
|
try {
|
|
15149
15518
|
const custom = readFileSync9(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
15150
15519
|
if (custom) {
|
|
@@ -15206,7 +15575,7 @@ var init_heartbeat2 = __esm({
|
|
|
15206
15575
|
init_store5();
|
|
15207
15576
|
init_checks();
|
|
15208
15577
|
init_log();
|
|
15209
|
-
HEARTBEAT_MD_PATH =
|
|
15578
|
+
HEARTBEAT_MD_PATH = join15(WORKSPACE_PATH, "HEARTBEAT.md");
|
|
15210
15579
|
HEARTBEAT_OK = "HEARTBEAT_OK";
|
|
15211
15580
|
HEARTBEAT_JOB_TYPE = "heartbeat";
|
|
15212
15581
|
DEFAULT_INTERVAL_MS = 30 * 60 * 1e3;
|
|
@@ -15215,8 +15584,8 @@ var init_heartbeat2 = __esm({
|
|
|
15215
15584
|
});
|
|
15216
15585
|
|
|
15217
15586
|
// src/bootstrap/profile.ts
|
|
15218
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as
|
|
15219
|
-
import { join as
|
|
15587
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync17 } from "fs";
|
|
15588
|
+
import { join as join16 } from "path";
|
|
15220
15589
|
function hasActiveProfile(chatId) {
|
|
15221
15590
|
return activeProfiles.has(chatId);
|
|
15222
15591
|
}
|
|
@@ -15345,7 +15714,7 @@ function extractUserUpdates(text) {
|
|
|
15345
15714
|
return { cleanText, updates };
|
|
15346
15715
|
}
|
|
15347
15716
|
function appendToUserProfile(key, value) {
|
|
15348
|
-
if (!
|
|
15717
|
+
if (!existsSync17(USER_PATH2)) return;
|
|
15349
15718
|
const content = readFileSync10(USER_PATH2, "utf-8");
|
|
15350
15719
|
const line = `- **${key}**: ${value}`;
|
|
15351
15720
|
if (content.includes(line)) return;
|
|
@@ -15361,7 +15730,7 @@ var init_profile = __esm({
|
|
|
15361
15730
|
"use strict";
|
|
15362
15731
|
init_paths();
|
|
15363
15732
|
init_log();
|
|
15364
|
-
USER_PATH2 =
|
|
15733
|
+
USER_PATH2 = join16(IDENTITY_PATH, "USER.md");
|
|
15365
15734
|
activeProfiles = /* @__PURE__ */ new Map();
|
|
15366
15735
|
}
|
|
15367
15736
|
});
|
|
@@ -16658,8 +17027,8 @@ __export(session_log_exports2, {
|
|
|
16658
17027
|
startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
|
|
16659
17028
|
tailSessionLog: () => tailSessionLog
|
|
16660
17029
|
});
|
|
16661
|
-
import { existsSync as
|
|
16662
|
-
import { join as
|
|
17030
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, appendFileSync, readdirSync as readdirSync9, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
|
|
17031
|
+
import { join as join17, basename } from "path";
|
|
16663
17032
|
import { createInterface as createInterface6 } from "readline";
|
|
16664
17033
|
function getRetentionDays() {
|
|
16665
17034
|
const env = process.env.SESSION_LOG_RETENTION_DAYS;
|
|
@@ -16671,13 +17040,13 @@ function getRetentionDays() {
|
|
|
16671
17040
|
}
|
|
16672
17041
|
function cleanupSessionLogs(retentionDays) {
|
|
16673
17042
|
const days = retentionDays ?? getRetentionDays();
|
|
16674
|
-
if (!
|
|
17043
|
+
if (!existsSync18(SESSION_LOGS_PATH)) return 0;
|
|
16675
17044
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
16676
17045
|
let cleaned = 0;
|
|
16677
17046
|
try {
|
|
16678
17047
|
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
16679
17048
|
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
16680
|
-
const filePath =
|
|
17049
|
+
const filePath = join17(SESSION_LOGS_PATH, file);
|
|
16681
17050
|
try {
|
|
16682
17051
|
const { mtimeMs } = statSync7(filePath);
|
|
16683
17052
|
if (mtimeMs < cutoff) {
|
|
@@ -16703,21 +17072,21 @@ function startSessionLogCleanupTimer() {
|
|
|
16703
17072
|
return timer;
|
|
16704
17073
|
}
|
|
16705
17074
|
function listSessionLogs() {
|
|
16706
|
-
if (!
|
|
17075
|
+
if (!existsSync18(SESSION_LOGS_PATH)) return [];
|
|
16707
17076
|
const logs = [];
|
|
16708
17077
|
for (const file of readdirSync9(SESSION_LOGS_PATH)) {
|
|
16709
17078
|
if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
|
|
16710
|
-
const filePath =
|
|
17079
|
+
const filePath = join17(SESSION_LOGS_PATH, file);
|
|
16711
17080
|
try {
|
|
16712
|
-
const
|
|
17081
|
+
const stat4 = statSync7(filePath);
|
|
16713
17082
|
const match = file.match(/^session-(.+?)-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})\.log$/);
|
|
16714
17083
|
logs.push({
|
|
16715
17084
|
filename: file,
|
|
16716
17085
|
filePath,
|
|
16717
17086
|
chatId: match?.[1] ?? "unknown",
|
|
16718
17087
|
timestamp: match?.[2]?.replace(/-/g, (m, i) => i > 9 ? ":" : m) ?? "unknown",
|
|
16719
|
-
sizeBytes:
|
|
16720
|
-
modifiedAt:
|
|
17088
|
+
sizeBytes: stat4.size,
|
|
17089
|
+
modifiedAt: stat4.mtime
|
|
16721
17090
|
});
|
|
16722
17091
|
} catch {
|
|
16723
17092
|
}
|
|
@@ -16726,7 +17095,7 @@ function listSessionLogs() {
|
|
|
16726
17095
|
return logs;
|
|
16727
17096
|
}
|
|
16728
17097
|
async function* tailSessionLog(filePath, lines = 50) {
|
|
16729
|
-
if (!
|
|
17098
|
+
if (!existsSync18(filePath)) {
|
|
16730
17099
|
yield `File not found: ${filePath}`;
|
|
16731
17100
|
return;
|
|
16732
17101
|
}
|
|
@@ -16754,12 +17123,12 @@ var init_session_log2 = __esm({
|
|
|
16754
17123
|
constructor(chatId, backend2, model2) {
|
|
16755
17124
|
this.backend = backend2;
|
|
16756
17125
|
this.model = model2;
|
|
16757
|
-
if (!
|
|
17126
|
+
if (!existsSync18(SESSION_LOGS_PATH)) {
|
|
16758
17127
|
mkdirSync8(SESSION_LOGS_PATH, { recursive: true });
|
|
16759
17128
|
}
|
|
16760
17129
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
16761
17130
|
const sanitizedChatId = chatId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
16762
|
-
this.filePath =
|
|
17131
|
+
this.filePath = join17(SESSION_LOGS_PATH, `session-${sanitizedChatId}-${ts2}.log`);
|
|
16763
17132
|
const header2 = [
|
|
16764
17133
|
"\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
17134
|
`CC-Claw Agent Session \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -17162,7 +17531,7 @@ var init_gate = __esm({
|
|
|
17162
17531
|
// src/voice/stt.ts
|
|
17163
17532
|
import crypto from "crypto";
|
|
17164
17533
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
17165
|
-
import { readFile as readFile2, unlink } from "fs/promises";
|
|
17534
|
+
import { readFile as readFile2, unlink as unlink2 } from "fs/promises";
|
|
17166
17535
|
import { promisify as promisify2 } from "util";
|
|
17167
17536
|
function ensureFfmpeg() {
|
|
17168
17537
|
if (ffmpegAvailable === true) return;
|
|
@@ -17316,10 +17685,10 @@ async function mp3ToOgg(mp3Buffer) {
|
|
|
17316
17685
|
await writeFile6(tmpMp3, mp3Buffer);
|
|
17317
17686
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpMp3, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17318
17687
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17319
|
-
|
|
17688
|
+
unlink2(tmpMp3).catch((err) => {
|
|
17320
17689
|
error("[tts] cleanup failed:", err);
|
|
17321
17690
|
});
|
|
17322
|
-
|
|
17691
|
+
unlink2(tmpOgg).catch((err) => {
|
|
17323
17692
|
error("[tts] cleanup failed:", err);
|
|
17324
17693
|
});
|
|
17325
17694
|
return oggBuffer;
|
|
@@ -17332,10 +17701,10 @@ async function macOsTts(text, voice2 = "Samantha") {
|
|
|
17332
17701
|
await execFileAsync2("say", ["-v", voice2, "-o", tmpAiff, text]);
|
|
17333
17702
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpAiff, "-c:a", "libopus", "-b:a", "64k", tmpOgg]);
|
|
17334
17703
|
const oggBuffer = await readFile2(tmpOgg);
|
|
17335
|
-
|
|
17704
|
+
unlink2(tmpAiff).catch((err) => {
|
|
17336
17705
|
error("[tts] cleanup failed:", err);
|
|
17337
17706
|
});
|
|
17338
|
-
|
|
17707
|
+
unlink2(tmpOgg).catch((err) => {
|
|
17339
17708
|
error("[tts] cleanup failed:", err);
|
|
17340
17709
|
});
|
|
17341
17710
|
return oggBuffer;
|
|
@@ -17367,9 +17736,9 @@ var init_stt = __esm({
|
|
|
17367
17736
|
});
|
|
17368
17737
|
|
|
17369
17738
|
// src/media/image-gen.ts
|
|
17370
|
-
import { mkdirSync as mkdirSync9, existsSync as
|
|
17739
|
+
import { mkdirSync as mkdirSync9, existsSync as existsSync19, unlink as unlink3, readdir as readdir2, stat as stat2 } from "fs";
|
|
17371
17740
|
import { writeFile } from "fs/promises";
|
|
17372
|
-
import { join as
|
|
17741
|
+
import { join as join18 } from "path";
|
|
17373
17742
|
async function generateImage(prompt) {
|
|
17374
17743
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
17375
17744
|
if (!apiKey) {
|
|
@@ -17416,12 +17785,12 @@ async function generateImage(prompt) {
|
|
|
17416
17785
|
if (!imageData) {
|
|
17417
17786
|
throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
|
|
17418
17787
|
}
|
|
17419
|
-
if (!
|
|
17788
|
+
if (!existsSync19(IMAGE_OUTPUT_DIR)) {
|
|
17420
17789
|
mkdirSync9(IMAGE_OUTPUT_DIR, { recursive: true });
|
|
17421
17790
|
}
|
|
17422
17791
|
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
17423
17792
|
const filename = `img_${Date.now()}.${ext}`;
|
|
17424
|
-
const filePath =
|
|
17793
|
+
const filePath = join18(IMAGE_OUTPUT_DIR, filename);
|
|
17425
17794
|
const buffer = Buffer.from(imageData, "base64");
|
|
17426
17795
|
await writeFile(filePath, buffer);
|
|
17427
17796
|
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
@@ -17431,21 +17800,21 @@ function isImageGenAvailable() {
|
|
|
17431
17800
|
return !!process.env.GEMINI_API_KEY;
|
|
17432
17801
|
}
|
|
17433
17802
|
function cleanupGeneratedImage(filePath) {
|
|
17434
|
-
|
|
17803
|
+
unlink3(filePath, (err) => {
|
|
17435
17804
|
if (err) warn(`[image-gen] cleanup failed for ${filePath}: ${err.message}`);
|
|
17436
17805
|
else log(`[image-gen] cleaned up ${filePath}`);
|
|
17437
17806
|
});
|
|
17438
17807
|
}
|
|
17439
17808
|
function pruneImageCache() {
|
|
17440
|
-
|
|
17809
|
+
readdir2(IMAGE_OUTPUT_DIR, (err, files) => {
|
|
17441
17810
|
if (err || !files) return;
|
|
17442
|
-
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) =>
|
|
17811
|
+
const imageFiles = files.filter((f) => /\.(png|jpg)$/.test(f)).map((f) => join18(IMAGE_OUTPUT_DIR, f));
|
|
17443
17812
|
if (imageFiles.length === 0) return;
|
|
17444
17813
|
const now = Date.now();
|
|
17445
17814
|
let statsPending = imageFiles.length;
|
|
17446
17815
|
const fileStats = [];
|
|
17447
17816
|
for (const fp of imageFiles) {
|
|
17448
|
-
|
|
17817
|
+
stat2(fp, (serr, s) => {
|
|
17449
17818
|
statsPending--;
|
|
17450
17819
|
if (!serr && s) fileStats.push({ path: fp, mtime: s.mtimeMs });
|
|
17451
17820
|
if (statsPending === 0) {
|
|
@@ -17454,7 +17823,7 @@ function pruneImageCache() {
|
|
|
17454
17823
|
(f, idx) => idx >= MAX_GENERATED_IMAGES || now - f.mtime > IMAGE_MAX_AGE_MS
|
|
17455
17824
|
);
|
|
17456
17825
|
for (const { path } of toDelete) {
|
|
17457
|
-
|
|
17826
|
+
unlink3(path, (derr) => {
|
|
17458
17827
|
if (!derr) log(`[image-gen] pruned old image: ${path}`);
|
|
17459
17828
|
});
|
|
17460
17829
|
}
|
|
@@ -17471,8 +17840,8 @@ var init_image_gen = __esm({
|
|
|
17471
17840
|
MAX_GENERATED_IMAGES = 20;
|
|
17472
17841
|
IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
17473
17842
|
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
17474
|
-
IMAGE_OUTPUT_DIR =
|
|
17475
|
-
process.env.CC_CLAW_HOME ??
|
|
17843
|
+
IMAGE_OUTPUT_DIR = join18(
|
|
17844
|
+
process.env.CC_CLAW_HOME ?? join18(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
17476
17845
|
"data",
|
|
17477
17846
|
"images"
|
|
17478
17847
|
);
|
|
@@ -17506,7 +17875,7 @@ function makeToolActionCallback(chatId, channel, level) {
|
|
|
17506
17875
|
};
|
|
17507
17876
|
}
|
|
17508
17877
|
async function processFileSends2(chatId, channel, text) {
|
|
17509
|
-
const fileSendPattern = /\[SEND_FILE
|
|
17878
|
+
const fileSendPattern = /\[\s*SEND_FILE:\s*(.+?)\s*\]/g;
|
|
17510
17879
|
const filePaths = [];
|
|
17511
17880
|
for (const match of text.matchAll(fileSendPattern)) {
|
|
17512
17881
|
filePaths.push(match[1].trim());
|
|
@@ -17527,7 +17896,7 @@ async function processFileSends2(chatId, channel, text) {
|
|
|
17527
17896
|
return text.replace(fileSendPattern, "").trim();
|
|
17528
17897
|
}
|
|
17529
17898
|
async function processImageGenerations(chatId, channel, text) {
|
|
17530
|
-
const pattern = /\[GENERATE_IMAGE
|
|
17899
|
+
const pattern = /\[\s*GENERATE_IMAGE:\s*(.+?)\s*\]/g;
|
|
17531
17900
|
const prompts = [];
|
|
17532
17901
|
for (const match of text.matchAll(pattern)) {
|
|
17533
17902
|
prompts.push(match[1].trim());
|
|
@@ -17555,7 +17924,7 @@ async function processImageGenerations(chatId, channel, text) {
|
|
|
17555
17924
|
return text.replace(pattern, "").trim();
|
|
17556
17925
|
}
|
|
17557
17926
|
async function processReaction(chatId, channel, text, messageId) {
|
|
17558
|
-
const reactPatternGlobal = /\[REACT
|
|
17927
|
+
const reactPatternGlobal = /\[\s*REACT:\s*(.+?)\s*\]/g;
|
|
17559
17928
|
if (!reactPatternGlobal.test(text)) return text;
|
|
17560
17929
|
let reacted = false;
|
|
17561
17930
|
reactPatternGlobal.lastIndex = 0;
|
|
@@ -17569,7 +17938,7 @@ async function processReaction(chatId, channel, text, messageId) {
|
|
|
17569
17938
|
reacted = true;
|
|
17570
17939
|
}
|
|
17571
17940
|
}
|
|
17572
|
-
return text.replace(/\[REACT
|
|
17941
|
+
return text.replace(/\[\s*REACT:\s*(.+?)\s*\]/g, "").trim();
|
|
17573
17942
|
}
|
|
17574
17943
|
async function sendResponse(chatId, channel, text, messageId, replyToMessageId) {
|
|
17575
17944
|
text = await processReaction(chatId, channel, text, messageId);
|
|
@@ -17582,9 +17951,9 @@ async function sendResponse(chatId, channel, text, messageId, replyToMessageId)
|
|
|
17582
17951
|
}
|
|
17583
17952
|
}
|
|
17584
17953
|
let afterHistory = afterUpdates;
|
|
17585
|
-
const historySearchMatch = afterHistory.match(/\[HISTORY_SEARCH
|
|
17954
|
+
const historySearchMatch = afterHistory.match(/\[\s*HISTORY_SEARCH:\s*([^\]]+?)\s*\]/);
|
|
17586
17955
|
if (historySearchMatch) {
|
|
17587
|
-
afterHistory = afterHistory.replace(/\[HISTORY_SEARCH
|
|
17956
|
+
afterHistory = afterHistory.replace(/\[\s*HISTORY_SEARCH:\s*[^\]]+?\s*\]/g, "").trim();
|
|
17588
17957
|
const hsQuery = historySearchMatch[1].trim();
|
|
17589
17958
|
const hsResults = searchMessageLog(chatId, hsQuery, 10);
|
|
17590
17959
|
if (hsResults.length > 0) {
|
|
@@ -17929,33 +18298,33 @@ var init_video = __esm({
|
|
|
17929
18298
|
});
|
|
17930
18299
|
|
|
17931
18300
|
// src/router/media.ts
|
|
17932
|
-
import { join as
|
|
17933
|
-
import { mkdir, writeFile as writeFile2, readdir as
|
|
18301
|
+
import { join as join19 } from "path";
|
|
18302
|
+
import { mkdir as mkdir2, writeFile as writeFile2, readdir as readdir3, stat as stat3, unlink as unlink4 } from "fs/promises";
|
|
17934
18303
|
function getMediaRetentionMs() {
|
|
17935
18304
|
const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
|
|
17936
18305
|
return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
|
|
17937
18306
|
}
|
|
17938
18307
|
async function saveMedia(buffer, prefix, ext) {
|
|
17939
|
-
await
|
|
18308
|
+
await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
|
|
17940
18309
|
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
17941
|
-
const fullPath =
|
|
18310
|
+
const fullPath = join19(MEDIA_INCOMING_PATH, filename);
|
|
17942
18311
|
await writeFile2(fullPath, buffer);
|
|
17943
18312
|
return fullPath;
|
|
17944
18313
|
}
|
|
17945
18314
|
async function cleanupOldMedia() {
|
|
17946
18315
|
try {
|
|
17947
|
-
await
|
|
18316
|
+
await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
|
|
17948
18317
|
const retentionMs = getMediaRetentionMs();
|
|
17949
18318
|
const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
|
|
17950
|
-
const files = await
|
|
18319
|
+
const files = await readdir3(MEDIA_INCOMING_PATH);
|
|
17951
18320
|
const now = Date.now();
|
|
17952
18321
|
let removed = 0;
|
|
17953
18322
|
for (const file of files) {
|
|
17954
18323
|
try {
|
|
17955
|
-
const filePath =
|
|
17956
|
-
const s = await
|
|
18324
|
+
const filePath = join19(MEDIA_INCOMING_PATH, file);
|
|
18325
|
+
const s = await stat3(filePath);
|
|
17957
18326
|
if (now - s.mtimeMs > retentionMs) {
|
|
17958
|
-
await
|
|
18327
|
+
await unlink4(filePath);
|
|
17959
18328
|
removed++;
|
|
17960
18329
|
}
|
|
17961
18330
|
} catch {
|
|
@@ -18170,7 +18539,21 @@ ${content}
|
|
|
18170
18539
|
await sendResponse(chatId, channel, mediaResponse, msg.messageId);
|
|
18171
18540
|
} catch (err) {
|
|
18172
18541
|
error("[router] Media error:", err);
|
|
18173
|
-
|
|
18542
|
+
const errStr = errorMessage(err);
|
|
18543
|
+
if (errStr.includes("file is too big") || errStr.includes("too big")) {
|
|
18544
|
+
const host = process.env.CC_CLAW_API_HOST ?? "192.168.68.10";
|
|
18545
|
+
const port = process.env.CC_CLAW_PORT ?? "3141";
|
|
18546
|
+
await channel.sendText(
|
|
18547
|
+
chatId,
|
|
18548
|
+
`File too large for Telegram (max 20MB via Bot API).
|
|
18549
|
+
|
|
18550
|
+
Upload directly instead:
|
|
18551
|
+
http://${host}:${port}/upload`,
|
|
18552
|
+
{ parseMode: "plain" }
|
|
18553
|
+
);
|
|
18554
|
+
} else {
|
|
18555
|
+
await channel.sendText(chatId, `Error processing file: ${errStr}`, { parseMode: "plain" });
|
|
18556
|
+
}
|
|
18174
18557
|
}
|
|
18175
18558
|
}
|
|
18176
18559
|
var MEDIA_INCOMING_PATH;
|
|
@@ -18188,7 +18571,7 @@ var init_media = __esm({
|
|
|
18188
18571
|
init_helpers();
|
|
18189
18572
|
init_response();
|
|
18190
18573
|
init_live_status();
|
|
18191
|
-
MEDIA_INCOMING_PATH =
|
|
18574
|
+
MEDIA_INCOMING_PATH = join19(MEDIA_PATH, "incoming");
|
|
18192
18575
|
}
|
|
18193
18576
|
});
|
|
18194
18577
|
|
|
@@ -18500,9 +18883,9 @@ var install_exports = {};
|
|
|
18500
18883
|
__export(install_exports, {
|
|
18501
18884
|
installSkillFromGitHub: () => installSkillFromGitHub
|
|
18502
18885
|
});
|
|
18503
|
-
import { mkdir as
|
|
18504
|
-
import { existsSync as
|
|
18505
|
-
import { join as
|
|
18886
|
+
import { mkdir as mkdir3, readdir as readdir4, readFile as readFile4, cp } from "fs/promises";
|
|
18887
|
+
import { existsSync as existsSync20 } from "fs";
|
|
18888
|
+
import { join as join20, basename as basename2 } from "path";
|
|
18506
18889
|
import { execSync as execSync4 } from "child_process";
|
|
18507
18890
|
async function installSkillFromGitHub(urlOrShorthand) {
|
|
18508
18891
|
let repoUrl;
|
|
@@ -18513,36 +18896,36 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
18513
18896
|
}
|
|
18514
18897
|
repoUrl = parsed.cloneUrl;
|
|
18515
18898
|
subPath = parsed.subPath;
|
|
18516
|
-
const tmpDir =
|
|
18899
|
+
const tmpDir = join20("/tmp", `cc-claw-skill-${Date.now()}`);
|
|
18517
18900
|
try {
|
|
18518
18901
|
log(`[skill-install] Cloning ${repoUrl} to ${tmpDir}`);
|
|
18519
18902
|
execSync4(`git clone --depth 1 ${repoUrl} ${tmpDir}`, {
|
|
18520
18903
|
stdio: "pipe",
|
|
18521
18904
|
timeout: 3e4
|
|
18522
18905
|
});
|
|
18523
|
-
if (!
|
|
18906
|
+
if (!existsSync20(join20(tmpDir, ".git"))) {
|
|
18524
18907
|
return { success: false, error: "Git clone failed: no .git directory produced" };
|
|
18525
18908
|
}
|
|
18526
|
-
const searchRoot = subPath ?
|
|
18909
|
+
const searchRoot = subPath ? join20(tmpDir, subPath) : tmpDir;
|
|
18527
18910
|
const skillDir = await findSkillDir(searchRoot);
|
|
18528
18911
|
if (!skillDir) {
|
|
18529
18912
|
return { success: false, error: "No SKILL.md found in the repository." };
|
|
18530
18913
|
}
|
|
18531
18914
|
const skillFolderName = basename2(skillDir);
|
|
18532
|
-
const destDir =
|
|
18533
|
-
if (
|
|
18915
|
+
const destDir = join20(SKILLS_PATH, skillFolderName);
|
|
18916
|
+
if (existsSync20(destDir)) {
|
|
18534
18917
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
18535
18918
|
}
|
|
18536
|
-
await
|
|
18919
|
+
await mkdir3(destDir, { recursive: true });
|
|
18537
18920
|
await cp(skillDir, destDir, { recursive: true });
|
|
18538
18921
|
let skillName = skillFolderName;
|
|
18539
18922
|
try {
|
|
18540
|
-
const content = await readFile4(
|
|
18923
|
+
const content = await readFile4(join20(destDir, "SKILL.md"), "utf-8");
|
|
18541
18924
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
18542
18925
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
18543
18926
|
} catch {
|
|
18544
18927
|
try {
|
|
18545
|
-
const content = await readFile4(
|
|
18928
|
+
const content = await readFile4(join20(destDir, "skill.md"), "utf-8");
|
|
18546
18929
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
18547
18930
|
if (nameMatch) skillName = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
18548
18931
|
} catch {
|
|
@@ -18577,35 +18960,35 @@ function parseGitHubUrl(input) {
|
|
|
18577
18960
|
async function findSkillDir(root) {
|
|
18578
18961
|
const candidates = ["SKILL.md", "skill.md"];
|
|
18579
18962
|
for (const c of candidates) {
|
|
18580
|
-
if (
|
|
18963
|
+
if (existsSync20(join20(root, c))) return root;
|
|
18581
18964
|
}
|
|
18582
18965
|
try {
|
|
18583
|
-
const entries = await
|
|
18966
|
+
const entries = await readdir4(root, { withFileTypes: true });
|
|
18584
18967
|
for (const entry of entries) {
|
|
18585
18968
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
18586
18969
|
for (const c of candidates) {
|
|
18587
|
-
if (
|
|
18588
|
-
return
|
|
18970
|
+
if (existsSync20(join20(root, entry.name, c))) {
|
|
18971
|
+
return join20(root, entry.name);
|
|
18589
18972
|
}
|
|
18590
18973
|
}
|
|
18591
18974
|
}
|
|
18592
18975
|
} catch {
|
|
18593
18976
|
}
|
|
18594
18977
|
try {
|
|
18595
|
-
const entries = await
|
|
18978
|
+
const entries = await readdir4(root, { withFileTypes: true });
|
|
18596
18979
|
for (const entry of entries) {
|
|
18597
18980
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
18598
18981
|
let subEntries;
|
|
18599
18982
|
try {
|
|
18600
|
-
subEntries = await
|
|
18983
|
+
subEntries = await readdir4(join20(root, entry.name), { withFileTypes: true });
|
|
18601
18984
|
} catch {
|
|
18602
18985
|
continue;
|
|
18603
18986
|
}
|
|
18604
18987
|
for (const sub of subEntries) {
|
|
18605
18988
|
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
18606
18989
|
for (const c of candidates) {
|
|
18607
|
-
if (
|
|
18608
|
-
return
|
|
18990
|
+
if (existsSync20(join20(root, entry.name, sub.name, c))) {
|
|
18991
|
+
return join20(root, entry.name, sub.name);
|
|
18609
18992
|
}
|
|
18610
18993
|
}
|
|
18611
18994
|
}
|
|
@@ -18629,10 +19012,10 @@ __export(discover_exports, {
|
|
|
18629
19012
|
invalidateSkillCache: () => invalidateSkillCache,
|
|
18630
19013
|
stripFrontmatter: () => stripFrontmatter2
|
|
18631
19014
|
});
|
|
18632
|
-
import { readdir as
|
|
19015
|
+
import { readdir as readdir5, readFile as readFile5 } from "fs/promises";
|
|
18633
19016
|
import { createHash } from "crypto";
|
|
18634
19017
|
import { homedir as homedir5 } from "os";
|
|
18635
|
-
import { join as
|
|
19018
|
+
import { join as join21 } from "path";
|
|
18636
19019
|
function invalidateSkillCache() {
|
|
18637
19020
|
cachedSkills = null;
|
|
18638
19021
|
cacheTimestamp = 0;
|
|
@@ -18650,7 +19033,7 @@ async function discoverAllSkills() {
|
|
|
18650
19033
|
const rawSkills = [];
|
|
18651
19034
|
rawSkills.push(...await scanSkillDir(SKILLS_PATH, "cc-claw"));
|
|
18652
19035
|
for (const backendId of getAllBackendIds()) {
|
|
18653
|
-
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [
|
|
19036
|
+
const dirs = BACKEND_SKILL_DIRS[backendId] ?? [join21(homedir5(), `.${backendId}`, "skills")];
|
|
18654
19037
|
for (const dir of dirs) {
|
|
18655
19038
|
rawSkills.push(...await scanSkillDir(dir, backendId));
|
|
18656
19039
|
}
|
|
@@ -18669,7 +19052,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
18669
19052
|
const results = [];
|
|
18670
19053
|
let entries;
|
|
18671
19054
|
try {
|
|
18672
|
-
entries = await
|
|
19055
|
+
entries = await readdir5(skillsDir, { withFileTypes: true });
|
|
18673
19056
|
} catch {
|
|
18674
19057
|
return results;
|
|
18675
19058
|
}
|
|
@@ -18678,7 +19061,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
18678
19061
|
let content;
|
|
18679
19062
|
let resolvedPath;
|
|
18680
19063
|
for (const candidate of SKILL_FILE_CANDIDATES) {
|
|
18681
|
-
const p =
|
|
19064
|
+
const p = join21(skillsDir, entry.name, candidate);
|
|
18682
19065
|
try {
|
|
18683
19066
|
content = await readFile5(p, "utf-8");
|
|
18684
19067
|
resolvedPath = p;
|
|
@@ -18695,9 +19078,7 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
18695
19078
|
description: frontmatter.description,
|
|
18696
19079
|
filePath: resolvedPath,
|
|
18697
19080
|
source,
|
|
18698
|
-
contentHash: hash
|
|
18699
|
-
compatibleBackends: frontmatter.compatibleBackends,
|
|
18700
|
-
recommendedModel: frontmatter.recommendedModel
|
|
19081
|
+
contentHash: hash
|
|
18701
19082
|
});
|
|
18702
19083
|
}
|
|
18703
19084
|
return results;
|
|
@@ -18726,9 +19107,7 @@ function mergeAndDeduplicate(raw) {
|
|
|
18726
19107
|
filePath: primary.filePath,
|
|
18727
19108
|
source: primary.source,
|
|
18728
19109
|
sources: [...new Set(allSources)],
|
|
18729
|
-
contentHash: primary.contentHash
|
|
18730
|
-
compatibleBackends: primary.compatibleBackends,
|
|
18731
|
-
recommendedModel: primary.recommendedModel
|
|
19110
|
+
contentHash: primary.contentHash
|
|
18732
19111
|
});
|
|
18733
19112
|
} else {
|
|
18734
19113
|
for (const [hash, copies] of byHash) {
|
|
@@ -18740,9 +19119,7 @@ function mergeAndDeduplicate(raw) {
|
|
|
18740
19119
|
filePath: primary.filePath,
|
|
18741
19120
|
source: primary.source,
|
|
18742
19121
|
sources: [...new Set(allSources)],
|
|
18743
|
-
contentHash: hash
|
|
18744
|
-
compatibleBackends: primary.compatibleBackends,
|
|
18745
|
-
recommendedModel: primary.recommendedModel
|
|
19122
|
+
contentHash: hash
|
|
18746
19123
|
});
|
|
18747
19124
|
}
|
|
18748
19125
|
}
|
|
@@ -18762,13 +19139,9 @@ function parseFrontmatter2(content, fallbackName) {
|
|
|
18762
19139
|
const fm = fmMatch[1];
|
|
18763
19140
|
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
18764
19141
|
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
19142
|
return {
|
|
18768
19143
|
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
|
|
19144
|
+
description: descMatch?.[1]?.trim().replace(/^["']|["']$/g, "") ?? ""
|
|
18772
19145
|
};
|
|
18773
19146
|
}
|
|
18774
19147
|
function stripFrontmatter2(content) {
|
|
@@ -18782,15 +19155,15 @@ var init_discover = __esm({
|
|
|
18782
19155
|
init_backends();
|
|
18783
19156
|
SKILL_FILE_CANDIDATES = ["SKILL.md", "skill.md"];
|
|
18784
19157
|
BACKEND_SKILL_DIRS = {
|
|
18785
|
-
claude: [
|
|
18786
|
-
gemini: [
|
|
19158
|
+
claude: [join21(homedir5(), ".claude", "skills")],
|
|
19159
|
+
gemini: [join21(homedir5(), ".gemini", "skills")],
|
|
18787
19160
|
codex: [
|
|
18788
|
-
|
|
18789
|
-
|
|
19161
|
+
join21(homedir5(), ".agents", "skills"),
|
|
19162
|
+
join21(homedir5(), ".codex", "skills")
|
|
18790
19163
|
],
|
|
18791
19164
|
cursor: [
|
|
18792
|
-
|
|
18793
|
-
|
|
19165
|
+
join21(homedir5(), ".cursor", "skills"),
|
|
19166
|
+
join21(homedir5(), ".cursor", "skills-cursor")
|
|
18794
19167
|
]
|
|
18795
19168
|
};
|
|
18796
19169
|
CACHE_TTL_MS2 = 3e5;
|
|
@@ -20014,13 +20387,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
20014
20387
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
20015
20388
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
20016
20389
|
if (current === "frozen") {
|
|
20017
|
-
const { readFileSync: readFileSync29, existsSync:
|
|
20018
|
-
const { join:
|
|
20390
|
+
const { readFileSync: readFileSync29, existsSync: existsSync58 } = await import("fs");
|
|
20391
|
+
const { join: join37 } = await import("path");
|
|
20019
20392
|
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 =
|
|
20393
|
+
const soulPath = join37(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
20394
|
+
const userPath = join37(CC_CLAW_HOME3, "identity/USER.md");
|
|
20395
|
+
const soul = existsSync58(soulPath) ? readFileSync29(soulPath, "utf-8") : "";
|
|
20396
|
+
const user = existsSync58(userPath) ? readFileSync29(userPath, "utf-8") : "";
|
|
20024
20397
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
20025
20398
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
20026
20399
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
@@ -20097,11 +20470,11 @@ var init_evolve2 = __esm({
|
|
|
20097
20470
|
});
|
|
20098
20471
|
|
|
20099
20472
|
// src/optimizer/identity-audit.ts
|
|
20100
|
-
import { readFileSync as readFileSync11, existsSync as
|
|
20101
|
-
import { join as
|
|
20473
|
+
import { readFileSync as readFileSync11, existsSync as existsSync21, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
20474
|
+
import { join as join22 } from "path";
|
|
20102
20475
|
function readIdentityFile2(filename) {
|
|
20103
20476
|
try {
|
|
20104
|
-
return readFileSync11(
|
|
20477
|
+
return readFileSync11(join22(IDENTITY_PATH, filename), "utf-8");
|
|
20105
20478
|
} catch {
|
|
20106
20479
|
return "";
|
|
20107
20480
|
}
|
|
@@ -20116,13 +20489,13 @@ function getMtime(filepath) {
|
|
|
20116
20489
|
function findBackupFiles() {
|
|
20117
20490
|
const backups = [];
|
|
20118
20491
|
const dirs = [IDENTITY_PATH];
|
|
20119
|
-
const contextDir =
|
|
20120
|
-
if (
|
|
20492
|
+
const contextDir = join22(IDENTITY_PATH, "..", "workspace", "context");
|
|
20493
|
+
if (existsSync21(contextDir)) dirs.push(contextDir);
|
|
20121
20494
|
for (const dir of dirs) {
|
|
20122
20495
|
try {
|
|
20123
20496
|
for (const entry of readdirSync10(dir)) {
|
|
20124
20497
|
if (entry.endsWith(".bak") || /\.bak\.\d{4}-\d{2}-\d{2}/.test(entry)) {
|
|
20125
|
-
backups.push(
|
|
20498
|
+
backups.push(join22(dir, entry));
|
|
20126
20499
|
}
|
|
20127
20500
|
}
|
|
20128
20501
|
} catch {
|
|
@@ -20143,9 +20516,9 @@ function computeIdentityStats(pendingProposals, driftPercent) {
|
|
|
20143
20516
|
userChars,
|
|
20144
20517
|
ccClawChars,
|
|
20145
20518
|
boilerplateChars,
|
|
20146
|
-
soulMtime: getMtime(
|
|
20147
|
-
userMtime: getMtime(
|
|
20148
|
-
ccClawMtime: getMtime(
|
|
20519
|
+
soulMtime: getMtime(join22(IDENTITY_PATH, "SOUL.md")),
|
|
20520
|
+
userMtime: getMtime(join22(IDENTITY_PATH, "USER.md")),
|
|
20521
|
+
ccClawMtime: getMtime(join22(IDENTITY_PATH, "CC-CLAW.md")),
|
|
20149
20522
|
backupFiles: findBackupFiles(),
|
|
20150
20523
|
estimatedTokens: Math.ceil(ccClawChars / 4),
|
|
20151
20524
|
pendingEvolveProposals: pendingProposals,
|
|
@@ -20254,8 +20627,8 @@ var init_identity_audit = __esm({
|
|
|
20254
20627
|
});
|
|
20255
20628
|
|
|
20256
20629
|
// src/optimizer/skill-audit.ts
|
|
20257
|
-
import { readFileSync as readFileSync12, existsSync as
|
|
20258
|
-
import { join as
|
|
20630
|
+
import { readFileSync as readFileSync12, existsSync as existsSync22 } from "fs";
|
|
20631
|
+
import { join as join23, basename as basename3 } from "path";
|
|
20259
20632
|
function parseFrontmatter3(content) {
|
|
20260
20633
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
20261
20634
|
if (!fmMatch) return {};
|
|
@@ -20270,10 +20643,6 @@ function parseFrontmatter3(content) {
|
|
|
20270
20643
|
const singleDesc = fm.match(/^description:\s*(.+)/m);
|
|
20271
20644
|
if (singleDesc) result.description = singleDesc[1].trim().replace(/^["']|["']$/g, "");
|
|
20272
20645
|
}
|
|
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
20646
|
return result;
|
|
20278
20647
|
}
|
|
20279
20648
|
function detectDependentSkills(content) {
|
|
@@ -20298,7 +20667,7 @@ function computeSkillStats(skillPath) {
|
|
|
20298
20667
|
const content = readFileSync12(skillPath, "utf-8");
|
|
20299
20668
|
const lines = content.split("\n");
|
|
20300
20669
|
return {
|
|
20301
|
-
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(
|
|
20670
|
+
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join23(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
20302
20671
|
skillPath,
|
|
20303
20672
|
lineCount: lines.length,
|
|
20304
20673
|
charCount: content.length,
|
|
@@ -20318,11 +20687,11 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
20318
20687
|
const results = [];
|
|
20319
20688
|
for (const name of depNames) {
|
|
20320
20689
|
const candidates = [
|
|
20321
|
-
|
|
20322
|
-
|
|
20690
|
+
join23(ccClawSkillsDir, name, "SKILL.md"),
|
|
20691
|
+
join23(ccClawSkillsDir, `${name}-skill`, "SKILL.md")
|
|
20323
20692
|
];
|
|
20324
20693
|
for (const candidate of candidates) {
|
|
20325
|
-
if (
|
|
20694
|
+
if (existsSync22(candidate)) {
|
|
20326
20695
|
try {
|
|
20327
20696
|
const content = readFileSync12(candidate, "utf-8");
|
|
20328
20697
|
results.push({
|
|
@@ -20353,8 +20722,6 @@ Lines: ${stats.lineCount} (cap: 500 \u2014 over 500 = progressive disclosure nee
|
|
|
20353
20722
|
Chars: ${stats.charCount} (~${stats.estimatedTokens} tokens)
|
|
20354
20723
|
Frontmatter name: ${stats.frontmatter.name || "(missing)"}
|
|
20355
20724
|
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
20725
|
Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.join(", ") : "none"}`);
|
|
20359
20726
|
sections.push(`[Rubric \u2014 Evaluate Each Area]
|
|
20360
20727
|
|
|
@@ -20362,7 +20729,6 @@ Referenced skills: ${stats.dependentSkills.length > 0 ? stats.dependentSkills.jo
|
|
|
20362
20729
|
- Is 'name' present and descriptive?
|
|
20363
20730
|
- Does 'description' include BOTH what the skill does AND trigger info (when to use it)?
|
|
20364
20731
|
- A good description tells the AI exactly when to activate this skill
|
|
20365
|
-
- Should 'compatible_backends' or 'recommended_model' be specified?
|
|
20366
20732
|
|
|
20367
20733
|
2. BODY STRUCTURE
|
|
20368
20734
|
- Over 500 lines? \u2192 needs progressive disclosure (essentials in body, details in references/)
|
|
@@ -20439,8 +20805,8 @@ __export(analyze_exports2, {
|
|
|
20439
20805
|
});
|
|
20440
20806
|
import { spawn as spawn7 } from "child_process";
|
|
20441
20807
|
import { createInterface as createInterface7 } from "readline";
|
|
20442
|
-
import { readFileSync as readFileSync13, existsSync as
|
|
20443
|
-
import { join as
|
|
20808
|
+
import { readFileSync as readFileSync13, existsSync as existsSync23, readdirSync as readdirSync12 } from "fs";
|
|
20809
|
+
import { join as join24 } from "path";
|
|
20444
20810
|
import { homedir as homedir7 } from "os";
|
|
20445
20811
|
function parseOptimizeOutput(raw, validAreas) {
|
|
20446
20812
|
if (!raw || raw.includes("NO_FINDINGS")) return [];
|
|
@@ -20570,20 +20936,20 @@ function getModelDisplayInfo(chatId) {
|
|
|
20570
20936
|
}
|
|
20571
20937
|
function readIdentityFile3(filename) {
|
|
20572
20938
|
try {
|
|
20573
|
-
return readFileSync13(
|
|
20939
|
+
return readFileSync13(join24(IDENTITY_PATH, filename), "utf-8");
|
|
20574
20940
|
} catch {
|
|
20575
20941
|
return "";
|
|
20576
20942
|
}
|
|
20577
20943
|
}
|
|
20578
20944
|
function loadContextFiles2() {
|
|
20579
|
-
const contextDir =
|
|
20945
|
+
const contextDir = join24(homedir7(), ".cc-claw", "workspace", "context");
|
|
20580
20946
|
const results = [];
|
|
20581
|
-
if (!
|
|
20947
|
+
if (!existsSync23(contextDir)) return results;
|
|
20582
20948
|
try {
|
|
20583
20949
|
for (const entry of readdirSync12(contextDir)) {
|
|
20584
20950
|
if (!entry.endsWith(".md")) continue;
|
|
20585
20951
|
try {
|
|
20586
|
-
const content = readFileSync13(
|
|
20952
|
+
const content = readFileSync13(join24(contextDir, entry), "utf-8");
|
|
20587
20953
|
results.push({ name: entry, content });
|
|
20588
20954
|
} catch {
|
|
20589
20955
|
}
|
|
@@ -20634,7 +21000,7 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20634
21000
|
const stats = computeSkillStats(skillPath);
|
|
20635
21001
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
20636
21002
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
20637
|
-
const ccClawSkillsDir =
|
|
21003
|
+
const ccClawSkillsDir = join24(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20638
21004
|
const skillContent = readFileSync13(skillPath, "utf-8");
|
|
20639
21005
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
20640
21006
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
@@ -20649,13 +21015,13 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
20649
21015
|
};
|
|
20650
21016
|
}
|
|
20651
21017
|
function listCcClawSkills() {
|
|
20652
|
-
const skillsDir =
|
|
21018
|
+
const skillsDir = join24(homedir7(), ".cc-claw", "workspace", "skills");
|
|
20653
21019
|
const entries = [];
|
|
20654
|
-
if (!
|
|
21020
|
+
if (!existsSync23(skillsDir)) return entries;
|
|
20655
21021
|
try {
|
|
20656
21022
|
for (const dir of readdirSync12(skillsDir)) {
|
|
20657
|
-
const skillFile =
|
|
20658
|
-
if (!
|
|
21023
|
+
const skillFile = join24(skillsDir, dir, "SKILL.md");
|
|
21024
|
+
if (!existsSync23(skillFile)) continue;
|
|
20659
21025
|
let description = "skill";
|
|
20660
21026
|
try {
|
|
20661
21027
|
const content = readFileSync13(skillFile, "utf-8");
|
|
@@ -20978,8 +21344,8 @@ var init_ui2 = __esm({
|
|
|
20978
21344
|
});
|
|
20979
21345
|
|
|
20980
21346
|
// src/router/optimize.ts
|
|
20981
|
-
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as
|
|
20982
|
-
import { join as
|
|
21347
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync7, existsSync as existsSync24, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
21348
|
+
import { join as join25, dirname as dirname4 } from "path";
|
|
20983
21349
|
import { homedir as homedir8 } from "os";
|
|
20984
21350
|
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
20985
21351
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
@@ -21160,7 +21526,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
21160
21526
|
} = await Promise.resolve().then(() => (init_ui2(), ui_exports));
|
|
21161
21527
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
21162
21528
|
if (!modelInfo) return;
|
|
21163
|
-
const skillPath =
|
|
21529
|
+
const skillPath = join25(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
|
|
21164
21530
|
const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
|
|
21165
21531
|
chatId,
|
|
21166
21532
|
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
@@ -21249,7 +21615,7 @@ async function applyFinding(chatId, channel, index) {
|
|
|
21249
21615
|
await showFinding(chatId, channel, index + 1);
|
|
21250
21616
|
return;
|
|
21251
21617
|
}
|
|
21252
|
-
if (!
|
|
21618
|
+
if (!existsSync24(targetPath)) {
|
|
21253
21619
|
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
21254
21620
|
session2.skipped.push(index);
|
|
21255
21621
|
await showFinding(chatId, channel, index + 1);
|
|
@@ -21331,14 +21697,14 @@ async function finishReview(chatId, channel) {
|
|
|
21331
21697
|
activeSessions.delete(chatId);
|
|
21332
21698
|
}
|
|
21333
21699
|
function resolveTargetFile(location, auditTarget) {
|
|
21334
|
-
const ccClawHome =
|
|
21700
|
+
const ccClawHome = join25(homedir8(), ".cc-claw");
|
|
21335
21701
|
const filePart = location.split(":")[0]?.trim();
|
|
21336
21702
|
if (!filePart) return null;
|
|
21337
|
-
if (filePart === "SOUL.md") return
|
|
21338
|
-
if (filePart === "USER.md") return
|
|
21339
|
-
if (filePart === "CC-CLAW.md") return
|
|
21703
|
+
if (filePart === "SOUL.md") return join25(ccClawHome, "identity", "SOUL.md");
|
|
21704
|
+
if (filePart === "USER.md") return join25(ccClawHome, "identity", "USER.md");
|
|
21705
|
+
if (filePart === "CC-CLAW.md") return join25(ccClawHome, "identity", "CC-CLAW.md");
|
|
21340
21706
|
if (filePart === "SKILL.md" && auditTarget !== "identity") {
|
|
21341
|
-
return
|
|
21707
|
+
return join25(ccClawHome, "workspace", "skills", auditTarget, "SKILL.md");
|
|
21342
21708
|
}
|
|
21343
21709
|
return null;
|
|
21344
21710
|
}
|
|
@@ -21346,7 +21712,7 @@ function pruneBackups2(absolutePath) {
|
|
|
21346
21712
|
const dir = dirname4(absolutePath);
|
|
21347
21713
|
const baseName = absolutePath.split("/").pop() ?? "";
|
|
21348
21714
|
try {
|
|
21349
|
-
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) =>
|
|
21715
|
+
const backups = readdirSync13(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join25(dir, f));
|
|
21350
21716
|
while (backups.length > 3) {
|
|
21351
21717
|
const oldest = backups.shift();
|
|
21352
21718
|
try {
|
|
@@ -21368,6 +21734,193 @@ var init_optimize = __esm({
|
|
|
21368
21734
|
}
|
|
21369
21735
|
});
|
|
21370
21736
|
|
|
21737
|
+
// src/skills/auto-create.ts
|
|
21738
|
+
var auto_create_exports = {};
|
|
21739
|
+
__export(auto_create_exports, {
|
|
21740
|
+
buildSessionExtractionPrompt: () => buildSessionExtractionPrompt,
|
|
21741
|
+
buildSkillExtractionPrompt: () => buildSkillExtractionPrompt,
|
|
21742
|
+
clearPendingDraft: () => clearPendingDraft,
|
|
21743
|
+
getPendingDraft: () => getPendingDraft,
|
|
21744
|
+
isSkillWorthy: () => isSkillWorthy,
|
|
21745
|
+
parseExtractedSkill: () => parseExtractedSkill,
|
|
21746
|
+
saveSkill: () => saveSkill,
|
|
21747
|
+
storePendingDraft: () => storePendingDraft
|
|
21748
|
+
});
|
|
21749
|
+
import { join as join26 } from "path";
|
|
21750
|
+
import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
21751
|
+
function isSkillWorthy(signals) {
|
|
21752
|
+
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
21753
|
+
if (toolUseCount < 12) return false;
|
|
21754
|
+
if (elapsedMs < 3e5) return false;
|
|
21755
|
+
if (tokenOutput < 3e3) return false;
|
|
21756
|
+
const words = userMessage.split(/\s+/).length;
|
|
21757
|
+
if (words < 5) return false;
|
|
21758
|
+
const skillPatterns = /\b(use|using|run|with|the)\s+(the\s+)?[\w-]+\s+skill\b/i;
|
|
21759
|
+
if (skillPatterns.test(userMessage)) {
|
|
21760
|
+
log(`[auto-skill] Skipping \u2014 message references an existing skill`);
|
|
21761
|
+
return false;
|
|
21762
|
+
}
|
|
21763
|
+
log(`[auto-skill] Skill-worthy: tools=${toolUseCount}, tokens=${tokenOutput}, elapsed=${elapsedMs}ms`);
|
|
21764
|
+
return true;
|
|
21765
|
+
}
|
|
21766
|
+
function buildSkillExtractionPrompt(userMessage, assistantResponse) {
|
|
21767
|
+
const cappedResponse = assistantResponse.length > 8e3 ? assistantResponse.slice(0, 8e3) + "\n\n[...truncated...]" : assistantResponse;
|
|
21768
|
+
return [
|
|
21769
|
+
"You are a skill extraction assistant. Analyze the following completed task and create a reusable SKILL.md file.",
|
|
21770
|
+
"",
|
|
21771
|
+
"A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
|
|
21772
|
+
"Focus on the PROCESS and APPROACH, not the specific details of this particular task.",
|
|
21773
|
+
"",
|
|
21774
|
+
"## Completed Task",
|
|
21775
|
+
"",
|
|
21776
|
+
`**User request:** ${userMessage}`,
|
|
21777
|
+
"",
|
|
21778
|
+
`**Assistant response:**`,
|
|
21779
|
+
cappedResponse,
|
|
21780
|
+
"",
|
|
21781
|
+
"## Output Format",
|
|
21782
|
+
"",
|
|
21783
|
+
"Output ONLY the SKILL.md content with this exact format:",
|
|
21784
|
+
"",
|
|
21785
|
+
"```",
|
|
21786
|
+
"---",
|
|
21787
|
+
'name: "skill-name-in-kebab-case"',
|
|
21788
|
+
'description: "What this skill does and when to use it"',
|
|
21789
|
+
"---",
|
|
21790
|
+
"",
|
|
21791
|
+
"## [Skill Title]",
|
|
21792
|
+
"",
|
|
21793
|
+
"[Step-by-step instructions for accomplishing this type of task]",
|
|
21794
|
+
"[Include decision points, best practices, and common pitfalls]",
|
|
21795
|
+
"[Keep it general enough to be reusable, specific enough to be helpful]",
|
|
21796
|
+
"```",
|
|
21797
|
+
"",
|
|
21798
|
+
"Rules:",
|
|
21799
|
+
"- The skill name should be descriptive and kebab-case",
|
|
21800
|
+
"- The description should explain WHAT it does AND WHEN to use it",
|
|
21801
|
+
"- Instructions should be general (not tied to specific file names or projects)",
|
|
21802
|
+
"- Include any important caveats or prerequisites",
|
|
21803
|
+
"- Keep it under 100 lines"
|
|
21804
|
+
].join("\n");
|
|
21805
|
+
}
|
|
21806
|
+
function buildSessionExtractionPrompt(sessionMessages) {
|
|
21807
|
+
const transcript = [];
|
|
21808
|
+
let totalChars = 0;
|
|
21809
|
+
const CAP = 12e3;
|
|
21810
|
+
for (const msg of sessionMessages) {
|
|
21811
|
+
const prefix = msg.role === "user" ? "USER" : "ASSISTANT";
|
|
21812
|
+
const text = msg.text.length > 2e3 ? msg.text.slice(0, 2e3) + " [...]" : msg.text;
|
|
21813
|
+
if (totalChars + text.length > CAP) {
|
|
21814
|
+
transcript.push(`[...earlier messages truncated...]`);
|
|
21815
|
+
break;
|
|
21816
|
+
}
|
|
21817
|
+
transcript.push(`[${prefix}]: ${text}`);
|
|
21818
|
+
totalChars += text.length;
|
|
21819
|
+
}
|
|
21820
|
+
return [
|
|
21821
|
+
"You are a skill extraction assistant. Review the following session transcript and create a reusable SKILL.md file.",
|
|
21822
|
+
"",
|
|
21823
|
+
"A skill is a set of instructions that an AI agent can follow to accomplish a similar task in the future.",
|
|
21824
|
+
"Analyze the ENTIRE session to understand:",
|
|
21825
|
+
"- What the user was trying to accomplish",
|
|
21826
|
+
"- What tools and approaches the AI used",
|
|
21827
|
+
"- Any feedback the user gave (corrections, preferences, refinements)",
|
|
21828
|
+
"- The overall workflow pattern",
|
|
21829
|
+
"",
|
|
21830
|
+
"Focus on the PROCESS and APPROACH, not the specific details of this session.",
|
|
21831
|
+
"Incorporate user feedback as best practices or decision points in the skill.",
|
|
21832
|
+
"",
|
|
21833
|
+
"## Session Transcript",
|
|
21834
|
+
"",
|
|
21835
|
+
...transcript,
|
|
21836
|
+
"",
|
|
21837
|
+
"## Output Format",
|
|
21838
|
+
"",
|
|
21839
|
+
"Output ONLY the SKILL.md content with this exact format:",
|
|
21840
|
+
"",
|
|
21841
|
+
"```",
|
|
21842
|
+
"---",
|
|
21843
|
+
'name: "skill-name-in-kebab-case"',
|
|
21844
|
+
'description: "What this skill does and when to use it"',
|
|
21845
|
+
"---",
|
|
21846
|
+
"",
|
|
21847
|
+
"## [Skill Title]",
|
|
21848
|
+
"",
|
|
21849
|
+
"[Step-by-step instructions for accomplishing this type of task]",
|
|
21850
|
+
"[Include decision points, best practices, and common pitfalls]",
|
|
21851
|
+
"[Incorporate any user preferences or corrections from the session]",
|
|
21852
|
+
"[Keep it general enough to be reusable, specific enough to be helpful]",
|
|
21853
|
+
"```",
|
|
21854
|
+
"",
|
|
21855
|
+
"Rules:",
|
|
21856
|
+
"- The skill name should be descriptive and kebab-case",
|
|
21857
|
+
"- The description should explain WHAT it does AND WHEN to use it",
|
|
21858
|
+
"- Instructions should be general (not tied to specific file names or projects)",
|
|
21859
|
+
"- If the user corrected the AI during the session, bake those corrections into the skill as best practices",
|
|
21860
|
+
"- Include any important caveats or prerequisites",
|
|
21861
|
+
"- Keep it under 100 lines"
|
|
21862
|
+
].join("\n");
|
|
21863
|
+
}
|
|
21864
|
+
function parseExtractedSkill(llmResponse) {
|
|
21865
|
+
let content = llmResponse;
|
|
21866
|
+
const fenceMatch = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*?)```/);
|
|
21867
|
+
if (fenceMatch) content = fenceMatch[1].trim();
|
|
21868
|
+
let fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
21869
|
+
if (!fmMatch) {
|
|
21870
|
+
const greedyFence = llmResponse.match(/```(?:markdown|md)?\s*\n([\s\S]*)```/);
|
|
21871
|
+
if (greedyFence) {
|
|
21872
|
+
content = greedyFence[1].trim();
|
|
21873
|
+
fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
21874
|
+
}
|
|
21875
|
+
}
|
|
21876
|
+
if (!fmMatch) {
|
|
21877
|
+
fmMatch = llmResponse.match(/---\s*\n([\s\S]*?)\n---/);
|
|
21878
|
+
if (fmMatch) {
|
|
21879
|
+
const fmStart = llmResponse.indexOf(fmMatch[0]);
|
|
21880
|
+
content = llmResponse.slice(fmStart).replace(/```\s*$/, "").trim();
|
|
21881
|
+
}
|
|
21882
|
+
}
|
|
21883
|
+
if (!fmMatch) {
|
|
21884
|
+
warn("[auto-skill] No frontmatter found in extracted skill");
|
|
21885
|
+
return null;
|
|
21886
|
+
}
|
|
21887
|
+
const nameMatch = fmMatch[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);
|
|
21888
|
+
if (!nameMatch) {
|
|
21889
|
+
warn("[auto-skill] No name field in skill frontmatter");
|
|
21890
|
+
return null;
|
|
21891
|
+
}
|
|
21892
|
+
const name = nameMatch[1].trim().toLowerCase().replace(/\s+/g, "-");
|
|
21893
|
+
return { name, content };
|
|
21894
|
+
}
|
|
21895
|
+
async function saveSkill(name, content) {
|
|
21896
|
+
const dir = join26(SKILLS_PATH, name);
|
|
21897
|
+
await mkdir4(dir, { recursive: true });
|
|
21898
|
+
const filePath = join26(dir, "SKILL.md");
|
|
21899
|
+
await writeFile4(filePath, content, "utf-8");
|
|
21900
|
+
invalidateSkillCache();
|
|
21901
|
+
log(`[auto-skill] Saved skill "${name}" to ${filePath}`);
|
|
21902
|
+
return { path: filePath };
|
|
21903
|
+
}
|
|
21904
|
+
function storePendingDraft(chatId, draft) {
|
|
21905
|
+
pendingDrafts.set(chatId, draft);
|
|
21906
|
+
}
|
|
21907
|
+
function getPendingDraft(chatId) {
|
|
21908
|
+
return pendingDrafts.get(chatId);
|
|
21909
|
+
}
|
|
21910
|
+
function clearPendingDraft(chatId) {
|
|
21911
|
+
pendingDrafts.delete(chatId);
|
|
21912
|
+
}
|
|
21913
|
+
var pendingDrafts;
|
|
21914
|
+
var init_auto_create = __esm({
|
|
21915
|
+
"src/skills/auto-create.ts"() {
|
|
21916
|
+
"use strict";
|
|
21917
|
+
init_paths();
|
|
21918
|
+
init_discover();
|
|
21919
|
+
init_log();
|
|
21920
|
+
pendingDrafts = /* @__PURE__ */ new Map();
|
|
21921
|
+
}
|
|
21922
|
+
});
|
|
21923
|
+
|
|
21371
21924
|
// src/council/types.ts
|
|
21372
21925
|
var COUNCIL_MIN_PARTICIPANTS, COUNCIL_MAX_ROUNDS, COUNCIL_WIZARD_TIMEOUT_MS;
|
|
21373
21926
|
var init_types4 = __esm({
|
|
@@ -21720,6 +22273,49 @@ Use /skills to see it.`, { parseMode: "plain" });
|
|
|
21720
22273
|
await channel.sendText(chatId, `Installation failed: ${result.error}`, { parseMode: "plain" });
|
|
21721
22274
|
}
|
|
21722
22275
|
}
|
|
22276
|
+
async function handleExtractSkillCommand(chatId, commandArgs, msg, channel) {
|
|
22277
|
+
const { getLog: getLog2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
22278
|
+
const sessionMessages = getLog2(chatId);
|
|
22279
|
+
if (sessionMessages.length < 2) {
|
|
22280
|
+
await channel.sendText(chatId, "No session history to extract from. Have a conversation first, then run /extract_skill.", { parseMode: "plain" });
|
|
22281
|
+
return;
|
|
22282
|
+
}
|
|
22283
|
+
await channel.sendText(chatId, `Reviewing session (${Math.floor(sessionMessages.length / 2)} exchanges)...`, { parseMode: "plain" });
|
|
22284
|
+
const { buildSessionExtractionPrompt: buildSessionExtractionPrompt2, parseExtractedSkill: parseExtractedSkill2, storePendingDraft: storePendingDraft2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22285
|
+
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
22286
|
+
const { getMode: getMode3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22287
|
+
const prompt = buildSessionExtractionPrompt2(sessionMessages);
|
|
22288
|
+
const response = await askAgent3(chatId, prompt, { permMode: getMode3(chatId) });
|
|
22289
|
+
const extracted = parseExtractedSkill2(response.text);
|
|
22290
|
+
if (!extracted) {
|
|
22291
|
+
await channel.sendText(chatId, "Could not extract a skill from this session. The session may be too short or unfocused.", { parseMode: "plain" });
|
|
22292
|
+
return;
|
|
22293
|
+
}
|
|
22294
|
+
storePendingDraft2(chatId, {
|
|
22295
|
+
name: extracted.name,
|
|
22296
|
+
content: extracted.content,
|
|
22297
|
+
userMessage: sessionMessages.filter((m) => m.role === "user").map((m) => m.text).join("\n"),
|
|
22298
|
+
assistantResponse: sessionMessages.filter((m) => m.role === "assistant").map((m) => m.text).join("\n")
|
|
22299
|
+
});
|
|
22300
|
+
const preview = extracted.content.length > 3500 ? extracted.content.slice(0, 3500) + "\n\n[...truncated...]" : extracted.content;
|
|
22301
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
22302
|
+
await channel.sendText(chatId, `Extracted skill: "${extracted.name}"
|
|
22303
|
+
|
|
22304
|
+
${preview}`, { parseMode: "plain" });
|
|
22305
|
+
await channel.sendKeyboard(
|
|
22306
|
+
chatId,
|
|
22307
|
+
`Save this skill?`,
|
|
22308
|
+
[[
|
|
22309
|
+
{ label: "\u2705 Save Skill", data: "skill:confirm-save", style: "success" },
|
|
22310
|
+
{ label: "\u2715 Discard", data: "skill:discard" }
|
|
22311
|
+
]]
|
|
22312
|
+
);
|
|
22313
|
+
} else {
|
|
22314
|
+
const { saveSkill: saveSkill2 } = await Promise.resolve().then(() => (init_auto_create(), auto_create_exports));
|
|
22315
|
+
const result = await saveSkill2(extracted.name, extracted.content);
|
|
22316
|
+
await channel.sendText(chatId, `Skill "${extracted.name}" saved to ${result.path}`, { parseMode: "plain" });
|
|
22317
|
+
}
|
|
22318
|
+
}
|
|
21723
22319
|
async function handleSetupProfileCommand(chatId, commandArgs, msg, channel) {
|
|
21724
22320
|
await startProfileWizard(chatId, channel);
|
|
21725
22321
|
}
|
|
@@ -23364,6 +23960,10 @@ async function handleCommand(msg, channel) {
|
|
|
23364
23960
|
case "skill-install":
|
|
23365
23961
|
await handleSkillInstallCommand(chatId, commandArgs, msg, channel);
|
|
23366
23962
|
break;
|
|
23963
|
+
case "extract_skill":
|
|
23964
|
+
case "extractskill":
|
|
23965
|
+
await handleExtractSkillCommand(chatId, commandArgs, msg, channel);
|
|
23966
|
+
break;
|
|
23367
23967
|
case "setup-profile":
|
|
23368
23968
|
await handleSetupProfileCommand(chatId, commandArgs, msg, channel);
|
|
23369
23969
|
break;
|
|
@@ -23450,134 +24050,6 @@ var init_commands = __esm({
|
|
|
23450
24050
|
}
|
|
23451
24051
|
});
|
|
23452
24052
|
|
|
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
24053
|
// src/router/callbacks.ts
|
|
23582
24054
|
import { readFile as readFile7 } from "fs/promises";
|
|
23583
24055
|
async function handleCallback(chatId, data, channel, messageId) {
|
|
@@ -23980,7 +24452,107 @@ This cannot be undone.`,
|
|
|
23980
24452
|
}
|
|
23981
24453
|
} else if (rest.startsWith("edit:")) {
|
|
23982
24454
|
const id = parseInt(rest.slice(5), 10);
|
|
24455
|
+
const editJob = getJobById(id);
|
|
24456
|
+
if (!editJob) {
|
|
24457
|
+
await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
|
|
24458
|
+
return;
|
|
24459
|
+
}
|
|
24460
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24461
|
+
const backendLabel = editJob.backend ? `${editJob.backend}/${editJob.model ?? "default"}` : "chat default";
|
|
24462
|
+
await channel.sendKeyboard(
|
|
24463
|
+
chatId,
|
|
24464
|
+
`Edit Job #${id}
|
|
24465
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
24466
|
+
What do you want to change?`,
|
|
24467
|
+
[
|
|
24468
|
+
[
|
|
24469
|
+
{ label: "\u{1F4C5} Schedule", data: `job:editwiz:${id}` },
|
|
24470
|
+
{ label: "\u{1F527} Backend/Model", data: `job:editbackend:${id}` }
|
|
24471
|
+
],
|
|
24472
|
+
[
|
|
24473
|
+
{ label: "\u23F1 Timeout", data: `job:edittimeout:${id}` },
|
|
24474
|
+
{ label: "\u{1F4DD} Full Edit", data: `job:editwiz:${id}` }
|
|
24475
|
+
],
|
|
24476
|
+
[{ label: "\u2190 Back to Job", data: `job:view:${id}` }]
|
|
24477
|
+
]
|
|
24478
|
+
);
|
|
24479
|
+
} else {
|
|
24480
|
+
await startEditWizard(chatId, id, channel);
|
|
24481
|
+
}
|
|
24482
|
+
} else if (rest.startsWith("editwiz:")) {
|
|
24483
|
+
const id = parseInt(rest.slice(8), 10);
|
|
23983
24484
|
await startEditWizard(chatId, id, channel);
|
|
24485
|
+
} else if (rest.startsWith("editbackend:")) {
|
|
24486
|
+
const id = parseInt(rest.slice(12), 10);
|
|
24487
|
+
const { getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24488
|
+
const adapters2 = getAllAdapters5().filter((a) => a.id !== "ollama");
|
|
24489
|
+
const buttons = adapters2.map((a) => ({
|
|
24490
|
+
label: a.displayName,
|
|
24491
|
+
data: `job:setbackend:${id}:${a.id}`
|
|
24492
|
+
}));
|
|
24493
|
+
const rows = [];
|
|
24494
|
+
for (let i = 0; i < buttons.length; i += 2) {
|
|
24495
|
+
rows.push(buttons.slice(i, i + 2));
|
|
24496
|
+
}
|
|
24497
|
+
rows.push([{ label: "\u2190 Back", data: `job:edit:${id}` }]);
|
|
24498
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24499
|
+
await channel.sendKeyboard(chatId, `Pick a backend for Job #${id}:`, rows);
|
|
24500
|
+
}
|
|
24501
|
+
} else if (rest.startsWith("setbackend:")) {
|
|
24502
|
+
const parts = rest.slice(11).split(":");
|
|
24503
|
+
const id = parseInt(parts[0], 10);
|
|
24504
|
+
const backend2 = parts[1];
|
|
24505
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24506
|
+
const adapter = getAdapter4(backend2);
|
|
24507
|
+
const models = Object.entries(adapter.availableModels);
|
|
24508
|
+
const buttons = models.map(([modelId, info]) => ({
|
|
24509
|
+
label: info.label?.split("\u2014")[0]?.trim() ?? modelId,
|
|
24510
|
+
data: `job:setmodel:${id}:${backend2}:${modelId}`
|
|
24511
|
+
}));
|
|
24512
|
+
const rows = [];
|
|
24513
|
+
for (let i = 0; i < buttons.length; i += 2) {
|
|
24514
|
+
rows.push(buttons.slice(i, i + 2));
|
|
24515
|
+
}
|
|
24516
|
+
rows.push([{ label: "\u2190 Back", data: `job:editbackend:${id}` }]);
|
|
24517
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24518
|
+
await channel.sendKeyboard(chatId, `Pick a model for ${adapter.displayName}:`, rows);
|
|
24519
|
+
}
|
|
24520
|
+
} else if (rest.startsWith("setmodel:")) {
|
|
24521
|
+
const parts = rest.slice(9).split(":");
|
|
24522
|
+
const id = parseInt(parts[0], 10);
|
|
24523
|
+
const backend2 = parts[1];
|
|
24524
|
+
const model2 = parts.slice(2).join(":");
|
|
24525
|
+
const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
24526
|
+
updateJobFields(id, { backend: backend2, model: model2 });
|
|
24527
|
+
const { getAdapter: getAdapter4 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
24528
|
+
const adapter = getAdapter4(backend2);
|
|
24529
|
+
await channel.sendText(chatId, `Job #${id} updated: ${adapter.displayName} / ${model2}`, { parseMode: "plain" });
|
|
24530
|
+
await sendJobDetail(chatId, id, channel);
|
|
24531
|
+
} else if (rest.startsWith("edittimeout:")) {
|
|
24532
|
+
const id = parseInt(rest.slice(12), 10);
|
|
24533
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
24534
|
+
await channel.sendKeyboard(chatId, `Set timeout for Job #${id}:`, [
|
|
24535
|
+
[
|
|
24536
|
+
{ label: "2 min", data: `job:settimeout:${id}:120` },
|
|
24537
|
+
{ label: "5 min", data: `job:settimeout:${id}:300` },
|
|
24538
|
+
{ label: "10 min", data: `job:settimeout:${id}:600` }
|
|
24539
|
+
],
|
|
24540
|
+
[
|
|
24541
|
+
{ label: "15 min", data: `job:settimeout:${id}:900` },
|
|
24542
|
+
{ label: "30 min", data: `job:settimeout:${id}:1800` },
|
|
24543
|
+
{ label: "60 min", data: `job:settimeout:${id}:3600` }
|
|
24544
|
+
],
|
|
24545
|
+
[{ label: "\u2190 Back", data: `job:edit:${id}` }]
|
|
24546
|
+
]);
|
|
24547
|
+
}
|
|
24548
|
+
} else if (rest.startsWith("settimeout:")) {
|
|
24549
|
+
const parts = rest.slice(11).split(":");
|
|
24550
|
+
const id = parseInt(parts[0], 10);
|
|
24551
|
+
const timeout = parseInt(parts[1], 10);
|
|
24552
|
+
const { updateJob: updateJobFields } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
24553
|
+
updateJobFields(id, { timeout });
|
|
24554
|
+
await channel.sendText(chatId, `Job #${id} timeout updated: ${timeout}s (${Math.round(timeout / 60)} min)`, { parseMode: "plain" });
|
|
24555
|
+
await sendJobDetail(chatId, id, channel);
|
|
23984
24556
|
} else if (rest === "back") {
|
|
23985
24557
|
await sendJobsBoard(chatId, channel, 1, messageId);
|
|
23986
24558
|
} else {
|
|
@@ -24786,14 +25358,6 @@ Use /skills to see all available skills.`, { parseMode: "plain" });
|
|
|
24786
25358
|
await channel.sendText(chatId, `Skill "${skillName}" not found.`, { parseMode: "plain" });
|
|
24787
25359
|
return;
|
|
24788
25360
|
}
|
|
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
25361
|
const raw = await readFile7(skill.filePath, "utf-8");
|
|
24798
25362
|
const skillContent = stripFrontmatter2(raw);
|
|
24799
25363
|
const tags = skill.sources.join(", ");
|
|
@@ -26322,7 +26886,7 @@ var init_cron = __esm({
|
|
|
26322
26886
|
});
|
|
26323
26887
|
|
|
26324
26888
|
// src/agents/runners/wrap-backend.ts
|
|
26325
|
-
import { join as
|
|
26889
|
+
import { join as join27 } from "path";
|
|
26326
26890
|
function buildMcpCommands(backendId) {
|
|
26327
26891
|
const exe = backendId === BACKEND.CURSOR ? "agent" : backendId;
|
|
26328
26892
|
return {
|
|
@@ -26416,7 +26980,7 @@ function wrapBackendAdapter(adapter) {
|
|
|
26416
26980
|
const configPath = writeMcpConfigFile(server);
|
|
26417
26981
|
return ["--mcp-config", configPath];
|
|
26418
26982
|
},
|
|
26419
|
-
getSkillPath: () =>
|
|
26983
|
+
getSkillPath: () => join27(SKILLS_PATH, `agent-${adapter.id}.md`)
|
|
26420
26984
|
};
|
|
26421
26985
|
}
|
|
26422
26986
|
var BACKEND_CAPABILITIES;
|
|
@@ -26478,18 +27042,18 @@ var init_wrap_backend = __esm({
|
|
|
26478
27042
|
});
|
|
26479
27043
|
|
|
26480
27044
|
// src/agents/runners/config-loader.ts
|
|
26481
|
-
import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as
|
|
26482
|
-
import { join as
|
|
27045
|
+
import { readFileSync as readFileSync15, readdirSync as readdirSync14, existsSync as existsSync25, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
27046
|
+
import { join as join28 } from "path";
|
|
26483
27047
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
26484
27048
|
function resolveExecutable2(config2) {
|
|
26485
|
-
if (
|
|
27049
|
+
if (existsSync25(config2.executable)) return config2.executable;
|
|
26486
27050
|
try {
|
|
26487
27051
|
return execFileSync3("which", [config2.executable], { encoding: "utf-8" }).trim();
|
|
26488
27052
|
} catch {
|
|
26489
27053
|
}
|
|
26490
27054
|
for (const fallback of config2.executableFallbacks ?? []) {
|
|
26491
27055
|
const resolved = fallback.replace(/^~/, process.env.HOME ?? "");
|
|
26492
|
-
if (
|
|
27056
|
+
if (existsSync25(resolved)) return resolved;
|
|
26493
27057
|
}
|
|
26494
27058
|
return config2.executable;
|
|
26495
27059
|
}
|
|
@@ -26615,7 +27179,7 @@ function configToRunner(config2) {
|
|
|
26615
27179
|
prepareMcpInjection() {
|
|
26616
27180
|
return [];
|
|
26617
27181
|
},
|
|
26618
|
-
getSkillPath: () =>
|
|
27182
|
+
getSkillPath: () => join28(SKILLS_PATH, `agent-${config2.id}.md`)
|
|
26619
27183
|
};
|
|
26620
27184
|
}
|
|
26621
27185
|
function loadRunnerConfig(filePath) {
|
|
@@ -26628,14 +27192,14 @@ function loadRunnerConfig(filePath) {
|
|
|
26628
27192
|
}
|
|
26629
27193
|
}
|
|
26630
27194
|
function loadAllRunnerConfigs() {
|
|
26631
|
-
if (!
|
|
27195
|
+
if (!existsSync25(RUNNERS_PATH)) {
|
|
26632
27196
|
mkdirSync10(RUNNERS_PATH, { recursive: true });
|
|
26633
27197
|
return [];
|
|
26634
27198
|
}
|
|
26635
27199
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
26636
27200
|
const configs = [];
|
|
26637
27201
|
for (const file of files) {
|
|
26638
|
-
const config2 = loadRunnerConfig(
|
|
27202
|
+
const config2 = loadRunnerConfig(join28(RUNNERS_PATH, file));
|
|
26639
27203
|
if (config2) configs.push(config2);
|
|
26640
27204
|
}
|
|
26641
27205
|
return configs;
|
|
@@ -26656,16 +27220,16 @@ function registerConfigRunners() {
|
|
|
26656
27220
|
return count;
|
|
26657
27221
|
}
|
|
26658
27222
|
function watchRunnerConfigs(onChange) {
|
|
26659
|
-
if (!
|
|
27223
|
+
if (!existsSync25(RUNNERS_PATH)) return;
|
|
26660
27224
|
for (const prev of watchedFiles) {
|
|
26661
|
-
if (!
|
|
27225
|
+
if (!existsSync25(prev)) {
|
|
26662
27226
|
unwatchFile(prev);
|
|
26663
27227
|
watchedFiles.delete(prev);
|
|
26664
27228
|
}
|
|
26665
27229
|
}
|
|
26666
27230
|
const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
26667
27231
|
for (const file of files) {
|
|
26668
|
-
const fullPath =
|
|
27232
|
+
const fullPath = join28(RUNNERS_PATH, file);
|
|
26669
27233
|
if (watchedFiles.has(fullPath)) continue;
|
|
26670
27234
|
watchedFiles.add(fullPath);
|
|
26671
27235
|
watchFile(fullPath, { interval: 5e3 }, () => {
|
|
@@ -27100,6 +27664,8 @@ var init_telegram2 = __esm({
|
|
|
27100
27664
|
{ command: "newchat", description: "Start a fresh conversation" },
|
|
27101
27665
|
{ command: "summarize", description: "Save session to memory (or 'all' for pre-restart)" },
|
|
27102
27666
|
{ command: "stop", description: "Cancel the current running task" },
|
|
27667
|
+
{ command: "debug", description: "Toggle session debug logging" },
|
|
27668
|
+
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
27103
27669
|
// Backend & model
|
|
27104
27670
|
{ command: "backend", description: "Switch AI backend (Claude/Gemini/Codex/Cursor)" },
|
|
27105
27671
|
{ command: "claude", description: "Switch to Claude backend" },
|
|
@@ -27148,6 +27714,8 @@ var init_telegram2 = __esm({
|
|
|
27148
27714
|
{ command: "mcp", description: "List MCP servers across all backends" },
|
|
27149
27715
|
// Skills & profile
|
|
27150
27716
|
{ command: "skills", description: "List and invoke skills" },
|
|
27717
|
+
{ command: "extract_skill", description: "Extract a reusable skill from this session" },
|
|
27718
|
+
{ command: "skill_install", description: "Install a skill from GitHub" },
|
|
27151
27719
|
{ command: "voice", description: "Toggle voice responses" },
|
|
27152
27720
|
{ command: "voice_config", description: "Configure voice provider and voice" },
|
|
27153
27721
|
{ command: "response_style", description: "Set the AI response style (concise/normal/detailed)" },
|
|
@@ -27731,19 +28299,19 @@ var init_telegram2 = __esm({
|
|
|
27731
28299
|
});
|
|
27732
28300
|
|
|
27733
28301
|
// src/skills/bootstrap.ts
|
|
27734
|
-
import { existsSync as
|
|
27735
|
-
import { readdir as
|
|
27736
|
-
import { join as
|
|
28302
|
+
import { existsSync as existsSync26 } from "fs";
|
|
28303
|
+
import { readdir as readdir6, readFile as readFile8, writeFile as writeFile5, copyFile } from "fs/promises";
|
|
28304
|
+
import { join as join29, dirname as dirname5 } from "path";
|
|
27737
28305
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
27738
28306
|
async function copyAgentManifestSkills() {
|
|
27739
|
-
if (!
|
|
28307
|
+
if (!existsSync26(PKG_SKILLS)) return;
|
|
27740
28308
|
try {
|
|
27741
|
-
const entries = await
|
|
28309
|
+
const entries = await readdir6(PKG_SKILLS, { withFileTypes: true });
|
|
27742
28310
|
for (const entry of entries) {
|
|
27743
28311
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
27744
|
-
const src =
|
|
27745
|
-
const dest =
|
|
27746
|
-
if (
|
|
28312
|
+
const src = join29(PKG_SKILLS, entry.name);
|
|
28313
|
+
const dest = join29(SKILLS_PATH, entry.name);
|
|
28314
|
+
if (existsSync26(dest)) continue;
|
|
27747
28315
|
await copyFile(src, dest);
|
|
27748
28316
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
27749
28317
|
}
|
|
@@ -27753,10 +28321,10 @@ async function copyAgentManifestSkills() {
|
|
|
27753
28321
|
}
|
|
27754
28322
|
async function bootstrapSkills() {
|
|
27755
28323
|
await copyAgentManifestSkills();
|
|
27756
|
-
const usmDir =
|
|
27757
|
-
if (
|
|
28324
|
+
const usmDir = join29(SKILLS_PATH, USM_DIR_NAME);
|
|
28325
|
+
if (existsSync26(usmDir)) return;
|
|
27758
28326
|
try {
|
|
27759
|
-
const entries = await
|
|
28327
|
+
const entries = await readdir6(SKILLS_PATH);
|
|
27760
28328
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
27761
28329
|
if (dirs.length > 0) return;
|
|
27762
28330
|
} catch (err) {
|
|
@@ -27777,8 +28345,8 @@ async function bootstrapSkills() {
|
|
|
27777
28345
|
}
|
|
27778
28346
|
}
|
|
27779
28347
|
async function patchUsmForCcClaw(usmDir) {
|
|
27780
|
-
const skillPath =
|
|
27781
|
-
if (!
|
|
28348
|
+
const skillPath = join29(usmDir, "SKILL.md");
|
|
28349
|
+
if (!existsSync26(skillPath)) return;
|
|
27782
28350
|
try {
|
|
27783
28351
|
let content = await readFile8(skillPath, "utf-8");
|
|
27784
28352
|
let patched = false;
|
|
@@ -27823,8 +28391,8 @@ var init_bootstrap = __esm({
|
|
|
27823
28391
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
27824
28392
|
USM_DIR_NAME = "universal-skills-manager";
|
|
27825
28393
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
27826
|
-
PKG_ROOT =
|
|
27827
|
-
PKG_SKILLS =
|
|
28394
|
+
PKG_ROOT = join29(dirname5(fileURLToPath2(import.meta.url)), "..", "..");
|
|
28395
|
+
PKG_SKILLS = join29(PKG_ROOT, "skills");
|
|
27828
28396
|
}
|
|
27829
28397
|
});
|
|
27830
28398
|
|
|
@@ -28046,13 +28614,13 @@ __export(ai_skill_exports, {
|
|
|
28046
28614
|
generateAiSkill: () => generateAiSkill,
|
|
28047
28615
|
installAiSkill: () => installAiSkill
|
|
28048
28616
|
});
|
|
28049
|
-
import { existsSync as
|
|
28050
|
-
import { join as
|
|
28617
|
+
import { existsSync as existsSync27, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "fs";
|
|
28618
|
+
import { join as join30 } from "path";
|
|
28051
28619
|
import { homedir as homedir9 } from "os";
|
|
28052
28620
|
function generateAiSkill() {
|
|
28053
28621
|
const version = VERSION;
|
|
28054
28622
|
let systemState = "";
|
|
28055
|
-
if (
|
|
28623
|
+
if (existsSync27(DB_PATH)) {
|
|
28056
28624
|
try {
|
|
28057
28625
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
28058
28626
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -28492,8 +29060,8 @@ function installAiSkill() {
|
|
|
28492
29060
|
const failed = [];
|
|
28493
29061
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
28494
29062
|
for (const dir of dirs) {
|
|
28495
|
-
const skillDir =
|
|
28496
|
-
const skillPath =
|
|
29063
|
+
const skillDir = join30(dir, "cc-claw-cli");
|
|
29064
|
+
const skillPath = join30(skillDir, "SKILL.md");
|
|
28497
29065
|
try {
|
|
28498
29066
|
mkdirSync11(skillDir, { recursive: true });
|
|
28499
29067
|
writeFileSync8(skillPath, skill, "utf-8");
|
|
@@ -28512,11 +29080,11 @@ var init_ai_skill = __esm({
|
|
|
28512
29080
|
init_paths();
|
|
28513
29081
|
init_version();
|
|
28514
29082
|
BACKEND_SKILL_DIRS2 = {
|
|
28515
|
-
"cc-claw": [
|
|
28516
|
-
claude: [
|
|
28517
|
-
gemini: [
|
|
28518
|
-
codex: [
|
|
28519
|
-
cursor: [
|
|
29083
|
+
"cc-claw": [join30(homedir9(), ".cc-claw", "workspace", "skills")],
|
|
29084
|
+
claude: [join30(homedir9(), ".claude", "skills")],
|
|
29085
|
+
gemini: [join30(homedir9(), ".gemini", "skills")],
|
|
29086
|
+
codex: [join30(homedir9(), ".agents", "skills")],
|
|
29087
|
+
cursor: [join30(homedir9(), ".cursor", "skills"), join30(homedir9(), ".cursor", "skills-cursor")]
|
|
28520
29088
|
};
|
|
28521
29089
|
}
|
|
28522
29090
|
});
|
|
@@ -28526,21 +29094,21 @@ var index_exports = {};
|
|
|
28526
29094
|
__export(index_exports, {
|
|
28527
29095
|
main: () => main
|
|
28528
29096
|
});
|
|
28529
|
-
import { mkdirSync as mkdirSync12, existsSync as
|
|
28530
|
-
import { join as
|
|
29097
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync28, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync17 } from "fs";
|
|
29098
|
+
import { join as join31 } from "path";
|
|
28531
29099
|
import dotenv from "dotenv";
|
|
28532
29100
|
function migrateLayout() {
|
|
28533
29101
|
const moves = [
|
|
28534
|
-
[
|
|
28535
|
-
[
|
|
28536
|
-
[
|
|
28537
|
-
[
|
|
28538
|
-
[
|
|
28539
|
-
[
|
|
28540
|
-
[
|
|
29102
|
+
[join31(CC_CLAW_HOME, "cc-claw.db"), join31(DATA_PATH, "cc-claw.db")],
|
|
29103
|
+
[join31(CC_CLAW_HOME, "cc-claw.db-shm"), join31(DATA_PATH, "cc-claw.db-shm")],
|
|
29104
|
+
[join31(CC_CLAW_HOME, "cc-claw.db-wal"), join31(DATA_PATH, "cc-claw.db-wal")],
|
|
29105
|
+
[join31(CC_CLAW_HOME, "cc-claw.log"), join31(LOGS_PATH, "cc-claw.log")],
|
|
29106
|
+
[join31(CC_CLAW_HOME, "cc-claw.log.1"), join31(LOGS_PATH, "cc-claw.log.1")],
|
|
29107
|
+
[join31(CC_CLAW_HOME, "cc-claw.error.log"), join31(LOGS_PATH, "cc-claw.error.log")],
|
|
29108
|
+
[join31(CC_CLAW_HOME, "cc-claw.error.log.1"), join31(LOGS_PATH, "cc-claw.error.log.1")]
|
|
28541
29109
|
];
|
|
28542
29110
|
for (const [from, to] of moves) {
|
|
28543
|
-
if (
|
|
29111
|
+
if (existsSync28(from) && !existsSync28(to)) {
|
|
28544
29112
|
try {
|
|
28545
29113
|
renameSync2(from, to);
|
|
28546
29114
|
} catch {
|
|
@@ -28717,10 +29285,10 @@ async function main() {
|
|
|
28717
29285
|
try {
|
|
28718
29286
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
28719
29287
|
const { writeFileSync: writeFileSync13, mkdirSync: mkdirSync19 } = await import("fs");
|
|
28720
|
-
const { join:
|
|
28721
|
-
const skillDir =
|
|
29288
|
+
const { join: join37 } = await import("path");
|
|
29289
|
+
const skillDir = join37(SKILLS_PATH, "cc-claw-cli");
|
|
28722
29290
|
mkdirSync19(skillDir, { recursive: true });
|
|
28723
|
-
writeFileSync13(
|
|
29291
|
+
writeFileSync13(join37(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
28724
29292
|
log("[cc-claw] AI skill updated");
|
|
28725
29293
|
} catch {
|
|
28726
29294
|
}
|
|
@@ -28820,10 +29388,10 @@ var init_index = __esm({
|
|
|
28820
29388
|
init_health3();
|
|
28821
29389
|
init_image_gen();
|
|
28822
29390
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SESSION_LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
28823
|
-
if (!
|
|
29391
|
+
if (!existsSync28(dir)) mkdirSync12(dir, { recursive: true });
|
|
28824
29392
|
}
|
|
28825
29393
|
migrateLayout();
|
|
28826
|
-
if (
|
|
29394
|
+
if (existsSync28(ENV_PATH)) {
|
|
28827
29395
|
dotenv.config({ path: ENV_PATH });
|
|
28828
29396
|
} else {
|
|
28829
29397
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -28844,12 +29412,12 @@ __export(api_client_exports, {
|
|
|
28844
29412
|
apiPost: () => apiPost,
|
|
28845
29413
|
isDaemonRunning: () => isDaemonRunning
|
|
28846
29414
|
});
|
|
28847
|
-
import { readFileSync as readFileSync18, existsSync as
|
|
29415
|
+
import { readFileSync as readFileSync18, existsSync as existsSync29 } from "fs";
|
|
28848
29416
|
import { request as httpRequest, Agent } from "http";
|
|
28849
29417
|
function getToken() {
|
|
28850
29418
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
28851
29419
|
try {
|
|
28852
|
-
if (
|
|
29420
|
+
if (existsSync29(TOKEN_PATH)) return readFileSync18(TOKEN_PATH, "utf-8").trim();
|
|
28853
29421
|
} catch {
|
|
28854
29422
|
}
|
|
28855
29423
|
return null;
|
|
@@ -28948,10 +29516,10 @@ __export(service_exports2, {
|
|
|
28948
29516
|
serviceStatus: () => serviceStatus,
|
|
28949
29517
|
uninstallService: () => uninstallService
|
|
28950
29518
|
});
|
|
28951
|
-
import { existsSync as
|
|
29519
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
|
|
28952
29520
|
import { execFileSync as execFileSync4, execSync as execSync5 } from "child_process";
|
|
28953
29521
|
import { homedir as homedir10, platform } from "os";
|
|
28954
|
-
import { join as
|
|
29522
|
+
import { join as join32, dirname as dirname6 } from "path";
|
|
28955
29523
|
function xmlEscape(s) {
|
|
28956
29524
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
28957
29525
|
}
|
|
@@ -28960,7 +29528,7 @@ function resolveExecutable3(name) {
|
|
|
28960
29528
|
return execFileSync4("which", [name], { encoding: "utf-8" }).trim();
|
|
28961
29529
|
} catch {
|
|
28962
29530
|
const fallback = process.argv[1];
|
|
28963
|
-
if (fallback &&
|
|
29531
|
+
if (fallback && existsSync30(fallback)) return fallback;
|
|
28964
29532
|
throw new Error(`Cannot find '${name}' executable. Install globally: npm install -g cc-claw`);
|
|
28965
29533
|
}
|
|
28966
29534
|
}
|
|
@@ -28969,14 +29537,14 @@ function getPathDirs() {
|
|
|
28969
29537
|
const home = homedir10();
|
|
28970
29538
|
const dirs = /* @__PURE__ */ new Set([
|
|
28971
29539
|
nodeBin,
|
|
28972
|
-
|
|
29540
|
+
join32(home, ".local", "bin"),
|
|
28973
29541
|
"/usr/local/bin",
|
|
28974
29542
|
"/usr/bin",
|
|
28975
29543
|
"/bin"
|
|
28976
29544
|
]);
|
|
28977
29545
|
try {
|
|
28978
29546
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
28979
|
-
if (prefix) dirs.add(
|
|
29547
|
+
if (prefix) dirs.add(join32(prefix, "bin"));
|
|
28980
29548
|
} catch {
|
|
28981
29549
|
}
|
|
28982
29550
|
return [...dirs].join(":");
|
|
@@ -29035,9 +29603,9 @@ function generatePlist() {
|
|
|
29035
29603
|
}
|
|
29036
29604
|
function installMacOS() {
|
|
29037
29605
|
const agentsDir = dirname6(PLIST_PATH);
|
|
29038
|
-
if (!
|
|
29039
|
-
if (!
|
|
29040
|
-
if (
|
|
29606
|
+
if (!existsSync30(agentsDir)) mkdirSync13(agentsDir, { recursive: true });
|
|
29607
|
+
if (!existsSync30(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
29608
|
+
if (existsSync30(PLIST_PATH)) {
|
|
29041
29609
|
try {
|
|
29042
29610
|
execFileSync4("launchctl", ["unload", PLIST_PATH]);
|
|
29043
29611
|
} catch {
|
|
@@ -29049,7 +29617,7 @@ function installMacOS() {
|
|
|
29049
29617
|
console.log(" Service loaded and starting.");
|
|
29050
29618
|
}
|
|
29051
29619
|
function uninstallMacOS() {
|
|
29052
|
-
if (!
|
|
29620
|
+
if (!existsSync30(PLIST_PATH)) {
|
|
29053
29621
|
console.log(" No service found to uninstall.");
|
|
29054
29622
|
return;
|
|
29055
29623
|
}
|
|
@@ -29124,8 +29692,8 @@ WantedBy=default.target
|
|
|
29124
29692
|
`;
|
|
29125
29693
|
}
|
|
29126
29694
|
function installLinux() {
|
|
29127
|
-
if (!
|
|
29128
|
-
if (!
|
|
29695
|
+
if (!existsSync30(SYSTEMD_DIR)) mkdirSync13(SYSTEMD_DIR, { recursive: true });
|
|
29696
|
+
if (!existsSync30(LOGS_PATH)) mkdirSync13(LOGS_PATH, { recursive: true });
|
|
29129
29697
|
writeFileSync9(UNIT_PATH, generateUnit());
|
|
29130
29698
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
29131
29699
|
execFileSync4("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -29134,7 +29702,7 @@ function installLinux() {
|
|
|
29134
29702
|
console.log(" Service enabled and started.");
|
|
29135
29703
|
}
|
|
29136
29704
|
function uninstallLinux() {
|
|
29137
|
-
if (!
|
|
29705
|
+
if (!existsSync30(UNIT_PATH)) {
|
|
29138
29706
|
console.log(" No service found to uninstall.");
|
|
29139
29707
|
return;
|
|
29140
29708
|
}
|
|
@@ -29159,7 +29727,7 @@ function statusLinux() {
|
|
|
29159
29727
|
}
|
|
29160
29728
|
}
|
|
29161
29729
|
function installService() {
|
|
29162
|
-
if (!
|
|
29730
|
+
if (!existsSync30(join32(CC_CLAW_HOME, ".env"))) {
|
|
29163
29731
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
29164
29732
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
29165
29733
|
process.exitCode = 1;
|
|
@@ -29188,9 +29756,9 @@ var init_service2 = __esm({
|
|
|
29188
29756
|
"use strict";
|
|
29189
29757
|
init_paths();
|
|
29190
29758
|
PLIST_LABEL = "com.cc-claw";
|
|
29191
|
-
PLIST_PATH =
|
|
29192
|
-
SYSTEMD_DIR =
|
|
29193
|
-
UNIT_PATH =
|
|
29759
|
+
PLIST_PATH = join32(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
29760
|
+
SYSTEMD_DIR = join32(homedir10(), ".config", "systemd", "user");
|
|
29761
|
+
UNIT_PATH = join32(SYSTEMD_DIR, "cc-claw.service");
|
|
29194
29762
|
}
|
|
29195
29763
|
});
|
|
29196
29764
|
|
|
@@ -29387,7 +29955,7 @@ var status_exports = {};
|
|
|
29387
29955
|
__export(status_exports, {
|
|
29388
29956
|
statusCommand: () => statusCommand
|
|
29389
29957
|
});
|
|
29390
|
-
import { existsSync as
|
|
29958
|
+
import { existsSync as existsSync31, statSync as statSync10 } from "fs";
|
|
29391
29959
|
async function statusCommand(globalOpts, localOpts) {
|
|
29392
29960
|
try {
|
|
29393
29961
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
@@ -29427,7 +29995,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
29427
29995
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
29428
29996
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
29429
29997
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
29430
|
-
const dbStat =
|
|
29998
|
+
const dbStat = existsSync31(DB_PATH) ? statSync10(DB_PATH) : null;
|
|
29431
29999
|
let daemonRunning = false;
|
|
29432
30000
|
let daemonInfo = {};
|
|
29433
30001
|
try {
|
|
@@ -29539,13 +30107,13 @@ __export(doctor_exports, {
|
|
|
29539
30107
|
doctorCommand: () => doctorCommand,
|
|
29540
30108
|
doctorErrors: () => doctorErrors
|
|
29541
30109
|
});
|
|
29542
|
-
import { existsSync as
|
|
30110
|
+
import { existsSync as existsSync32, accessSync, constants } from "fs";
|
|
29543
30111
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
29544
30112
|
async function doctorCommand(globalOpts, localOpts) {
|
|
29545
30113
|
const checks = [];
|
|
29546
30114
|
const dbChecks = checkDatabase();
|
|
29547
30115
|
checks.push(...dbChecks);
|
|
29548
|
-
if (
|
|
30116
|
+
if (existsSync32(DB_PATH)) {
|
|
29549
30117
|
try {
|
|
29550
30118
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
29551
30119
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -29571,7 +30139,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29571
30139
|
checks.push({ name: "Database health", status: "error", message: err.message });
|
|
29572
30140
|
}
|
|
29573
30141
|
}
|
|
29574
|
-
if (
|
|
30142
|
+
if (existsSync32(ENV_PATH)) {
|
|
29575
30143
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
29576
30144
|
} else {
|
|
29577
30145
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
@@ -29614,7 +30182,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29614
30182
|
} catch {
|
|
29615
30183
|
}
|
|
29616
30184
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
29617
|
-
if (
|
|
30185
|
+
if (existsSync32(tokenPath)) {
|
|
29618
30186
|
try {
|
|
29619
30187
|
accessSync(tokenPath, constants.R_OK);
|
|
29620
30188
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -29680,7 +30248,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
29680
30248
|
const errorChecks = checks.filter(
|
|
29681
30249
|
(c) => ["Rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
29682
30250
|
);
|
|
29683
|
-
if (errorChecks.length > 0 &&
|
|
30251
|
+
if (errorChecks.length > 0 && existsSync32(ERROR_LOG_PATH)) {
|
|
29684
30252
|
try {
|
|
29685
30253
|
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
29686
30254
|
writeFileSync13(ERROR_LOG_PATH, "");
|
|
@@ -29809,10 +30377,10 @@ var logs_exports = {};
|
|
|
29809
30377
|
__export(logs_exports, {
|
|
29810
30378
|
logsCommand: () => logsCommand
|
|
29811
30379
|
});
|
|
29812
|
-
import { existsSync as
|
|
30380
|
+
import { existsSync as existsSync33, readFileSync as readFileSync22, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
29813
30381
|
async function logsCommand(opts) {
|
|
29814
30382
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
29815
|
-
if (!
|
|
30383
|
+
if (!existsSync33(logFile)) {
|
|
29816
30384
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
29817
30385
|
process.exit(1);
|
|
29818
30386
|
}
|
|
@@ -29965,11 +30533,11 @@ __export(gemini_exports, {
|
|
|
29965
30533
|
geminiReorder: () => geminiReorder,
|
|
29966
30534
|
geminiRotation: () => geminiRotation
|
|
29967
30535
|
});
|
|
29968
|
-
import { existsSync as
|
|
29969
|
-
import { join as
|
|
30536
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync24, chmodSync } from "fs";
|
|
30537
|
+
import { join as join33 } from "path";
|
|
29970
30538
|
import { createInterface as createInterface8 } from "readline";
|
|
29971
30539
|
function requireDb() {
|
|
29972
|
-
if (!
|
|
30540
|
+
if (!existsSync35(DB_PATH)) {
|
|
29973
30541
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
29974
30542
|
process.exit(1);
|
|
29975
30543
|
}
|
|
@@ -29994,8 +30562,8 @@ async function resolveSlotId(idOrLabel) {
|
|
|
29994
30562
|
function resolveOAuthEmail(configHome) {
|
|
29995
30563
|
if (!configHome) return null;
|
|
29996
30564
|
try {
|
|
29997
|
-
const accountsPath =
|
|
29998
|
-
if (!
|
|
30565
|
+
const accountsPath = join33(configHome, ".gemini", "google_accounts.json");
|
|
30566
|
+
if (!existsSync35(accountsPath)) return null;
|
|
29999
30567
|
const accounts = JSON.parse(readFileSync24(accountsPath, "utf-8"));
|
|
30000
30568
|
return accounts.active || null;
|
|
30001
30569
|
} catch {
|
|
@@ -30078,14 +30646,14 @@ async function geminiAddKey(globalOpts, opts) {
|
|
|
30078
30646
|
}
|
|
30079
30647
|
async function geminiAddAccount(globalOpts, opts) {
|
|
30080
30648
|
await requireWriteDb();
|
|
30081
|
-
const slotsDir =
|
|
30082
|
-
if (!
|
|
30649
|
+
const slotsDir = join33(CC_CLAW_HOME, "gemini-slots");
|
|
30650
|
+
if (!existsSync35(slotsDir)) mkdirSync14(slotsDir, { recursive: true });
|
|
30083
30651
|
const { addGeminiSlot: addGeminiSlot2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
30084
30652
|
const tempId = Date.now();
|
|
30085
|
-
const slotDir =
|
|
30653
|
+
const slotDir = join33(slotsDir, `slot-${tempId}`);
|
|
30086
30654
|
mkdirSync14(slotDir, { recursive: true, mode: 448 });
|
|
30087
|
-
mkdirSync14(
|
|
30088
|
-
writeFileSync10(
|
|
30655
|
+
mkdirSync14(join33(slotDir, ".gemini"), { recursive: true });
|
|
30656
|
+
writeFileSync10(join33(slotDir, ".gemini", "settings.json"), JSON.stringify({
|
|
30089
30657
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
30090
30658
|
}, null, 2));
|
|
30091
30659
|
console.log("");
|
|
@@ -30102,8 +30670,8 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
30102
30670
|
});
|
|
30103
30671
|
} catch {
|
|
30104
30672
|
}
|
|
30105
|
-
const oauthPath =
|
|
30106
|
-
if (!
|
|
30673
|
+
const oauthPath = join33(slotDir, ".gemini", "oauth_creds.json");
|
|
30674
|
+
if (!existsSync35(oauthPath)) {
|
|
30107
30675
|
console.log(error2("\n No OAuth credentials found. Sign-in may have failed."));
|
|
30108
30676
|
console.log(" The slot directory is preserved at: " + slotDir);
|
|
30109
30677
|
console.log(" Re-run: cc-claw gemini add-account\n");
|
|
@@ -30111,7 +30679,7 @@ async function geminiAddAccount(globalOpts, opts) {
|
|
|
30111
30679
|
}
|
|
30112
30680
|
let accountEmail = "unknown";
|
|
30113
30681
|
try {
|
|
30114
|
-
const accounts = JSON.parse(__require("fs").readFileSync(
|
|
30682
|
+
const accounts = JSON.parse(__require("fs").readFileSync(join33(slotDir, ".gemini", "google_accounts.json"), "utf-8"));
|
|
30115
30683
|
accountEmail = accounts.active || accountEmail;
|
|
30116
30684
|
} catch {
|
|
30117
30685
|
}
|
|
@@ -30222,9 +30790,9 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
30222
30790
|
outputError("NO_CONFIG", `Slot "${idOrLabel}" has no config directory \u2014 cannot re-login.`);
|
|
30223
30791
|
return;
|
|
30224
30792
|
}
|
|
30225
|
-
const settingsPath =
|
|
30226
|
-
if (!
|
|
30227
|
-
mkdirSync14(
|
|
30793
|
+
const settingsPath = join33(slot.configHome, ".gemini", "settings.json");
|
|
30794
|
+
if (!existsSync35(settingsPath)) {
|
|
30795
|
+
mkdirSync14(join33(slot.configHome, ".gemini"), { recursive: true });
|
|
30228
30796
|
writeFileSync10(settingsPath, JSON.stringify({
|
|
30229
30797
|
security: { auth: { selectedType: "oauth-personal" } }
|
|
30230
30798
|
}, null, 2));
|
|
@@ -30248,8 +30816,8 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
30248
30816
|
});
|
|
30249
30817
|
} catch {
|
|
30250
30818
|
}
|
|
30251
|
-
const oauthPath =
|
|
30252
|
-
if (!
|
|
30819
|
+
const oauthPath = join33(slot.configHome, ".gemini", "oauth_creds.json");
|
|
30820
|
+
if (!existsSync35(oauthPath)) {
|
|
30253
30821
|
console.log(error2("\n Re-login failed \u2014 no OAuth credentials found."));
|
|
30254
30822
|
console.log(` Try again: cc-claw gemini re-login ${idOrLabel}
|
|
30255
30823
|
`);
|
|
@@ -30259,7 +30827,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
30259
30827
|
setGeminiSlotEnabled2(slotId, true);
|
|
30260
30828
|
let accountEmail = slot.label;
|
|
30261
30829
|
try {
|
|
30262
|
-
const accounts = JSON.parse(readFileSync24(
|
|
30830
|
+
const accounts = JSON.parse(readFileSync24(join33(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
30263
30831
|
if (accounts.active) accountEmail = accounts.active;
|
|
30264
30832
|
} catch {
|
|
30265
30833
|
}
|
|
@@ -30318,11 +30886,11 @@ __export(backend_cmd_factory_exports, {
|
|
|
30318
30886
|
makeReorder: () => makeReorder,
|
|
30319
30887
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
30320
30888
|
});
|
|
30321
|
-
import { existsSync as
|
|
30322
|
-
import { join as
|
|
30889
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync15, readFileSync as readFileSync25 } from "fs";
|
|
30890
|
+
import { join as join34 } from "path";
|
|
30323
30891
|
import { createInterface as createInterface9 } from "readline";
|
|
30324
30892
|
function requireDb2() {
|
|
30325
|
-
if (!
|
|
30893
|
+
if (!existsSync36(DB_PATH)) {
|
|
30326
30894
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30327
30895
|
process.exit(1);
|
|
30328
30896
|
}
|
|
@@ -30411,10 +30979,10 @@ function makeAddAccount(backend2, displayName) {
|
|
|
30411
30979
|
process.exit(1);
|
|
30412
30980
|
}
|
|
30413
30981
|
await requireWriteDb2();
|
|
30414
|
-
const slotsDir =
|
|
30415
|
-
if (!
|
|
30982
|
+
const slotsDir = join34(CC_CLAW_HOME, config2.slotsSubdir);
|
|
30983
|
+
if (!existsSync36(slotsDir)) mkdirSync15(slotsDir, { recursive: true });
|
|
30416
30984
|
const tempId = Date.now();
|
|
30417
|
-
const slotDir =
|
|
30985
|
+
const slotDir = join34(slotsDir, `slot-${tempId}`);
|
|
30418
30986
|
mkdirSync15(slotDir, { recursive: true, mode: 448 });
|
|
30419
30987
|
if (config2.preSetup) config2.preSetup(slotDir);
|
|
30420
30988
|
console.log("");
|
|
@@ -30665,12 +31233,12 @@ var init_backend_cmd_factory = __esm({
|
|
|
30665
31233
|
envValue: (slotDir) => slotDir,
|
|
30666
31234
|
envOverrides: { ANTHROPIC_API_KEY: void 0 },
|
|
30667
31235
|
preSetup: (slotDir) => {
|
|
30668
|
-
mkdirSync15(
|
|
31236
|
+
mkdirSync15(join34(slotDir, ".claude"), { recursive: true });
|
|
30669
31237
|
},
|
|
30670
31238
|
verifyCredentials: (slotDir) => {
|
|
30671
|
-
const claudeJson =
|
|
30672
|
-
const claudeJsonNested =
|
|
30673
|
-
if (
|
|
31239
|
+
const claudeJson = join34(slotDir, ".claude.json");
|
|
31240
|
+
const claudeJsonNested = join34(slotDir, ".claude", ".claude.json");
|
|
31241
|
+
if (existsSync36(claudeJson)) {
|
|
30674
31242
|
try {
|
|
30675
31243
|
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
30676
31244
|
return Boolean(data.oauthAccount);
|
|
@@ -30678,7 +31246,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
30678
31246
|
return false;
|
|
30679
31247
|
}
|
|
30680
31248
|
}
|
|
30681
|
-
if (
|
|
31249
|
+
if (existsSync36(claudeJsonNested)) {
|
|
30682
31250
|
try {
|
|
30683
31251
|
const data = JSON.parse(readFileSync25(claudeJsonNested, "utf-8"));
|
|
30684
31252
|
return Boolean(data.oauthAccount);
|
|
@@ -30701,8 +31269,8 @@ var init_backend_cmd_factory = __esm({
|
|
|
30701
31269
|
} catch {
|
|
30702
31270
|
}
|
|
30703
31271
|
try {
|
|
30704
|
-
const claudeJson =
|
|
30705
|
-
if (
|
|
31272
|
+
const claudeJson = join34(slotDir, ".claude.json");
|
|
31273
|
+
if (existsSync36(claudeJson)) {
|
|
30706
31274
|
const data = JSON.parse(readFileSync25(claudeJson, "utf-8"));
|
|
30707
31275
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
30708
31276
|
}
|
|
@@ -30718,11 +31286,11 @@ var init_backend_cmd_factory = __esm({
|
|
|
30718
31286
|
envValue: (slotDir) => slotDir,
|
|
30719
31287
|
envOverrides: { OPENAI_API_KEY: void 0 },
|
|
30720
31288
|
verifyCredentials: (slotDir) => {
|
|
30721
|
-
return
|
|
31289
|
+
return existsSync36(join34(slotDir, "auth.json"));
|
|
30722
31290
|
},
|
|
30723
31291
|
extractLabel: (slotDir) => {
|
|
30724
31292
|
try {
|
|
30725
|
-
const authData = JSON.parse(readFileSync25(
|
|
31293
|
+
const authData = JSON.parse(readFileSync25(join34(slotDir, "auth.json"), "utf-8"));
|
|
30726
31294
|
if (authData.email) return authData.email;
|
|
30727
31295
|
if (authData.account_name) return authData.account_name;
|
|
30728
31296
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -30746,9 +31314,9 @@ __export(ollama_exports3, {
|
|
|
30746
31314
|
ollamaRemove: () => ollamaRemove,
|
|
30747
31315
|
ollamaTest: () => ollamaTest
|
|
30748
31316
|
});
|
|
30749
|
-
import { existsSync as
|
|
31317
|
+
import { existsSync as existsSync37 } from "fs";
|
|
30750
31318
|
function requireDb3() {
|
|
30751
|
-
if (!
|
|
31319
|
+
if (!existsSync37(DB_PATH)) {
|
|
30752
31320
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
30753
31321
|
process.exit(1);
|
|
30754
31322
|
}
|
|
@@ -31007,12 +31575,12 @@ __export(backend_exports, {
|
|
|
31007
31575
|
backendList: () => backendList,
|
|
31008
31576
|
backendSet: () => backendSet
|
|
31009
31577
|
});
|
|
31010
|
-
import { existsSync as
|
|
31578
|
+
import { existsSync as existsSync38 } from "fs";
|
|
31011
31579
|
async function backendList(globalOpts) {
|
|
31012
31580
|
const { getAvailableAdapters: getAvailableAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
31013
31581
|
const chatId = resolveChatId2(globalOpts);
|
|
31014
31582
|
let activeBackend = null;
|
|
31015
|
-
if (
|
|
31583
|
+
if (existsSync38(DB_PATH)) {
|
|
31016
31584
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31017
31585
|
const readDb = openDatabaseReadOnly2();
|
|
31018
31586
|
try {
|
|
@@ -31043,7 +31611,7 @@ async function backendList(globalOpts) {
|
|
|
31043
31611
|
}
|
|
31044
31612
|
async function backendGet(globalOpts) {
|
|
31045
31613
|
const chatId = resolveChatId2(globalOpts);
|
|
31046
|
-
if (!
|
|
31614
|
+
if (!existsSync38(DB_PATH)) {
|
|
31047
31615
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31048
31616
|
process.exit(1);
|
|
31049
31617
|
}
|
|
@@ -31087,13 +31655,13 @@ __export(model_exports, {
|
|
|
31087
31655
|
modelList: () => modelList,
|
|
31088
31656
|
modelSet: () => modelSet
|
|
31089
31657
|
});
|
|
31090
|
-
import { existsSync as
|
|
31658
|
+
import { existsSync as existsSync39 } from "fs";
|
|
31091
31659
|
async function modelList(globalOpts) {
|
|
31092
31660
|
const chatId = resolveChatId2(globalOpts);
|
|
31093
31661
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
31094
31662
|
const { getAdapter: getAdapter4, getAllAdapters: getAllAdapters5 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
31095
31663
|
let backendId = "claude";
|
|
31096
|
-
if (
|
|
31664
|
+
if (existsSync39(DB_PATH)) {
|
|
31097
31665
|
const readDb = openDatabaseReadOnly2();
|
|
31098
31666
|
try {
|
|
31099
31667
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -31126,7 +31694,7 @@ async function modelList(globalOpts) {
|
|
|
31126
31694
|
}
|
|
31127
31695
|
async function modelGet(globalOpts) {
|
|
31128
31696
|
const chatId = resolveChatId2(globalOpts);
|
|
31129
|
-
if (!
|
|
31697
|
+
if (!existsSync39(DB_PATH)) {
|
|
31130
31698
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31131
31699
|
process.exit(1);
|
|
31132
31700
|
}
|
|
@@ -31170,9 +31738,9 @@ __export(memory_exports2, {
|
|
|
31170
31738
|
memoryList: () => memoryList,
|
|
31171
31739
|
memorySearch: () => memorySearch
|
|
31172
31740
|
});
|
|
31173
|
-
import { existsSync as
|
|
31741
|
+
import { existsSync as existsSync40 } from "fs";
|
|
31174
31742
|
async function memoryList(globalOpts) {
|
|
31175
|
-
if (!
|
|
31743
|
+
if (!existsSync40(DB_PATH)) {
|
|
31176
31744
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31177
31745
|
process.exit(1);
|
|
31178
31746
|
}
|
|
@@ -31196,7 +31764,7 @@ async function memoryList(globalOpts) {
|
|
|
31196
31764
|
});
|
|
31197
31765
|
}
|
|
31198
31766
|
async function memorySearch(globalOpts, query) {
|
|
31199
|
-
if (!
|
|
31767
|
+
if (!existsSync40(DB_PATH)) {
|
|
31200
31768
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31201
31769
|
process.exit(1);
|
|
31202
31770
|
}
|
|
@@ -31218,7 +31786,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
31218
31786
|
});
|
|
31219
31787
|
}
|
|
31220
31788
|
async function memoryHistory(globalOpts, opts) {
|
|
31221
|
-
if (!
|
|
31789
|
+
if (!existsSync40(DB_PATH)) {
|
|
31222
31790
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31223
31791
|
process.exit(1);
|
|
31224
31792
|
}
|
|
@@ -31266,7 +31834,7 @@ __export(cron_exports2, {
|
|
|
31266
31834
|
cronList: () => cronList,
|
|
31267
31835
|
cronRuns: () => cronRuns
|
|
31268
31836
|
});
|
|
31269
|
-
import { existsSync as
|
|
31837
|
+
import { existsSync as existsSync41 } from "fs";
|
|
31270
31838
|
function parseFallbacks(raw) {
|
|
31271
31839
|
return raw.slice(0, 3).map((f) => {
|
|
31272
31840
|
const [backend2, ...rest] = f.split(":");
|
|
@@ -31287,7 +31855,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
31287
31855
|
return val;
|
|
31288
31856
|
}
|
|
31289
31857
|
async function cronList(globalOpts) {
|
|
31290
|
-
if (!
|
|
31858
|
+
if (!existsSync41(DB_PATH)) {
|
|
31291
31859
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31292
31860
|
process.exit(1);
|
|
31293
31861
|
}
|
|
@@ -31325,7 +31893,7 @@ async function cronList(globalOpts) {
|
|
|
31325
31893
|
});
|
|
31326
31894
|
}
|
|
31327
31895
|
async function cronHealth(globalOpts) {
|
|
31328
|
-
if (!
|
|
31896
|
+
if (!existsSync41(DB_PATH)) {
|
|
31329
31897
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31330
31898
|
process.exit(1);
|
|
31331
31899
|
}
|
|
@@ -31486,7 +32054,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
31486
32054
|
}
|
|
31487
32055
|
}
|
|
31488
32056
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
31489
|
-
if (!
|
|
32057
|
+
if (!existsSync41(DB_PATH)) {
|
|
31490
32058
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31491
32059
|
process.exit(1);
|
|
31492
32060
|
}
|
|
@@ -31533,9 +32101,9 @@ __export(agents_exports, {
|
|
|
31533
32101
|
runnersList: () => runnersList,
|
|
31534
32102
|
tasksList: () => tasksList
|
|
31535
32103
|
});
|
|
31536
|
-
import { existsSync as
|
|
32104
|
+
import { existsSync as existsSync42 } from "fs";
|
|
31537
32105
|
async function agentsList(globalOpts) {
|
|
31538
|
-
if (!
|
|
32106
|
+
if (!existsSync42(DB_PATH)) {
|
|
31539
32107
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31540
32108
|
process.exit(1);
|
|
31541
32109
|
}
|
|
@@ -31566,7 +32134,7 @@ async function agentsList(globalOpts) {
|
|
|
31566
32134
|
});
|
|
31567
32135
|
}
|
|
31568
32136
|
async function tasksList(globalOpts) {
|
|
31569
|
-
if (!
|
|
32137
|
+
if (!existsSync42(DB_PATH)) {
|
|
31570
32138
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31571
32139
|
process.exit(1);
|
|
31572
32140
|
}
|
|
@@ -31694,10 +32262,10 @@ __export(db_exports, {
|
|
|
31694
32262
|
dbPath: () => dbPath,
|
|
31695
32263
|
dbStats: () => dbStats
|
|
31696
32264
|
});
|
|
31697
|
-
import { existsSync as
|
|
32265
|
+
import { existsSync as existsSync43, statSync as statSync11, copyFileSync as copyFileSync3, mkdirSync as mkdirSync16 } from "fs";
|
|
31698
32266
|
import { dirname as dirname7 } from "path";
|
|
31699
32267
|
async function dbStats(globalOpts) {
|
|
31700
|
-
if (!
|
|
32268
|
+
if (!existsSync43(DB_PATH)) {
|
|
31701
32269
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
31702
32270
|
process.exit(1);
|
|
31703
32271
|
}
|
|
@@ -31705,7 +32273,7 @@ async function dbStats(globalOpts) {
|
|
|
31705
32273
|
const readDb = openDatabaseReadOnly2();
|
|
31706
32274
|
const mainSize = statSync11(DB_PATH).size;
|
|
31707
32275
|
const walPath = DB_PATH + "-wal";
|
|
31708
|
-
const walSize =
|
|
32276
|
+
const walSize = existsSync43(walPath) ? statSync11(walPath).size : 0;
|
|
31709
32277
|
const tableNames = readDb.prepare(
|
|
31710
32278
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
31711
32279
|
).all();
|
|
@@ -31739,7 +32307,7 @@ async function dbPath(globalOpts) {
|
|
|
31739
32307
|
output({ path: DB_PATH }, (d) => d.path);
|
|
31740
32308
|
}
|
|
31741
32309
|
async function dbBackup(globalOpts, destPath) {
|
|
31742
|
-
if (!
|
|
32310
|
+
if (!existsSync43(DB_PATH)) {
|
|
31743
32311
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
31744
32312
|
process.exit(1);
|
|
31745
32313
|
}
|
|
@@ -31748,7 +32316,7 @@ async function dbBackup(globalOpts, destPath) {
|
|
|
31748
32316
|
mkdirSync16(dirname7(dest), { recursive: true });
|
|
31749
32317
|
copyFileSync3(DB_PATH, dest);
|
|
31750
32318
|
const walPath = DB_PATH + "-wal";
|
|
31751
|
-
if (
|
|
32319
|
+
if (existsSync43(walPath)) copyFileSync3(walPath, dest + "-wal");
|
|
31752
32320
|
output({ path: dest, sizeBytes: statSync11(dest).size }, (d) => {
|
|
31753
32321
|
const b = d;
|
|
31754
32322
|
return `
|
|
@@ -31777,9 +32345,9 @@ __export(usage_exports, {
|
|
|
31777
32345
|
usageCost: () => usageCost,
|
|
31778
32346
|
usageTokens: () => usageTokens
|
|
31779
32347
|
});
|
|
31780
|
-
import { existsSync as
|
|
32348
|
+
import { existsSync as existsSync44 } from "fs";
|
|
31781
32349
|
function ensureDb() {
|
|
31782
|
-
if (!
|
|
32350
|
+
if (!existsSync44(DB_PATH)) {
|
|
31783
32351
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
31784
32352
|
process.exit(1);
|
|
31785
32353
|
}
|
|
@@ -31969,9 +32537,9 @@ __export(config_exports2, {
|
|
|
31969
32537
|
configList: () => configList,
|
|
31970
32538
|
configSet: () => configSet
|
|
31971
32539
|
});
|
|
31972
|
-
import { existsSync as
|
|
32540
|
+
import { existsSync as existsSync45, readFileSync as readFileSync26 } from "fs";
|
|
31973
32541
|
async function configList(globalOpts) {
|
|
31974
|
-
if (!
|
|
32542
|
+
if (!existsSync45(DB_PATH)) {
|
|
31975
32543
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
31976
32544
|
process.exit(1);
|
|
31977
32545
|
}
|
|
@@ -32005,7 +32573,7 @@ async function configGet(globalOpts, key) {
|
|
|
32005
32573
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
32006
32574
|
process.exit(1);
|
|
32007
32575
|
}
|
|
32008
|
-
if (!
|
|
32576
|
+
if (!existsSync45(DB_PATH)) {
|
|
32009
32577
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32010
32578
|
process.exit(1);
|
|
32011
32579
|
}
|
|
@@ -32051,7 +32619,7 @@ async function configSet(globalOpts, key, value) {
|
|
|
32051
32619
|
}
|
|
32052
32620
|
}
|
|
32053
32621
|
async function configEnv(_globalOpts) {
|
|
32054
|
-
if (!
|
|
32622
|
+
if (!existsSync45(ENV_PATH)) {
|
|
32055
32623
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
32056
32624
|
process.exit(1);
|
|
32057
32625
|
}
|
|
@@ -32105,9 +32673,9 @@ __export(session_exports, {
|
|
|
32105
32673
|
sessionGet: () => sessionGet,
|
|
32106
32674
|
sessionNew: () => sessionNew
|
|
32107
32675
|
});
|
|
32108
|
-
import { existsSync as
|
|
32676
|
+
import { existsSync as existsSync46 } from "fs";
|
|
32109
32677
|
async function sessionGet(globalOpts) {
|
|
32110
|
-
if (!
|
|
32678
|
+
if (!existsSync46(DB_PATH)) {
|
|
32111
32679
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32112
32680
|
process.exit(1);
|
|
32113
32681
|
}
|
|
@@ -32168,9 +32736,9 @@ __export(permissions_exports, {
|
|
|
32168
32736
|
verboseGet: () => verboseGet,
|
|
32169
32737
|
verboseSet: () => verboseSet
|
|
32170
32738
|
});
|
|
32171
|
-
import { existsSync as
|
|
32739
|
+
import { existsSync as existsSync47 } from "fs";
|
|
32172
32740
|
function ensureDb2() {
|
|
32173
|
-
if (!
|
|
32741
|
+
if (!existsSync47(DB_PATH)) {
|
|
32174
32742
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32175
32743
|
process.exit(1);
|
|
32176
32744
|
}
|
|
@@ -32317,9 +32885,9 @@ __export(cwd_exports, {
|
|
|
32317
32885
|
cwdGet: () => cwdGet,
|
|
32318
32886
|
cwdSet: () => cwdSet
|
|
32319
32887
|
});
|
|
32320
|
-
import { existsSync as
|
|
32888
|
+
import { existsSync as existsSync48 } from "fs";
|
|
32321
32889
|
async function cwdGet(globalOpts) {
|
|
32322
|
-
if (!
|
|
32890
|
+
if (!existsSync48(DB_PATH)) {
|
|
32323
32891
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32324
32892
|
process.exit(1);
|
|
32325
32893
|
}
|
|
@@ -32381,9 +32949,9 @@ __export(voice_exports, {
|
|
|
32381
32949
|
voiceGet: () => voiceGet,
|
|
32382
32950
|
voiceSet: () => voiceSet
|
|
32383
32951
|
});
|
|
32384
|
-
import { existsSync as
|
|
32952
|
+
import { existsSync as existsSync49 } from "fs";
|
|
32385
32953
|
async function voiceGet(globalOpts) {
|
|
32386
|
-
if (!
|
|
32954
|
+
if (!existsSync49(DB_PATH)) {
|
|
32387
32955
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32388
32956
|
process.exit(1);
|
|
32389
32957
|
}
|
|
@@ -32432,9 +33000,9 @@ __export(heartbeat_exports2, {
|
|
|
32432
33000
|
heartbeatGet: () => heartbeatGet,
|
|
32433
33001
|
heartbeatSet: () => heartbeatSet
|
|
32434
33002
|
});
|
|
32435
|
-
import { existsSync as
|
|
33003
|
+
import { existsSync as existsSync50 } from "fs";
|
|
32436
33004
|
async function heartbeatGet(globalOpts) {
|
|
32437
|
-
if (!
|
|
33005
|
+
if (!existsSync50(DB_PATH)) {
|
|
32438
33006
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32439
33007
|
process.exit(1);
|
|
32440
33008
|
}
|
|
@@ -32645,9 +33213,9 @@ __export(summarizer_exports, {
|
|
|
32645
33213
|
summarizerGet: () => summarizerGet,
|
|
32646
33214
|
summarizerSet: () => summarizerSet
|
|
32647
33215
|
});
|
|
32648
|
-
import { existsSync as
|
|
33216
|
+
import { existsSync as existsSync51 } from "fs";
|
|
32649
33217
|
async function summarizerGet(globalOpts) {
|
|
32650
|
-
if (!
|
|
33218
|
+
if (!existsSync51(DB_PATH)) {
|
|
32651
33219
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32652
33220
|
process.exit(1);
|
|
32653
33221
|
}
|
|
@@ -32691,9 +33259,9 @@ __export(thinking_exports, {
|
|
|
32691
33259
|
thinkingGet: () => thinkingGet,
|
|
32692
33260
|
thinkingSet: () => thinkingSet
|
|
32693
33261
|
});
|
|
32694
|
-
import { existsSync as
|
|
33262
|
+
import { existsSync as existsSync52 } from "fs";
|
|
32695
33263
|
async function thinkingGet(globalOpts) {
|
|
32696
|
-
if (!
|
|
33264
|
+
if (!existsSync52(DB_PATH)) {
|
|
32697
33265
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32698
33266
|
process.exit(1);
|
|
32699
33267
|
}
|
|
@@ -32737,9 +33305,9 @@ __export(chats_exports, {
|
|
|
32737
33305
|
chatsList: () => chatsList,
|
|
32738
33306
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
32739
33307
|
});
|
|
32740
|
-
import { existsSync as
|
|
33308
|
+
import { existsSync as existsSync53 } from "fs";
|
|
32741
33309
|
async function chatsList(_globalOpts) {
|
|
32742
|
-
if (!
|
|
33310
|
+
if (!existsSync53(DB_PATH)) {
|
|
32743
33311
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32744
33312
|
process.exit(1);
|
|
32745
33313
|
}
|
|
@@ -32867,9 +33435,9 @@ var mcps_exports2 = {};
|
|
|
32867
33435
|
__export(mcps_exports2, {
|
|
32868
33436
|
mcpsList: () => mcpsList
|
|
32869
33437
|
});
|
|
32870
|
-
import { existsSync as
|
|
33438
|
+
import { existsSync as existsSync54 } from "fs";
|
|
32871
33439
|
async function mcpsList(_globalOpts) {
|
|
32872
|
-
if (!
|
|
33440
|
+
if (!existsSync54(DB_PATH)) {
|
|
32873
33441
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
32874
33442
|
process.exit(1);
|
|
32875
33443
|
}
|
|
@@ -32906,11 +33474,11 @@ __export(chat_exports2, {
|
|
|
32906
33474
|
chatSend: () => chatSend
|
|
32907
33475
|
});
|
|
32908
33476
|
import { request as httpRequest2 } from "http";
|
|
32909
|
-
import { readFileSync as readFileSync27, existsSync as
|
|
33477
|
+
import { readFileSync as readFileSync27, existsSync as existsSync55 } from "fs";
|
|
32910
33478
|
function getToken2() {
|
|
32911
33479
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
32912
33480
|
try {
|
|
32913
|
-
if (
|
|
33481
|
+
if (existsSync55(TOKEN_PATH2)) return readFileSync27(TOKEN_PATH2, "utf-8").trim();
|
|
32914
33482
|
} catch {
|
|
32915
33483
|
}
|
|
32916
33484
|
return null;
|
|
@@ -33398,7 +33966,7 @@ __export(completion_exports, {
|
|
|
33398
33966
|
completionCommand: () => completionCommand
|
|
33399
33967
|
});
|
|
33400
33968
|
import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync17 } from "fs";
|
|
33401
|
-
import { join as
|
|
33969
|
+
import { join as join35 } from "path";
|
|
33402
33970
|
import { homedir as homedir11 } from "os";
|
|
33403
33971
|
async function completionCommand(opts) {
|
|
33404
33972
|
const shell = opts.shell ?? detectShell();
|
|
@@ -33414,10 +33982,10 @@ async function completionCommand(opts) {
|
|
|
33414
33982
|
process.exit(1);
|
|
33415
33983
|
}
|
|
33416
33984
|
if (opts.install) {
|
|
33417
|
-
const dir =
|
|
33985
|
+
const dir = join35(homedir11(), ".config", "cc-claw", "completions");
|
|
33418
33986
|
mkdirSync17(dir, { recursive: true });
|
|
33419
33987
|
const filename = shell === "zsh" ? "_cc-claw" : shell === "fish" ? "cc-claw.fish" : "cc-claw.bash";
|
|
33420
|
-
const filepath =
|
|
33988
|
+
const filepath = join35(dir, filename);
|
|
33421
33989
|
writeFileSync11(filepath, script, "utf-8");
|
|
33422
33990
|
console.log(`\u2713 Completion script written to ${filepath}
|
|
33423
33991
|
`);
|
|
@@ -33590,9 +34158,9 @@ __export(evolve_exports2, {
|
|
|
33590
34158
|
evolveStatus: () => evolveStatus,
|
|
33591
34159
|
evolveUndo: () => evolveUndo
|
|
33592
34160
|
});
|
|
33593
|
-
import { existsSync as
|
|
34161
|
+
import { existsSync as existsSync56 } from "fs";
|
|
33594
34162
|
function ensureDb3() {
|
|
33595
|
-
if (!
|
|
34163
|
+
if (!existsSync56(DB_PATH)) {
|
|
33596
34164
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
33597
34165
|
process.exit(1);
|
|
33598
34166
|
}
|
|
@@ -34066,10 +34634,10 @@ var init_optimize2 = __esm({
|
|
|
34066
34634
|
|
|
34067
34635
|
// src/setup.ts
|
|
34068
34636
|
var setup_exports = {};
|
|
34069
|
-
import { existsSync as
|
|
34637
|
+
import { existsSync as existsSync57, writeFileSync as writeFileSync12, readFileSync as readFileSync28, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
34070
34638
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
34071
34639
|
import { createInterface as createInterface11 } from "readline";
|
|
34072
|
-
import { join as
|
|
34640
|
+
import { join as join36 } from "path";
|
|
34073
34641
|
function divider2() {
|
|
34074
34642
|
console.log(dim("\u2500".repeat(55)));
|
|
34075
34643
|
}
|
|
@@ -34144,10 +34712,10 @@ async function setup() {
|
|
|
34144
34712
|
}
|
|
34145
34713
|
console.log("");
|
|
34146
34714
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
34147
|
-
if (!
|
|
34715
|
+
if (!existsSync57(dir)) mkdirSync18(dir, { recursive: true });
|
|
34148
34716
|
}
|
|
34149
34717
|
const env = {};
|
|
34150
|
-
const envSource =
|
|
34718
|
+
const envSource = existsSync57(ENV_PATH) ? ENV_PATH : existsSync57(".env") ? ".env" : null;
|
|
34151
34719
|
if (envSource) {
|
|
34152
34720
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
34153
34721
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
@@ -34157,8 +34725,8 @@ async function setup() {
|
|
|
34157
34725
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
34158
34726
|
}
|
|
34159
34727
|
}
|
|
34160
|
-
const cwdDb =
|
|
34161
|
-
if (
|
|
34728
|
+
const cwdDb = join36(process.cwd(), "cc-claw.db");
|
|
34729
|
+
if (existsSync57(cwdDb) && !existsSync57(DB_PATH)) {
|
|
34162
34730
|
const { size } = statSync12(cwdDb);
|
|
34163
34731
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
34164
34732
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|