cc-claw 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +548 -54
- 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.
|
|
51
|
+
VERSION = true ? "0.3.0" : (() => {
|
|
52
52
|
try {
|
|
53
53
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
54
54
|
} catch {
|
|
@@ -864,10 +864,13 @@ __export(store_exports2, {
|
|
|
864
864
|
clearThinkingLevel: () => clearThinkingLevel,
|
|
865
865
|
clearUsage: () => clearUsage,
|
|
866
866
|
completeJobRun: () => completeJobRun,
|
|
867
|
+
deleteBookmark: () => deleteBookmark,
|
|
868
|
+
findBookmarksByPrefix: () => findBookmarksByPrefix,
|
|
867
869
|
forgetMemory: () => forgetMemory,
|
|
868
870
|
getActiveJobs: () => getActiveJobs,
|
|
869
871
|
getActiveWatches: () => getActiveWatches,
|
|
870
872
|
getAllBackendLimits: () => getAllBackendLimits,
|
|
873
|
+
getAllBookmarks: () => getAllBookmarks,
|
|
871
874
|
getAllChatAliases: () => getAllChatAliases,
|
|
872
875
|
getAllJobs: () => getAllJobs,
|
|
873
876
|
getAllMemoriesWithEmbeddings: () => getAllMemoriesWithEmbeddings,
|
|
@@ -876,6 +879,7 @@ __export(store_exports2, {
|
|
|
876
879
|
getBackend: () => getBackend,
|
|
877
880
|
getBackendLimit: () => getBackendLimit,
|
|
878
881
|
getBackendUsageInWindow: () => getBackendUsageInWindow,
|
|
882
|
+
getBookmark: () => getBookmark,
|
|
879
883
|
getChatIdByAlias: () => getChatIdByAlias,
|
|
880
884
|
getChatUsageByModel: () => getChatUsageByModel,
|
|
881
885
|
getCwd: () => getCwd,
|
|
@@ -887,6 +891,7 @@ __export(store_exports2, {
|
|
|
887
891
|
getMemoriesWithoutEmbeddings: () => getMemoriesWithoutEmbeddings,
|
|
888
892
|
getMode: () => getMode,
|
|
889
893
|
getModel: () => getModel,
|
|
894
|
+
getRecentBookmarks: () => getRecentBookmarks,
|
|
890
895
|
getRecentMemories: () => getRecentMemories,
|
|
891
896
|
getSessionId: () => getSessionId,
|
|
892
897
|
getSessionStartedAt: () => getSessionStartedAt,
|
|
@@ -929,13 +934,15 @@ __export(store_exports2, {
|
|
|
929
934
|
setThinkingLevel: () => setThinkingLevel,
|
|
930
935
|
setVerboseLevel: () => setVerboseLevel,
|
|
931
936
|
toggleTool: () => toggleTool,
|
|
937
|
+
touchBookmark: () => touchBookmark,
|
|
932
938
|
updateHeartbeatTimestamps: () => updateHeartbeatTimestamps,
|
|
933
939
|
updateJob: () => updateJob,
|
|
934
940
|
updateJobEnabled: () => updateJobEnabled,
|
|
935
941
|
updateJobLastRun: () => updateJobLastRun,
|
|
936
942
|
updateJobNextRun: () => updateJobNextRun,
|
|
937
943
|
updateMemoryEmbedding: () => updateMemoryEmbedding,
|
|
938
|
-
updateSessionSummaryEmbedding: () => updateSessionSummaryEmbedding
|
|
944
|
+
updateSessionSummaryEmbedding: () => updateSessionSummaryEmbedding,
|
|
945
|
+
upsertBookmark: () => upsertBookmark
|
|
939
946
|
});
|
|
940
947
|
import Database from "better-sqlite3";
|
|
941
948
|
function openDatabaseReadOnly() {
|
|
@@ -1297,6 +1304,16 @@ function initDatabase() {
|
|
|
1297
1304
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1298
1305
|
);
|
|
1299
1306
|
`);
|
|
1307
|
+
db.exec(`
|
|
1308
|
+
CREATE TABLE IF NOT EXISTS cwd_bookmarks (
|
|
1309
|
+
chatId TEXT NOT NULL,
|
|
1310
|
+
alias TEXT NOT NULL,
|
|
1311
|
+
path TEXT NOT NULL,
|
|
1312
|
+
manual INTEGER NOT NULL DEFAULT 0,
|
|
1313
|
+
lastUsed INTEGER NOT NULL DEFAULT 0,
|
|
1314
|
+
PRIMARY KEY (chatId, alias)
|
|
1315
|
+
)
|
|
1316
|
+
`);
|
|
1300
1317
|
try {
|
|
1301
1318
|
db.exec("ALTER TABLE memories ADD COLUMN embedding TEXT");
|
|
1302
1319
|
} catch {
|
|
@@ -1491,6 +1508,58 @@ function setCwd(chatId, cwd) {
|
|
|
1491
1508
|
function clearCwd(chatId) {
|
|
1492
1509
|
db.prepare("DELETE FROM chat_cwd WHERE chat_id = ?").run(chatId);
|
|
1493
1510
|
}
|
|
1511
|
+
function upsertBookmark(chatId, alias, path, manual) {
|
|
1512
|
+
const now = Date.now();
|
|
1513
|
+
const existing = db.prepare(
|
|
1514
|
+
"SELECT manual FROM cwd_bookmarks WHERE chatId = ? AND alias = ?"
|
|
1515
|
+
).get(chatId, alias);
|
|
1516
|
+
if (existing) {
|
|
1517
|
+
if (existing.manual === 1 && !manual) {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
db.prepare(
|
|
1521
|
+
"UPDATE cwd_bookmarks SET path = ?, manual = ?, lastUsed = ? WHERE chatId = ? AND alias = ?"
|
|
1522
|
+
).run(path, manual ? 1 : 0, now, chatId, alias);
|
|
1523
|
+
} else {
|
|
1524
|
+
db.prepare(
|
|
1525
|
+
"INSERT INTO cwd_bookmarks (chatId, alias, path, manual, lastUsed) VALUES (?, ?, ?, ?, ?)"
|
|
1526
|
+
).run(chatId, alias, path, manual ? 1 : 0, now);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
function touchBookmark(chatId, alias) {
|
|
1530
|
+
db.prepare(
|
|
1531
|
+
"UPDATE cwd_bookmarks SET lastUsed = ? WHERE chatId = ? AND alias = ?"
|
|
1532
|
+
).run(Date.now(), chatId, alias);
|
|
1533
|
+
}
|
|
1534
|
+
function getBookmark(chatId, alias) {
|
|
1535
|
+
const row = db.prepare(
|
|
1536
|
+
"SELECT alias, path, manual FROM cwd_bookmarks WHERE chatId = ? AND alias = ?"
|
|
1537
|
+
).get(chatId, alias);
|
|
1538
|
+
return row ? { alias: row.alias, path: row.path, manual: row.manual === 1 } : void 0;
|
|
1539
|
+
}
|
|
1540
|
+
function getRecentBookmarks(chatId, limit = 10) {
|
|
1541
|
+
return db.prepare(
|
|
1542
|
+
"SELECT alias, path FROM cwd_bookmarks WHERE chatId = ? ORDER BY lastUsed DESC LIMIT ?"
|
|
1543
|
+
).all(chatId, limit);
|
|
1544
|
+
}
|
|
1545
|
+
function findBookmarksByPrefix(chatId, prefix) {
|
|
1546
|
+
return db.prepare(
|
|
1547
|
+
"SELECT alias, path FROM cwd_bookmarks WHERE chatId = ? AND alias LIKE ? || '%'"
|
|
1548
|
+
).all(chatId, prefix);
|
|
1549
|
+
}
|
|
1550
|
+
function deleteBookmark(chatId, alias) {
|
|
1551
|
+
const result = db.prepare(
|
|
1552
|
+
"DELETE FROM cwd_bookmarks WHERE chatId = ? AND alias = ?"
|
|
1553
|
+
).run(chatId, alias);
|
|
1554
|
+
return result.changes > 0;
|
|
1555
|
+
}
|
|
1556
|
+
function getAllBookmarks(chatId) {
|
|
1557
|
+
return db.prepare(
|
|
1558
|
+
"SELECT alias, path, manual FROM cwd_bookmarks WHERE chatId = ? ORDER BY alias"
|
|
1559
|
+
).all(chatId).map(
|
|
1560
|
+
(r) => ({ alias: r.alias, path: r.path, manual: r.manual === 1 })
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1494
1563
|
function getModel(chatId) {
|
|
1495
1564
|
const row = db.prepare(
|
|
1496
1565
|
"SELECT model FROM chat_model WHERE chat_id = ?"
|
|
@@ -2199,13 +2268,13 @@ var init_claude = __esm({
|
|
|
2199
2268
|
displayName = "Claude";
|
|
2200
2269
|
availableModels = {
|
|
2201
2270
|
"claude-opus-4-6": {
|
|
2202
|
-
label: "Opus 4.6 \u2014 most capable",
|
|
2271
|
+
label: "Opus 4.6 \u2014 most capable, 1M context",
|
|
2203
2272
|
thinking: "adjustable",
|
|
2204
2273
|
thinkingLevels: ["auto", "off", "low", "medium", "high"],
|
|
2205
2274
|
defaultThinkingLevel: "medium"
|
|
2206
2275
|
},
|
|
2207
2276
|
"claude-sonnet-4-6": {
|
|
2208
|
-
label: "Sonnet 4.6 \u2014 balanced (default)",
|
|
2277
|
+
label: "Sonnet 4.6 \u2014 balanced (default), 1M context",
|
|
2209
2278
|
thinking: "adjustable",
|
|
2210
2279
|
thinkingLevels: ["auto", "off", "low", "medium", "high"],
|
|
2211
2280
|
defaultThinkingLevel: "medium"
|
|
@@ -2225,8 +2294,8 @@ var init_claude = __esm({
|
|
|
2225
2294
|
"claude-haiku-4-5": { in: 0.8, out: 4, cache: 0.08 }
|
|
2226
2295
|
};
|
|
2227
2296
|
contextWindow = {
|
|
2228
|
-
"claude-opus-4-6":
|
|
2229
|
-
"claude-sonnet-4-6":
|
|
2297
|
+
"claude-opus-4-6": 1e6,
|
|
2298
|
+
"claude-sonnet-4-6": 1e6,
|
|
2230
2299
|
"claude-haiku-4-5": 2e5
|
|
2231
2300
|
};
|
|
2232
2301
|
_resolvedPath = "";
|
|
@@ -8278,6 +8347,180 @@ var init_video = __esm({
|
|
|
8278
8347
|
}
|
|
8279
8348
|
});
|
|
8280
8349
|
|
|
8350
|
+
// src/shell/guard.ts
|
|
8351
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
8352
|
+
function isDestructive(command) {
|
|
8353
|
+
return DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(command));
|
|
8354
|
+
}
|
|
8355
|
+
function storePendingCommand(command, chatId, raw) {
|
|
8356
|
+
const id = randomUUID2().slice(0, 8);
|
|
8357
|
+
pendingCommands.set(id, { command, chatId, raw, createdAt: Date.now() });
|
|
8358
|
+
setTimeout(() => pendingCommands.delete(id), PENDING_TTL_MS);
|
|
8359
|
+
return id;
|
|
8360
|
+
}
|
|
8361
|
+
function getPendingCommand(id) {
|
|
8362
|
+
return pendingCommands.get(id);
|
|
8363
|
+
}
|
|
8364
|
+
function removePendingCommand(id) {
|
|
8365
|
+
return pendingCommands.delete(id);
|
|
8366
|
+
}
|
|
8367
|
+
var DESTRUCTIVE_PATTERNS, pendingCommands, PENDING_TTL_MS;
|
|
8368
|
+
var init_guard = __esm({
|
|
8369
|
+
"src/shell/guard.ts"() {
|
|
8370
|
+
"use strict";
|
|
8371
|
+
DESTRUCTIVE_PATTERNS = [
|
|
8372
|
+
/\brm\s+(-[a-zA-Z]*[fr][a-zA-Z]*|-[a-zA-Z]*[rf][a-zA-Z]*|--force|--recursive)\b/,
|
|
8373
|
+
/\brm\s+-[a-zA-Z]*\s+\/\s*$/,
|
|
8374
|
+
/\bmkfs\b/,
|
|
8375
|
+
/\bdd\b.*\bof=/,
|
|
8376
|
+
/\b(shutdown|reboot|halt|poweroff)\b/,
|
|
8377
|
+
/>\s*\/dev\/sd/,
|
|
8378
|
+
/\bchmod\s+-R\s+777\b/,
|
|
8379
|
+
/\bchown\s+-R\b/,
|
|
8380
|
+
/\bkillall\b/,
|
|
8381
|
+
/\blaunchctl\s+(unload|remove)\b/,
|
|
8382
|
+
/:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;?\s*:/
|
|
8383
|
+
];
|
|
8384
|
+
pendingCommands = /* @__PURE__ */ new Map();
|
|
8385
|
+
PENDING_TTL_MS = 5 * 60 * 1e3;
|
|
8386
|
+
}
|
|
8387
|
+
});
|
|
8388
|
+
|
|
8389
|
+
// src/shell/exec.ts
|
|
8390
|
+
import { execFile as execFile3 } from "child_process";
|
|
8391
|
+
function executeShell(command, cwd, timeoutMs = SHELL_TIMEOUT_MS) {
|
|
8392
|
+
return new Promise((resolve) => {
|
|
8393
|
+
execFile3(
|
|
8394
|
+
"/bin/zsh",
|
|
8395
|
+
["-c", command],
|
|
8396
|
+
{ cwd, timeout: timeoutMs, maxBuffer: 5 * 1024 * 1024 },
|
|
8397
|
+
(error3, stdout, stderr) => {
|
|
8398
|
+
const output2 = (stdout || "") + (stderr || "");
|
|
8399
|
+
if (error3 && "killed" in error3 && error3.killed) {
|
|
8400
|
+
resolve({
|
|
8401
|
+
output: `Command timed out after ${Math.round(timeoutMs / 1e3)} seconds`,
|
|
8402
|
+
exitCode: 124,
|
|
8403
|
+
timedOut: true
|
|
8404
|
+
});
|
|
8405
|
+
return;
|
|
8406
|
+
}
|
|
8407
|
+
const exitCode = error3 ? typeof error3.code === "number" ? error3.code : 1 : 0;
|
|
8408
|
+
resolve({
|
|
8409
|
+
output: output2,
|
|
8410
|
+
exitCode,
|
|
8411
|
+
timedOut: false
|
|
8412
|
+
});
|
|
8413
|
+
}
|
|
8414
|
+
);
|
|
8415
|
+
});
|
|
8416
|
+
}
|
|
8417
|
+
function formatCodeBlock(command, output2, exitCode) {
|
|
8418
|
+
return `<pre>$ ${command}
|
|
8419
|
+
${output2}
|
|
8420
|
+
[exit ${exitCode}]</pre>`;
|
|
8421
|
+
}
|
|
8422
|
+
function formatRaw(output2) {
|
|
8423
|
+
return output2.trim();
|
|
8424
|
+
}
|
|
8425
|
+
function shouldSendAsFile(output2) {
|
|
8426
|
+
return output2.length > OUTPUT_MAX_LENGTH;
|
|
8427
|
+
}
|
|
8428
|
+
var SHELL_TIMEOUT_MS, OUTPUT_MAX_LENGTH;
|
|
8429
|
+
var init_exec = __esm({
|
|
8430
|
+
"src/shell/exec.ts"() {
|
|
8431
|
+
"use strict";
|
|
8432
|
+
SHELL_TIMEOUT_MS = 6e4;
|
|
8433
|
+
OUTPUT_MAX_LENGTH = 4e3;
|
|
8434
|
+
}
|
|
8435
|
+
});
|
|
8436
|
+
|
|
8437
|
+
// src/shell/backend-cmd.ts
|
|
8438
|
+
import { execFile as execFile4 } from "child_process";
|
|
8439
|
+
function buildNativeResponse(backendId, command, output2) {
|
|
8440
|
+
const body = output2 || "(no output)";
|
|
8441
|
+
return `<pre>[${backendId}] ${command}
|
|
8442
|
+
${body}</pre>`;
|
|
8443
|
+
}
|
|
8444
|
+
function extractTextFromNdjson(raw) {
|
|
8445
|
+
const texts = [];
|
|
8446
|
+
for (const line of raw.split("\n")) {
|
|
8447
|
+
if (!line.trim()) continue;
|
|
8448
|
+
try {
|
|
8449
|
+
const obj = JSON.parse(line);
|
|
8450
|
+
if (obj.type === "result" && obj.result) {
|
|
8451
|
+
texts.push(typeof obj.result === "string" ? obj.result : JSON.stringify(obj.result));
|
|
8452
|
+
}
|
|
8453
|
+
if (obj.type === "assistant" && obj.message?.content) {
|
|
8454
|
+
for (const block of obj.message.content) {
|
|
8455
|
+
if (block.type === "text" && block.text) texts.push(block.text);
|
|
8456
|
+
}
|
|
8457
|
+
}
|
|
8458
|
+
if (obj.type === "text" && obj.text) {
|
|
8459
|
+
texts.push(obj.text);
|
|
8460
|
+
}
|
|
8461
|
+
} catch {
|
|
8462
|
+
}
|
|
8463
|
+
}
|
|
8464
|
+
return texts.join("\n").trim();
|
|
8465
|
+
}
|
|
8466
|
+
async function handleBackendCommand(command, chatId, channel) {
|
|
8467
|
+
let adapter;
|
|
8468
|
+
try {
|
|
8469
|
+
adapter = getAdapterForChat(chatId);
|
|
8470
|
+
} catch {
|
|
8471
|
+
await channel.sendText(chatId, "No backend set. Use /backend first.", "plain");
|
|
8472
|
+
return;
|
|
8473
|
+
}
|
|
8474
|
+
const sessionId = getSessionId(chatId);
|
|
8475
|
+
if (!sessionId) {
|
|
8476
|
+
await channel.sendText(chatId, "No active session. Start a conversation first.", "plain");
|
|
8477
|
+
return;
|
|
8478
|
+
}
|
|
8479
|
+
const cwd = getCwd(chatId) ?? `${process.env.HOME ?? "/tmp"}/.cc-claw/workspace`;
|
|
8480
|
+
try {
|
|
8481
|
+
const output2 = await spawnBackendCommand(adapter, command, sessionId, cwd);
|
|
8482
|
+
const formatted = buildNativeResponse(adapter.id, command, output2);
|
|
8483
|
+
await channel.sendText(chatId, formatted, "html");
|
|
8484
|
+
} catch (err) {
|
|
8485
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8486
|
+
await channel.sendText(chatId, `Backend command failed: ${msg}`, "plain");
|
|
8487
|
+
}
|
|
8488
|
+
}
|
|
8489
|
+
function spawnBackendCommand(adapter, command, sessionId, cwd) {
|
|
8490
|
+
return new Promise((resolve, reject) => {
|
|
8491
|
+
const config2 = adapter.buildSpawnConfig({
|
|
8492
|
+
prompt: command,
|
|
8493
|
+
sessionId,
|
|
8494
|
+
cwd,
|
|
8495
|
+
permMode: "plan",
|
|
8496
|
+
allowedTools: []
|
|
8497
|
+
});
|
|
8498
|
+
const env = { ...process.env, ...adapter.getEnv() };
|
|
8499
|
+
execFile4(config2.executable, config2.args, {
|
|
8500
|
+
cwd,
|
|
8501
|
+
timeout: BACKEND_CMD_TIMEOUT_MS,
|
|
8502
|
+
env,
|
|
8503
|
+
maxBuffer: 5 * 1024 * 1024
|
|
8504
|
+
}, (error3, stdout, stderr) => {
|
|
8505
|
+
if (error3 && "killed" in error3 && error3.killed) {
|
|
8506
|
+
reject(new Error(`Backend command timed out after ${BACKEND_CMD_TIMEOUT_MS / 1e3} seconds.`));
|
|
8507
|
+
return;
|
|
8508
|
+
}
|
|
8509
|
+
const output2 = extractTextFromNdjson(stdout) || stdout || stderr || "";
|
|
8510
|
+
resolve(output2.trim());
|
|
8511
|
+
});
|
|
8512
|
+
});
|
|
8513
|
+
}
|
|
8514
|
+
var BACKEND_CMD_TIMEOUT_MS;
|
|
8515
|
+
var init_backend_cmd = __esm({
|
|
8516
|
+
"src/shell/backend-cmd.ts"() {
|
|
8517
|
+
"use strict";
|
|
8518
|
+
init_backends();
|
|
8519
|
+
init_store4();
|
|
8520
|
+
BACKEND_CMD_TIMEOUT_MS = 15e3;
|
|
8521
|
+
}
|
|
8522
|
+
});
|
|
8523
|
+
|
|
8281
8524
|
// src/router.ts
|
|
8282
8525
|
import { readFile as readFile5, writeFile as writeFile2, unlink as unlink2 } from "fs/promises";
|
|
8283
8526
|
import { resolve as resolvePath } from "path";
|
|
@@ -8367,6 +8610,17 @@ async function handleMessage(msg, channel) {
|
|
|
8367
8610
|
}
|
|
8368
8611
|
}
|
|
8369
8612
|
}
|
|
8613
|
+
if (msg.type === "text" && msg.text) {
|
|
8614
|
+
const text = msg.text.trim();
|
|
8615
|
+
if (text.startsWith("!!")) {
|
|
8616
|
+
const cmd = text.slice(2).trim();
|
|
8617
|
+
if (cmd) return handleRawShell(cmd, chatId, channel);
|
|
8618
|
+
}
|
|
8619
|
+
if (text.startsWith("!") && text.length > 1) {
|
|
8620
|
+
const cmd = text.slice(1).trim();
|
|
8621
|
+
if (cmd) return handleShell(cmd, chatId, channel);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8370
8624
|
switch (msg.type) {
|
|
8371
8625
|
case "command":
|
|
8372
8626
|
await handleCommand(msg, channel);
|
|
@@ -8386,6 +8640,9 @@ async function handleMessage(msg, channel) {
|
|
|
8386
8640
|
}
|
|
8387
8641
|
async function handleCommand(msg, channel) {
|
|
8388
8642
|
const { chatId, command, commandArgs } = msg;
|
|
8643
|
+
if (command?.startsWith("/")) {
|
|
8644
|
+
return handleBackendCommand(command, chatId, channel);
|
|
8645
|
+
}
|
|
8389
8646
|
switch (command) {
|
|
8390
8647
|
case "start":
|
|
8391
8648
|
case "help":
|
|
@@ -8520,15 +8777,7 @@ Tap to toggle:`,
|
|
|
8520
8777
|
case "backend": {
|
|
8521
8778
|
const requestedBackend = (commandArgs ?? "").trim().toLowerCase();
|
|
8522
8779
|
if (requestedBackend && getAllBackendIds().includes(requestedBackend)) {
|
|
8523
|
-
|
|
8524
|
-
});
|
|
8525
|
-
clearSession(chatId);
|
|
8526
|
-
clearModel(chatId);
|
|
8527
|
-
clearThinkingLevel(chatId);
|
|
8528
|
-
setBackend(chatId, requestedBackend);
|
|
8529
|
-
const adapter = getAdapter(requestedBackend);
|
|
8530
|
-
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${adapter.displayName}`, detail: { field: "backend", value: requestedBackend } });
|
|
8531
|
-
await channel.sendText(chatId, `Switched to <b>${adapter.displayName}</b>.`, "html");
|
|
8780
|
+
await sendBackendSwitchConfirmation(chatId, requestedBackend, channel);
|
|
8532
8781
|
break;
|
|
8533
8782
|
}
|
|
8534
8783
|
const currentBackend = getBackend(chatId);
|
|
@@ -8552,15 +8801,7 @@ Tap to toggle:`,
|
|
|
8552
8801
|
case "codex": {
|
|
8553
8802
|
const backendId = command;
|
|
8554
8803
|
if (getAllBackendIds().includes(backendId)) {
|
|
8555
|
-
|
|
8556
|
-
});
|
|
8557
|
-
clearSession(chatId);
|
|
8558
|
-
clearModel(chatId);
|
|
8559
|
-
clearThinkingLevel(chatId);
|
|
8560
|
-
setBackend(chatId, backendId);
|
|
8561
|
-
const adapter = getAdapter(backendId);
|
|
8562
|
-
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${adapter.displayName}`, detail: { field: "backend", value: backendId } });
|
|
8563
|
-
await channel.sendText(chatId, `Switched to <b>${adapter.displayName}</b>.`, "html");
|
|
8804
|
+
await sendBackendSwitchConfirmation(chatId, backendId, channel);
|
|
8564
8805
|
} else {
|
|
8565
8806
|
await channel.sendText(chatId, `Backend "${command}" is not available.`, "plain");
|
|
8566
8807
|
}
|
|
@@ -8733,11 +8974,28 @@ Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, "plain
|
|
|
8733
8974
|
case "cwd": {
|
|
8734
8975
|
if (!commandArgs) {
|
|
8735
8976
|
const current = getCwd(chatId);
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8977
|
+
const recents = getRecentBookmarks(chatId, 10);
|
|
8978
|
+
if (recents.length === 0) {
|
|
8979
|
+
await channel.sendText(
|
|
8980
|
+
chatId,
|
|
8981
|
+
current ? `Working directory: ${current}
|
|
8982
|
+
|
|
8983
|
+
No saved bookmarks yet. Set a directory with /cwd <path> to auto-save.` : "No working directory set. Usage: /cwd ~/projects/my-app",
|
|
8984
|
+
"plain"
|
|
8985
|
+
);
|
|
8986
|
+
return;
|
|
8987
|
+
}
|
|
8988
|
+
const text = current ? `Current: ${current}
|
|
8989
|
+
|
|
8990
|
+
Recent directories:` : "Recent directories:";
|
|
8991
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
8992
|
+
const buttons = recents.map((r) => [{ label: r.alias, data: `cwdpick:${r.alias}` }]);
|
|
8993
|
+
await channel.sendKeyboard(chatId, text, buttons);
|
|
8994
|
+
} else {
|
|
8995
|
+
const list = recents.map((r) => ` ${r.alias} \u2192 ${r.path}`).join("\n");
|
|
8996
|
+
await channel.sendText(chatId, `${text}
|
|
8997
|
+
${list}`, "plain");
|
|
8998
|
+
}
|
|
8741
8999
|
return;
|
|
8742
9000
|
}
|
|
8743
9001
|
if (commandArgs === "reset" || commandArgs === "clear") {
|
|
@@ -8745,12 +9003,71 @@ Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, "plain
|
|
|
8745
9003
|
await channel.sendText(chatId, "Working directory cleared. Using default.", "plain");
|
|
8746
9004
|
return;
|
|
8747
9005
|
}
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
9006
|
+
if (commandArgs === "aliases") {
|
|
9007
|
+
const all = getAllBookmarks(chatId);
|
|
9008
|
+
if (all.length === 0) {
|
|
9009
|
+
await channel.sendText(chatId, "No bookmarks saved yet.", "plain");
|
|
9010
|
+
return;
|
|
9011
|
+
}
|
|
9012
|
+
const lines = all.map((b) => ` ${b.manual ? "[manual]" : "[auto]"} ${b.alias} \u2192 ${b.path}`);
|
|
9013
|
+
await channel.sendText(chatId, `Directory bookmarks:
|
|
9014
|
+
${lines.join("\n")}`, "plain");
|
|
9015
|
+
return;
|
|
9016
|
+
}
|
|
9017
|
+
if (commandArgs.startsWith("unalias ")) {
|
|
9018
|
+
const aliasName = commandArgs.slice(8).trim();
|
|
9019
|
+
if (!aliasName) {
|
|
9020
|
+
await channel.sendText(chatId, "Usage: /cwd unalias <name>", "plain");
|
|
9021
|
+
return;
|
|
9022
|
+
}
|
|
9023
|
+
const deleted = deleteBookmark(chatId, aliasName);
|
|
9024
|
+
await channel.sendText(chatId, deleted ? `Bookmark '${aliasName}' removed.` : `Bookmark '${aliasName}' not found.`, "plain");
|
|
9025
|
+
return;
|
|
9026
|
+
}
|
|
9027
|
+
if (commandArgs.startsWith("alias ")) {
|
|
9028
|
+
const parts = commandArgs.slice(6).trim().split(/\s+/);
|
|
9029
|
+
if (parts.length < 2) {
|
|
9030
|
+
await channel.sendText(chatId, "Usage: /cwd alias <name> <path>", "plain");
|
|
9031
|
+
return;
|
|
9032
|
+
}
|
|
9033
|
+
const [aliasName, ...pathParts] = parts;
|
|
9034
|
+
const aliasPath = pathParts.join(" ").replace(/^~/, process.env.HOME ?? "");
|
|
9035
|
+
upsertBookmark(chatId, aliasName, aliasPath, true);
|
|
9036
|
+
await channel.sendText(chatId, `Bookmark saved: ${aliasName} \u2192 ${aliasPath}`, "plain");
|
|
9037
|
+
return;
|
|
9038
|
+
}
|
|
9039
|
+
const arg = commandArgs;
|
|
9040
|
+
if (arg.startsWith("/") || arg.startsWith("~")) {
|
|
9041
|
+
const resolvedPath = arg.startsWith("~") ? arg.replace("~", process.env.HOME ?? "") : arg;
|
|
9042
|
+
setCwd(chatId, resolvedPath);
|
|
9043
|
+
const basename2 = resolvedPath.split("/").filter(Boolean).pop();
|
|
9044
|
+
if (basename2) upsertBookmark(chatId, basename2, resolvedPath, false);
|
|
9045
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${resolvedPath}`, detail: { field: "cwd", value: resolvedPath } });
|
|
9046
|
+
await sendCwdSessionChoice(chatId, resolvedPath, channel);
|
|
9047
|
+
return;
|
|
9048
|
+
}
|
|
9049
|
+
const exact = getBookmark(chatId, arg);
|
|
9050
|
+
if (exact) {
|
|
9051
|
+
setCwd(chatId, exact.path);
|
|
9052
|
+
touchBookmark(chatId, arg);
|
|
9053
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${exact.path}`, detail: { field: "cwd", value: exact.path } });
|
|
9054
|
+
await sendCwdSessionChoice(chatId, exact.path, channel);
|
|
9055
|
+
return;
|
|
9056
|
+
}
|
|
9057
|
+
const matches = findBookmarksByPrefix(chatId, arg);
|
|
9058
|
+
if (matches.length === 1) {
|
|
9059
|
+
setCwd(chatId, matches[0].path);
|
|
9060
|
+
touchBookmark(chatId, matches[0].alias);
|
|
9061
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${matches[0].path}`, detail: { field: "cwd", value: matches[0].path } });
|
|
9062
|
+
await sendCwdSessionChoice(chatId, matches[0].path, channel);
|
|
9063
|
+
return;
|
|
9064
|
+
}
|
|
9065
|
+
if (matches.length > 1 && typeof channel.sendKeyboard === "function") {
|
|
9066
|
+
const buttons = matches.map((m) => [{ label: `${m.alias} \u2192 ${m.path}`, data: `cwdpick:${m.alias}` }]);
|
|
9067
|
+
await channel.sendKeyboard(chatId, `Multiple matches for "${arg}":`, buttons);
|
|
9068
|
+
return;
|
|
9069
|
+
}
|
|
9070
|
+
await channel.sendText(chatId, `Directory alias '${arg}' not found. Use /cwd aliases to see saved bookmarks.`, "plain");
|
|
8754
9071
|
break;
|
|
8755
9072
|
}
|
|
8756
9073
|
case "memory": {
|
|
@@ -9255,7 +9572,7 @@ Use /skills to see it.`, "plain");
|
|
|
9255
9572
|
lines.push("", "\u2501\u2501 <b>Built-in</b> \u2501\u2501");
|
|
9256
9573
|
lines.push(` \u2705 <b>cc-claw</b> <i>Agent orchestrator (spawn, tasks, inbox)</i>`);
|
|
9257
9574
|
}
|
|
9258
|
-
const { execFile:
|
|
9575
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
9259
9576
|
const { homedir: homedir5 } = await import("os");
|
|
9260
9577
|
const discoveryCwd = homedir5();
|
|
9261
9578
|
const runnerResults = await Promise.allSettled(
|
|
@@ -9264,7 +9581,7 @@ Use /skills to see it.`, "plain");
|
|
|
9264
9581
|
if (!listCmd.length) return Promise.resolve({ runner, output: "" });
|
|
9265
9582
|
const exe = runner.getExecutablePath();
|
|
9266
9583
|
return new Promise((resolve) => {
|
|
9267
|
-
|
|
9584
|
+
execFile5(exe, listCmd.slice(1), {
|
|
9268
9585
|
encoding: "utf-8",
|
|
9269
9586
|
timeout: 3e4,
|
|
9270
9587
|
cwd: discoveryCwd,
|
|
@@ -9711,31 +10028,65 @@ async function sendResponse(chatId, channel, text) {
|
|
|
9711
10028
|
function isImageExt(ext) {
|
|
9712
10029
|
return ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"].includes(ext);
|
|
9713
10030
|
}
|
|
10031
|
+
async function sendBackendSwitchConfirmation(chatId, target, channel) {
|
|
10032
|
+
const current = getBackend(chatId);
|
|
10033
|
+
const targetAdapter = getAdapter(target);
|
|
10034
|
+
if (current === target) {
|
|
10035
|
+
await channel.sendText(chatId, `Already using ${targetAdapter.displayName}.`, "plain");
|
|
10036
|
+
return;
|
|
10037
|
+
}
|
|
10038
|
+
const currentLabel = current ? getAdapter(current).displayName : "current backend";
|
|
10039
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
10040
|
+
await channel.sendKeyboard(
|
|
10041
|
+
chatId,
|
|
10042
|
+
`\u26A0\uFE0F Switching to ${targetAdapter.displayName} will summarize and reset your current session.
|
|
10043
|
+
|
|
10044
|
+
What would you like to do?`,
|
|
10045
|
+
[
|
|
10046
|
+
[{ label: `Stay on ${currentLabel}`, data: "backend_cancel" }],
|
|
10047
|
+
[{ label: `Switch to ${targetAdapter.displayName} + summarize`, data: `backend_confirm:${target}` }]
|
|
10048
|
+
]
|
|
10049
|
+
);
|
|
10050
|
+
} else {
|
|
10051
|
+
await doBackendSwitch(chatId, target, channel);
|
|
10052
|
+
}
|
|
10053
|
+
}
|
|
10054
|
+
async function doBackendSwitch(chatId, backendId, channel) {
|
|
10055
|
+
summarizeSession(chatId).catch(() => {
|
|
10056
|
+
});
|
|
10057
|
+
clearSession(chatId);
|
|
10058
|
+
clearModel(chatId);
|
|
10059
|
+
clearThinkingLevel(chatId);
|
|
10060
|
+
setBackend(chatId, backendId);
|
|
10061
|
+
const adapter = getAdapter(backendId);
|
|
10062
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${adapter.displayName}`, detail: { field: "backend", value: backendId } });
|
|
10063
|
+
await channel.sendText(
|
|
10064
|
+
chatId,
|
|
10065
|
+
`Backend switched to ${adapter.displayName}.
|
|
10066
|
+
Default model: ${adapter.defaultModel}
|
|
10067
|
+
Session reset. Ready!`,
|
|
10068
|
+
"plain"
|
|
10069
|
+
);
|
|
10070
|
+
}
|
|
9714
10071
|
async function handleCallback(chatId, data, channel) {
|
|
9715
10072
|
if (data.startsWith("backend:")) {
|
|
9716
10073
|
const chosen = data.slice(8);
|
|
9717
10074
|
if (!getAllBackendIds().includes(chosen)) return;
|
|
9718
10075
|
const previous = getBackend(chatId);
|
|
9719
10076
|
if (chosen === previous) {
|
|
9720
|
-
const
|
|
9721
|
-
await channel.sendText(chatId, `Already using ${
|
|
10077
|
+
const adapter = getAdapter(chosen);
|
|
10078
|
+
await channel.sendText(chatId, `Already using ${adapter.displayName}.`, "plain");
|
|
9722
10079
|
return;
|
|
9723
10080
|
}
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
const
|
|
9731
|
-
|
|
9732
|
-
await channel.sendText(
|
|
9733
|
-
chatId,
|
|
9734
|
-
`Backend switched to ${adapter.displayName}.
|
|
9735
|
-
Default model: ${adapter.defaultModel}
|
|
9736
|
-
Session reset. Ready!`,
|
|
9737
|
-
"plain"
|
|
9738
|
-
);
|
|
10081
|
+
await sendBackendSwitchConfirmation(chatId, chosen, channel);
|
|
10082
|
+
} else if (data.startsWith("backend_confirm:")) {
|
|
10083
|
+
const chosen = data.slice(16);
|
|
10084
|
+
if (!getAllBackendIds().includes(chosen)) return;
|
|
10085
|
+
await doBackendSwitch(chatId, chosen, channel);
|
|
10086
|
+
} else if (data === "backend_cancel") {
|
|
10087
|
+
const current = getBackend(chatId);
|
|
10088
|
+
const label2 = current ? getAdapter(current).displayName : "current backend";
|
|
10089
|
+
await channel.sendText(chatId, `No change. Staying on ${label2}.`, "plain");
|
|
9739
10090
|
} else if (data.startsWith("model:")) {
|
|
9740
10091
|
const chosen = data.slice(6);
|
|
9741
10092
|
let adapter;
|
|
@@ -9834,6 +10185,51 @@ ${PERM_MODES[chosen]}`,
|
|
|
9834
10185
|
} else {
|
|
9835
10186
|
await channel.sendText(chatId, "Preference not saved.", "plain");
|
|
9836
10187
|
}
|
|
10188
|
+
} else if (data.startsWith("shell:")) {
|
|
10189
|
+
const parts = data.split(":");
|
|
10190
|
+
const action = parts[1];
|
|
10191
|
+
const id = parts[2];
|
|
10192
|
+
if (action === "confirm") {
|
|
10193
|
+
const pending = getPendingCommand(id);
|
|
10194
|
+
if (!pending) {
|
|
10195
|
+
await channel.sendText(chatId, "Confirmation expired. Please re-send the command.", "plain");
|
|
10196
|
+
return;
|
|
10197
|
+
}
|
|
10198
|
+
removePendingCommand(id);
|
|
10199
|
+
if (pending.raw) {
|
|
10200
|
+
await handleRawShell(pending.command, pending.chatId, channel, true);
|
|
10201
|
+
} else {
|
|
10202
|
+
await handleShell(pending.command, pending.chatId, channel, true);
|
|
10203
|
+
}
|
|
10204
|
+
} else if (action === "cancel") {
|
|
10205
|
+
removePendingCommand(id);
|
|
10206
|
+
await channel.sendText(chatId, "Command cancelled.", "plain");
|
|
10207
|
+
}
|
|
10208
|
+
} else if (data.startsWith("cwd:")) {
|
|
10209
|
+
const parts = data.split(":");
|
|
10210
|
+
const action = parts[1];
|
|
10211
|
+
const targetChatId = parts.slice(2).join(":");
|
|
10212
|
+
if (action === "keep") {
|
|
10213
|
+
await channel.sendText(chatId, "Session kept. The agent will continue with existing context.", "plain");
|
|
10214
|
+
} else if (action === "summarize") {
|
|
10215
|
+
await summarizeSession(targetChatId);
|
|
10216
|
+
clearSession(targetChatId);
|
|
10217
|
+
await channel.sendText(chatId, "Session summarized and reset. Context preserved in memory.", "plain");
|
|
10218
|
+
} else if (action === "reset") {
|
|
10219
|
+
clearSession(targetChatId);
|
|
10220
|
+
await channel.sendText(chatId, "Session reset. Clean slate.", "plain");
|
|
10221
|
+
}
|
|
10222
|
+
} else if (data.startsWith("cwdpick:")) {
|
|
10223
|
+
const alias = data.slice(8);
|
|
10224
|
+
const bookmark = getBookmark(chatId, alias);
|
|
10225
|
+
if (!bookmark) {
|
|
10226
|
+
await channel.sendText(chatId, `Bookmark '${alias}' no longer exists.`, "plain");
|
|
10227
|
+
return;
|
|
10228
|
+
}
|
|
10229
|
+
setCwd(chatId, bookmark.path);
|
|
10230
|
+
touchBookmark(chatId, alias);
|
|
10231
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Working directory set to ${bookmark.path}`, detail: { field: "cwd", value: bookmark.path } });
|
|
10232
|
+
await sendCwdSessionChoice(chatId, bookmark.path, channel);
|
|
9837
10233
|
} else if (data.startsWith("skill:")) {
|
|
9838
10234
|
const parts = data.slice(6).split(":");
|
|
9839
10235
|
let skillName;
|
|
@@ -9871,6 +10267,101 @@ ${PERM_MODES[chosen]}`,
|
|
|
9871
10267
|
await sendResponse(chatId, channel, response.text);
|
|
9872
10268
|
}
|
|
9873
10269
|
}
|
|
10270
|
+
async function handleShell(command, chatId, channel, skipGuard = false) {
|
|
10271
|
+
if (!skipGuard && isDestructive(command)) {
|
|
10272
|
+
const id = storePendingCommand(command, chatId, false);
|
|
10273
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
10274
|
+
await channel.sendKeyboard(
|
|
10275
|
+
chatId,
|
|
10276
|
+
`\u26A0\uFE0F This command looks potentially destructive.
|
|
10277
|
+
|
|
10278
|
+
Command: ${command}`,
|
|
10279
|
+
[[
|
|
10280
|
+
{ label: "Run anyway", data: `shell:confirm:${id}` },
|
|
10281
|
+
{ label: "Cancel", data: `shell:cancel:${id}` }
|
|
10282
|
+
]]
|
|
10283
|
+
);
|
|
10284
|
+
} else {
|
|
10285
|
+
await channel.sendText(chatId, `\u26A0\uFE0F Destructive command blocked: ${command}
|
|
10286
|
+
No keyboard available to confirm.`, "plain");
|
|
10287
|
+
}
|
|
10288
|
+
return;
|
|
10289
|
+
}
|
|
10290
|
+
const cwd = getCwd(chatId) ?? `${process.env.HOME ?? "/tmp"}/.cc-claw/workspace`;
|
|
10291
|
+
const result = await executeShell(command, cwd);
|
|
10292
|
+
logActivity(getDb(), {
|
|
10293
|
+
chatId,
|
|
10294
|
+
source: "telegram",
|
|
10295
|
+
eventType: "shell_command",
|
|
10296
|
+
summary: `Shell: ${command.slice(0, 100)}`,
|
|
10297
|
+
detail: { command, exitCode: result.exitCode, outputLength: result.output.length, timedOut: result.timedOut }
|
|
10298
|
+
});
|
|
10299
|
+
const formatted = formatCodeBlock(command, result.output, result.exitCode);
|
|
10300
|
+
if (shouldSendAsFile(formatted)) {
|
|
10301
|
+
const buffer = Buffer.from(result.output, "utf-8");
|
|
10302
|
+
await channel.sendFile(chatId, buffer, "output.txt", "text/plain");
|
|
10303
|
+
await channel.sendText(chatId, `Output too long (${result.output.length} chars), sent as file.`, "plain");
|
|
10304
|
+
} else {
|
|
10305
|
+
await channel.sendText(chatId, formatted, "html");
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
async function handleRawShell(command, chatId, channel, skipGuard = false) {
|
|
10309
|
+
if (!skipGuard && isDestructive(command)) {
|
|
10310
|
+
const id = storePendingCommand(command, chatId, true);
|
|
10311
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
10312
|
+
await channel.sendKeyboard(
|
|
10313
|
+
chatId,
|
|
10314
|
+
`\u26A0\uFE0F This command looks potentially destructive.
|
|
10315
|
+
|
|
10316
|
+
Command: ${command}`,
|
|
10317
|
+
[[
|
|
10318
|
+
{ label: "Run anyway", data: `shell:confirm:${id}` },
|
|
10319
|
+
{ label: "Cancel", data: `shell:cancel:${id}` }
|
|
10320
|
+
]]
|
|
10321
|
+
);
|
|
10322
|
+
} else {
|
|
10323
|
+
await channel.sendText(chatId, `\u26A0\uFE0F Destructive command blocked: ${command}
|
|
10324
|
+
No keyboard available to confirm.`, "plain");
|
|
10325
|
+
}
|
|
10326
|
+
return;
|
|
10327
|
+
}
|
|
10328
|
+
const cwd = getCwd(chatId) ?? `${process.env.HOME ?? "/tmp"}/.cc-claw/workspace`;
|
|
10329
|
+
const result = await executeShell(command, cwd);
|
|
10330
|
+
logActivity(getDb(), {
|
|
10331
|
+
chatId,
|
|
10332
|
+
source: "telegram",
|
|
10333
|
+
eventType: "shell_command",
|
|
10334
|
+
summary: `Shell: ${command.slice(0, 100)}`,
|
|
10335
|
+
detail: { command, exitCode: result.exitCode, outputLength: result.output.length, timedOut: result.timedOut }
|
|
10336
|
+
});
|
|
10337
|
+
const formatted = formatRaw(result.output);
|
|
10338
|
+
if (shouldSendAsFile(formatted)) {
|
|
10339
|
+
const buffer = Buffer.from(result.output, "utf-8");
|
|
10340
|
+
await channel.sendFile(chatId, buffer, "output.txt", "text/plain");
|
|
10341
|
+
await channel.sendText(chatId, `Output too long (${result.output.length} chars), sent as file.`, "plain");
|
|
10342
|
+
} else {
|
|
10343
|
+
await channel.sendText(chatId, formatted, "plain");
|
|
10344
|
+
}
|
|
10345
|
+
}
|
|
10346
|
+
async function sendCwdSessionChoice(chatId, path, channel) {
|
|
10347
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
10348
|
+
await channel.sendKeyboard(
|
|
10349
|
+
chatId,
|
|
10350
|
+
`Working directory set to: ${path}
|
|
10351
|
+
|
|
10352
|
+
Changing directories mid-session may confuse the agent.
|
|
10353
|
+
What would you like to do?`,
|
|
10354
|
+
[[
|
|
10355
|
+
{ label: "Keep session", data: `cwd:keep:${chatId}` },
|
|
10356
|
+
{ label: "Summarize & reset", data: `cwd:summarize:${chatId}` },
|
|
10357
|
+
{ label: "Reset session", data: `cwd:reset:${chatId}` }
|
|
10358
|
+
]]
|
|
10359
|
+
);
|
|
10360
|
+
} else {
|
|
10361
|
+
await channel.sendText(chatId, `Working directory set to: ${path}
|
|
10362
|
+
Session kept.`, "plain");
|
|
10363
|
+
}
|
|
10364
|
+
}
|
|
9874
10365
|
function parseIntervalToMs(input) {
|
|
9875
10366
|
const match = input.trim().match(/^(\d+)\s*(m|min|h|hr|hour|s|sec)$/i);
|
|
9876
10367
|
if (!match) return null;
|
|
@@ -9938,6 +10429,9 @@ var init_router = __esm({
|
|
|
9938
10429
|
init_registry();
|
|
9939
10430
|
init_registry2();
|
|
9940
10431
|
init_store3();
|
|
10432
|
+
init_guard();
|
|
10433
|
+
init_exec();
|
|
10434
|
+
init_backend_cmd();
|
|
9941
10435
|
PERM_MODES = {
|
|
9942
10436
|
yolo: "YOLO \u2014 all tools, full autopilot",
|
|
9943
10437
|
safe: "Safe \u2014 only my allowed tools",
|
package/package.json
CHANGED