cc-claw 0.3.10 → 0.3.11
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 +594 -245
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -48,7 +48,7 @@ var VERSION;
|
|
|
48
48
|
var init_version = __esm({
|
|
49
49
|
"src/version.ts"() {
|
|
50
50
|
"use strict";
|
|
51
|
-
VERSION = true ? "0.3.
|
|
51
|
+
VERSION = true ? "0.3.11" : (() => {
|
|
52
52
|
try {
|
|
53
53
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
54
54
|
} catch {
|
|
@@ -1144,6 +1144,7 @@ function initDatabase() {
|
|
|
1144
1144
|
model TEXT,
|
|
1145
1145
|
thinking TEXT,
|
|
1146
1146
|
timeout INTEGER,
|
|
1147
|
+
fallbacks TEXT,
|
|
1147
1148
|
session_type TEXT NOT NULL DEFAULT 'isolated',
|
|
1148
1149
|
channel TEXT,
|
|
1149
1150
|
target TEXT,
|
|
@@ -1263,6 +1264,10 @@ function initDatabase() {
|
|
|
1263
1264
|
db.exec("ALTER TABLE jobs ADD COLUMN timeout INTEGER");
|
|
1264
1265
|
} catch {
|
|
1265
1266
|
}
|
|
1267
|
+
try {
|
|
1268
|
+
db.exec("ALTER TABLE jobs ADD COLUMN fallbacks TEXT");
|
|
1269
|
+
} catch {
|
|
1270
|
+
}
|
|
1266
1271
|
}
|
|
1267
1272
|
} catch {
|
|
1268
1273
|
}
|
|
@@ -1955,8 +1960,8 @@ function getBackendUsageInWindow(backend2, windowType) {
|
|
|
1955
1960
|
function insertJob(params) {
|
|
1956
1961
|
const result = db.prepare(`
|
|
1957
1962
|
INSERT INTO jobs (schedule_type, cron, at_time, every_ms, description, chat_id,
|
|
1958
|
-
backend, model, thinking, timeout, session_type, channel, target, delivery_mode, timezone)
|
|
1959
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1963
|
+
backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone)
|
|
1964
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1960
1965
|
`).run(
|
|
1961
1966
|
params.scheduleType,
|
|
1962
1967
|
params.cron ?? null,
|
|
@@ -1968,6 +1973,7 @@ function insertJob(params) {
|
|
|
1968
1973
|
params.model ?? null,
|
|
1969
1974
|
params.thinking ?? null,
|
|
1970
1975
|
params.timeout ?? null,
|
|
1976
|
+
params.fallbacks?.length ? JSON.stringify(params.fallbacks) : null,
|
|
1971
1977
|
params.sessionType ?? "isolated",
|
|
1972
1978
|
params.channel ?? null,
|
|
1973
1979
|
params.target ?? null,
|
|
@@ -1976,14 +1982,19 @@ function insertJob(params) {
|
|
|
1976
1982
|
);
|
|
1977
1983
|
return getJobById(Number(result.lastInsertRowid));
|
|
1978
1984
|
}
|
|
1985
|
+
function mapJobRow(row) {
|
|
1986
|
+
if (!row) return void 0;
|
|
1987
|
+
row.fallbacks = row.fallbacks ? JSON.parse(row.fallbacks) : [];
|
|
1988
|
+
return row;
|
|
1989
|
+
}
|
|
1979
1990
|
function getJobById(id) {
|
|
1980
|
-
return db.prepare(`${JOB_SELECT} WHERE id = ?`).get(id);
|
|
1991
|
+
return mapJobRow(db.prepare(`${JOB_SELECT} WHERE id = ?`).get(id));
|
|
1981
1992
|
}
|
|
1982
1993
|
function getActiveJobs() {
|
|
1983
|
-
return db.prepare(`${JOB_SELECT} WHERE active = 1 AND enabled = 1`).all();
|
|
1994
|
+
return db.prepare(`${JOB_SELECT} WHERE active = 1 AND enabled = 1`).all().map((r) => mapJobRow(r));
|
|
1984
1995
|
}
|
|
1985
1996
|
function getAllJobs() {
|
|
1986
|
-
return db.prepare(`${JOB_SELECT} WHERE active = 1 ORDER BY id`).all();
|
|
1997
|
+
return db.prepare(`${JOB_SELECT} WHERE active = 1 ORDER BY id`).all().map((r) => mapJobRow(r));
|
|
1987
1998
|
}
|
|
1988
1999
|
function updateJobEnabled(id, enabled) {
|
|
1989
2000
|
const result = db.prepare("UPDATE jobs SET enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
|
|
@@ -2283,7 +2294,7 @@ var init_store4 = __esm({
|
|
|
2283
2294
|
ALL_TOOLS = ["Read", "Glob", "Grep", "Bash", "Write", "Edit", "WebFetch", "WebSearch", "Agent", "AskUserQuestion"];
|
|
2284
2295
|
JOB_SELECT = `
|
|
2285
2296
|
SELECT id, schedule_type as scheduleType, cron, at_time as atTime, every_ms as everyMs,
|
|
2286
|
-
description, chat_id as chatId, backend, model, thinking, timeout,
|
|
2297
|
+
description, chat_id as chatId, backend, model, thinking, timeout, fallbacks,
|
|
2287
2298
|
session_type as sessionType, channel, target, delivery_mode as deliveryMode,
|
|
2288
2299
|
timezone, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
|
|
2289
2300
|
next_run_at as nextRunAt, consecutive_failures as consecutiveFailures
|
|
@@ -2982,17 +2993,21 @@ async function injectMemoryContext(userMessage) {
|
|
|
2982
2993
|
if (combinedMemories.length === 0 && combinedSessions.length === 0) return null;
|
|
2983
2994
|
const lines = [];
|
|
2984
2995
|
for (const m of combinedMemories) {
|
|
2985
|
-
|
|
2996
|
+
let text = `- [${m.type}] ${m.trigger}: ${m.content}`;
|
|
2997
|
+
if (text.length > MAX_MEMORY_CHARS) text = text.slice(0, MAX_MEMORY_CHARS) + "\u2026";
|
|
2998
|
+
lines.push(text);
|
|
2986
2999
|
}
|
|
2987
3000
|
for (const s of combinedSessions) {
|
|
2988
3001
|
const date = s.created_at.split("T")[0] ?? s.created_at.split(" ")[0];
|
|
2989
|
-
|
|
3002
|
+
let text = `- [episodic] ${date} session (${s.message_count} msgs): ${s.summary}${s.topics ? ` Topics: ${s.topics}` : ""}`;
|
|
3003
|
+
if (text.length > MAX_SESSION_CHARS) text = text.slice(0, MAX_SESSION_CHARS) + "\u2026";
|
|
3004
|
+
lines.push(text);
|
|
2990
3005
|
}
|
|
2991
3006
|
return `[Memory context]
|
|
2992
3007
|
${lines.join("\n")}
|
|
2993
3008
|
[End memory context]`;
|
|
2994
3009
|
}
|
|
2995
|
-
var MEMORY_DECAY_RATE, SESSION_DECAY_RATE, VECTOR_TOP_K, FTS_TOP_K, FINAL_TOP_K_MEMORIES, FINAL_TOP_K_SESSIONS;
|
|
3010
|
+
var MEMORY_DECAY_RATE, SESSION_DECAY_RATE, VECTOR_TOP_K, FTS_TOP_K, FINAL_TOP_K_MEMORIES, FINAL_TOP_K_SESSIONS, MAX_MEMORY_CHARS, MAX_SESSION_CHARS;
|
|
2996
3011
|
var init_inject = __esm({
|
|
2997
3012
|
"src/memory/inject.ts"() {
|
|
2998
3013
|
"use strict";
|
|
@@ -3004,6 +3019,8 @@ var init_inject = __esm({
|
|
|
3004
3019
|
FTS_TOP_K = 20;
|
|
3005
3020
|
FINAL_TOP_K_MEMORIES = 5;
|
|
3006
3021
|
FINAL_TOP_K_SESSIONS = 3;
|
|
3022
|
+
MAX_MEMORY_CHARS = 500;
|
|
3023
|
+
MAX_SESSION_CHARS = 800;
|
|
3007
3024
|
}
|
|
3008
3025
|
});
|
|
3009
3026
|
|
|
@@ -3114,6 +3131,7 @@ function syncNativeCliFiles() {
|
|
|
3114
3131
|
"## System Capabilities",
|
|
3115
3132
|
"",
|
|
3116
3133
|
"- To send a file to the user: write [SEND_FILE:/absolute/path] in your response",
|
|
3134
|
+
"- To generate an image: write [GENERATE_IMAGE:detailed prompt] in your response (requires GEMINI_API_KEY)",
|
|
3117
3135
|
"- To suggest saving a user preference: write [UPDATE_USER:key=value] in your response",
|
|
3118
3136
|
"- For heartbeat checks: respond with exactly HEARTBEAT_OK if nothing needs attention",
|
|
3119
3137
|
"- Your working directory is ~/.cc-claw/workspace/",
|
|
@@ -3147,29 +3165,8 @@ var init_init = __esm({
|
|
|
3147
3165
|
});
|
|
3148
3166
|
|
|
3149
3167
|
// src/bootstrap/loader.ts
|
|
3150
|
-
import { readFileSync as readFileSync3, existsSync as existsSync5, readdirSync
|
|
3168
|
+
import { readFileSync as readFileSync3, existsSync as existsSync5, readdirSync } from "fs";
|
|
3151
3169
|
import { join as join4 } from "path";
|
|
3152
|
-
function loadFileWithCache(path, cache) {
|
|
3153
|
-
if (!existsSync5(path)) return null;
|
|
3154
|
-
try {
|
|
3155
|
-
const stat = statSync2(path);
|
|
3156
|
-
if (stat.mtimeMs !== cache.mtime) {
|
|
3157
|
-
cache.content = readFileSync3(path, "utf-8").trim();
|
|
3158
|
-
cache.mtime = stat.mtimeMs;
|
|
3159
|
-
log(`[bootstrap] Loaded ${path} (${cache.content.length} chars)`);
|
|
3160
|
-
syncNativeCliFiles();
|
|
3161
|
-
}
|
|
3162
|
-
return cache.content;
|
|
3163
|
-
} catch {
|
|
3164
|
-
return null;
|
|
3165
|
-
}
|
|
3166
|
-
}
|
|
3167
|
-
function loadSoul() {
|
|
3168
|
-
return loadFileWithCache(SOUL_PATH2, soulCacheObj);
|
|
3169
|
-
}
|
|
3170
|
-
function loadUser() {
|
|
3171
|
-
return loadFileWithCache(USER_PATH2, userCacheObj);
|
|
3172
|
-
}
|
|
3173
3170
|
function searchContext(userMessage) {
|
|
3174
3171
|
if (!existsSync5(CONTEXT_DIR2)) return null;
|
|
3175
3172
|
const msgWords = new Set(
|
|
@@ -3201,24 +3198,16 @@ function searchContext(userMessage) {
|
|
|
3201
3198
|
} catch {
|
|
3202
3199
|
}
|
|
3203
3200
|
if (bestMatch && bestMatch.score >= 2) {
|
|
3201
|
+
if (bestMatch.content.length > MAX_CONTEXT_CHARS) {
|
|
3202
|
+
return bestMatch.content.slice(0, MAX_CONTEXT_CHARS) + "\n\u2026(truncated)";
|
|
3203
|
+
}
|
|
3204
3204
|
return bestMatch.content;
|
|
3205
3205
|
}
|
|
3206
3206
|
return null;
|
|
3207
3207
|
}
|
|
3208
3208
|
async function assembleBootstrapPrompt(userMessage, tier = "full", chatId) {
|
|
3209
3209
|
const sections = [];
|
|
3210
|
-
|
|
3211
|
-
if (soul) {
|
|
3212
|
-
sections.push(`[System instructions \u2014 follow these]
|
|
3213
|
-
${soul}`);
|
|
3214
|
-
}
|
|
3215
|
-
if (tier === "full" || tier === "heartbeat") {
|
|
3216
|
-
const user = loadUser();
|
|
3217
|
-
if (user) {
|
|
3218
|
-
sections.push(`[User profile]
|
|
3219
|
-
${user}`);
|
|
3220
|
-
}
|
|
3221
|
-
}
|
|
3210
|
+
syncNativeCliFiles();
|
|
3222
3211
|
if (tier === "full") {
|
|
3223
3212
|
const ctx = searchContext(userMessage);
|
|
3224
3213
|
if (ctx) {
|
|
@@ -3226,9 +3215,11 @@ ${user}`);
|
|
|
3226
3215
|
${ctx}`);
|
|
3227
3216
|
}
|
|
3228
3217
|
}
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3218
|
+
if (tier !== "slim") {
|
|
3219
|
+
const memory2 = await injectMemoryContext(userMessage);
|
|
3220
|
+
if (memory2) {
|
|
3221
|
+
sections.push(memory2);
|
|
3222
|
+
}
|
|
3232
3223
|
}
|
|
3233
3224
|
if (chatId && (tier === "full" || tier === "heartbeat")) {
|
|
3234
3225
|
const orchestrationContext = buildOrchestrationContext(chatId);
|
|
@@ -3285,7 +3276,7 @@ ${boardText}`);
|
|
|
3285
3276
|
return null;
|
|
3286
3277
|
}
|
|
3287
3278
|
}
|
|
3288
|
-
var
|
|
3279
|
+
var CONTEXT_DIR2, MAX_CONTEXT_CHARS, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
|
|
3289
3280
|
var init_loader = __esm({
|
|
3290
3281
|
"src/bootstrap/loader.ts"() {
|
|
3291
3282
|
"use strict";
|
|
@@ -3296,11 +3287,8 @@ var init_loader = __esm({
|
|
|
3296
3287
|
init_store3();
|
|
3297
3288
|
init_store4();
|
|
3298
3289
|
init_store();
|
|
3299
|
-
SOUL_PATH2 = join4(WORKSPACE_PATH, "SOUL.md");
|
|
3300
|
-
USER_PATH2 = join4(WORKSPACE_PATH, "USER.md");
|
|
3301
3290
|
CONTEXT_DIR2 = join4(WORKSPACE_PATH, "context");
|
|
3302
|
-
|
|
3303
|
-
userCacheObj = { content: null, mtime: 0 };
|
|
3291
|
+
MAX_CONTEXT_CHARS = 4e3;
|
|
3304
3292
|
ACTIVITY_TOKEN_BUDGET = 1500;
|
|
3305
3293
|
INBOX_TOKEN_BUDGET = 2e3;
|
|
3306
3294
|
WHITEBOARD_TOKEN_BUDGET = 500;
|
|
@@ -3452,9 +3440,9 @@ ${transcript}`;
|
|
|
3452
3440
|
if (ev.type === "result") {
|
|
3453
3441
|
resultText = ev.resultText || accumulatedText;
|
|
3454
3442
|
if (ev.usage) {
|
|
3455
|
-
inputTokens
|
|
3456
|
-
outputTokens
|
|
3457
|
-
cacheReadTokens
|
|
3443
|
+
inputTokens = ev.usage.input;
|
|
3444
|
+
outputTokens = ev.usage.output;
|
|
3445
|
+
cacheReadTokens = ev.usage.cacheRead;
|
|
3458
3446
|
}
|
|
3459
3447
|
if (adapter.shouldKillOnResult()) {
|
|
3460
3448
|
rl2.close();
|
|
@@ -3579,20 +3567,20 @@ var init_registry = __esm({
|
|
|
3579
3567
|
});
|
|
3580
3568
|
|
|
3581
3569
|
// src/agents/roles.ts
|
|
3582
|
-
function buildRoleInstructions(role, task, persona) {
|
|
3570
|
+
function buildRoleInstructions(role, task, persona, includeTools = true) {
|
|
3583
3571
|
const rolePrompt = persona ?? ROLE_PRESETS[role] ?? ROLE_PRESETS.worker;
|
|
3584
3572
|
const capitalizedRole = role.charAt(0).toUpperCase() + role.slice(1);
|
|
3585
|
-
|
|
3573
|
+
const parts = [
|
|
3586
3574
|
`## Your Role: ${capitalizedRole}`,
|
|
3587
3575
|
"",
|
|
3588
3576
|
rolePrompt,
|
|
3589
|
-
""
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3577
|
+
""
|
|
3578
|
+
];
|
|
3579
|
+
if (includeTools) {
|
|
3580
|
+
parts.push(ORCHESTRATOR_TOOLS, "");
|
|
3581
|
+
}
|
|
3582
|
+
parts.push("## Task", "", task);
|
|
3583
|
+
return parts.join("\n");
|
|
3596
3584
|
}
|
|
3597
3585
|
var ROLE_PRESETS, ORCHESTRATOR_TOOLS;
|
|
3598
3586
|
var init_roles = __esm({
|
|
@@ -3660,7 +3648,8 @@ function buildAgentPrompt(opts, runnerSkillPath) {
|
|
|
3660
3648
|
}
|
|
3661
3649
|
parts.push("");
|
|
3662
3650
|
}
|
|
3663
|
-
|
|
3651
|
+
const includeTools = opts.includeOrchestratorTools !== false;
|
|
3652
|
+
parts.push(buildRoleInstructions(opts.role ?? "worker", opts.task, opts.persona, includeTools));
|
|
3664
3653
|
return parts.join("\n");
|
|
3665
3654
|
}
|
|
3666
3655
|
function buildSpawnEnv(runner, isSubAgent = false) {
|
|
@@ -4243,7 +4232,8 @@ async function startAgent(agentId, chatId, opts) {
|
|
|
4243
4232
|
role: opts.role,
|
|
4244
4233
|
persona: opts.persona,
|
|
4245
4234
|
extraArgs: mcpExtraArgs.length ? mcpExtraArgs : void 0,
|
|
4246
|
-
isSubAgent: true
|
|
4235
|
+
isSubAgent: true,
|
|
4236
|
+
includeOrchestratorTools: process.env.DASHBOARD_ENABLED === "1"
|
|
4247
4237
|
}, {
|
|
4248
4238
|
onText: () => {
|
|
4249
4239
|
updateAgentOutput(db3, agentId);
|
|
@@ -5866,8 +5856,8 @@ function askAgent(chatId, userMessage, opts) {
|
|
|
5866
5856
|
return withChatLock(chatId, () => askAgentImpl(chatId, userMessage, opts));
|
|
5867
5857
|
}
|
|
5868
5858
|
async function askAgentImpl(chatId, userMessage, opts) {
|
|
5869
|
-
const { cwd, onStream, model: model2, permMode, onToolAction, bootstrapTier, timeoutMs } = opts ?? {};
|
|
5870
|
-
const adapter = getAdapterForChat(chatId);
|
|
5859
|
+
const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs } = opts ?? {};
|
|
5860
|
+
const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(chatId);
|
|
5871
5861
|
const mode = permMode ?? "yolo";
|
|
5872
5862
|
const thinkingLevel = getThinkingLevel(chatId);
|
|
5873
5863
|
const resolvedCwd = cwd ?? WORKSPACE_PATH;
|
|
@@ -5934,6 +5924,14 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
5934
5924
|
}
|
|
5935
5925
|
if (result.resultText) {
|
|
5936
5926
|
appendToLog(chatId, userMessage, result.resultText);
|
|
5927
|
+
const AUTO_SUMMARIZE_THRESHOLD = 30;
|
|
5928
|
+
const pairCount = getMessagePairCount(chatId);
|
|
5929
|
+
if (pairCount >= AUTO_SUMMARIZE_THRESHOLD) {
|
|
5930
|
+
log(`[agent] Auto-summarizing chat ${chatId} after ${pairCount} turns`);
|
|
5931
|
+
summarizeSession(chatId).catch((err) => {
|
|
5932
|
+
warn(`[agent] Auto-summarize failed for chat ${chatId}: ${err}`);
|
|
5933
|
+
});
|
|
5934
|
+
}
|
|
5937
5935
|
}
|
|
5938
5936
|
return {
|
|
5939
5937
|
text: result.resultText || `(No response from ${adapter.displayName})`,
|
|
@@ -6125,6 +6123,9 @@ var init_delivery = __esm({
|
|
|
6125
6123
|
// src/scheduler/retry.ts
|
|
6126
6124
|
function classifyError(err) {
|
|
6127
6125
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6126
|
+
for (const pattern of EXHAUSTED_PATTERNS) {
|
|
6127
|
+
if (pattern.test(msg)) return "exhausted";
|
|
6128
|
+
}
|
|
6128
6129
|
for (const pattern of TRANSIENT_PATTERNS) {
|
|
6129
6130
|
if (pattern.test(msg)) return "transient";
|
|
6130
6131
|
}
|
|
@@ -6138,10 +6139,21 @@ function getBackoffMs(retryCount) {
|
|
|
6138
6139
|
const jitter = Math.floor(base * 0.2 * (Math.random() * 2 - 1));
|
|
6139
6140
|
return base + jitter;
|
|
6140
6141
|
}
|
|
6141
|
-
var TRANSIENT_PATTERNS, PERMANENT_PATTERNS, BACKOFF_MS, MAX_RETRIES, AUTO_PAUSE_THRESHOLD;
|
|
6142
|
+
var EXHAUSTED_PATTERNS, TRANSIENT_PATTERNS, PERMANENT_PATTERNS, BACKOFF_MS, MAX_RETRIES, AUTO_PAUSE_THRESHOLD;
|
|
6142
6143
|
var init_retry = __esm({
|
|
6143
6144
|
"src/scheduler/retry.ts"() {
|
|
6144
6145
|
"use strict";
|
|
6146
|
+
EXHAUSTED_PATTERNS = [
|
|
6147
|
+
/out of.*usage/i,
|
|
6148
|
+
/\d+-hour limit reached/i,
|
|
6149
|
+
/usage limit reached/i,
|
|
6150
|
+
/hit your.*limit/i,
|
|
6151
|
+
/usage.?limit/i,
|
|
6152
|
+
/exceeded your current quota/i,
|
|
6153
|
+
/RESOURCE.?EXHAUSTED/i,
|
|
6154
|
+
/insufficient.?quota/i,
|
|
6155
|
+
/check your plan and billing/i
|
|
6156
|
+
];
|
|
6145
6157
|
TRANSIENT_PATTERNS = [
|
|
6146
6158
|
/rate.?limit/i,
|
|
6147
6159
|
/too many requests/i,
|
|
@@ -6458,23 +6470,49 @@ async function runWithRetry(job, model2, runId, t0) {
|
|
|
6458
6470
|
if (job.thinking && job.thinking !== "auto") {
|
|
6459
6471
|
setThinkingLevel(chatId, job.thinking);
|
|
6460
6472
|
}
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6473
|
+
const timeoutMs = job.timeout ? job.timeout * 1e3 : void 0;
|
|
6474
|
+
const primaryBackend = resolveJobBackendId(job);
|
|
6475
|
+
const chain = [
|
|
6476
|
+
{ backend: primaryBackend, model: model2 },
|
|
6477
|
+
...job.fallbacks ?? []
|
|
6478
|
+
];
|
|
6479
|
+
for (let chainIdx = 0; chainIdx < chain.length; chainIdx++) {
|
|
6480
|
+
const { backend: currentBackend, model: currentModel } = chain[chainIdx];
|
|
6481
|
+
const isFallback = chainIdx > 0;
|
|
6482
|
+
if (isFallback) {
|
|
6483
|
+
log(`[scheduler] Job #${job.id} falling back to ${currentBackend}:${currentModel} (fallback ${chainIdx}/${job.fallbacks.length})`);
|
|
6484
|
+
}
|
|
6485
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
6486
|
+
try {
|
|
6487
|
+
const response = await askAgent(chatId, job.description, {
|
|
6488
|
+
model: currentModel,
|
|
6489
|
+
backend: currentBackend,
|
|
6490
|
+
bootstrapTier: "slim",
|
|
6491
|
+
timeoutMs
|
|
6492
|
+
});
|
|
6493
|
+
if (isFallback) {
|
|
6494
|
+
response.text = `[Fallback: ran on ${currentBackend}:${currentModel}]
|
|
6495
|
+
|
|
6496
|
+
${response.text}`;
|
|
6497
|
+
}
|
|
6498
|
+
return response;
|
|
6499
|
+
} catch (err) {
|
|
6500
|
+
lastError = err;
|
|
6501
|
+
const errorClass = classifyError(err);
|
|
6502
|
+
if (errorClass === "exhausted") {
|
|
6503
|
+
log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${errorMessage(err)}`);
|
|
6504
|
+
break;
|
|
6505
|
+
}
|
|
6506
|
+
if (errorClass === "permanent" || attempt >= MAX_RETRIES) {
|
|
6507
|
+
throw err;
|
|
6508
|
+
}
|
|
6509
|
+
const backoffMs = getBackoffMs(attempt);
|
|
6510
|
+
log(`[scheduler] Job #${job.id} transient error (attempt ${attempt + 1}/${MAX_RETRIES}), retrying in ${backoffMs / 1e3}s: ${errorMessage(err)}`);
|
|
6511
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
6471
6512
|
}
|
|
6472
|
-
const backoffMs = getBackoffMs(attempt);
|
|
6473
|
-
log(`[scheduler] Job #${job.id} transient error (attempt ${attempt + 1}/${MAX_RETRIES}), retrying in ${backoffMs / 1e3}s: ${errorMessage(err)}`);
|
|
6474
|
-
await new Promise((r) => setTimeout(r, backoffMs));
|
|
6475
6513
|
}
|
|
6476
6514
|
}
|
|
6477
|
-
throw lastError ?? new Error("
|
|
6515
|
+
throw lastError ?? new Error("All backends exhausted");
|
|
6478
6516
|
}
|
|
6479
6517
|
function resolveJobBackendId(job) {
|
|
6480
6518
|
return job.backend ?? (() => {
|
|
@@ -7146,6 +7184,7 @@ var init_telegram2 = __esm({
|
|
|
7146
7184
|
// Skills & profile
|
|
7147
7185
|
{ command: "skills", description: "List and invoke skills" },
|
|
7148
7186
|
{ command: "voice", description: "Toggle voice responses" },
|
|
7187
|
+
{ command: "imagine", description: "Generate an image from a prompt" },
|
|
7149
7188
|
{ command: "heartbeat", description: "Configure proactive heartbeat" },
|
|
7150
7189
|
{ command: "chats", description: "Manage multi-chat aliases" }
|
|
7151
7190
|
]);
|
|
@@ -7791,7 +7830,7 @@ async function finalizeProfile(chatId, state, channel) {
|
|
|
7791
7830
|
"<!-- Add any additional preferences below this line -->",
|
|
7792
7831
|
""
|
|
7793
7832
|
].join("\n");
|
|
7794
|
-
writeFileSync4(
|
|
7833
|
+
writeFileSync4(USER_PATH2, content, "utf-8");
|
|
7795
7834
|
activeProfiles.delete(chatId);
|
|
7796
7835
|
log(`[profile] User profile saved for chat ${chatId}`);
|
|
7797
7836
|
await channel.sendText(
|
|
@@ -7817,23 +7856,23 @@ function extractUserUpdates(text) {
|
|
|
7817
7856
|
return { cleanText, updates };
|
|
7818
7857
|
}
|
|
7819
7858
|
function appendToUserProfile(key, value) {
|
|
7820
|
-
if (!existsSync11(
|
|
7821
|
-
const content = readFileSync7(
|
|
7859
|
+
if (!existsSync11(USER_PATH2)) return;
|
|
7860
|
+
const content = readFileSync7(USER_PATH2, "utf-8");
|
|
7822
7861
|
const line = `- **${key}**: ${value}`;
|
|
7823
7862
|
if (content.includes(line)) return;
|
|
7824
7863
|
const updated = content.trimEnd() + `
|
|
7825
7864
|
${line}
|
|
7826
7865
|
`;
|
|
7827
|
-
writeFileSync4(
|
|
7866
|
+
writeFileSync4(USER_PATH2, updated, "utf-8");
|
|
7828
7867
|
log(`[profile] Appended preference: ${key}=${value}`);
|
|
7829
7868
|
}
|
|
7830
|
-
var
|
|
7869
|
+
var USER_PATH2, activeProfiles;
|
|
7831
7870
|
var init_profile = __esm({
|
|
7832
7871
|
"src/bootstrap/profile.ts"() {
|
|
7833
7872
|
"use strict";
|
|
7834
7873
|
init_paths();
|
|
7835
7874
|
init_log();
|
|
7836
|
-
|
|
7875
|
+
USER_PATH2 = join12(WORKSPACE_PATH, "USER.md");
|
|
7837
7876
|
activeProfiles = /* @__PURE__ */ new Map();
|
|
7838
7877
|
}
|
|
7839
7878
|
});
|
|
@@ -8069,6 +8108,83 @@ var init_format_time = __esm({
|
|
|
8069
8108
|
}
|
|
8070
8109
|
});
|
|
8071
8110
|
|
|
8111
|
+
// src/media/image-gen.ts
|
|
8112
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync13 } from "fs";
|
|
8113
|
+
import { join as join14 } from "path";
|
|
8114
|
+
async function generateImage(prompt) {
|
|
8115
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
8116
|
+
if (!apiKey) {
|
|
8117
|
+
throw new Error("Image generation requires GEMINI_API_KEY. Configure it in ~/.cc-claw/.env");
|
|
8118
|
+
}
|
|
8119
|
+
log(`[image-gen] Generating image: "${prompt.slice(0, 100)}"`);
|
|
8120
|
+
const response = await fetch(
|
|
8121
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${IMAGE_MODEL}:generateContent?key=${apiKey}`,
|
|
8122
|
+
{
|
|
8123
|
+
method: "POST",
|
|
8124
|
+
headers: { "Content-Type": "application/json" },
|
|
8125
|
+
body: JSON.stringify({
|
|
8126
|
+
contents: [
|
|
8127
|
+
{
|
|
8128
|
+
parts: [{ text: prompt }]
|
|
8129
|
+
}
|
|
8130
|
+
],
|
|
8131
|
+
generationConfig: {
|
|
8132
|
+
responseModalities: ["TEXT", "IMAGE"]
|
|
8133
|
+
}
|
|
8134
|
+
})
|
|
8135
|
+
}
|
|
8136
|
+
);
|
|
8137
|
+
if (!response.ok) {
|
|
8138
|
+
const errText = await response.text();
|
|
8139
|
+
throw new Error(`Gemini image API error: ${response.status} ${errText.slice(0, 300)}`);
|
|
8140
|
+
}
|
|
8141
|
+
const data = await response.json();
|
|
8142
|
+
const parts = data.candidates?.[0]?.content?.parts;
|
|
8143
|
+
if (!parts || parts.length === 0) {
|
|
8144
|
+
throw new Error("Gemini returned no content for image generation");
|
|
8145
|
+
}
|
|
8146
|
+
let imageData = null;
|
|
8147
|
+
let mimeType = "image/png";
|
|
8148
|
+
let textResponse = null;
|
|
8149
|
+
for (const part of parts) {
|
|
8150
|
+
if (part.inlineData) {
|
|
8151
|
+
imageData = part.inlineData.data;
|
|
8152
|
+
mimeType = part.inlineData.mimeType ?? "image/png";
|
|
8153
|
+
} else if (part.text) {
|
|
8154
|
+
textResponse = part.text;
|
|
8155
|
+
}
|
|
8156
|
+
}
|
|
8157
|
+
if (!imageData) {
|
|
8158
|
+
throw new Error(textResponse ?? "Gemini did not generate an image. The prompt may have been filtered.");
|
|
8159
|
+
}
|
|
8160
|
+
if (!existsSync13(IMAGE_OUTPUT_DIR)) {
|
|
8161
|
+
mkdirSync5(IMAGE_OUTPUT_DIR, { recursive: true });
|
|
8162
|
+
}
|
|
8163
|
+
const ext = mimeType.includes("jpeg") || mimeType.includes("jpg") ? "jpg" : "png";
|
|
8164
|
+
const filename = `img_${Date.now()}.${ext}`;
|
|
8165
|
+
const filePath = join14(IMAGE_OUTPUT_DIR, filename);
|
|
8166
|
+
const buffer = Buffer.from(imageData, "base64");
|
|
8167
|
+
writeFileSync5(filePath, buffer);
|
|
8168
|
+
log(`[image-gen] Saved ${buffer.length} bytes to ${filePath}`);
|
|
8169
|
+
return { filePath, text: textResponse, mimeType };
|
|
8170
|
+
}
|
|
8171
|
+
function isImageGenAvailable() {
|
|
8172
|
+
return !!process.env.GEMINI_API_KEY;
|
|
8173
|
+
}
|
|
8174
|
+
var IMAGE_MODEL, IMAGE_OUTPUT_DIR;
|
|
8175
|
+
var init_image_gen = __esm({
|
|
8176
|
+
"src/media/image-gen.ts"() {
|
|
8177
|
+
"use strict";
|
|
8178
|
+
init_log();
|
|
8179
|
+
IMAGE_MODEL = "gemini-3.1-flash-image-preview";
|
|
8180
|
+
IMAGE_OUTPUT_DIR = join14(
|
|
8181
|
+
process.env.CC_CLAW_HOME ?? join14(process.env.HOME ?? "/tmp", ".cc-claw"),
|
|
8182
|
+
"data",
|
|
8183
|
+
"images"
|
|
8184
|
+
);
|
|
8185
|
+
}
|
|
8186
|
+
});
|
|
8187
|
+
|
|
8072
8188
|
// src/voice/stt.ts
|
|
8073
8189
|
import crypto from "crypto";
|
|
8074
8190
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -9137,7 +9253,7 @@ async function handleCommand(msg, channel) {
|
|
|
9137
9253
|
case "help":
|
|
9138
9254
|
await channel.sendText(
|
|
9139
9255
|
chatId,
|
|
9140
|
-
"Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/readonly/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/agents - List active sub-agents\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
|
|
9256
|
+
"Hey! I'm CC-Claw \u2014 your personal AI assistant on Telegram.\n\nI use AI coding CLIs (Claude, Gemini, Codex) as my brain. Just send me a message to get started.\n\nCommands:\n/backend [name] - Switch AI backend (or /claude /gemini /codex)\n/model - Switch model for active backend\n/summarizer - Configure session summarization model\n/status - Show session, model, backend, and usage\n/cost - Show estimated API cost (use /cost all for all-time)\n/usage - Show usage per backend with limits\n/limits - Configure usage limits per backend\n/newchat - Start a fresh conversation\n/summarize - Save session to memory (without resetting)\n/summarize all - Summarize all pending sessions (pre-restart)\n/cwd <path> - Set working directory\n/cwd - Show current working directory\n/memory - List stored memories\n/forget <keyword> - Remove a memory\n/voice - Toggle voice responses\n/imagine <prompt> - Generate an image (or /image)\n/cron <description> - Schedule a task (or /schedule)\n/cron - List scheduled jobs (or /jobs)\n/cron cancel <id> - Cancel a job\n/cron pause <id> - Pause a job\n/cron resume <id> - Resume a job\n/cron run <id> - Trigger a job now\n/cron runs [id] - View run history\n/cron edit <id> - Edit a job\n/cron health - Scheduler health\n/skills - List skills from all backends\n/skill-install <url> - Install a skill from GitHub\n/setup-profile - Set up your user profile\n/chats - List authorized chats and aliases\n/heartbeat - Proactive awareness (on/off/interval/hours)\n/history - List recent session summaries\n/stop - Cancel the current running task\n/tools - Configure which tools the agent can use\n/permissions - Switch permission mode (yolo/safe/readonly/plan)\n/verbose - Tool visibility (off/normal/verbose)\n/agents - List active sub-agents\n/tasks - Show task board for current orchestration\n/stopagent <id> - Cancel a specific sub-agent\n/stopall - Cancel all sub-agents in this chat\n/runners - List registered CLI runners\n/mcps - List registered MCP servers\n/help - Show this message",
|
|
9141
9257
|
"plain"
|
|
9142
9258
|
);
|
|
9143
9259
|
break;
|
|
@@ -9620,6 +9736,30 @@ ${lines.join("\n")}`, "plain");
|
|
|
9620
9736
|
);
|
|
9621
9737
|
break;
|
|
9622
9738
|
}
|
|
9739
|
+
case "imagine":
|
|
9740
|
+
case "image": {
|
|
9741
|
+
if (!commandArgs) {
|
|
9742
|
+
await channel.sendText(chatId, "Usage: /imagine <prompt>\nExample: /imagine a cat astronaut on Mars", "plain");
|
|
9743
|
+
return;
|
|
9744
|
+
}
|
|
9745
|
+
if (!isImageGenAvailable()) {
|
|
9746
|
+
await channel.sendText(chatId, "Image generation requires GEMINI_API_KEY. Configure it in ~/.cc-claw/.env", "plain");
|
|
9747
|
+
return;
|
|
9748
|
+
}
|
|
9749
|
+
await channel.sendText(chatId, "\u{1F3A8} Generating image\u2026", "plain");
|
|
9750
|
+
try {
|
|
9751
|
+
const result = await generateImage(commandArgs);
|
|
9752
|
+
const file = await readFile5(result.filePath);
|
|
9753
|
+
const name = result.filePath.split("/").pop() ?? "image.png";
|
|
9754
|
+
await channel.sendFile(chatId, file, name);
|
|
9755
|
+
if (result.text) {
|
|
9756
|
+
await channel.sendText(chatId, result.text, "plain");
|
|
9757
|
+
}
|
|
9758
|
+
} catch (err) {
|
|
9759
|
+
await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, "plain");
|
|
9760
|
+
}
|
|
9761
|
+
break;
|
|
9762
|
+
}
|
|
9623
9763
|
case "schedule": {
|
|
9624
9764
|
if (!commandArgs) {
|
|
9625
9765
|
await channel.sendText(
|
|
@@ -9876,8 +10016,8 @@ Use /skills to see it.`, "plain");
|
|
|
9876
10016
|
if (!lim.max_input_tokens) continue;
|
|
9877
10017
|
const u = getBackendUsageInWindow(lim.backend, lim.window);
|
|
9878
10018
|
const pct = (u.input_tokens / lim.max_input_tokens * 100).toFixed(0);
|
|
9879
|
-
const
|
|
9880
|
-
lines.push(` ${lim.backend} (${lim.window}): ${pct}% of ${(lim.max_input_tokens / 1e3).toFixed(0)}K${
|
|
10019
|
+
const warn3 = u.input_tokens / lim.max_input_tokens >= lim.warn_pct;
|
|
10020
|
+
lines.push(` ${lim.backend} (${lim.window}): ${pct}% of ${(lim.max_input_tokens / 1e3).toFixed(0)}K${warn3 ? " \u26A0\uFE0F" : ""}`);
|
|
9881
10021
|
}
|
|
9882
10022
|
}
|
|
9883
10023
|
await channel.sendText(chatId, lines.join("\n"), "plain");
|
|
@@ -10377,7 +10517,16 @@ async function handleText(msg, channel) {
|
|
|
10377
10517
|
return;
|
|
10378
10518
|
}
|
|
10379
10519
|
if (isChatBusy(chatId)) {
|
|
10380
|
-
|
|
10520
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
10521
|
+
pendingInterrupts.set(chatId, { msg, channel });
|
|
10522
|
+
await channel.sendKeyboard(chatId, "\u23F3 Agent is working on a request\u2026", [
|
|
10523
|
+
[
|
|
10524
|
+
{ label: "\u{1F4E5} Queue message", data: `interrupt:queue:${chatId}` },
|
|
10525
|
+
{ label: "\u26A1 Send now", data: `interrupt:now:${chatId}` }
|
|
10526
|
+
]
|
|
10527
|
+
]);
|
|
10528
|
+
return;
|
|
10529
|
+
}
|
|
10381
10530
|
}
|
|
10382
10531
|
let typingActive = true;
|
|
10383
10532
|
const typingLoop = async () => {
|
|
@@ -10401,6 +10550,30 @@ async function handleText(msg, channel) {
|
|
|
10401
10550
|
} catch (err) {
|
|
10402
10551
|
error("[router] Error:", err);
|
|
10403
10552
|
const errMsg = errorMessage(err);
|
|
10553
|
+
const errorClass = classifyError(err);
|
|
10554
|
+
if (errorClass === "exhausted" && typeof channel.sendKeyboard === "function") {
|
|
10555
|
+
const currentBackend = getBackend(chatId) ?? "claude";
|
|
10556
|
+
const { getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
10557
|
+
const otherBackends = getAllAdapters2().filter((a) => a.id !== currentBackend);
|
|
10558
|
+
if (otherBackends.length > 0) {
|
|
10559
|
+
pendingFallbackMessages.set(chatId, { msg, channel });
|
|
10560
|
+
const buttons = otherBackends.map((a) => ({
|
|
10561
|
+
label: `\u{1F504} ${a.displayName}`,
|
|
10562
|
+
data: `fallback:${a.id}:${chatId}`
|
|
10563
|
+
}));
|
|
10564
|
+
buttons.push({ label: "\u274C Wait", data: `fallback:wait:${chatId}` });
|
|
10565
|
+
await channel.sendKeyboard(
|
|
10566
|
+
chatId,
|
|
10567
|
+
`\u26A0\uFE0F ${getAdapter(currentBackend).displayName} is out of usage.
|
|
10568
|
+
|
|
10569
|
+
${errMsg}
|
|
10570
|
+
|
|
10571
|
+
Switch to another backend?`,
|
|
10572
|
+
[buttons]
|
|
10573
|
+
);
|
|
10574
|
+
return;
|
|
10575
|
+
}
|
|
10576
|
+
}
|
|
10404
10577
|
const userMsg = diagnoseAgentError(errMsg, chatId);
|
|
10405
10578
|
await channel.sendText(chatId, userMsg, "plain");
|
|
10406
10579
|
} finally {
|
|
@@ -10505,16 +10678,48 @@ async function processFileSends2(chatId, channel, text) {
|
|
|
10505
10678
|
}
|
|
10506
10679
|
return text.replace(fileSendPattern, "").trim();
|
|
10507
10680
|
}
|
|
10681
|
+
async function processImageGenerations(chatId, channel, text) {
|
|
10682
|
+
const pattern = /\[GENERATE_IMAGE:(.+?)\]/g;
|
|
10683
|
+
const prompts = [];
|
|
10684
|
+
for (const match of text.matchAll(pattern)) {
|
|
10685
|
+
prompts.push(match[1].trim());
|
|
10686
|
+
}
|
|
10687
|
+
if (prompts.length === 0) return text;
|
|
10688
|
+
if (!isImageGenAvailable()) {
|
|
10689
|
+
log("[router] [GENERATE_IMAGE] marker found but GEMINI_API_KEY not set");
|
|
10690
|
+
return text.replace(pattern, "(Image generation unavailable \u2014 GEMINI_API_KEY not configured)").trim();
|
|
10691
|
+
}
|
|
10692
|
+
for (const prompt of prompts) {
|
|
10693
|
+
try {
|
|
10694
|
+
const result = await generateImage(prompt);
|
|
10695
|
+
const file = await readFile5(result.filePath);
|
|
10696
|
+
const name = result.filePath.split("/").pop() ?? "image.png";
|
|
10697
|
+
await channel.sendFile(chatId, file, name);
|
|
10698
|
+
if (result.text) {
|
|
10699
|
+
await channel.sendText(chatId, result.text, "plain");
|
|
10700
|
+
}
|
|
10701
|
+
} catch (err) {
|
|
10702
|
+
error(`[router] Image generation failed for "${prompt.slice(0, 50)}":`, err);
|
|
10703
|
+
await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, "plain");
|
|
10704
|
+
}
|
|
10705
|
+
}
|
|
10706
|
+
return text.replace(pattern, "").trim();
|
|
10707
|
+
}
|
|
10508
10708
|
async function processReaction(chatId, channel, text, messageId) {
|
|
10509
|
-
const
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10709
|
+
const reactPatternGlobal = /\[REACT:(.+?)\]/g;
|
|
10710
|
+
if (!reactPatternGlobal.test(text)) return text;
|
|
10711
|
+
let reacted = false;
|
|
10712
|
+
reactPatternGlobal.lastIndex = 0;
|
|
10713
|
+
let match;
|
|
10714
|
+
while ((match = reactPatternGlobal.exec(text)) !== null) {
|
|
10715
|
+
const emoji = match[1].trim();
|
|
10716
|
+
if (!reacted && messageId && typeof channel.reactToMessage === "function" && ALLOWED_REACTION_EMOJIS.has(emoji)) {
|
|
10717
|
+
channel.reactToMessage(chatId, messageId, emoji).catch(() => {
|
|
10718
|
+
});
|
|
10719
|
+
reacted = true;
|
|
10720
|
+
}
|
|
10516
10721
|
}
|
|
10517
|
-
return text.replace(
|
|
10722
|
+
return text.replace(/\[REACT:(.+?)\]/g, "").trim();
|
|
10518
10723
|
}
|
|
10519
10724
|
async function sendResponse(chatId, channel, text, messageId) {
|
|
10520
10725
|
text = await processReaction(chatId, channel, text, messageId);
|
|
@@ -10526,7 +10731,8 @@ async function sendResponse(chatId, channel, text, messageId) {
|
|
|
10526
10731
|
]);
|
|
10527
10732
|
}
|
|
10528
10733
|
}
|
|
10529
|
-
const
|
|
10734
|
+
const afterFiles = await processFileSends2(chatId, channel, afterUpdates);
|
|
10735
|
+
const cleanText = await processImageGenerations(chatId, channel, afterFiles);
|
|
10530
10736
|
if (!cleanText) return;
|
|
10531
10737
|
if (isVoiceEnabled(chatId)) {
|
|
10532
10738
|
try {
|
|
@@ -10746,6 +10952,45 @@ ${PERM_MODES[chosen]}`,
|
|
|
10746
10952
|
touchBookmark(chatId, alias);
|
|
10747
10953
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${bookmark.path}`, detail: { field: "cwd", value: bookmark.path } });
|
|
10748
10954
|
await sendCwdSessionChoice(chatId, bookmark.path, channel);
|
|
10955
|
+
} else if (data.startsWith("interrupt:")) {
|
|
10956
|
+
const parts = data.split(":");
|
|
10957
|
+
const action = parts[1];
|
|
10958
|
+
const targetChatId = parts.slice(2).join(":");
|
|
10959
|
+
const pending = pendingInterrupts.get(targetChatId);
|
|
10960
|
+
if (action === "now" && pending) {
|
|
10961
|
+
pendingInterrupts.delete(targetChatId);
|
|
10962
|
+
stopAgent(targetChatId);
|
|
10963
|
+
await channel.sendText(chatId, "\u26A1 Stopping current task and processing your message\u2026", "plain");
|
|
10964
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
10965
|
+
await handleMessage(pending.msg, pending.channel);
|
|
10966
|
+
} else if (action === "queue" && pending) {
|
|
10967
|
+
pendingInterrupts.delete(targetChatId);
|
|
10968
|
+
await channel.sendText(chatId, "\u{1F4E5} Message queued \u2014 will process after current task.", "plain");
|
|
10969
|
+
handleMessage(pending.msg, pending.channel).catch(() => {
|
|
10970
|
+
});
|
|
10971
|
+
} else {
|
|
10972
|
+
await channel.sendText(chatId, "Message already processed or expired.", "plain");
|
|
10973
|
+
}
|
|
10974
|
+
} else if (data.startsWith("fallback:")) {
|
|
10975
|
+
const parts = data.split(":");
|
|
10976
|
+
const targetBackend = parts[1];
|
|
10977
|
+
const targetChatId = parts[2];
|
|
10978
|
+
const pendingMsg = pendingFallbackMessages.get(targetChatId);
|
|
10979
|
+
if (targetBackend === "wait") {
|
|
10980
|
+
pendingFallbackMessages.delete(targetChatId);
|
|
10981
|
+
await channel.sendText(chatId, "OK \u2014 you can switch manually with /backend when ready.", "plain");
|
|
10982
|
+
} else if (pendingMsg) {
|
|
10983
|
+
pendingFallbackMessages.delete(targetChatId);
|
|
10984
|
+
const { summarizeSession: summarizeSession2 } = await Promise.resolve().then(() => (init_summarize(), summarize_exports));
|
|
10985
|
+
await summarizeSession2(targetChatId);
|
|
10986
|
+
clearSession(targetChatId);
|
|
10987
|
+
setBackend(targetChatId, targetBackend);
|
|
10988
|
+
const adapter = getAdapter(targetBackend);
|
|
10989
|
+
await channel.sendText(chatId, `Switched to ${adapter.displayName}. Resending your message\u2026`, "plain");
|
|
10990
|
+
await handleMessage(pendingMsg.msg, pendingMsg.channel);
|
|
10991
|
+
} else {
|
|
10992
|
+
await channel.sendText(chatId, "Fallback expired. Use /backend to switch manually.", "plain");
|
|
10993
|
+
}
|
|
10749
10994
|
} else if (data.startsWith("skills:page:")) {
|
|
10750
10995
|
const page = parseInt(data.slice(12), 10);
|
|
10751
10996
|
const skills2 = await discoverAllSkills();
|
|
@@ -10957,7 +11202,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
|
|
|
10957
11202
|
const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
|
|
10958
11203
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
10959
11204
|
}
|
|
10960
|
-
var PERM_MODES, VERBOSE_LEVELS, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
|
|
11205
|
+
var PERM_MODES, VERBOSE_LEVELS, pendingInterrupts, pendingFallbackMessages, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
|
|
10961
11206
|
var init_router = __esm({
|
|
10962
11207
|
"src/router.ts"() {
|
|
10963
11208
|
"use strict";
|
|
@@ -10968,6 +11213,8 @@ var init_router = __esm({
|
|
|
10968
11213
|
init_log();
|
|
10969
11214
|
init_format_time();
|
|
10970
11215
|
init_agent();
|
|
11216
|
+
init_retry();
|
|
11217
|
+
init_image_gen();
|
|
10971
11218
|
init_stt();
|
|
10972
11219
|
init_store4();
|
|
10973
11220
|
init_summarize();
|
|
@@ -10996,6 +11243,8 @@ var init_router = __esm({
|
|
|
10996
11243
|
normal: "Normal \u2014 summarized actions",
|
|
10997
11244
|
verbose: "Verbose \u2014 full details"
|
|
10998
11245
|
};
|
|
11246
|
+
pendingInterrupts = /* @__PURE__ */ new Map();
|
|
11247
|
+
pendingFallbackMessages = /* @__PURE__ */ new Map();
|
|
10999
11248
|
CLI_INSTALL_HINTS = {
|
|
11000
11249
|
claude: "Install: npm install -g @anthropic-ai/claude-code",
|
|
11001
11250
|
gemini: "Install: npm install -g @anthropic-ai/gemini-cli",
|
|
@@ -11090,19 +11339,19 @@ var init_router = __esm({
|
|
|
11090
11339
|
});
|
|
11091
11340
|
|
|
11092
11341
|
// src/skills/bootstrap.ts
|
|
11093
|
-
import { existsSync as
|
|
11342
|
+
import { existsSync as existsSync14 } from "fs";
|
|
11094
11343
|
import { readdir as readdir3, readFile as readFile6, writeFile as writeFile3, copyFile } from "fs/promises";
|
|
11095
|
-
import { join as
|
|
11344
|
+
import { join as join15, dirname as dirname3 } from "path";
|
|
11096
11345
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11097
11346
|
async function copyAgentManifestSkills() {
|
|
11098
|
-
if (!
|
|
11347
|
+
if (!existsSync14(PKG_SKILLS)) return;
|
|
11099
11348
|
try {
|
|
11100
11349
|
const entries = await readdir3(PKG_SKILLS, { withFileTypes: true });
|
|
11101
11350
|
for (const entry of entries) {
|
|
11102
11351
|
if (!entry.isFile() || !entry.name.startsWith("agent-") || !entry.name.endsWith(".md")) continue;
|
|
11103
|
-
const src =
|
|
11104
|
-
const dest =
|
|
11105
|
-
if (
|
|
11352
|
+
const src = join15(PKG_SKILLS, entry.name);
|
|
11353
|
+
const dest = join15(SKILLS_PATH, entry.name);
|
|
11354
|
+
if (existsSync14(dest)) continue;
|
|
11106
11355
|
await copyFile(src, dest);
|
|
11107
11356
|
log(`[skills] Bootstrapped ${entry.name} to ${SKILLS_PATH}`);
|
|
11108
11357
|
}
|
|
@@ -11112,8 +11361,8 @@ async function copyAgentManifestSkills() {
|
|
|
11112
11361
|
}
|
|
11113
11362
|
async function bootstrapSkills() {
|
|
11114
11363
|
await copyAgentManifestSkills();
|
|
11115
|
-
const usmDir =
|
|
11116
|
-
if (
|
|
11364
|
+
const usmDir = join15(SKILLS_PATH, USM_DIR_NAME);
|
|
11365
|
+
if (existsSync14(usmDir)) return;
|
|
11117
11366
|
try {
|
|
11118
11367
|
const entries = await readdir3(SKILLS_PATH);
|
|
11119
11368
|
const dirs = entries.filter((e) => !e.startsWith("."));
|
|
@@ -11135,8 +11384,8 @@ async function bootstrapSkills() {
|
|
|
11135
11384
|
}
|
|
11136
11385
|
}
|
|
11137
11386
|
async function patchUsmForCcClaw(usmDir) {
|
|
11138
|
-
const skillPath =
|
|
11139
|
-
if (!
|
|
11387
|
+
const skillPath = join15(usmDir, "SKILL.md");
|
|
11388
|
+
if (!existsSync14(skillPath)) return;
|
|
11140
11389
|
try {
|
|
11141
11390
|
let content = await readFile6(skillPath, "utf-8");
|
|
11142
11391
|
let patched = false;
|
|
@@ -11181,8 +11430,8 @@ var init_bootstrap = __esm({
|
|
|
11181
11430
|
USM_REPO = "jacob-bd/universal-skills-manager";
|
|
11182
11431
|
USM_DIR_NAME = "universal-skills-manager";
|
|
11183
11432
|
CC_CLAW_ECOSYSTEM_PATCH = `| **CC-Claw** | \`~/.cc-claw/workspace/skills/\` | N/A (daemon, no project scope) |`;
|
|
11184
|
-
PKG_ROOT =
|
|
11185
|
-
PKG_SKILLS =
|
|
11433
|
+
PKG_ROOT = join15(dirname3(fileURLToPath3(import.meta.url)), "..", "..");
|
|
11434
|
+
PKG_SKILLS = join15(PKG_ROOT, "skills");
|
|
11186
11435
|
}
|
|
11187
11436
|
});
|
|
11188
11437
|
|
|
@@ -11396,13 +11645,13 @@ __export(ai_skill_exports, {
|
|
|
11396
11645
|
generateAiSkill: () => generateAiSkill,
|
|
11397
11646
|
installAiSkill: () => installAiSkill
|
|
11398
11647
|
});
|
|
11399
|
-
import { existsSync as
|
|
11400
|
-
import { join as
|
|
11648
|
+
import { existsSync as existsSync15, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
11649
|
+
import { join as join16 } from "path";
|
|
11401
11650
|
import { homedir as homedir4 } from "os";
|
|
11402
11651
|
function generateAiSkill() {
|
|
11403
11652
|
const version = VERSION;
|
|
11404
11653
|
let systemState = "";
|
|
11405
|
-
if (
|
|
11654
|
+
if (existsSync15(DB_PATH)) {
|
|
11406
11655
|
try {
|
|
11407
11656
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = (init_store4(), __toCommonJS(store_exports3));
|
|
11408
11657
|
const readDb = openDatabaseReadOnly2();
|
|
@@ -11467,11 +11716,10 @@ cc-claw logs --error # Show error log
|
|
|
11467
11716
|
\`\`\`bash
|
|
11468
11717
|
cc-claw backend list --json # Available backends
|
|
11469
11718
|
cc-claw backend get --json # Active backend
|
|
11470
|
-
cc-claw backend set claude # Switch backend
|
|
11719
|
+
cc-claw backend set claude # Switch backend (claude/gemini/codex)
|
|
11471
11720
|
cc-claw model list --json # Models for active backend
|
|
11721
|
+
cc-claw model get --json # Active model
|
|
11472
11722
|
cc-claw model set claude-opus-4-6 # Switch model
|
|
11473
|
-
cc-claw thinking get --json # Current thinking level
|
|
11474
|
-
cc-claw thinking set high # Set thinking level
|
|
11475
11723
|
\`\`\`
|
|
11476
11724
|
|
|
11477
11725
|
### Chat
|
|
@@ -11491,12 +11739,23 @@ cc-claw memory add "key" "value" # Save a memory (needs daemon)
|
|
|
11491
11739
|
cc-claw memory forget "keyword" # Delete memories (needs daemon)
|
|
11492
11740
|
\`\`\`
|
|
11493
11741
|
|
|
11742
|
+
### Session
|
|
11743
|
+
\`\`\`bash
|
|
11744
|
+
cc-claw session get --json # Current session ID and age
|
|
11745
|
+
cc-claw session new # Clear session (newchat + summarize)
|
|
11746
|
+
\`\`\`
|
|
11747
|
+
|
|
11494
11748
|
### Scheduler (cron/schedule are aliases)
|
|
11495
11749
|
\`\`\`bash
|
|
11496
11750
|
cc-claw cron list --json # All scheduled jobs
|
|
11497
11751
|
cc-claw cron health --json # Scheduler health
|
|
11498
11752
|
cc-claw cron runs --json # Run history
|
|
11499
11753
|
cc-claw cron create --description "Morning briefing" --cron "0 9 * * *" --backend claude
|
|
11754
|
+
cc-claw cron edit 3 --model gemini-3-flash-preview # Change job model
|
|
11755
|
+
cc-claw cron edit 3 --backend gemini --model gemini-3-flash-preview # Change backend + model
|
|
11756
|
+
cc-claw cron edit 3 --description "New task" --timeout 300 # Edit multiple fields
|
|
11757
|
+
cc-claw cron create --description "Report" --cron "0 9 * * *" --backend claude --model claude-sonnet-4-6 --fallback codex:gpt-5.4 --fallback gemini:gemini-3-flash-preview # With fallback chain
|
|
11758
|
+
cc-claw cron edit 3 --fallback codex:gpt-5.4 --fallback gemini:gemini-3-flash-preview # Set fallbacks
|
|
11500
11759
|
cc-claw cron cancel 3 # Cancel job #3
|
|
11501
11760
|
cc-claw cron pause 3 # Pause job
|
|
11502
11761
|
cc-claw cron resume 3 # Resume job
|
|
@@ -11527,7 +11786,14 @@ cc-claw usage cost --json # Session cost estimate
|
|
|
11527
11786
|
cc-claw usage cost --all --json # All-time cost by model
|
|
11528
11787
|
cc-claw usage tokens --json # Backend usage in last 24h
|
|
11529
11788
|
cc-claw usage limits list --json # Current limits
|
|
11530
|
-
cc-claw usage limits set claude daily 500000
|
|
11789
|
+
cc-claw usage limits set claude daily 500000 # Set a limit
|
|
11790
|
+
cc-claw usage limits clear claude daily # Remove a limit
|
|
11791
|
+
\`\`\`
|
|
11792
|
+
|
|
11793
|
+
### Thinking / Reasoning
|
|
11794
|
+
\`\`\`bash
|
|
11795
|
+
cc-claw thinking get --json # Current thinking level
|
|
11796
|
+
cc-claw thinking set high # Set level (auto/off/low/medium/high/extra_high)
|
|
11531
11797
|
\`\`\`
|
|
11532
11798
|
|
|
11533
11799
|
### Permissions & Tools
|
|
@@ -11537,10 +11803,71 @@ cc-claw permissions set yolo # Set mode (yolo/safe/readonly/plan)
|
|
|
11537
11803
|
cc-claw tools list --json # Tools with enabled/disabled status
|
|
11538
11804
|
cc-claw tools enable Bash # Enable a tool
|
|
11539
11805
|
cc-claw tools disable WebFetch # Disable a tool
|
|
11806
|
+
cc-claw tools reset # Reset to defaults (all on)
|
|
11540
11807
|
cc-claw verbose get --json # Tool visibility level
|
|
11541
11808
|
cc-claw verbose set verbose # Set level (off/normal/verbose)
|
|
11542
11809
|
\`\`\`
|
|
11543
11810
|
|
|
11811
|
+
### Working Directory
|
|
11812
|
+
\`\`\`bash
|
|
11813
|
+
cc-claw cwd get --json # Show current working directory
|
|
11814
|
+
cc-claw cwd set /path/to/dir # Set working directory
|
|
11815
|
+
cc-claw cwd clear # Reset to default
|
|
11816
|
+
\`\`\`
|
|
11817
|
+
|
|
11818
|
+
### Voice
|
|
11819
|
+
\`\`\`bash
|
|
11820
|
+
cc-claw voice get --json # Show voice status
|
|
11821
|
+
cc-claw voice set on # Enable voice responses
|
|
11822
|
+
cc-claw voice set off # Disable voice responses
|
|
11823
|
+
\`\`\`
|
|
11824
|
+
|
|
11825
|
+
### Image Generation
|
|
11826
|
+
\`\`\`bash
|
|
11827
|
+
# Via Telegram command:
|
|
11828
|
+
/imagine a futuristic city at sunset # Generate image from prompt
|
|
11829
|
+
/image a cat astronaut on Mars # Alias for /imagine
|
|
11830
|
+
|
|
11831
|
+
# Via AI marker in responses:
|
|
11832
|
+
# The AI can write [GENERATE_IMAGE:prompt] in its response to trigger generation
|
|
11833
|
+
# Requires GEMINI_API_KEY in ~/.cc-claw/.env
|
|
11834
|
+
\`\`\`
|
|
11835
|
+
|
|
11836
|
+
### Heartbeat
|
|
11837
|
+
\`\`\`bash
|
|
11838
|
+
cc-claw heartbeat get --json # Show heartbeat config
|
|
11839
|
+
cc-claw heartbeat set on # Enable proactive awareness
|
|
11840
|
+
cc-claw heartbeat set off # Disable heartbeat
|
|
11841
|
+
cc-claw heartbeat set interval 30m # Set check interval
|
|
11842
|
+
cc-claw heartbeat set hours 9-22 # Set active hours
|
|
11843
|
+
\`\`\`
|
|
11844
|
+
|
|
11845
|
+
### Summarizer
|
|
11846
|
+
\`\`\`bash
|
|
11847
|
+
cc-claw summarizer get --json # Current summarizer config
|
|
11848
|
+
cc-claw summarizer set auto # Auto (use active backend's summarizer)
|
|
11849
|
+
cc-claw summarizer set off # Disable summarization
|
|
11850
|
+
cc-claw summarizer set claude:claude-haiku-4-5 # Pin specific backend:model
|
|
11851
|
+
\`\`\`
|
|
11852
|
+
|
|
11853
|
+
### Chat Aliases
|
|
11854
|
+
\`\`\`bash
|
|
11855
|
+
cc-claw chats list --json # Authorized chats and aliases
|
|
11856
|
+
cc-claw chats alias 123456789 "work" # Set alias for a chat
|
|
11857
|
+
cc-claw chats remove-alias "work" # Remove alias
|
|
11858
|
+
\`\`\`
|
|
11859
|
+
|
|
11860
|
+
### Skills
|
|
11861
|
+
\`\`\`bash
|
|
11862
|
+
cc-claw skills list --json # All skills from all backends
|
|
11863
|
+
cc-claw skills install <github-url> # Install skill from GitHub
|
|
11864
|
+
\`\`\`
|
|
11865
|
+
|
|
11866
|
+
### MCP Servers
|
|
11867
|
+
\`\`\`bash
|
|
11868
|
+
cc-claw mcps list --json # Registered MCP servers
|
|
11869
|
+
\`\`\`
|
|
11870
|
+
|
|
11544
11871
|
### Database
|
|
11545
11872
|
\`\`\`bash
|
|
11546
11873
|
cc-claw db stats --json # Row counts, file size, WAL status
|
|
@@ -11561,15 +11888,10 @@ cc-claw service uninstall # Remove service
|
|
|
11561
11888
|
|
|
11562
11889
|
### Other
|
|
11563
11890
|
\`\`\`bash
|
|
11564
|
-
cc-claw
|
|
11565
|
-
cc-claw
|
|
11566
|
-
cc-claw
|
|
11567
|
-
cc-claw
|
|
11568
|
-
cc-claw session new # Clear session
|
|
11569
|
-
cc-claw cwd get --json # Working directory
|
|
11570
|
-
cc-claw voice get --json # Voice status
|
|
11571
|
-
cc-claw heartbeat get --json # Heartbeat config
|
|
11572
|
-
cc-claw summarizer get --json # Summarizer config
|
|
11891
|
+
cc-claw setup # Interactive configuration wizard
|
|
11892
|
+
cc-claw tui # Interactive terminal chat
|
|
11893
|
+
cc-claw completion --shell zsh # Generate shell completions (bash/zsh/fish)
|
|
11894
|
+
cc-claw --ai # Generate/install SKILL.md for AI tools
|
|
11573
11895
|
\`\`\`
|
|
11574
11896
|
|
|
11575
11897
|
## JSON Output Format
|
|
@@ -11625,11 +11947,11 @@ function installAiSkill() {
|
|
|
11625
11947
|
const failed = [];
|
|
11626
11948
|
for (const [backend2, dirs] of Object.entries(BACKEND_SKILL_DIRS2)) {
|
|
11627
11949
|
for (const dir of dirs) {
|
|
11628
|
-
const skillDir =
|
|
11629
|
-
const skillPath =
|
|
11950
|
+
const skillDir = join16(dir, "cc-claw-cli");
|
|
11951
|
+
const skillPath = join16(skillDir, "SKILL.md");
|
|
11630
11952
|
try {
|
|
11631
|
-
|
|
11632
|
-
|
|
11953
|
+
mkdirSync6(skillDir, { recursive: true });
|
|
11954
|
+
writeFileSync6(skillPath, skill, "utf-8");
|
|
11633
11955
|
installed.push(skillPath);
|
|
11634
11956
|
} catch {
|
|
11635
11957
|
failed.push(skillPath);
|
|
@@ -11645,10 +11967,10 @@ var init_ai_skill = __esm({
|
|
|
11645
11967
|
init_paths();
|
|
11646
11968
|
init_version();
|
|
11647
11969
|
BACKEND_SKILL_DIRS2 = {
|
|
11648
|
-
"cc-claw": [
|
|
11649
|
-
claude: [
|
|
11650
|
-
gemini: [
|
|
11651
|
-
codex: [
|
|
11970
|
+
"cc-claw": [join16(homedir4(), ".cc-claw", "workspace", "skills")],
|
|
11971
|
+
claude: [join16(homedir4(), ".claude", "skills")],
|
|
11972
|
+
gemini: [join16(homedir4(), ".gemini", "skills")],
|
|
11973
|
+
codex: [join16(homedir4(), ".agents", "skills")]
|
|
11652
11974
|
};
|
|
11653
11975
|
}
|
|
11654
11976
|
});
|
|
@@ -11658,21 +11980,21 @@ var index_exports = {};
|
|
|
11658
11980
|
__export(index_exports, {
|
|
11659
11981
|
main: () => main
|
|
11660
11982
|
});
|
|
11661
|
-
import { mkdirSync as
|
|
11662
|
-
import { join as
|
|
11983
|
+
import { mkdirSync as mkdirSync7, existsSync as existsSync16, renameSync, statSync as statSync2 } from "fs";
|
|
11984
|
+
import { join as join17 } from "path";
|
|
11663
11985
|
import dotenv from "dotenv";
|
|
11664
11986
|
function migrateLayout() {
|
|
11665
11987
|
const moves = [
|
|
11666
|
-
[
|
|
11667
|
-
[
|
|
11668
|
-
[
|
|
11669
|
-
[
|
|
11670
|
-
[
|
|
11671
|
-
[
|
|
11672
|
-
[
|
|
11988
|
+
[join17(CC_CLAW_HOME, "cc-claw.db"), join17(DATA_PATH, "cc-claw.db")],
|
|
11989
|
+
[join17(CC_CLAW_HOME, "cc-claw.db-shm"), join17(DATA_PATH, "cc-claw.db-shm")],
|
|
11990
|
+
[join17(CC_CLAW_HOME, "cc-claw.db-wal"), join17(DATA_PATH, "cc-claw.db-wal")],
|
|
11991
|
+
[join17(CC_CLAW_HOME, "cc-claw.log"), join17(LOGS_PATH, "cc-claw.log")],
|
|
11992
|
+
[join17(CC_CLAW_HOME, "cc-claw.log.1"), join17(LOGS_PATH, "cc-claw.log.1")],
|
|
11993
|
+
[join17(CC_CLAW_HOME, "cc-claw.error.log"), join17(LOGS_PATH, "cc-claw.error.log")],
|
|
11994
|
+
[join17(CC_CLAW_HOME, "cc-claw.error.log.1"), join17(LOGS_PATH, "cc-claw.error.log.1")]
|
|
11673
11995
|
];
|
|
11674
11996
|
for (const [from, to] of moves) {
|
|
11675
|
-
if (
|
|
11997
|
+
if (existsSync16(from) && !existsSync16(to)) {
|
|
11676
11998
|
try {
|
|
11677
11999
|
renameSync(from, to);
|
|
11678
12000
|
} catch {
|
|
@@ -11683,7 +12005,7 @@ function migrateLayout() {
|
|
|
11683
12005
|
function rotateLogs() {
|
|
11684
12006
|
for (const file of [LOG_PATH, ERROR_LOG_PATH]) {
|
|
11685
12007
|
try {
|
|
11686
|
-
const { size } =
|
|
12008
|
+
const { size } = statSync2(file);
|
|
11687
12009
|
if (size > LOG_MAX_BYTES) {
|
|
11688
12010
|
const archivePath = `${file}.1`;
|
|
11689
12011
|
try {
|
|
@@ -11767,11 +12089,11 @@ async function main() {
|
|
|
11767
12089
|
bootstrapSkills().catch((err) => error("[cc-claw] Skill bootstrap failed:", err));
|
|
11768
12090
|
try {
|
|
11769
12091
|
const { generateAiSkill: generateAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
11770
|
-
const { writeFileSync:
|
|
11771
|
-
const { join:
|
|
11772
|
-
const skillDir =
|
|
11773
|
-
|
|
11774
|
-
|
|
12092
|
+
const { writeFileSync: writeFileSync9, mkdirSync: mkdirSync11 } = await import("fs");
|
|
12093
|
+
const { join: join20 } = await import("path");
|
|
12094
|
+
const skillDir = join20(SKILLS_PATH, "cc-claw-cli");
|
|
12095
|
+
mkdirSync11(skillDir, { recursive: true });
|
|
12096
|
+
writeFileSync9(join20(skillDir, "SKILL.md"), generateAiSkill2(), "utf-8");
|
|
11775
12097
|
log("[cc-claw] AI skill updated");
|
|
11776
12098
|
} catch {
|
|
11777
12099
|
}
|
|
@@ -11831,10 +12153,10 @@ var init_index = __esm({
|
|
|
11831
12153
|
init_bootstrap2();
|
|
11832
12154
|
init_health3();
|
|
11833
12155
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
11834
|
-
if (!
|
|
12156
|
+
if (!existsSync16(dir)) mkdirSync7(dir, { recursive: true });
|
|
11835
12157
|
}
|
|
11836
12158
|
migrateLayout();
|
|
11837
|
-
if (
|
|
12159
|
+
if (existsSync16(ENV_PATH)) {
|
|
11838
12160
|
dotenv.config({ path: ENV_PATH });
|
|
11839
12161
|
} else {
|
|
11840
12162
|
console.error(`[cc-claw] Config not found at ${ENV_PATH} \u2014 run 'cc-claw setup' first`);
|
|
@@ -11855,10 +12177,10 @@ __export(service_exports, {
|
|
|
11855
12177
|
serviceStatus: () => serviceStatus,
|
|
11856
12178
|
uninstallService: () => uninstallService
|
|
11857
12179
|
});
|
|
11858
|
-
import { existsSync as
|
|
12180
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
11859
12181
|
import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
|
|
11860
12182
|
import { homedir as homedir5, platform } from "os";
|
|
11861
|
-
import { join as
|
|
12183
|
+
import { join as join18, dirname as dirname4 } from "path";
|
|
11862
12184
|
function resolveExecutable2(name) {
|
|
11863
12185
|
try {
|
|
11864
12186
|
return execFileSync2("which", [name], { encoding: "utf-8" }).trim();
|
|
@@ -11871,14 +12193,14 @@ function getPathDirs() {
|
|
|
11871
12193
|
const home = homedir5();
|
|
11872
12194
|
const dirs = /* @__PURE__ */ new Set([
|
|
11873
12195
|
nodeBin,
|
|
11874
|
-
|
|
12196
|
+
join18(home, ".local", "bin"),
|
|
11875
12197
|
"/usr/local/bin",
|
|
11876
12198
|
"/usr/bin",
|
|
11877
12199
|
"/bin"
|
|
11878
12200
|
]);
|
|
11879
12201
|
try {
|
|
11880
12202
|
const prefix = execSync5("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
11881
|
-
if (prefix) dirs.add(
|
|
12203
|
+
if (prefix) dirs.add(join18(prefix, "bin"));
|
|
11882
12204
|
} catch {
|
|
11883
12205
|
}
|
|
11884
12206
|
return [...dirs].join(":");
|
|
@@ -11933,21 +12255,21 @@ function generatePlist() {
|
|
|
11933
12255
|
}
|
|
11934
12256
|
function installMacOS() {
|
|
11935
12257
|
const agentsDir = dirname4(PLIST_PATH);
|
|
11936
|
-
if (!
|
|
11937
|
-
if (!
|
|
11938
|
-
if (
|
|
12258
|
+
if (!existsSync17(agentsDir)) mkdirSync8(agentsDir, { recursive: true });
|
|
12259
|
+
if (!existsSync17(LOGS_PATH)) mkdirSync8(LOGS_PATH, { recursive: true });
|
|
12260
|
+
if (existsSync17(PLIST_PATH)) {
|
|
11939
12261
|
try {
|
|
11940
12262
|
execFileSync2("launchctl", ["unload", PLIST_PATH]);
|
|
11941
12263
|
} catch {
|
|
11942
12264
|
}
|
|
11943
12265
|
}
|
|
11944
|
-
|
|
12266
|
+
writeFileSync7(PLIST_PATH, generatePlist());
|
|
11945
12267
|
console.log(` Installed: ${PLIST_PATH}`);
|
|
11946
12268
|
execFileSync2("launchctl", ["load", PLIST_PATH]);
|
|
11947
12269
|
console.log(" Service loaded and starting.");
|
|
11948
12270
|
}
|
|
11949
12271
|
function uninstallMacOS() {
|
|
11950
|
-
if (!
|
|
12272
|
+
if (!existsSync17(PLIST_PATH)) {
|
|
11951
12273
|
console.log(" No service found to uninstall.");
|
|
11952
12274
|
return;
|
|
11953
12275
|
}
|
|
@@ -11998,9 +12320,9 @@ WantedBy=default.target
|
|
|
11998
12320
|
`;
|
|
11999
12321
|
}
|
|
12000
12322
|
function installLinux() {
|
|
12001
|
-
if (!
|
|
12002
|
-
if (!
|
|
12003
|
-
|
|
12323
|
+
if (!existsSync17(SYSTEMD_DIR)) mkdirSync8(SYSTEMD_DIR, { recursive: true });
|
|
12324
|
+
if (!existsSync17(LOGS_PATH)) mkdirSync8(LOGS_PATH, { recursive: true });
|
|
12325
|
+
writeFileSync7(UNIT_PATH, generateUnit());
|
|
12004
12326
|
console.log(` Installed: ${UNIT_PATH}`);
|
|
12005
12327
|
execFileSync2("systemctl", ["--user", "daemon-reload"]);
|
|
12006
12328
|
execFileSync2("systemctl", ["--user", "enable", "cc-claw"]);
|
|
@@ -12008,7 +12330,7 @@ function installLinux() {
|
|
|
12008
12330
|
console.log(" Service enabled and started.");
|
|
12009
12331
|
}
|
|
12010
12332
|
function uninstallLinux() {
|
|
12011
|
-
if (!
|
|
12333
|
+
if (!existsSync17(UNIT_PATH)) {
|
|
12012
12334
|
console.log(" No service found to uninstall.");
|
|
12013
12335
|
return;
|
|
12014
12336
|
}
|
|
@@ -12033,7 +12355,7 @@ function statusLinux() {
|
|
|
12033
12355
|
}
|
|
12034
12356
|
}
|
|
12035
12357
|
function installService() {
|
|
12036
|
-
if (!
|
|
12358
|
+
if (!existsSync17(join18(CC_CLAW_HOME, ".env"))) {
|
|
12037
12359
|
console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
|
|
12038
12360
|
console.error(" Run 'cc-claw setup' before installing the service.");
|
|
12039
12361
|
process.exitCode = 1;
|
|
@@ -12062,9 +12384,9 @@ var init_service = __esm({
|
|
|
12062
12384
|
"use strict";
|
|
12063
12385
|
init_paths();
|
|
12064
12386
|
PLIST_LABEL = "com.cc-claw";
|
|
12065
|
-
PLIST_PATH =
|
|
12066
|
-
SYSTEMD_DIR =
|
|
12067
|
-
UNIT_PATH =
|
|
12387
|
+
PLIST_PATH = join18(homedir5(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
12388
|
+
SYSTEMD_DIR = join18(homedir5(), ".config", "systemd", "user");
|
|
12389
|
+
UNIT_PATH = join18(SYSTEMD_DIR, "cc-claw.service");
|
|
12068
12390
|
}
|
|
12069
12391
|
});
|
|
12070
12392
|
|
|
@@ -12263,12 +12585,12 @@ __export(api_client_exports, {
|
|
|
12263
12585
|
apiPost: () => apiPost,
|
|
12264
12586
|
isDaemonRunning: () => isDaemonRunning
|
|
12265
12587
|
});
|
|
12266
|
-
import { readFileSync as readFileSync12, existsSync as
|
|
12588
|
+
import { readFileSync as readFileSync12, existsSync as existsSync18 } from "fs";
|
|
12267
12589
|
import { request as httpRequest } from "http";
|
|
12268
12590
|
function getToken() {
|
|
12269
12591
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
12270
12592
|
try {
|
|
12271
|
-
if (
|
|
12593
|
+
if (existsSync18(TOKEN_PATH)) return readFileSync12(TOKEN_PATH, "utf-8").trim();
|
|
12272
12594
|
} catch {
|
|
12273
12595
|
}
|
|
12274
12596
|
return null;
|
|
@@ -12361,7 +12683,7 @@ var status_exports = {};
|
|
|
12361
12683
|
__export(status_exports, {
|
|
12362
12684
|
statusCommand: () => statusCommand
|
|
12363
12685
|
});
|
|
12364
|
-
import { existsSync as
|
|
12686
|
+
import { existsSync as existsSync19, statSync as statSync3 } from "fs";
|
|
12365
12687
|
async function statusCommand(globalOpts, localOpts) {
|
|
12366
12688
|
try {
|
|
12367
12689
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
@@ -12401,7 +12723,7 @@ async function statusCommand(globalOpts, localOpts) {
|
|
|
12401
12723
|
const cwdRow = readDb.prepare("SELECT cwd FROM chat_cwd WHERE chat_id = ?").get(chatId);
|
|
12402
12724
|
const voiceRow = readDb.prepare("SELECT enabled FROM chat_voice WHERE chat_id = ?").get(chatId);
|
|
12403
12725
|
const usageRow = readDb.prepare("SELECT * FROM chat_usage WHERE chat_id = ?").get(chatId);
|
|
12404
|
-
const dbStat =
|
|
12726
|
+
const dbStat = existsSync19(DB_PATH) ? statSync3(DB_PATH) : null;
|
|
12405
12727
|
let daemonRunning = false;
|
|
12406
12728
|
let daemonInfo = {};
|
|
12407
12729
|
if (localOpts.deep) {
|
|
@@ -12492,12 +12814,12 @@ var doctor_exports = {};
|
|
|
12492
12814
|
__export(doctor_exports, {
|
|
12493
12815
|
doctorCommand: () => doctorCommand
|
|
12494
12816
|
});
|
|
12495
|
-
import { existsSync as
|
|
12817
|
+
import { existsSync as existsSync20, statSync as statSync4, accessSync, constants } from "fs";
|
|
12496
12818
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
12497
12819
|
async function doctorCommand(globalOpts, localOpts) {
|
|
12498
12820
|
const checks = [];
|
|
12499
|
-
if (
|
|
12500
|
-
const size =
|
|
12821
|
+
if (existsSync20(DB_PATH)) {
|
|
12822
|
+
const size = statSync4(DB_PATH).size;
|
|
12501
12823
|
checks.push({ name: "Database", status: "ok", message: `${DB_PATH} (${(size / 1024).toFixed(0)}KB)` });
|
|
12502
12824
|
try {
|
|
12503
12825
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
@@ -12526,7 +12848,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
12526
12848
|
} else {
|
|
12527
12849
|
checks.push({ name: "Database", status: "error", message: `Not found at ${DB_PATH}`, fix: "cc-claw setup" });
|
|
12528
12850
|
}
|
|
12529
|
-
if (
|
|
12851
|
+
if (existsSync20(ENV_PATH)) {
|
|
12530
12852
|
checks.push({ name: "Environment", status: "ok", message: `.env loaded` });
|
|
12531
12853
|
} else {
|
|
12532
12854
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
@@ -12566,7 +12888,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
12566
12888
|
checks.push({ name: "Daemon", status: "warning", message: "could not probe" });
|
|
12567
12889
|
}
|
|
12568
12890
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
12569
|
-
if (
|
|
12891
|
+
if (existsSync20(tokenPath)) {
|
|
12570
12892
|
try {
|
|
12571
12893
|
accessSync(tokenPath, constants.R_OK);
|
|
12572
12894
|
checks.push({ name: "API token", status: "ok", message: "token file readable" });
|
|
@@ -12591,7 +12913,7 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
12591
12913
|
}
|
|
12592
12914
|
} catch {
|
|
12593
12915
|
}
|
|
12594
|
-
if (
|
|
12916
|
+
if (existsSync20(ERROR_LOG_PATH)) {
|
|
12595
12917
|
try {
|
|
12596
12918
|
const { readFileSync: readFileSync17 } = await import("fs");
|
|
12597
12919
|
const logContent = readFileSync17(ERROR_LOG_PATH, "utf-8");
|
|
@@ -12710,10 +13032,10 @@ var logs_exports = {};
|
|
|
12710
13032
|
__export(logs_exports, {
|
|
12711
13033
|
logsCommand: () => logsCommand
|
|
12712
13034
|
});
|
|
12713
|
-
import { existsSync as
|
|
13035
|
+
import { existsSync as existsSync21, readFileSync as readFileSync13, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
12714
13036
|
async function logsCommand(opts) {
|
|
12715
13037
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
12716
|
-
if (!
|
|
13038
|
+
if (!existsSync21(logFile)) {
|
|
12717
13039
|
outputError("LOG_NOT_FOUND", `Log file not found: ${logFile}`);
|
|
12718
13040
|
process.exit(1);
|
|
12719
13041
|
}
|
|
@@ -12761,12 +13083,12 @@ __export(backend_exports, {
|
|
|
12761
13083
|
backendList: () => backendList,
|
|
12762
13084
|
backendSet: () => backendSet
|
|
12763
13085
|
});
|
|
12764
|
-
import { existsSync as
|
|
13086
|
+
import { existsSync as existsSync22 } from "fs";
|
|
12765
13087
|
async function backendList(globalOpts) {
|
|
12766
13088
|
const { getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
12767
13089
|
const chatId = resolveChatId(globalOpts);
|
|
12768
13090
|
let activeBackend = null;
|
|
12769
|
-
if (
|
|
13091
|
+
if (existsSync22(DB_PATH)) {
|
|
12770
13092
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
12771
13093
|
const readDb = openDatabaseReadOnly2();
|
|
12772
13094
|
try {
|
|
@@ -12797,7 +13119,7 @@ async function backendList(globalOpts) {
|
|
|
12797
13119
|
}
|
|
12798
13120
|
async function backendGet(globalOpts) {
|
|
12799
13121
|
const chatId = resolveChatId(globalOpts);
|
|
12800
|
-
if (!
|
|
13122
|
+
if (!existsSync22(DB_PATH)) {
|
|
12801
13123
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
12802
13124
|
process.exit(1);
|
|
12803
13125
|
}
|
|
@@ -12841,13 +13163,13 @@ __export(model_exports, {
|
|
|
12841
13163
|
modelList: () => modelList,
|
|
12842
13164
|
modelSet: () => modelSet
|
|
12843
13165
|
});
|
|
12844
|
-
import { existsSync as
|
|
13166
|
+
import { existsSync as existsSync23 } from "fs";
|
|
12845
13167
|
async function modelList(globalOpts) {
|
|
12846
13168
|
const chatId = resolveChatId(globalOpts);
|
|
12847
13169
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
12848
13170
|
const { getAdapter: getAdapter2, getAllAdapters: getAllAdapters2 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
|
|
12849
13171
|
let backendId = "claude";
|
|
12850
|
-
if (
|
|
13172
|
+
if (existsSync23(DB_PATH)) {
|
|
12851
13173
|
const readDb = openDatabaseReadOnly2();
|
|
12852
13174
|
try {
|
|
12853
13175
|
const row = readDb.prepare("SELECT backend FROM chat_backend WHERE chat_id = ?").get(chatId);
|
|
@@ -12880,7 +13202,7 @@ async function modelList(globalOpts) {
|
|
|
12880
13202
|
}
|
|
12881
13203
|
async function modelGet(globalOpts) {
|
|
12882
13204
|
const chatId = resolveChatId(globalOpts);
|
|
12883
|
-
if (!
|
|
13205
|
+
if (!existsSync23(DB_PATH)) {
|
|
12884
13206
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12885
13207
|
process.exit(1);
|
|
12886
13208
|
}
|
|
@@ -12924,9 +13246,9 @@ __export(memory_exports, {
|
|
|
12924
13246
|
memoryList: () => memoryList,
|
|
12925
13247
|
memorySearch: () => memorySearch
|
|
12926
13248
|
});
|
|
12927
|
-
import { existsSync as
|
|
13249
|
+
import { existsSync as existsSync24 } from "fs";
|
|
12928
13250
|
async function memoryList(globalOpts) {
|
|
12929
|
-
if (!
|
|
13251
|
+
if (!existsSync24(DB_PATH)) {
|
|
12930
13252
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
12931
13253
|
process.exit(1);
|
|
12932
13254
|
}
|
|
@@ -12950,7 +13272,7 @@ async function memoryList(globalOpts) {
|
|
|
12950
13272
|
});
|
|
12951
13273
|
}
|
|
12952
13274
|
async function memorySearch(globalOpts, query) {
|
|
12953
|
-
if (!
|
|
13275
|
+
if (!existsSync24(DB_PATH)) {
|
|
12954
13276
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12955
13277
|
process.exit(1);
|
|
12956
13278
|
}
|
|
@@ -12972,7 +13294,7 @@ async function memorySearch(globalOpts, query) {
|
|
|
12972
13294
|
});
|
|
12973
13295
|
}
|
|
12974
13296
|
async function memoryHistory(globalOpts, opts) {
|
|
12975
|
-
if (!
|
|
13297
|
+
if (!existsSync24(DB_PATH)) {
|
|
12976
13298
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
12977
13299
|
process.exit(1);
|
|
12978
13300
|
}
|
|
@@ -13020,7 +13342,17 @@ __export(cron_exports2, {
|
|
|
13020
13342
|
cronList: () => cronList,
|
|
13021
13343
|
cronRuns: () => cronRuns
|
|
13022
13344
|
});
|
|
13023
|
-
import { existsSync as
|
|
13345
|
+
import { existsSync as existsSync25 } from "fs";
|
|
13346
|
+
function parseFallbacks(raw) {
|
|
13347
|
+
return raw.slice(0, 3).map((f) => {
|
|
13348
|
+
const [backend2, ...rest] = f.split(":");
|
|
13349
|
+
const model2 = rest.join(":");
|
|
13350
|
+
if (!backend2 || !model2) {
|
|
13351
|
+
throw new Error(`Invalid fallback format "${f}" \u2014 expected backend:model (e.g. gemini:gemini-3-flash-preview)`);
|
|
13352
|
+
}
|
|
13353
|
+
return { backend: backend2, model: model2 };
|
|
13354
|
+
});
|
|
13355
|
+
}
|
|
13024
13356
|
function parseAndValidateTimeout(raw) {
|
|
13025
13357
|
if (!raw) return null;
|
|
13026
13358
|
const val = parseInt(raw, 10);
|
|
@@ -13031,7 +13363,7 @@ function parseAndValidateTimeout(raw) {
|
|
|
13031
13363
|
return val;
|
|
13032
13364
|
}
|
|
13033
13365
|
async function cronList(globalOpts) {
|
|
13034
|
-
if (!
|
|
13366
|
+
if (!existsSync25(DB_PATH)) {
|
|
13035
13367
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13036
13368
|
process.exit(1);
|
|
13037
13369
|
}
|
|
@@ -13052,15 +13384,24 @@ async function cronList(globalOpts) {
|
|
|
13052
13384
|
lines.push(` ${statusDot(status)} #${j.id} [${status}] ${schedule2}${tz}`);
|
|
13053
13385
|
lines.push(` ${j.description}`);
|
|
13054
13386
|
if (j.backend) lines.push(` Backend: ${j.backend}${j.model ? ` / ${j.model}` : ""}`);
|
|
13387
|
+
if (j.fallbacks) {
|
|
13388
|
+
try {
|
|
13389
|
+
const fb = JSON.parse(j.fallbacks);
|
|
13390
|
+
if (Array.isArray(fb) && fb.length > 0) {
|
|
13391
|
+
lines.push(` Fallbacks: ${fb.map((f) => `${f.backend}:${f.model}`).join(" \u2192 ")}`);
|
|
13392
|
+
}
|
|
13393
|
+
} catch {
|
|
13394
|
+
}
|
|
13395
|
+
}
|
|
13055
13396
|
if (j.timeout) lines.push(` Timeout: ${j.timeout}s`);
|
|
13056
|
-
if (j.next_run_at) lines.push(` Next: ${muted(j.next_run_at)}`);
|
|
13397
|
+
if (j.next_run_at) lines.push(` Next: ${muted(formatLocalDateTime(j.next_run_at))}`);
|
|
13057
13398
|
lines.push("");
|
|
13058
13399
|
}
|
|
13059
13400
|
return lines.join("\n");
|
|
13060
13401
|
});
|
|
13061
13402
|
}
|
|
13062
13403
|
async function cronHealth(globalOpts) {
|
|
13063
|
-
if (!
|
|
13404
|
+
if (!existsSync25(DB_PATH)) {
|
|
13064
13405
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13065
13406
|
process.exit(1);
|
|
13066
13407
|
}
|
|
@@ -13114,6 +13455,7 @@ async function cronCreate(globalOpts, opts) {
|
|
|
13114
13455
|
everyMs = unit.startsWith("h") ? num * 36e5 : unit.startsWith("m") ? num * 6e4 : num * 1e3;
|
|
13115
13456
|
}
|
|
13116
13457
|
}
|
|
13458
|
+
const fallbacks = opts.fallback?.length ? parseFallbacks(opts.fallback) : void 0;
|
|
13117
13459
|
const job = insertJob2({
|
|
13118
13460
|
scheduleType: schedType,
|
|
13119
13461
|
cron: opts.cron ?? null,
|
|
@@ -13125,6 +13467,7 @@ async function cronCreate(globalOpts, opts) {
|
|
|
13125
13467
|
model: opts.model ?? null,
|
|
13126
13468
|
thinking: opts.thinking ?? null,
|
|
13127
13469
|
timeout,
|
|
13470
|
+
fallbacks,
|
|
13128
13471
|
sessionType: opts.sessionType ?? "isolated",
|
|
13129
13472
|
deliveryMode: opts.delivery ?? "announce",
|
|
13130
13473
|
channel: opts.channel ?? null,
|
|
@@ -13209,6 +13552,11 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
13209
13552
|
updates.push("timezone = ?");
|
|
13210
13553
|
values.push(opts.timezone);
|
|
13211
13554
|
}
|
|
13555
|
+
if (opts.fallback?.length) {
|
|
13556
|
+
const fallbacks = parseFallbacks(opts.fallback);
|
|
13557
|
+
updates.push("fallbacks = ?");
|
|
13558
|
+
values.push(JSON.stringify(fallbacks));
|
|
13559
|
+
}
|
|
13212
13560
|
if (updates.length === 0) {
|
|
13213
13561
|
outputError("NO_CHANGES", "No fields to update. Specify fields with flags (e.g. --description, --cron).");
|
|
13214
13562
|
process.exit(1);
|
|
@@ -13224,7 +13572,7 @@ async function cronEdit(globalOpts, id, opts) {
|
|
|
13224
13572
|
}
|
|
13225
13573
|
}
|
|
13226
13574
|
async function cronRuns(globalOpts, jobId, opts) {
|
|
13227
|
-
if (!
|
|
13575
|
+
if (!existsSync25(DB_PATH)) {
|
|
13228
13576
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13229
13577
|
process.exit(1);
|
|
13230
13578
|
}
|
|
@@ -13243,7 +13591,7 @@ async function cronRuns(globalOpts, jobId, opts) {
|
|
|
13243
13591
|
const lines = ["", divider("Run History"), ""];
|
|
13244
13592
|
for (const r of list) {
|
|
13245
13593
|
const duration = r.duration_ms ? ` (${(r.duration_ms / 1e3).toFixed(1)}s)` : "";
|
|
13246
|
-
lines.push(` #${r.job_id} [${r.status}] ${r.started_at}${duration}`);
|
|
13594
|
+
lines.push(` #${r.job_id} [${r.status}] ${formatLocalDateTime(r.started_at)}${duration}`);
|
|
13247
13595
|
if (r.error) lines.push(` Error: ${r.error.slice(0, 100)}`);
|
|
13248
13596
|
lines.push("");
|
|
13249
13597
|
}
|
|
@@ -13257,6 +13605,7 @@ var init_cron2 = __esm({
|
|
|
13257
13605
|
init_paths();
|
|
13258
13606
|
init_resolve_chat();
|
|
13259
13607
|
init_types2();
|
|
13608
|
+
init_format_time();
|
|
13260
13609
|
}
|
|
13261
13610
|
});
|
|
13262
13611
|
|
|
@@ -13270,9 +13619,9 @@ __export(agents_exports, {
|
|
|
13270
13619
|
runnersList: () => runnersList,
|
|
13271
13620
|
tasksList: () => tasksList
|
|
13272
13621
|
});
|
|
13273
|
-
import { existsSync as
|
|
13622
|
+
import { existsSync as existsSync26 } from "fs";
|
|
13274
13623
|
async function agentsList(globalOpts) {
|
|
13275
|
-
if (!
|
|
13624
|
+
if (!existsSync26(DB_PATH)) {
|
|
13276
13625
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13277
13626
|
process.exit(1);
|
|
13278
13627
|
}
|
|
@@ -13303,7 +13652,7 @@ async function agentsList(globalOpts) {
|
|
|
13303
13652
|
});
|
|
13304
13653
|
}
|
|
13305
13654
|
async function tasksList(globalOpts) {
|
|
13306
|
-
if (!
|
|
13655
|
+
if (!existsSync26(DB_PATH)) {
|
|
13307
13656
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13308
13657
|
process.exit(1);
|
|
13309
13658
|
}
|
|
@@ -13431,18 +13780,18 @@ __export(db_exports, {
|
|
|
13431
13780
|
dbPath: () => dbPath,
|
|
13432
13781
|
dbStats: () => dbStats
|
|
13433
13782
|
});
|
|
13434
|
-
import { existsSync as
|
|
13783
|
+
import { existsSync as existsSync27, statSync as statSync5, copyFileSync, mkdirSync as mkdirSync9 } from "fs";
|
|
13435
13784
|
import { dirname as dirname5 } from "path";
|
|
13436
13785
|
async function dbStats(globalOpts) {
|
|
13437
|
-
if (!
|
|
13786
|
+
if (!existsSync27(DB_PATH)) {
|
|
13438
13787
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
13439
13788
|
process.exit(1);
|
|
13440
13789
|
}
|
|
13441
13790
|
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store4(), store_exports3));
|
|
13442
13791
|
const readDb = openDatabaseReadOnly2();
|
|
13443
|
-
const mainSize =
|
|
13792
|
+
const mainSize = statSync5(DB_PATH).size;
|
|
13444
13793
|
const walPath = DB_PATH + "-wal";
|
|
13445
|
-
const walSize =
|
|
13794
|
+
const walSize = existsSync27(walPath) ? statSync5(walPath).size : 0;
|
|
13446
13795
|
const tableNames = readDb.prepare(
|
|
13447
13796
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name"
|
|
13448
13797
|
).all();
|
|
@@ -13476,17 +13825,17 @@ async function dbPath(globalOpts) {
|
|
|
13476
13825
|
output({ path: DB_PATH }, (d) => d.path);
|
|
13477
13826
|
}
|
|
13478
13827
|
async function dbBackup(globalOpts, destPath) {
|
|
13479
|
-
if (!
|
|
13828
|
+
if (!existsSync27(DB_PATH)) {
|
|
13480
13829
|
outputError("DB_NOT_FOUND", `Database not found at ${DB_PATH}`);
|
|
13481
13830
|
process.exit(1);
|
|
13482
13831
|
}
|
|
13483
13832
|
const dest = destPath ?? `${DB_PATH}.backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
13484
13833
|
try {
|
|
13485
|
-
|
|
13834
|
+
mkdirSync9(dirname5(dest), { recursive: true });
|
|
13486
13835
|
copyFileSync(DB_PATH, dest);
|
|
13487
13836
|
const walPath = DB_PATH + "-wal";
|
|
13488
|
-
if (
|
|
13489
|
-
output({ path: dest, sizeBytes:
|
|
13837
|
+
if (existsSync27(walPath)) copyFileSync(walPath, dest + "-wal");
|
|
13838
|
+
output({ path: dest, sizeBytes: statSync5(dest).size }, (d) => {
|
|
13490
13839
|
const b = d;
|
|
13491
13840
|
return `
|
|
13492
13841
|
${success("Backup created:")} ${b.path} (${(b.sizeBytes / 1024).toFixed(0)}KB)
|
|
@@ -13514,9 +13863,9 @@ __export(usage_exports, {
|
|
|
13514
13863
|
usageCost: () => usageCost,
|
|
13515
13864
|
usageTokens: () => usageTokens
|
|
13516
13865
|
});
|
|
13517
|
-
import { existsSync as
|
|
13866
|
+
import { existsSync as existsSync28 } from "fs";
|
|
13518
13867
|
function ensureDb() {
|
|
13519
|
-
if (!
|
|
13868
|
+
if (!existsSync28(DB_PATH)) {
|
|
13520
13869
|
outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
|
|
13521
13870
|
process.exit(1);
|
|
13522
13871
|
}
|
|
@@ -13706,9 +14055,9 @@ __export(config_exports, {
|
|
|
13706
14055
|
configList: () => configList,
|
|
13707
14056
|
configSet: () => configSet
|
|
13708
14057
|
});
|
|
13709
|
-
import { existsSync as
|
|
14058
|
+
import { existsSync as existsSync29, readFileSync as readFileSync14 } from "fs";
|
|
13710
14059
|
async function configList(globalOpts) {
|
|
13711
|
-
if (!
|
|
14060
|
+
if (!existsSync29(DB_PATH)) {
|
|
13712
14061
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13713
14062
|
process.exit(1);
|
|
13714
14063
|
}
|
|
@@ -13742,7 +14091,7 @@ async function configGet(globalOpts, key) {
|
|
|
13742
14091
|
outputError("INVALID_KEY", `Unknown config key "${key}". Valid keys: ${RUNTIME_KEYS.join(", ")}`);
|
|
13743
14092
|
process.exit(1);
|
|
13744
14093
|
}
|
|
13745
|
-
if (!
|
|
14094
|
+
if (!existsSync29(DB_PATH)) {
|
|
13746
14095
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13747
14096
|
process.exit(1);
|
|
13748
14097
|
}
|
|
@@ -13788,7 +14137,7 @@ async function configSet(globalOpts, key, value) {
|
|
|
13788
14137
|
}
|
|
13789
14138
|
}
|
|
13790
14139
|
async function configEnv(_globalOpts) {
|
|
13791
|
-
if (!
|
|
14140
|
+
if (!existsSync29(ENV_PATH)) {
|
|
13792
14141
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
13793
14142
|
process.exit(1);
|
|
13794
14143
|
}
|
|
@@ -13841,9 +14190,9 @@ __export(session_exports, {
|
|
|
13841
14190
|
sessionGet: () => sessionGet,
|
|
13842
14191
|
sessionNew: () => sessionNew
|
|
13843
14192
|
});
|
|
13844
|
-
import { existsSync as
|
|
14193
|
+
import { existsSync as existsSync30 } from "fs";
|
|
13845
14194
|
async function sessionGet(globalOpts) {
|
|
13846
|
-
if (!
|
|
14195
|
+
if (!existsSync30(DB_PATH)) {
|
|
13847
14196
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13848
14197
|
process.exit(1);
|
|
13849
14198
|
}
|
|
@@ -13904,9 +14253,9 @@ __export(permissions_exports, {
|
|
|
13904
14253
|
verboseGet: () => verboseGet,
|
|
13905
14254
|
verboseSet: () => verboseSet
|
|
13906
14255
|
});
|
|
13907
|
-
import { existsSync as
|
|
14256
|
+
import { existsSync as existsSync31 } from "fs";
|
|
13908
14257
|
function ensureDb2() {
|
|
13909
|
-
if (!
|
|
14258
|
+
if (!existsSync31(DB_PATH)) {
|
|
13910
14259
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
13911
14260
|
process.exit(1);
|
|
13912
14261
|
}
|
|
@@ -14052,9 +14401,9 @@ __export(cwd_exports, {
|
|
|
14052
14401
|
cwdGet: () => cwdGet,
|
|
14053
14402
|
cwdSet: () => cwdSet
|
|
14054
14403
|
});
|
|
14055
|
-
import { existsSync as
|
|
14404
|
+
import { existsSync as existsSync32 } from "fs";
|
|
14056
14405
|
async function cwdGet(globalOpts) {
|
|
14057
|
-
if (!
|
|
14406
|
+
if (!existsSync32(DB_PATH)) {
|
|
14058
14407
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
14059
14408
|
process.exit(1);
|
|
14060
14409
|
}
|
|
@@ -14116,9 +14465,9 @@ __export(voice_exports, {
|
|
|
14116
14465
|
voiceGet: () => voiceGet,
|
|
14117
14466
|
voiceSet: () => voiceSet
|
|
14118
14467
|
});
|
|
14119
|
-
import { existsSync as
|
|
14468
|
+
import { existsSync as existsSync33 } from "fs";
|
|
14120
14469
|
async function voiceGet(globalOpts) {
|
|
14121
|
-
if (!
|
|
14470
|
+
if (!existsSync33(DB_PATH)) {
|
|
14122
14471
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
14123
14472
|
process.exit(1);
|
|
14124
14473
|
}
|
|
@@ -14167,9 +14516,9 @@ __export(heartbeat_exports, {
|
|
|
14167
14516
|
heartbeatGet: () => heartbeatGet,
|
|
14168
14517
|
heartbeatSet: () => heartbeatSet
|
|
14169
14518
|
});
|
|
14170
|
-
import { existsSync as
|
|
14519
|
+
import { existsSync as existsSync34 } from "fs";
|
|
14171
14520
|
async function heartbeatGet(globalOpts) {
|
|
14172
|
-
if (!
|
|
14521
|
+
if (!existsSync34(DB_PATH)) {
|
|
14173
14522
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
14174
14523
|
process.exit(1);
|
|
14175
14524
|
}
|
|
@@ -14279,9 +14628,9 @@ __export(chats_exports, {
|
|
|
14279
14628
|
chatsList: () => chatsList,
|
|
14280
14629
|
chatsRemoveAlias: () => chatsRemoveAlias
|
|
14281
14630
|
});
|
|
14282
|
-
import { existsSync as
|
|
14631
|
+
import { existsSync as existsSync35 } from "fs";
|
|
14283
14632
|
async function chatsList(_globalOpts) {
|
|
14284
|
-
if (!
|
|
14633
|
+
if (!existsSync35(DB_PATH)) {
|
|
14285
14634
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
14286
14635
|
process.exit(1);
|
|
14287
14636
|
}
|
|
@@ -14409,9 +14758,9 @@ var mcps_exports = {};
|
|
|
14409
14758
|
__export(mcps_exports, {
|
|
14410
14759
|
mcpsList: () => mcpsList
|
|
14411
14760
|
});
|
|
14412
|
-
import { existsSync as
|
|
14761
|
+
import { existsSync as existsSync36 } from "fs";
|
|
14413
14762
|
async function mcpsList(_globalOpts) {
|
|
14414
|
-
if (!
|
|
14763
|
+
if (!existsSync36(DB_PATH)) {
|
|
14415
14764
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
14416
14765
|
process.exit(1);
|
|
14417
14766
|
}
|
|
@@ -14448,11 +14797,11 @@ __export(chat_exports, {
|
|
|
14448
14797
|
chatSend: () => chatSend
|
|
14449
14798
|
});
|
|
14450
14799
|
import { request as httpRequest2 } from "http";
|
|
14451
|
-
import { readFileSync as readFileSync15, existsSync as
|
|
14800
|
+
import { readFileSync as readFileSync15, existsSync as existsSync37 } from "fs";
|
|
14452
14801
|
function getToken2() {
|
|
14453
14802
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
14454
14803
|
try {
|
|
14455
|
-
if (
|
|
14804
|
+
if (existsSync37(TOKEN_PATH2)) return readFileSync15(TOKEN_PATH2, "utf-8").trim();
|
|
14456
14805
|
} catch {
|
|
14457
14806
|
}
|
|
14458
14807
|
return null;
|
|
@@ -14878,10 +15227,10 @@ var init_completion = __esm({
|
|
|
14878
15227
|
|
|
14879
15228
|
// src/setup.ts
|
|
14880
15229
|
var setup_exports = {};
|
|
14881
|
-
import { existsSync as
|
|
15230
|
+
import { existsSync as existsSync38, writeFileSync as writeFileSync8, readFileSync as readFileSync16, copyFileSync as copyFileSync2, mkdirSync as mkdirSync10, statSync as statSync6 } from "fs";
|
|
14882
15231
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
14883
15232
|
import { createInterface as createInterface5 } from "readline";
|
|
14884
|
-
import { join as
|
|
15233
|
+
import { join as join19 } from "path";
|
|
14885
15234
|
function divider2() {
|
|
14886
15235
|
console.log(dim("\u2500".repeat(55)));
|
|
14887
15236
|
}
|
|
@@ -14955,10 +15304,10 @@ async function setup() {
|
|
|
14955
15304
|
}
|
|
14956
15305
|
console.log("");
|
|
14957
15306
|
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
14958
|
-
if (!
|
|
15307
|
+
if (!existsSync38(dir)) mkdirSync10(dir, { recursive: true });
|
|
14959
15308
|
}
|
|
14960
15309
|
const env = {};
|
|
14961
|
-
const envSource =
|
|
15310
|
+
const envSource = existsSync38(ENV_PATH) ? ENV_PATH : existsSync38(".env") ? ".env" : null;
|
|
14962
15311
|
if (envSource) {
|
|
14963
15312
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
14964
15313
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
@@ -14968,9 +15317,9 @@ async function setup() {
|
|
|
14968
15317
|
if (match) env[match[1].trim()] = match[2].trim();
|
|
14969
15318
|
}
|
|
14970
15319
|
}
|
|
14971
|
-
const cwdDb =
|
|
14972
|
-
if (
|
|
14973
|
-
const { size } =
|
|
15320
|
+
const cwdDb = join19(process.cwd(), "cc-claw.db");
|
|
15321
|
+
if (existsSync38(cwdDb) && !existsSync38(DB_PATH)) {
|
|
15322
|
+
const { size } = statSync6(cwdDb);
|
|
14974
15323
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
14975
15324
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
14976
15325
|
if (migrate) {
|
|
@@ -15163,7 +15512,7 @@ async function setup() {
|
|
|
15163
15512
|
envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
|
|
15164
15513
|
}
|
|
15165
15514
|
const envContent = envLines.join("\n") + "\n";
|
|
15166
|
-
|
|
15515
|
+
writeFileSync8(ENV_PATH, envContent, { mode: 384 });
|
|
15167
15516
|
console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
|
|
15168
15517
|
header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
|
|
15169
15518
|
console.log(" CC-Claw can run automatically in the background, starting");
|
|
@@ -15332,7 +15681,7 @@ function registerCronCommands(cmd) {
|
|
|
15332
15681
|
const { cronList: cronList2 } = await Promise.resolve().then(() => (init_cron2(), cron_exports2));
|
|
15333
15682
|
await cronList2(program.opts());
|
|
15334
15683
|
});
|
|
15335
|
-
cmd.command("create").description("Create a scheduled job").requiredOption("--description <text>", "Job description").option("--prompt <text>", "Agent prompt (defaults to description)").option("--cron <expr>", "Cron expression (e.g. '0 9 * * *')").option("--at <iso8601>", "One-shot time").option("--every <interval>", "Repeat interval (e.g. 30m, 1h)").option("--backend <name>", "Backend for this job").option("--model <name>", "Model for this job").option("--thinking <level>", "Thinking level").option("--timeout <seconds>", "Job timeout in seconds (30-3600)").option("--timezone <tz>", "IANA timezone", "UTC").option("--session-type <type>", "Session type (isolated/main)", "isolated").option("--delivery <mode>", "Delivery mode (announce/webhook/none)", "announce").option("--channel <name>", "Delivery channel").option("--target <id>", "Delivery target").option("--cwd <path>", "Working directory").action(async (opts) => {
|
|
15684
|
+
cmd.command("create").description("Create a scheduled job").requiredOption("--description <text>", "Job description").option("--prompt <text>", "Agent prompt (defaults to description)").option("--cron <expr>", "Cron expression (e.g. '0 9 * * *')").option("--at <iso8601>", "One-shot time").option("--every <interval>", "Repeat interval (e.g. 30m, 1h)").option("--backend <name>", "Backend for this job").option("--model <name>", "Model for this job").option("--thinking <level>", "Thinking level").option("--timeout <seconds>", "Job timeout in seconds (30-3600)").option("--fallback <backend:model>", "Fallback backend:model (repeatable, max 3)", (val, prev) => [...prev, val], []).option("--timezone <tz>", "IANA timezone", "UTC").option("--session-type <type>", "Session type (isolated/main)", "isolated").option("--delivery <mode>", "Delivery mode (announce/webhook/none)", "announce").option("--channel <name>", "Delivery channel").option("--target <id>", "Delivery target").option("--cwd <path>", "Working directory").action(async (opts) => {
|
|
15336
15685
|
const { cronCreate: cronCreate2 } = await Promise.resolve().then(() => (init_cron2(), cron_exports2));
|
|
15337
15686
|
await cronCreate2(program.opts(), opts);
|
|
15338
15687
|
});
|
|
@@ -15352,7 +15701,7 @@ function registerCronCommands(cmd) {
|
|
|
15352
15701
|
const { cronAction: cronAction2 } = await Promise.resolve().then(() => (init_cron2(), cron_exports2));
|
|
15353
15702
|
await cronAction2(program.opts(), "run", id);
|
|
15354
15703
|
});
|
|
15355
|
-
cmd.command("edit <id>").description("Edit a job (same flags as create)").option("--description <text>").option("--cron <expr>").option("--at <iso8601>").option("--every <interval>").option("--backend <name>").option("--model <name>").option("--thinking <level>").option("--timeout <seconds>", "Job timeout in seconds (30-3600)").option("--timezone <tz>").action(async (id, opts) => {
|
|
15704
|
+
cmd.command("edit <id>").description("Edit a job (same flags as create)").option("--description <text>").option("--cron <expr>").option("--at <iso8601>").option("--every <interval>").option("--backend <name>").option("--model <name>").option("--thinking <level>").option("--timeout <seconds>", "Job timeout in seconds (30-3600)").option("--fallback <backend:model>", "Fallback backend:model (repeatable, max 3)", (val, prev) => [...prev, val], []).option("--timezone <tz>").action(async (id, opts) => {
|
|
15356
15705
|
const { cronEdit: cronEdit2 } = await Promise.resolve().then(() => (init_cron2(), cron_exports2));
|
|
15357
15706
|
await cronEdit2(program.opts(), id, opts);
|
|
15358
15707
|
});
|