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 CHANGED
@@ -1,36 +1,134 @@
1
- # Quick Start
2
- 1. config enviroment
3
- cp .env.example .env
1
+ # Duclaw
4
2
 
5
- 2. set up redis server (without password)
3
+ AI Agent 管理框架 支持飞书 Channel、团队协作、定时任务、Skill 扩展、Web 管理界面。
6
4
 
7
- 3. Register Feishu Bot (https://open.feishu.cn/app) Get im:message 权限
5
+ 使用 Anthropic 标准协议(兼容 Claude / GLM / Kimi 等),一行命令启动你的 AI Agent。
8
6
 
9
- 飞书事件与回调,添加事件 im.message.receive_v1
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
- 4. pnpm install
15
+ # 初始化项目
16
+ mkdir my-agent && cd my-agent
17
+ duclaw init
16
18
 
17
- 5. pnpm start
19
+ # 编辑配置(见下方说明)
20
+ # 启动
21
+ duclaw start
22
+ ```
18
23
 
19
- # IMPORTANT
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
- 安装腾讯的skillcli 可以让智能体更强大
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
- # Todo Plan
36
- 定时,SOUL,和控制浏览器,前端管理界面
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
- try {
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 text2 = (0, import_fs5.readFileSync)(getJobPath(), `utf-8`);
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 dirname8 = sp.dirname(file);
38625
+ const dirname7 = sp.dirname(file);
38688
38626
  const basename4 = sp.basename(file);
38689
- const parent = this.fsw._getWatchedDir(dirname8);
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(dirname8, basename4);
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
- const claudePath = (0, import_path14.join)(currentDir, ".claude");
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 globalSkillsPath = (0, import_path14.join)((0, import_os3.homedir)(), ".agents", "skills");
40187
- if ((0, import_fs8.existsSync)(globalSkillsPath) && (0, import_fs8.statSync)(globalSkillsPath).isDirectory()) {
40188
- paths.push(globalSkillsPath);
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 || "a4fd749ac2824c55a88946fc13a50e23.VPMAm8nUNaAWWS3E",
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: pkg.version || "unknown",
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);