@xiaodye/runeclaw 1.0.1 → 1.0.4
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/README.md +13 -14
- package/dist/index.js +129 -49
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img src="./assets/runeclaw-logo.svg" alt="RuneClaw logo" width="720" />
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
1
|
# RuneClaw
|
|
6
2
|
|
|
7
3
|
RuneClaw 是一个基于 TypeScript 的本地 AI Agent CLI。它把模型调用、文件工具、RAG、记忆、MCP、子 Agent、定时任务、插件和飞书通道放在同一个运行时里,适合做可迭代的个人 Agent 实验。
|
|
8
4
|
|
|
5
|
+

|
|
6
|
+
|
|
9
7
|
## 特性
|
|
10
8
|
|
|
11
9
|
- DeepSeek 兼容模型接入,默认使用 `deepseek-v4-flash`
|
|
@@ -13,7 +11,7 @@ RuneClaw 是一个基于 TypeScript 的本地 AI Agent CLI。它把模型调用
|
|
|
13
11
|
- 本地文件工具:读取、写入、编辑、列目录、grep、glob
|
|
14
12
|
- Shell 工具:可执行命令并返回输出
|
|
15
13
|
- 记忆系统:跨会话保存、读取、搜索记忆
|
|
16
|
-
- RAG
|
|
14
|
+
- RAG 知识库:启动时加 `--rag` 自动导入 `docs/`,也支持手动导入其它文档
|
|
17
15
|
- MCP 接入:可挂载外部工具,当前默认有 GitHub mock 示例
|
|
18
16
|
- 子 Agent:支持任务分发与执行记录查看
|
|
19
17
|
- 定时任务:支持查看任务与执行日志
|
|
@@ -68,13 +66,13 @@ runeclaw
|
|
|
68
66
|
|
|
69
67
|
`runeclaw` 支持以下命令:
|
|
70
68
|
|
|
71
|
-
| 命令
|
|
72
|
-
|
|
|
73
|
-
| `runeclaw` / `runeclaw start`
|
|
74
|
-
| `runeclaw init`
|
|
75
|
-
| `runeclaw continue`
|
|
76
|
-
| `runeclaw help` / `--help`
|
|
77
|
-
| `runeclaw version` / `--version` | 查看版本号
|
|
69
|
+
| 命令 | 说明 |
|
|
70
|
+
| -------------------------------- | ----------------------------------------------------- |
|
|
71
|
+
| `runeclaw` / `runeclaw start` | 启动交互式 Agent(默认) |
|
|
72
|
+
| `runeclaw init` | 运行初始化向导,生成 `runeclaw.config.json` 与 `.env` |
|
|
73
|
+
| `runeclaw continue` | 启动 Agent(预留会话续接) |
|
|
74
|
+
| `runeclaw help` / `--help` | 查看帮助 |
|
|
75
|
+
| `runeclaw version` / `--version` | 查看版本号 |
|
|
78
76
|
|
|
79
77
|
## 发布到 npm
|
|
80
78
|
|
|
@@ -89,6 +87,7 @@ pnpm run release -- major # 主版本号 +1(1.0.0 → 2.0.0)
|
|
|
89
87
|
```
|
|
90
88
|
|
|
91
89
|
> 说明:
|
|
90
|
+
>
|
|
92
91
|
> - 发布用 `npm publish --registry https://registry.npmjs.org/`,命令行参数优先级最高,能覆盖任何 `.npmrc` 里的镜像源配置(镜像源只读,无法发布),也无需改动全局 npm 配置;
|
|
93
92
|
> - `npm publish` 的 `prepublishOnly` 会自动执行 `pnpm build`,无需手动构建;
|
|
94
93
|
> - 首次发布前需要先 `npm login`。
|
|
@@ -150,7 +149,7 @@ SERPER_API_KEY=
|
|
|
150
149
|
|
|
151
150
|
### 基础
|
|
152
151
|
|
|
153
|
-
-
|
|
152
|
+
- `/exit` 退出
|
|
154
153
|
- `/context` 查看上下文占用
|
|
155
154
|
- `/usage` 查看 token 和成本统计
|
|
156
155
|
- `status` 查看当前消息、记忆和知识库状态
|
|
@@ -222,7 +221,7 @@ src/
|
|
|
222
221
|
|
|
223
222
|
## 说明
|
|
224
223
|
|
|
225
|
-
-
|
|
224
|
+
- 启动时默认**不**自动导入知识库;加 `--rag`(或 `pnpm start:rag`)才会导入 `docs/` 下的 `.md` 文档
|
|
226
225
|
- 当前项目里的飞书配置只应通过 `.env` 提供,不要把 secret 写进 `runeclaw.config.json`
|
|
227
226
|
- Web 搜索工具会优先使用 `TAVILY_API_KEY`,没有时可用 `SERPER_API_KEY`
|
|
228
227
|
|
package/dist/index.js
CHANGED
|
@@ -265,7 +265,7 @@ function TextField(props) {
|
|
|
265
265
|
onChange(value.slice(0, -1));
|
|
266
266
|
return;
|
|
267
267
|
}
|
|
268
|
-
if (!input || input.split("").some((
|
|
268
|
+
if (!input || input.split("").some((c2) => c2.charCodeAt(0) < 32)) return;
|
|
269
269
|
onChange(value + input);
|
|
270
270
|
});
|
|
271
271
|
const display = mask && !reveal ? "\u2022".repeat(value.length) : value;
|
|
@@ -1325,14 +1325,14 @@ var init_tracker = __esm({
|
|
|
1325
1325
|
const totalInputLike = t.inputTokens + t.cacheReadTokens + t.cacheWriteTokens;
|
|
1326
1326
|
const hitRate = totalInputLike > 0 ? t.cacheReadTokens / totalInputLike : 0;
|
|
1327
1327
|
const baselineCost = (() => {
|
|
1328
|
-
let
|
|
1328
|
+
let c2 = 0;
|
|
1329
1329
|
for (const s of this.steps) {
|
|
1330
1330
|
const p = PRICE_TABLE[s.model] || PRICE_TABLE["mock-model"];
|
|
1331
1331
|
const inputLike = s.inputTokens + s.cacheReadTokens + s.cacheWriteTokens;
|
|
1332
|
-
|
|
1333
|
-
|
|
1332
|
+
c2 += inputLike * p.input / 1e6;
|
|
1333
|
+
c2 += s.outputTokens * p.output / 1e6;
|
|
1334
1334
|
}
|
|
1335
|
-
return
|
|
1335
|
+
return c2;
|
|
1336
1336
|
})();
|
|
1337
1337
|
return {
|
|
1338
1338
|
...t,
|
|
@@ -1631,6 +1631,44 @@ var init_defense = __esm({
|
|
|
1631
1631
|
}
|
|
1632
1632
|
});
|
|
1633
1633
|
|
|
1634
|
+
// src/ui/theme.ts
|
|
1635
|
+
var c;
|
|
1636
|
+
var init_theme = __esm({
|
|
1637
|
+
"src/ui/theme.ts"() {
|
|
1638
|
+
"use strict";
|
|
1639
|
+
c = {
|
|
1640
|
+
reset: "\x1B[0m",
|
|
1641
|
+
dim: "\x1B[2m",
|
|
1642
|
+
bold: "\x1B[1m",
|
|
1643
|
+
red: "\x1B[31m",
|
|
1644
|
+
green: "\x1B[32m",
|
|
1645
|
+
yellow: "\x1B[33m",
|
|
1646
|
+
blue: "\x1B[34m",
|
|
1647
|
+
magenta: "\x1B[35m",
|
|
1648
|
+
cyan: "\x1B[36m",
|
|
1649
|
+
gray: "\x1B[90m"
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
});
|
|
1653
|
+
|
|
1654
|
+
// src/ui/markdown.ts
|
|
1655
|
+
import { marked } from "marked";
|
|
1656
|
+
import { markedTerminal } from "marked-terminal";
|
|
1657
|
+
function renderMarkdown(text) {
|
|
1658
|
+
try {
|
|
1659
|
+
const result = marked.parse(text);
|
|
1660
|
+
return typeof result === "string" ? result : String(result);
|
|
1661
|
+
} catch {
|
|
1662
|
+
return text;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
var init_markdown = __esm({
|
|
1666
|
+
"src/ui/markdown.ts"() {
|
|
1667
|
+
"use strict";
|
|
1668
|
+
marked.use(markedTerminal());
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1634
1672
|
// src/agent/loop.ts
|
|
1635
1673
|
import { streamText } from "ai";
|
|
1636
1674
|
async function agentLoop(model2, registry2, messages, system, tracker) {
|
|
@@ -1645,7 +1683,7 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
|
|
|
1645
1683
|
while (step < MAX_STEPS) {
|
|
1646
1684
|
step++;
|
|
1647
1685
|
console.log(`
|
|
1648
|
-
|
|
1686
|
+
${c.dim}\u2500\u2500 Step ${step} \u2500\u2500${c.reset}`);
|
|
1649
1687
|
let hasToolCall = false;
|
|
1650
1688
|
let fullText = "";
|
|
1651
1689
|
let shouldBreak = false;
|
|
@@ -1674,11 +1712,11 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
|
|
|
1674
1712
|
hasToolCall = true;
|
|
1675
1713
|
lastToolCall = { name: part.toolName, input: part.input };
|
|
1676
1714
|
console.log(
|
|
1677
|
-
`
|
|
1715
|
+
` ${c.cyan}\u25B8 \u8C03\u7528 ${c.bold}${part.toolName}${c.reset}${c.dim}(${JSON.stringify(part.input)})${c.reset}`
|
|
1678
1716
|
);
|
|
1679
1717
|
const detection = detect(part.toolName, part.input);
|
|
1680
1718
|
if (detection.stuck) {
|
|
1681
|
-
console.log(` ${detection.message}`);
|
|
1719
|
+
console.log(` ${c.yellow}\u26A0 ${detection.message}${c.reset}`);
|
|
1682
1720
|
if (detection.level === "critical") {
|
|
1683
1721
|
shouldBreak = true;
|
|
1684
1722
|
} else {
|
|
@@ -1694,7 +1732,7 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
|
|
|
1694
1732
|
case "tool-result": {
|
|
1695
1733
|
const output = typeof part.output === "string" ? part.output : JSON.stringify(part.output);
|
|
1696
1734
|
const preview = output.length > 120 ? output.slice(0, 120) + "..." : output;
|
|
1697
|
-
console.log(`
|
|
1735
|
+
console.log(` ${c.gray}\u21B3 \u7ED3\u679C ${part.toolName}:${c.reset} ${c.dim}${preview}${c.reset}`);
|
|
1698
1736
|
if (lastToolCall) {
|
|
1699
1737
|
recordResult(lastToolCall.name, lastToolCall.input, part.output);
|
|
1700
1738
|
}
|
|
@@ -1708,7 +1746,7 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
|
|
|
1708
1746
|
} catch (error) {
|
|
1709
1747
|
if (attempt > MAX_RETRIES || !isRetryable(error)) throw error;
|
|
1710
1748
|
const delay = calculateDelay(attempt);
|
|
1711
|
-
console.log(` [\u91CD\u8BD5] \u7B2C ${attempt}/${MAX_RETRIES} \u6B21\uFF0C${delay}ms \u540E
|
|
1749
|
+
console.log(` ${c.yellow}\u26A0 [\u91CD\u8BD5] \u7B2C ${attempt}/${MAX_RETRIES} \u6B21\uFF0C${delay}ms \u540E...${c.reset}`);
|
|
1712
1750
|
await sleep(delay);
|
|
1713
1751
|
hasToolCall = false;
|
|
1714
1752
|
fullText = "";
|
|
@@ -1717,35 +1755,43 @@ async function agentLoop(model2, registry2, messages, system, tracker) {
|
|
|
1717
1755
|
}
|
|
1718
1756
|
}
|
|
1719
1757
|
if (shouldBreak) {
|
|
1720
|
-
console.log(
|
|
1758
|
+
console.log(`
|
|
1759
|
+
${c.yellow}[\u5FAA\u73AF\u68C0\u6D4B\u89E6\u53D1\uFF0CAgent \u5DF2\u505C\u6B62]${c.reset}`);
|
|
1721
1760
|
break;
|
|
1722
1761
|
}
|
|
1723
1762
|
for (const msg of stepResponse.messages) pushMessage(msg);
|
|
1724
1763
|
const norm = normalizeUsage(stepUsage);
|
|
1725
1764
|
const stepRecord = tracker?.record(model2?.modelId || "mock-model", norm);
|
|
1726
1765
|
if (stepRecord && (norm.cacheReadTokens > 0 || norm.cacheWriteTokens > 0)) {
|
|
1727
|
-
const tag = norm.cacheReadTokens > 0 ?
|
|
1766
|
+
const tag = norm.cacheReadTokens > 0 ? `${c.green}\u2713 cache hit${c.reset}` : `${c.yellow}\u270E cache write${c.reset}`;
|
|
1728
1767
|
const detail = norm.cacheReadTokens > 0 ? `read ${norm.cacheReadTokens}` : `write ${norm.cacheWriteTokens}`;
|
|
1729
|
-
console.log(` [${tag}] ${detail} tokens \xB7 \u672C\u6B65 $${stepRecord.cost.toFixed(5)}`);
|
|
1768
|
+
console.log(` [${tag}] ${c.dim}${detail} tokens \xB7 \u672C\u6B65 $${stepRecord.cost.toFixed(5)}${c.reset}`);
|
|
1730
1769
|
}
|
|
1731
1770
|
if (estimateMessageTokens(messages) > COMPACT_TOKEN_THRESHOLD) {
|
|
1732
|
-
console.log(` [Token] ~${estimateMessageTokens(messages)} \u8D85\u9884\u7B97\uFF0C\u89E6\u53D1\u538B\u7F29
|
|
1771
|
+
console.log(` ${c.yellow}[Token] ~${estimateMessageTokens(messages)} \u8D85\u9884\u7B97\uFF0C\u89E6\u53D1\u538B\u7F29...${c.reset}`);
|
|
1733
1772
|
const compacted = await compactContext(model2, messages, timestamps);
|
|
1734
1773
|
messages.splice(0, messages.length, ...compacted.messages);
|
|
1735
1774
|
timestamps.clear();
|
|
1736
1775
|
for (let i = 0; i < messages.length; i++) timestamps.set(i, Date.now());
|
|
1737
1776
|
console.log(
|
|
1738
|
-
` [\u538B\u7F29] TTL \u8F6F\u526A ${compacted.softPruned}/\u786C\u6E05 ${compacted.hardPruned}\uFF0C\u6458\u8981\u79FB\u9664 ${compacted.compressedCount} \u6761 \u2192 ~${compacted.tokenEstimate} tokens`
|
|
1777
|
+
` ${c.yellow}[\u538B\u7F29] TTL \u8F6F\u526A ${compacted.softPruned}/\u786C\u6E05 ${compacted.hardPruned}\uFF0C\u6458\u8981\u79FB\u9664 ${compacted.compressedCount} \u6761 \u2192 ~${compacted.tokenEstimate} tokens${c.reset}`
|
|
1739
1778
|
);
|
|
1740
1779
|
}
|
|
1741
1780
|
if (!hasToolCall) {
|
|
1742
|
-
if (fullText)
|
|
1781
|
+
if (fullText) {
|
|
1782
|
+
const rawLines = fullText.split("\n").length;
|
|
1783
|
+
if (rawLines > 0) process.stdout.write(`\x1B[${rawLines}A\x1B[J`);
|
|
1784
|
+
const rendered = renderMarkdown(fullText);
|
|
1785
|
+
process.stdout.write(rendered);
|
|
1786
|
+
process.stdout.write("\n");
|
|
1787
|
+
}
|
|
1743
1788
|
break;
|
|
1744
1789
|
}
|
|
1745
|
-
console.log(
|
|
1790
|
+
console.log(` ${c.dim}\u2192 \u7EE7\u7EED\u4E0B\u4E00\u6B65...${c.reset}`);
|
|
1746
1791
|
}
|
|
1747
1792
|
if (step >= MAX_STEPS) {
|
|
1748
|
-
console.log(
|
|
1793
|
+
console.log(`
|
|
1794
|
+
${c.yellow}[\u8FBE\u5230\u6700\u5927\u6B65\u6570]${c.reset}`);
|
|
1749
1795
|
}
|
|
1750
1796
|
}
|
|
1751
1797
|
var MAX_STEPS, MAX_RETRIES;
|
|
@@ -1756,6 +1802,8 @@ var init_loop = __esm({
|
|
|
1756
1802
|
init_retry();
|
|
1757
1803
|
init_tracker();
|
|
1758
1804
|
init_defense();
|
|
1805
|
+
init_theme();
|
|
1806
|
+
init_markdown();
|
|
1759
1807
|
MAX_STEPS = 15;
|
|
1760
1808
|
MAX_RETRIES = 3;
|
|
1761
1809
|
}
|
|
@@ -3037,10 +3085,10 @@ function renderContextMatrix(snapshot) {
|
|
|
3037
3085
|
const rowCells = [];
|
|
3038
3086
|
for (let col = 0; col < 16; col++) {
|
|
3039
3087
|
const idx = row * 16 + col;
|
|
3040
|
-
const
|
|
3041
|
-
if (
|
|
3042
|
-
else if (
|
|
3043
|
-
else rowCells.push(fg(
|
|
3088
|
+
const c2 = cells[idx];
|
|
3089
|
+
if (c2 === -1) rowCells.push(fg(COLORS.free, "\u25CB"));
|
|
3090
|
+
else if (c2 === -2) rowCells.push(fg(COLORS.buffer, "\u25A2"));
|
|
3091
|
+
else rowCells.push(fg(c2, "\u25CF"));
|
|
3044
3092
|
}
|
|
3045
3093
|
lines.push(rowCells.join(" "));
|
|
3046
3094
|
}
|
|
@@ -3884,10 +3932,10 @@ function createRagTools(vectorStore2, embedFn2) {
|
|
|
3884
3932
|
const chunks = chunkDocument(path4, text);
|
|
3885
3933
|
const embeddings = await embed(
|
|
3886
3934
|
embedFn2,
|
|
3887
|
-
chunks.map((
|
|
3935
|
+
chunks.map((c2) => c2.text)
|
|
3888
3936
|
);
|
|
3889
3937
|
vectorStore2.addBatch(
|
|
3890
|
-
chunks.map((
|
|
3938
|
+
chunks.map((c2, i) => ({ chunk: c2, embedding: embeddings[i] }))
|
|
3891
3939
|
);
|
|
3892
3940
|
return `\u5DF2\u5BFC\u5165 ${chunks.length} \u4E2A\u6587\u6863\u7247\u6BB5\uFF08\u6765\u6E90: ${path4}\uFF09\u3002\u77E5\u8BC6\u5E93\u5171 ${vectorStore2.size()} \u4E2A\u7247\u6BB5\u3002`;
|
|
3893
3941
|
} catch (e) {
|
|
@@ -4712,7 +4760,7 @@ var init_gateway = __esm({
|
|
|
4712
4760
|
if (typeof content === "string") {
|
|
4713
4761
|
replyText = content;
|
|
4714
4762
|
} else if (Array.isArray(content)) {
|
|
4715
|
-
replyText = content.filter((
|
|
4763
|
+
replyText = content.filter((c2) => c2.type === "text").map((c2) => c2.text).join("");
|
|
4716
4764
|
}
|
|
4717
4765
|
}
|
|
4718
4766
|
if (replyText) {
|
|
@@ -4863,8 +4911,8 @@ var init_feishu = __esm({
|
|
|
4863
4911
|
const { Hono } = await import("hono");
|
|
4864
4912
|
const { serve } = await import("@hono/node-server");
|
|
4865
4913
|
const app = new Hono();
|
|
4866
|
-
app.post("/webhook/feishu", async (
|
|
4867
|
-
const body = await
|
|
4914
|
+
app.post("/webhook/feishu", async (c2) => {
|
|
4915
|
+
const body = await c2.req.json();
|
|
4868
4916
|
if (body.header?.event_type === "im.message.receive_v1") {
|
|
4869
4917
|
const event = body.event;
|
|
4870
4918
|
if (event.message?.message_type === "text") {
|
|
@@ -4881,9 +4929,9 @@ var init_feishu = __esm({
|
|
|
4881
4929
|
}
|
|
4882
4930
|
}
|
|
4883
4931
|
}
|
|
4884
|
-
return
|
|
4932
|
+
return c2.json({ code: 0 });
|
|
4885
4933
|
});
|
|
4886
|
-
app.get("/", (
|
|
4934
|
+
app.get("/", (c2) => {
|
|
4887
4935
|
const feishuStatus = this.config.appId ? "\u5DF2\u8FDE\u63A5\uFF08\u957F\u8FDE\u63A5\u6A21\u5F0F\uFF09" : "\u672A\u914D\u7F6E";
|
|
4888
4936
|
const html = `<!DOCTYPE html>
|
|
4889
4937
|
<html lang="zh">
|
|
@@ -4957,9 +5005,9 @@ var init_feishu = __esm({
|
|
|
4957
5005
|
</script>
|
|
4958
5006
|
</body>
|
|
4959
5007
|
</html>`;
|
|
4960
|
-
return
|
|
5008
|
+
return c2.html(html);
|
|
4961
5009
|
});
|
|
4962
|
-
app.get("/health", (
|
|
5010
|
+
app.get("/health", (c2) => c2.text("OK"));
|
|
4963
5011
|
this.httpServer = serve({ fetch: app.fetch, port: this.config.port });
|
|
4964
5012
|
console.log(` Dashboard: http://localhost:${this.config.port}`);
|
|
4965
5013
|
}
|
|
@@ -6075,6 +6123,7 @@ import process2 from "node:process";
|
|
|
6075
6123
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
6076
6124
|
import fs8 from "node:fs";
|
|
6077
6125
|
import { createInterface } from "node:readline";
|
|
6126
|
+
import boxen from "boxen";
|
|
6078
6127
|
async function connectMCP() {
|
|
6079
6128
|
const mockClient = new MockMCPClient();
|
|
6080
6129
|
const tools = await registry.registerMCPServer("github", mockClient);
|
|
@@ -6098,6 +6147,18 @@ function makePromptCtx() {
|
|
|
6098
6147
|
};
|
|
6099
6148
|
}
|
|
6100
6149
|
async function startAgent() {
|
|
6150
|
+
const restoreConsole = (() => {
|
|
6151
|
+
const originalLog = console.log;
|
|
6152
|
+
const originalInfo = console.info;
|
|
6153
|
+
console.log = () => {
|
|
6154
|
+
};
|
|
6155
|
+
console.info = () => {
|
|
6156
|
+
};
|
|
6157
|
+
return () => {
|
|
6158
|
+
console.log = originalLog;
|
|
6159
|
+
console.info = originalInfo;
|
|
6160
|
+
};
|
|
6161
|
+
})();
|
|
6101
6162
|
await connectMCP();
|
|
6102
6163
|
console.log(" \u52A0\u8F7D\u63D2\u4EF6...");
|
|
6103
6164
|
for (const [name, def] of availablePlugins) {
|
|
@@ -6105,7 +6166,7 @@ async function startAgent() {
|
|
|
6105
6166
|
const tools = await pluginManager.load(def);
|
|
6106
6167
|
console.log(` \u2713 ${name} \u2014 ${tools.length} \u4E2A\u5DE5\u5177`);
|
|
6107
6168
|
} catch {
|
|
6108
|
-
console.
|
|
6169
|
+
console.error(` \u2717 ${name} \u2014 \u52A0\u8F7D\u5931\u8D25`);
|
|
6109
6170
|
}
|
|
6110
6171
|
}
|
|
6111
6172
|
console.log(" \u542F\u52A8 Channel...");
|
|
@@ -6131,20 +6192,26 @@ ${message}`);
|
|
|
6131
6192
|
});
|
|
6132
6193
|
cronService.start();
|
|
6133
6194
|
const cronJobs = cronService.list();
|
|
6195
|
+
restoreConsole();
|
|
6134
6196
|
const store = new SessionStore("default");
|
|
6135
6197
|
let messages = [];
|
|
6136
6198
|
const timestamps = /* @__PURE__ */ new Map();
|
|
6137
6199
|
const tracker = new UsageTracker(".usage/today.jsonl");
|
|
6138
6200
|
const rl = createInterface({ input: process2.stdin, output: process2.stdout });
|
|
6139
6201
|
function ask() {
|
|
6140
|
-
rl.question(
|
|
6202
|
+
rl.question(`
|
|
6203
|
+
${c.cyan}\u276F${c.reset} `, async (input) => {
|
|
6141
6204
|
const trimmed = input.trim();
|
|
6142
|
-
if (
|
|
6205
|
+
if (trimmed === "/exit") {
|
|
6143
6206
|
console.log("Bye!");
|
|
6144
6207
|
cronService.stop();
|
|
6145
6208
|
await gateway.stopAll();
|
|
6146
6209
|
await pluginManager.unloadAll();
|
|
6147
6210
|
rl.close();
|
|
6211
|
+
process2.exit(0);
|
|
6212
|
+
}
|
|
6213
|
+
if (!trimmed) {
|
|
6214
|
+
ask();
|
|
6148
6215
|
return;
|
|
6149
6216
|
}
|
|
6150
6217
|
const ctx = {
|
|
@@ -6184,22 +6251,30 @@ ${message}`);
|
|
|
6184
6251
|
const role = registry.getRole();
|
|
6185
6252
|
const toolCount = registry.getActiveTools().length;
|
|
6186
6253
|
const hooks = hookPipeline.list();
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6254
|
+
const modelName = model?.modelId || config.model.name || "unknown";
|
|
6255
|
+
const splash = boxen(
|
|
6256
|
+
[
|
|
6257
|
+
` ${c.cyan}model:${c.reset} ${c.bold}${modelName}${c.reset}`,
|
|
6258
|
+
` ${c.cyan}directory:${c.reset} ${c.bold}${process2.cwd()}${c.reset}`,
|
|
6259
|
+
` ${c.cyan}role:${c.reset} ${c.yellow}${role}${c.reset} \xB7 \u53EF\u7528\u5DE5\u5177: ${c.green}${toolCount}${c.reset} \u4E2A`,
|
|
6260
|
+
"",
|
|
6261
|
+
` ${c.dim}Sub-Agent \u6DF1\u5EA6 ${agentRegistry.getConfig().maxSpawnDepth} / \u5E76\u53D1 ${agentRegistry.getConfig().maxConcurrent} \xB7 /exit \u9000\u51FA \xB7 /help \u547D\u4EE4${c.reset}`
|
|
6262
|
+
].join("\n"),
|
|
6263
|
+
{
|
|
6264
|
+
title: "RuneClaw",
|
|
6265
|
+
titleAlignment: "left",
|
|
6266
|
+
borderStyle: "round",
|
|
6267
|
+
borderColor: "cyan",
|
|
6268
|
+
padding: 1
|
|
6269
|
+
}
|
|
6270
|
+
);
|
|
6271
|
+
console.log(splash);
|
|
6194
6272
|
console.log(
|
|
6195
|
-
|
|
6273
|
+
`${c.green}\u2713 \u5C31\u7EEA${c.reset} \xB7 ${toolCount} \u4E2A\u5DE5\u5177 \xB7 ${availablePlugins.size} \u4E2A\u63D2\u4EF6\u5DF2\u52A0\u8F7D`
|
|
6196
6274
|
);
|
|
6197
6275
|
console.log("");
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
console.log(" /agents \u2014 \u67E5\u770B\u5B50 Agent \u6267\u884C\u8BB0\u5F55");
|
|
6201
|
-
console.log("");
|
|
6202
|
-
if (fs8.existsSync("docs")) {
|
|
6276
|
+
const enableRag = process2.argv.includes("--rag");
|
|
6277
|
+
if (enableRag && fs8.existsSync("docs")) {
|
|
6203
6278
|
const files = fs8.readdirSync("docs").filter((f) => f.endsWith(".md"));
|
|
6204
6279
|
if (files.length > 0) {
|
|
6205
6280
|
console.log(` \u53D1\u73B0 ${files.length} \u4E2A\u6587\u6863\uFF0C\u81EA\u52A8\u5BFC\u5165\u77E5\u8BC6\u5E93...`);
|
|
@@ -6209,15 +6284,17 @@ ${message}`);
|
|
|
6209
6284
|
const chunks = chunkDocument(path4, text);
|
|
6210
6285
|
const embeddings = await embed(
|
|
6211
6286
|
embedFn,
|
|
6212
|
-
chunks.map((
|
|
6287
|
+
chunks.map((c2) => c2.text)
|
|
6213
6288
|
);
|
|
6214
6289
|
vectorStore.addBatch(
|
|
6215
|
-
chunks.map((
|
|
6290
|
+
chunks.map((c2, i) => ({ chunk: c2, embedding: embeddings[i] }))
|
|
6216
6291
|
);
|
|
6217
6292
|
}
|
|
6218
6293
|
console.log(` \u77E5\u8BC6\u5E93\u5C31\u7EEA\uFF0C\u5171 ${vectorStore.size()} \u4E2A\u7247\u6BB5
|
|
6219
6294
|
`);
|
|
6220
6295
|
}
|
|
6296
|
+
} else if (fs8.existsSync("docs")) {
|
|
6297
|
+
console.log(" \u63D0\u793A: \u68C0\u6D4B\u5230 docs/ \u76EE\u5F55\uFF0C\u52A0 --rag \u53C2\u6570\u53EF\u81EA\u52A8\u5BFC\u5165\u77E5\u8BC6\u5E93\n");
|
|
6221
6298
|
}
|
|
6222
6299
|
ask();
|
|
6223
6300
|
}
|
|
@@ -6264,6 +6341,7 @@ var init_main = __esm({
|
|
|
6264
6341
|
init_spawn_tools();
|
|
6265
6342
|
init_agents();
|
|
6266
6343
|
init_loader();
|
|
6344
|
+
init_theme();
|
|
6267
6345
|
config = loadConfig();
|
|
6268
6346
|
provider = createOpenAI({
|
|
6269
6347
|
baseURL: config.model.baseURL,
|
|
@@ -6358,6 +6436,7 @@ var HELP_TEXT = `RuneClaw \u2014 \u672C\u5730 AI Agent \u8FD0\u884C\u65F6
|
|
|
6358
6436
|
\u9009\u9879:
|
|
6359
6437
|
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
6360
6438
|
-v, --version \u663E\u793A\u7248\u672C\u53F7
|
|
6439
|
+
--rag \u542F\u52A8\u65F6\u81EA\u52A8\u5BFC\u5165 docs/ \u4E0B\u7684\u6587\u6863\u5230\u77E5\u8BC6\u5E93
|
|
6361
6440
|
`;
|
|
6362
6441
|
function readVersion() {
|
|
6363
6442
|
try {
|
|
@@ -6381,6 +6460,7 @@ async function main() {
|
|
|
6381
6460
|
case "start":
|
|
6382
6461
|
case "continue":
|
|
6383
6462
|
case "--continue":
|
|
6463
|
+
case "--rag":
|
|
6384
6464
|
case void 0: {
|
|
6385
6465
|
const { startAgent: startAgent2 } = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
6386
6466
|
await startAgent2().catch((error) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaodye/runeclaw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Local AI agent runtime for tools, memory, RAG, sub-agents, plugins, cron jobs, and channels.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"init": "node dist/index.js init",
|
|
22
22
|
"continue": "node dist/index.js --continue",
|
|
23
23
|
"release": "node scripts/release.mjs",
|
|
24
|
-
"prepublishOnly": "pnpm build"
|
|
24
|
+
"prepublishOnly": "pnpm build",
|
|
25
|
+
"start:rag": "node dist/index.js --rag"
|
|
25
26
|
},
|
|
26
27
|
"keywords": [
|
|
27
28
|
"ai",
|
|
@@ -44,12 +45,15 @@
|
|
|
44
45
|
"@modelcontextprotocol/client": "2.0.0-alpha.2",
|
|
45
46
|
"ai": "^6.0.206",
|
|
46
47
|
"better-sqlite3": "^12.10.1",
|
|
48
|
+
"boxen": "^8.0.1",
|
|
47
49
|
"croner": "^10.0.1",
|
|
48
50
|
"dotenv": "^17.4.2",
|
|
49
51
|
"fast-glob": "^3.3.3",
|
|
50
52
|
"hono": "^4.12.27",
|
|
51
53
|
"ink": "^7.1.1",
|
|
52
54
|
"ink-select-input": "^6.2.0",
|
|
55
|
+
"marked": "^18.0.10",
|
|
56
|
+
"marked-terminal": "^7.3.0",
|
|
53
57
|
"react": "^19.2.8",
|
|
54
58
|
"sqlite-vec": "^0.1.9",
|
|
55
59
|
"turndown": "^7.2.4",
|
|
@@ -57,9 +61,9 @@
|
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|
|
59
63
|
"@types/better-sqlite3": "^7.6.13",
|
|
60
|
-
"@types/react": "^19.2.18",
|
|
61
64
|
"@types/json-schema": "^7.0.15",
|
|
62
65
|
"@types/node": "^25.9.3",
|
|
66
|
+
"@types/react": "^19.2.18",
|
|
63
67
|
"@types/turndown": "^5.0.6",
|
|
64
68
|
"ajv": "^8.20.0",
|
|
65
69
|
"esbuild": "^0.28.1",
|