dwclaw-cli 1.1.0 → 1.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/README.md +121 -23
- package/dist/bundle.js +50 -83
- package/dist/main.js +1 -1
- package/package.json +2 -8
- package/.claude/cron/jobs.json +0 -3
- package/docker/.env +0 -31
- package/docker/Dockerfile +0 -61
- package/docker/docker-compose.yml +0 -43
- package/docker/duclaw.json +0 -9
- package/docker/entrypoint.sh +0 -27
package/README.md
CHANGED
|
@@ -1,36 +1,134 @@
|
|
|
1
|
-
#
|
|
2
|
-
1. config enviroment
|
|
3
|
-
cp .env.example .env
|
|
1
|
+
# Duclaw
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
AI Agent 管理框架 — 支持飞书 Channel、团队协作、定时任务、Skill 扩展、Web 管理界面。
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
使用 Anthropic 标准协议(兼容 Claude / GLM / Kimi 等),一行命令启动你的 AI Agent。
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
## Quick Start
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
> **前提**:Node.js >= 18,Redis 运行中(默认 `redis://localhost:6379`)
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
```bash
|
|
12
|
+
# 安装
|
|
13
|
+
npm install -g dwclaw-cli
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
# 初始化项目
|
|
16
|
+
mkdir my-agent && cd my-agent
|
|
17
|
+
duclaw init
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
# 编辑配置(见下方说明)
|
|
20
|
+
# 启动
|
|
21
|
+
duclaw start
|
|
22
|
+
```
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
支持多用户,各种组织顺序参考下面:
|
|
21
|
-
{app} : {module} : {userId} : {scope?} : {id?}
|
|
22
|
-
agent:mem:${userId}:${date?}:${cron_title?}
|
|
24
|
+
## 配置
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
app 应用命名空间 agent
|
|
26
|
-
module 功能模块 mem, dedup, task, cron
|
|
27
|
-
userId 用户标识 user_abc123
|
|
28
|
-
scope? 可选 - 子作用域 日期 20260317、分组等
|
|
29
|
-
id? 可选 - 具体标识 cron_title、messageId 等
|
|
26
|
+
`duclaw init` 会生成以下文件:
|
|
30
27
|
|
|
28
|
+
| 文件 | 说明 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| `duclaw.json` | Channel 配置(飞书 appId / appSecret) |
|
|
31
|
+
| `.env` | LLM API、Redis、OSS 等环境变量 |
|
|
32
|
+
| `skills/` | 自定义技能目录 |
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
### duclaw.json
|
|
33
35
|
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"channels": {
|
|
39
|
+
"feishu": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"appId": "YOUR_FEISHU_APP_ID",
|
|
42
|
+
"appSecret": "YOUR_FEISHU_APP_SECRET"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
34
47
|
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
### .env (必填项)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# LLM 配置(Anthropic 兼容协议)
|
|
52
|
+
export ANTHROPIC_BASE_URL="https://api.anthropic.com"
|
|
53
|
+
export ANTHROPIC_AUTH_TOKEN="your-api-key"
|
|
54
|
+
export ANTHROPIC_MODEL="claude-sonnet-4-20250514"
|
|
55
|
+
|
|
56
|
+
# Redis
|
|
57
|
+
export REDIS_URL="redis://localhost:6379"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
支持的 LLM 提供商(只要兼容 Anthropic Messages API):
|
|
61
|
+
|
|
62
|
+
| 提供商 | BASE_URL | 示例模型 |
|
|
63
|
+
|--------|----------|----------|
|
|
64
|
+
| Anthropic | `https://api.anthropic.com` | `claude-sonnet-4-20250514` |
|
|
65
|
+
| 智谱 GLM | `https://open.bigmodel.cn/api/anthropic` | `glm-5` |
|
|
66
|
+
| Kimi | `https://api.moonshot.cn/anthropic` | `kimi-k2.5` |
|
|
67
|
+
|
|
68
|
+
> 如果 API 使用 Bearer Token 风格,需额外设置 `ANTHROPIC_AUTH_STYLE="bearer"`
|
|
69
|
+
|
|
70
|
+
## 飞书机器人配置
|
|
71
|
+
|
|
72
|
+
1. 前往 [飞书开放平台](https://open.feishu.cn/app) 创建应用
|
|
73
|
+
2. 添加权限:`im:message`(消息读写)
|
|
74
|
+
3. 添加事件订阅:`im.message.receive_v1`,订阅方式选择 **长连接**
|
|
75
|
+
4. 发布应用版本
|
|
76
|
+
5. 将 appId 和 appSecret 填入 `duclaw.json`
|
|
77
|
+
|
|
78
|
+
## CLI 命令
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
duclaw init [目录] # 初始化项目
|
|
82
|
+
duclaw start # 启动服务
|
|
83
|
+
duclaw # 等同于 start
|
|
84
|
+
duclaw --help # 帮助
|
|
85
|
+
duclaw --version # 版本
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 数据目录
|
|
89
|
+
|
|
90
|
+
运行时数据默认存储在 `~/.duclaw/`:
|
|
91
|
+
|
|
92
|
+
| 路径 | 说明 | 环境变量覆盖 |
|
|
93
|
+
|------|------|-------------|
|
|
94
|
+
| `~/.duclaw/cron/jobs.json` | 定时任务配置 | `JOB_PATH` |
|
|
95
|
+
| `~/.duclaw/cron/` | 任务执行历史 | `JOB_HISTORY_DIR` |
|
|
96
|
+
| `~/.duclaw/data/` | 临时文件 | `APP_TEMP_DIR` |
|
|
97
|
+
|
|
98
|
+
## Docker 部署
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cd docker
|
|
102
|
+
# 编辑 .env 和 duclaw.json
|
|
103
|
+
docker compose up -d
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
详见 [docker/README.md](docker/README.md)。
|
|
107
|
+
|
|
108
|
+
## 开发
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
git clone <repo>
|
|
112
|
+
cd agent-template-ts
|
|
113
|
+
pnpm install
|
|
114
|
+
|
|
115
|
+
# 复制并编辑配置
|
|
116
|
+
cp .env.example .env
|
|
117
|
+
# 编辑 duclaw.json
|
|
118
|
+
|
|
119
|
+
# 启动开发
|
|
120
|
+
pnpm start
|
|
121
|
+
|
|
122
|
+
# 构建
|
|
123
|
+
pnpm build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 依赖
|
|
127
|
+
|
|
128
|
+
- Node.js >= 18
|
|
129
|
+
- Redis(无密码)
|
|
130
|
+
- 飞书开放平台应用(用于 Channel 接入)
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
Private
|
package/dist/bundle.js
CHANGED
|
@@ -30122,65 +30122,6 @@ var require_node_cron = __commonJS({
|
|
|
30122
30122
|
}
|
|
30123
30123
|
});
|
|
30124
30124
|
|
|
30125
|
-
// package.json
|
|
30126
|
-
var require_package3 = __commonJS({
|
|
30127
|
-
"package.json"(exports2, module2) {
|
|
30128
|
-
module2.exports = {
|
|
30129
|
-
packageManager: "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
|
|
30130
|
-
name: "dwclaw-cli",
|
|
30131
|
-
version: "1.1.0",
|
|
30132
|
-
description: "AI Agent \u7BA1\u7406\u6846\u67B6 - \u652F\u6301\u98DE\u4E66 Channel\u3001\u56E2\u961F\u534F\u4F5C\u3001\u5B9A\u65F6\u4EFB\u52A1\u3001Web \u7BA1\u7406\u754C\u9762",
|
|
30133
|
-
bin: {
|
|
30134
|
-
duclaw: "./dist/main.js"
|
|
30135
|
-
},
|
|
30136
|
-
files: [
|
|
30137
|
-
"dist/",
|
|
30138
|
-
"skills/",
|
|
30139
|
-
".claude/cron/",
|
|
30140
|
-
"docker/Dockerfile",
|
|
30141
|
-
"docker/docker-compose.yml",
|
|
30142
|
-
"docker/entrypoint.sh",
|
|
30143
|
-
"docker/duclaw.json",
|
|
30144
|
-
"docker/.env"
|
|
30145
|
-
],
|
|
30146
|
-
scripts: {
|
|
30147
|
-
start: "npx tsx src/main.ts",
|
|
30148
|
-
"dev:web": "cd web && pnpm dev",
|
|
30149
|
-
build: "node build.js",
|
|
30150
|
-
"build:tsc": "tsc",
|
|
30151
|
-
"build:web": "cd web && pnpm build",
|
|
30152
|
-
"build:all": "pnpm build:web && pnpm build"
|
|
30153
|
-
},
|
|
30154
|
-
dependencies: {
|
|
30155
|
-
"@anthropic-ai/sdk": "^0.78.0",
|
|
30156
|
-
"@hono/node-server": "^1.19.11",
|
|
30157
|
-
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
30158
|
-
"ali-oss": "^6.23.0",
|
|
30159
|
-
"better-sqlite3": "^12.8.0",
|
|
30160
|
-
chokidar: "^5.0.0",
|
|
30161
|
-
dayjs: "^1.11.20",
|
|
30162
|
-
diff: "^8.0.3",
|
|
30163
|
-
dotenv: "^17.3.1",
|
|
30164
|
-
hono: "^4.12.9",
|
|
30165
|
-
"node-cron": "^4.2.1",
|
|
30166
|
-
redis: "^5.11.0"
|
|
30167
|
-
},
|
|
30168
|
-
devDependencies: {
|
|
30169
|
-
"@types/ali-oss": "^6.23.3",
|
|
30170
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
30171
|
-
"@types/diff": "^8.0.0",
|
|
30172
|
-
"@types/node": "^25.3.3",
|
|
30173
|
-
"@types/node-cron": "^3.0.11",
|
|
30174
|
-
esbuild: "^0.27.5",
|
|
30175
|
-
"javascript-obfuscator": "^5.4.1",
|
|
30176
|
-
tsx: "^4.21.0",
|
|
30177
|
-
typescript: "^5.9.3",
|
|
30178
|
-
vitest: "^4.0.18"
|
|
30179
|
-
}
|
|
30180
|
-
};
|
|
30181
|
-
}
|
|
30182
|
-
});
|
|
30183
|
-
|
|
30184
30125
|
// src/cli/index.ts
|
|
30185
30126
|
var import_fs = require("fs");
|
|
30186
30127
|
var import_path = require("path");
|
|
@@ -30301,14 +30242,7 @@ function printHelp() {
|
|
|
30301
30242
|
`);
|
|
30302
30243
|
}
|
|
30303
30244
|
function printVersion() {
|
|
30304
|
-
|
|
30305
|
-
const pkgRoot = (0, import_path.resolve)(__dirname_m, "..", "..");
|
|
30306
|
-
const pkgContent = (0, import_fs.readFileSync)((0, import_path.resolve)(pkgRoot, "package.json"), "utf-8");
|
|
30307
|
-
const pkg = JSON.parse(pkgContent);
|
|
30308
|
-
console.log(`duclaw v${pkg.version}`);
|
|
30309
|
-
} catch {
|
|
30310
|
-
console.log(`duclaw (version unknown)`);
|
|
30311
|
-
}
|
|
30245
|
+
console.log(`duclaw v${true ? "1.3.0" : "unknown"}`);
|
|
30312
30246
|
}
|
|
30313
30247
|
function getDuclawTemplate() {
|
|
30314
30248
|
return {
|
|
@@ -37801,7 +37735,11 @@ var updateJob = (id, syntax, title, description) => {
|
|
|
37801
37735
|
return { ...job };
|
|
37802
37736
|
};
|
|
37803
37737
|
var listJobs = () => {
|
|
37804
|
-
const
|
|
37738
|
+
const jobPath = getJobPath();
|
|
37739
|
+
if (!(0, import_fs5.existsSync)(jobPath)) {
|
|
37740
|
+
return [];
|
|
37741
|
+
}
|
|
37742
|
+
const text2 = (0, import_fs5.readFileSync)(jobPath, `utf-8`);
|
|
37805
37743
|
if (!text2) {
|
|
37806
37744
|
return [];
|
|
37807
37745
|
}
|
|
@@ -38684,9 +38622,9 @@ var NodeFsHandler = class {
|
|
|
38684
38622
|
if (this.fsw.closed) {
|
|
38685
38623
|
return;
|
|
38686
38624
|
}
|
|
38687
|
-
const
|
|
38625
|
+
const dirname7 = sp.dirname(file);
|
|
38688
38626
|
const basename4 = sp.basename(file);
|
|
38689
|
-
const parent = this.fsw._getWatchedDir(
|
|
38627
|
+
const parent = this.fsw._getWatchedDir(dirname7);
|
|
38690
38628
|
let prevStats = stats;
|
|
38691
38629
|
if (parent.has(basename4))
|
|
38692
38630
|
return;
|
|
@@ -38713,7 +38651,7 @@ var NodeFsHandler = class {
|
|
|
38713
38651
|
prevStats = newStats2;
|
|
38714
38652
|
}
|
|
38715
38653
|
} catch (error) {
|
|
38716
|
-
this.fsw._remove(
|
|
38654
|
+
this.fsw._remove(dirname7, basename4);
|
|
38717
38655
|
}
|
|
38718
38656
|
} else if (parent.has(basename4)) {
|
|
38719
38657
|
const at = newStats.atimeMs;
|
|
@@ -39877,6 +39815,14 @@ var executeCustomJob = async (job) => {
|
|
|
39877
39815
|
return await handler(job);
|
|
39878
39816
|
};
|
|
39879
39817
|
var startScheduler = () => {
|
|
39818
|
+
const jobPath = getJobPath();
|
|
39819
|
+
const jobDir = require("path").dirname(jobPath);
|
|
39820
|
+
if (!(0, import_fs6.existsSync)(jobDir)) {
|
|
39821
|
+
(0, import_fs6.mkdirSync)(jobDir, { recursive: true });
|
|
39822
|
+
}
|
|
39823
|
+
if (!(0, import_fs6.existsSync)(jobPath)) {
|
|
39824
|
+
(0, import_fs6.writeFileSync)(jobPath, JSON.stringify({ jobs: [] }, null, " "), "utf-8");
|
|
39825
|
+
}
|
|
39880
39826
|
loadAndScheduleJobs();
|
|
39881
39827
|
watchJobsFile();
|
|
39882
39828
|
};
|
|
@@ -40164,13 +40110,8 @@ var getSkillPaths = () => {
|
|
|
40164
40110
|
let currentDir = __dirname_m;
|
|
40165
40111
|
let projectRoot = "";
|
|
40166
40112
|
while (currentDir !== "/") {
|
|
40167
|
-
|
|
40168
|
-
if ((0, import_fs8.existsSync)(claudePath) && (0, import_fs8.statSync)(claudePath).isDirectory()) {
|
|
40113
|
+
if ((0, import_fs8.existsSync)((0, import_path14.join)(currentDir, "duclaw.json"))) {
|
|
40169
40114
|
projectRoot = currentDir;
|
|
40170
|
-
const skillsPath = (0, import_path14.join)(claudePath, "skills");
|
|
40171
|
-
if ((0, import_fs8.existsSync)(skillsPath)) {
|
|
40172
|
-
paths.push(skillsPath);
|
|
40173
|
-
}
|
|
40174
40115
|
break;
|
|
40175
40116
|
}
|
|
40176
40117
|
const parentDir = (0, import_path14.dirname)(currentDir);
|
|
@@ -40183,9 +40124,13 @@ var getSkillPaths = () => {
|
|
|
40183
40124
|
paths.push(rootSkillsPath);
|
|
40184
40125
|
}
|
|
40185
40126
|
}
|
|
40186
|
-
const
|
|
40187
|
-
if ((0, import_fs8.existsSync)(
|
|
40188
|
-
paths.push(
|
|
40127
|
+
const duclawSkillsPath = (0, import_path14.join)((0, import_os3.homedir)(), ".duclaw", "skills");
|
|
40128
|
+
if ((0, import_fs8.existsSync)(duclawSkillsPath) && (0, import_fs8.statSync)(duclawSkillsPath).isDirectory()) {
|
|
40129
|
+
paths.push(duclawSkillsPath);
|
|
40130
|
+
}
|
|
40131
|
+
const agentsSkillsPath = (0, import_path14.join)((0, import_os3.homedir)(), ".agents", "skills");
|
|
40132
|
+
if ((0, import_fs8.existsSync)(agentsSkillsPath) && (0, import_fs8.statSync)(agentsSkillsPath).isDirectory()) {
|
|
40133
|
+
paths.push(agentsSkillsPath);
|
|
40189
40134
|
}
|
|
40190
40135
|
return paths;
|
|
40191
40136
|
};
|
|
@@ -41455,7 +41400,7 @@ ${getSkillMeta()}
|
|
|
41455
41400
|
const anthropicOptions = {
|
|
41456
41401
|
baseURL: process.env.ANTHROPIC_BASE_URL,
|
|
41457
41402
|
model: process.env.ANTHROPIC_MODEL,
|
|
41458
|
-
apiKey: process.env.ANTHROPIC_AUTH_TOKEN
|
|
41403
|
+
apiKey: process.env.ANTHROPIC_AUTH_TOKEN,
|
|
41459
41404
|
authStyle: process.env.ANTHROPIC_AUTH_STYLE || void 0
|
|
41460
41405
|
};
|
|
41461
41406
|
const llmClient = createAnthropicAdapter(anthropicOptions);
|
|
@@ -46364,9 +46309,8 @@ toolRoutes.get("/skills/:name", (c) => {
|
|
|
46364
46309
|
var systemRoutes = new Hono2();
|
|
46365
46310
|
var startTime = Date.now();
|
|
46366
46311
|
systemRoutes.get("/system/info", (c) => {
|
|
46367
|
-
const pkg = require_package3();
|
|
46368
46312
|
return c.json({
|
|
46369
|
-
version:
|
|
46313
|
+
version: true ? "1.3.0" : "unknown",
|
|
46370
46314
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
46371
46315
|
env: process.env.NODE_ENV || "development",
|
|
46372
46316
|
nodeVersion: process.version
|
|
@@ -46401,8 +46345,31 @@ var cliResult = handleCliCommand(process.argv.slice(2));
|
|
|
46401
46345
|
if (cliResult !== void 0) {
|
|
46402
46346
|
process.exit(cliResult);
|
|
46403
46347
|
}
|
|
46348
|
+
function validateEnv() {
|
|
46349
|
+
const errors = [];
|
|
46350
|
+
const token = process.env.ANTHROPIC_AUTH_TOKEN;
|
|
46351
|
+
if (!token || token === "your-api-key-here") {
|
|
46352
|
+
errors.push(" - ANTHROPIC_AUTH_TOKEN \u672A\u8BBE\u7F6E\u6216\u4ECD\u4E3A\u5360\u4F4D\u7B26\uFF0C\u8BF7\u5728 .env \u4E2D\u586B\u5199\u6709\u6548\u7684 API Key");
|
|
46353
|
+
}
|
|
46354
|
+
if (!process.env.ANTHROPIC_BASE_URL) {
|
|
46355
|
+
errors.push(" - ANTHROPIC_BASE_URL \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u5728 .env \u4E2D\u586B\u5199 API \u5730\u5740");
|
|
46356
|
+
}
|
|
46357
|
+
if (!process.env.REDIS_URL) {
|
|
46358
|
+
errors.push(" - REDIS_URL \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u5728 .env \u4E2D\u586B\u5199 Redis \u8FDE\u63A5\u5730\u5740\uFF08\u5982 redis://localhost:6379\uFF09");
|
|
46359
|
+
}
|
|
46360
|
+
if (errors.length > 0) {
|
|
46361
|
+
console.error(`
|
|
46362
|
+
[\u542F\u52A8\u5931\u8D25] \u73AF\u5883\u53D8\u91CF\u6821\u9A8C\u4E0D\u901A\u8FC7\uFF1A
|
|
46363
|
+
${errors.join("\n")}
|
|
46364
|
+
`);
|
|
46365
|
+
console.error(` \u63D0\u793A\uFF1A\u8FD0\u884C duclaw init \u53EF\u751F\u6210 .env \u6A21\u677F
|
|
46366
|
+
`);
|
|
46367
|
+
process.exit(1);
|
|
46368
|
+
}
|
|
46369
|
+
}
|
|
46404
46370
|
async function main() {
|
|
46405
46371
|
loadEnv();
|
|
46372
|
+
validateEnv();
|
|
46406
46373
|
const cfg = loadConfig();
|
|
46407
46374
|
const { registry: registry2 } = createDefaultChannels();
|
|
46408
46375
|
const channels = getAllChannels(registry2);
|