@windaka-erp/erp-cli 0.3.2 → 0.4.1

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,93 +1,119 @@
1
1
  # @windaka-erp/erp-cli
2
2
 
3
- 物达通(WindaKa)物业管理 ERP 系统命令行工具,为人类和 AI Agent 设计。
3
+ > 物达通(WindaKa)物业管理 ERP 的命令行工具,为人类和 AI Agent 设计。
4
+ > 一句话:**用户说自然语言,AI Agent 通过本工具操作 ERP + 检索知识库**。
4
5
 
5
- ## 安装
6
+ ## 它能做什么
6
7
 
7
- 以下两种方式**任选其一**:
8
+ - **知识库问答**:对接 RAGFlow,问"消防巡检怎么处理""报修流程""物业费标准"等问题,先检索知识库拿到原文,再结合上下文回答。
9
+ - **ERP 操作**:查/建工单、分派、接单、确认、回访等工单全流程;查看项目、通讯录、费用(逐步完善中)。
10
+ - **AI Agent 友好**:统一 JSON 输出、`--dry-run` 预览、危险操作确认、意图路由文档,AI 装上 Skill 即用。
8
11
 
9
- **方式一 — 从 npm 安装(推荐):**
12
+ ## 安装
10
13
 
11
14
  ```bash
12
- # 安装 CLI
13
15
  npm install -g @windaka-erp/erp-cli
14
-
15
- # 安装 Skills(必需,让 AI Agent 知道如何使用 CLI)
16
- npx skills add windaka-erp/erp-cli -y -g
17
16
  ```
18
17
 
19
- **方式二 从源码安装:**
18
+ > 二进制已打包进 npm 包(含 Windows/macOS/Linux 的 x64/arm64 六个平台),安装即用,无需联网下载。
19
+
20
+ 国内用户用淘宝镜像加速:
20
21
 
21
22
  ```bash
22
- git clone https://github.com/windaka-erp/erp-cli.git
23
- cd erp-cli
24
- go build -o erp-cli .
23
+ npm install -g @windaka-erp/erp-cli --registry=https://registry.npmmirror.com
24
+ ```
25
25
 
26
- # 安装 Skills(必需)
27
- npx skills add windaka-erp/erp-cli -y -g
26
+ 验证:
27
+
28
+ ```bash
29
+ erp-cli --version
28
30
  ```
29
31
 
30
32
  ## 快速开始
31
33
 
32
- ### 人类用户
34
+ ### 1. 配置 ERP 地址
35
+
36
+ ```bash
37
+ erp-cli config init
38
+ # 默认连生产环境,也可指定:
39
+ erp-cli config init --base-url https://your-erp-server.com/prod-api
40
+ ```
41
+
42
+ ### 2. 登录(Device Flow,扫码/账号授权)
33
43
 
34
44
  ```bash
35
- # 1. 初始化配置
36
- erp-cli --as me config init
45
+ erp-cli auth login
46
+ # 输出登录链接 浏览器打开授权 → 自动保存 token
47
+ ```
37
48
 
38
- # 2. 登录
39
- erp-cli --as me auth login
49
+ ### 3. 开始用
40
50
 
41
- # 3. 开始使用
42
- erp-cli --as me workorder +list
51
+ ```bash
52
+ erp-cli workorder +list # 我的待办工单
53
+ erp-cli workorder +info --id 123 --dept-id 456 # 工单详情
54
+ erp-cli knowledge +search --question "报修流程" # 检索知识库
43
55
  ```
44
56
 
45
- ### AI Agent
57
+ ## 配置知识库(RAGFlow)
46
58
 
47
- > 以下步骤面向 AI Agent。详细步骤见 [INSTALL.md](./INSTALL.md)。
59
+ 知识库检索走独立的 RAGFlow 服务,首次用要配地址、API Key、知识库 ID:
48
60
 
49
61
  ```bash
50
- # 安装 CLI
51
- npm install -g @windaka-erp/erp-cli
62
+ erp-cli config ragflow
63
+ # 交互式引导:输入 RAGFlow 地址、API Key,自动列出知识库让你选
64
+ ```
52
65
 
53
- # 安装 Skills(必需)
54
- npx skills add windaka-erp/erp-cli -y -g
66
+ 验证:
55
67
 
56
- # 验证
57
- erp-cli --help
68
+ ```bash
69
+ erp-cli knowledge +check # 检查 RAGFlow 连通性
70
+ erp-cli knowledge +list-datasets # 列出知识库
58
71
  ```
59
72
 
60
- 安装完成即可使用。知识库检索无需登录;需要登录的操作(如工单查询)会在调用时自动引导。
73
+ > 知识库检索不需要 ERP 登录,走 RAGFlow 自己的 API Key。
61
74
 
62
- ## 命令
75
+ ## 命令一览
63
76
 
64
77
  ```
65
- erp-cli --as <id> config init/show/set/remove # 配置管理
66
- erp-cli --as <id> auth login/login-start/login-wait # 登录
67
- erp-cli --as <id> auth status/logout/list # 认证状态
68
- erp-cli --as <id> api GET/POST <path> # 原始 API 调用
69
- erp-cli --as <id> knowledge +search/+list-datasets # 知识库检索
70
- erp-cli --as <id> workorder +list/+create # 工单操作
78
+ config init/show/set/remove # 配置管理(ERP 地址、RAGFlow)
79
+ config ragflow # 交互式配置 RAGFlow 知识库
80
+ auth login/login-start/login-wait # 登录(两步登录适合 AI Agent)
81
+ auth status/logout # 登录状态
82
+ knowledge +search # 检索知识库(核心高频)
83
+ knowledge +list-datasets # 列出知识库
84
+ knowledge +check # 检查 RAGFlow 连通性
85
+ workorder +list # 我的待办工单
86
+ workorder +handle-list # 我经办的工单(含历史)
87
+ workorder +info # 工单详情(含处理记录)
88
+ workorder +search-user # 搜索员工(分派/转单用)
89
+ workorder +apportion # 分派工单
90
+ api GET/POST <path> # 原始 API 调用(兜底)
71
91
  ```
72
92
 
73
- ## 参数
93
+ 所有命令加 `--help` 看详细参数,加 `--dry-run` 预览请求不实际执行。
74
94
 
75
- - `--as <id>` — 用户标识(必填),企业微信用 Sender ID,本地用任意稳定标识(如 `me`)
95
+ ## 全局参数
96
+
97
+ - `--profile <name>` — 切换环境(prod/staging),默认 default
76
98
  - `--dry-run` — 预览请求内容,不实际执行
99
+ - `--format json|table|csv` — 输出格式,默认 json
100
+
101
+ > 单用户模式:一个环境(profile)一个登录 token,命令不带任何身份参数。
77
102
 
78
- ## Agent Skills
103
+ ## AI Agent 使用
79
104
 
80
- | Skill | 说明 |
81
- |-------|------|
82
- | `erp-cli` | ERP 系统总入口:知识库检索、工单管理、认证配置、意图路由 |
105
+ 本工具为 AI Agent 设计。配合 **Skills**(AI 使用文档,含意图路由、命令参数、行为规则)使用,AI 即可:
106
+ - 听懂用户口语("我的待办""消防巡检怎么处理")→ 自动选对命令
107
+ - 写操作前确认、危险操作 dry-run
108
+ - 知识库优先:非 ERP 操作的问题默认先检索知识库
83
109
 
84
- 安装 Skills 后,AI Agent 会自动读取 [skills/SKILL.md](./skills/SKILL.md) 获取完整的使用指南。
110
+ Skills 由作者单独发布,安装方式见对应平台说明。
85
111
 
86
- ## 要求
112
+ ## 环境要求
87
113
 
88
114
  - Node.js >= 14.0.0
89
- - 支持 Windows (x64/arm64)、macOS (x64/arm64)、Linux (x64/arm64)
115
+ - 平台:Windows / macOS / Linux(均支持 x64arm64
90
116
 
91
117
  ## License
92
118
 
93
- MIT
119
+ MIT
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@windaka-erp/erp-cli",
3
- "version": "0.3.2",
4
- "description": "ERP CLI - command-line tool for Windaka ERP system",
3
+ "version": "0.4.1",
4
+ "description": "物达通(WindaKa)物业管理 ERP 命令行工具,为人类和 AI Agent 设计",
5
5
  "bin": {
6
6
  "erp-cli": "scripts/run.js"
7
7
  },
8
- "scripts": {
9
- "postinstall": "node scripts/install.js"
10
- },
11
8
  "files": [
12
9
  "scripts/",
13
10
  "bin/",
@@ -36,8 +33,5 @@
36
33
  "cpu": [
37
34
  "x64",
38
35
  "arm64"
39
- ],
40
- "dependencies": {
41
- "@windaka-erp/erp-cli": "^0.2.0"
42
- }
36
+ ]
43
37
  }
package/scripts/run.js CHANGED
@@ -1,218 +1,53 @@
1
1
  #!/usr/bin/env node
2
- const { execFileSync, execSync } = require("child_process");
2
+ // run.js erp-cli 启动器。二进制已打包进 npm 包的 bin/ 目录,
3
+ // 按当前平台选择对应二进制执行。无需联网下载,不依赖 GitHub。
4
+ const { execFileSync } = require("child_process");
3
5
  const path = require("path");
4
6
  const fs = require("fs");
5
- const https = require("https");
6
- const http = require("http");
7
7
 
8
- const BINARY_NAME = "erp-cli";
9
- const VERSION = require("../package.json").version;
10
- const GITHUB_REPO = "windaka-erp/erp-cli";
11
- const GITEE_REPO = "xing-wenkai/erp-cli";
8
+ // Node 平台/架构 → 二进制文件名片段
9
+ const OS_MAP = { win32: "windows", darwin: "darwin", linux: "linux" };
10
+ const ARCH_MAP = { x64: "amd64", arm64: "arm64" };
12
11
 
13
- function getBinaryPath() {
14
- const binDir = path.join(__dirname, "..", "bin");
15
-
16
- const candidates = [
17
- path.join(binDir, `${BINARY_NAME}.exe`), // bin/erp-cli.exe (Windows)
18
- path.join(binDir, BINARY_NAME), // bin/erp-cli (macOS/Linux)
19
- path.join(__dirname, "..", `${BINARY_NAME}.exe`), // 根目录编译产物 (Windows)
20
- path.join(__dirname, "..", BINARY_NAME), // 根目录编译产物 (macOS/Linux)
21
- ];
22
-
23
- for (const p of candidates) {
24
- if (fs.existsSync(p)) return p;
25
- }
26
-
27
- console.error(`Error: ${BINARY_NAME} binary not found.`);
28
- console.error(`Please run: npm install`);
29
- process.exit(1);
30
- }
31
-
32
- // ── 静默自动更新 ──
33
-
34
- function getPlatformInfo() {
35
- const platform = process.platform;
36
- const arch = process.arch;
37
-
38
- const map = {
39
- win32_x64: { target: "windows-amd64", ext: ".exe" },
40
- win32_arm64: { target: "windows-arm64", ext: ".exe" },
41
- darwin_x64: { target: "darwin-amd64", ext: "" },
42
- darwin_arm64: { target: "darwin-arm64", ext: "" },
43
- linux_x64: { target: "linux-amd64", ext: "" },
44
- linux_arm64: { target: "linux-arm64", ext: "" },
45
- };
46
-
47
- return map[`${platform}_${arch}`] || null;
48
- }
49
-
50
- function getInstalledVersion(binaryPath) {
51
- try {
52
- const output = execSync(`"${binaryPath}" --version`, {
53
- encoding: "utf8",
54
- timeout: 5000,
55
- });
56
- const match = output.match(/version\s+v?(\S+)/i);
57
- return match ? match[1].replace(/^v/, "") : null;
58
- } catch {
59
- return null;
12
+ function getBinaryName() {
13
+ const os = OS_MAP[process.platform];
14
+ const arch = ARCH_MAP[process.arch];
15
+ if (!os || !arch) {
16
+ console.error(
17
+ `Error: unsupported platform ${process.platform}-${process.arch}.`
18
+ );
19
+ console.error(
20
+ `Supported: win32-x64, win32-arm64, darwin-x64, darwin-arm64, linux-x64, linux-arm64.`
21
+ );
22
+ process.exit(1);
60
23
  }
24
+ const ext = process.platform === "win32" ? ".exe" : "";
25
+ return `erp-cli-${os}-${arch}${ext}`;
61
26
  }
62
27
 
63
- function fetchJSON(url) {
64
- return new Promise((resolve, reject) => {
65
- const client = url.startsWith("https") ? https : http;
66
- client
67
- .get(url, { timeout: 5000 }, (res) => {
68
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
69
- fetchJSON(res.headers.location).then(resolve, reject);
70
- return;
71
- }
72
- if (res.statusCode !== 200) {
73
- reject(new Error(`HTTP ${res.statusCode}`));
74
- return;
75
- }
76
- let data = "";
77
- res.on("data", (chunk) => (data += chunk));
78
- res.on("end", () => {
79
- try {
80
- resolve(JSON.parse(data));
81
- } catch (e) {
82
- reject(e);
83
- }
84
- });
85
- })
86
- .on("error", reject)
87
- .on("timeout", function () {
88
- this.destroy();
89
- reject(new Error("timeout"));
90
- });
91
- });
92
- }
93
-
94
- function downloadFile(url, dest) {
95
- return new Promise((resolve, reject) => {
96
- const client = url.startsWith("https") ? https : http;
97
- client
98
- .get(url, (res) => {
99
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
100
- downloadFile(res.headers.location, dest).then(resolve, reject);
101
- return;
102
- }
103
- if (res.statusCode !== 200) {
104
- reject(new Error(`HTTP ${res.statusCode}`));
105
- return;
106
- }
107
- const stream = fs.createWriteStream(dest);
108
- res.pipe(stream);
109
- stream.on("finish", () => {
110
- stream.close();
111
- if (process.platform !== "win32") {
112
- fs.chmodSync(dest, 0o755);
113
- }
114
- resolve();
115
- });
116
- stream.on("error", reject);
117
- })
118
- .on("error", reject);
119
- });
120
- }
121
-
122
- async function silentAutoUpdate() {
123
- try {
124
- const binary = getBinaryPath();
125
- const installedVer = getInstalledVersion(binary);
126
-
127
- // 如果已安装版本和 package.json 一致,跳过
128
- if (installedVer === VERSION.replace(/^v/, "")) return;
129
-
130
- // 已安装版本比 package.json 新?跳过
131
- if (installedVer && installedVer > VERSION.replace(/^v/, "")) return;
28
+ function main() {
29
+ const binary = path.join(__dirname, "..", "bin", getBinaryName());
132
30
 
133
- // 检查上次更新时间,每天最多检查一次
134
- const updateMarker = path.join(
135
- require("os").homedir(),
136
- ".erp-cli",
137
- ".last-update-check"
31
+ if (!fs.existsSync(binary)) {
32
+ console.error(`Error: binary not found at ${binary}.`);
33
+ console.error(
34
+ `Try reinstalling: npm install -g @windaka-erp/erp-cli`
138
35
  );
139
- try {
140
- const lastCheck = parseInt(fs.readFileSync(updateMarker, "utf8"), 10);
141
- const ONE_DAY = 24 * 60 * 60 * 1000;
142
- if (Date.now() - lastCheck < ONE_DAY) return;
143
- } catch {
144
- // 文件不存在或读取失败,继续检查
145
- }
146
- if (installedVer && installedVer > VERSION.replace(/^v/, "")) return;
147
-
148
- // 需要更新:查询 Release 获取最新版本号(GitHub 优先,Gitee 回退)
149
- let release;
150
- try {
151
- release = await fetchJSON(
152
- `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`
153
- );
154
- } catch {
155
- // GitHub 不通,回退 Gitee API
156
- release = await fetchJSON(
157
- `https://gitee.com/api/v5/repos/${GITEE_REPO}/releases/latest`
158
- );
159
- }
160
-
161
- const latestTag = release.tag_name; // e.g. "v0.3.0"
162
- const latestVer = latestTag.replace(/^v/, "");
163
-
164
- // 最新版和 package.json 版本不一致?等 npm update 后再说
165
- if (latestVer !== VERSION.replace(/^v/, "")) return;
166
-
167
- // 下载新版本
168
- const info = getPlatformInfo();
169
- if (!info) return;
170
-
171
- const filename = `${BINARY_NAME}-${info.target}${info.ext}`;
172
- const asset = release.assets.find((a) => a.name === filename);
173
- if (!asset) return;
174
-
175
- const binDir = path.join(__dirname, "..", "bin");
176
- const binaryPath = path.join(binDir, `${BINARY_NAME}${info.ext}`);
177
-
178
- // 下载到临时文件,完成后原子替换
179
- const tmpPath = binaryPath + ".tmp";
180
- await downloadFile(asset.browser_download_url, tmpPath);
181
-
182
- // 替换旧二进制
183
- fs.renameSync(tmpPath, binaryPath);
36
+ process.exit(1);
37
+ }
184
38
 
185
- // 写入本次检查时间
39
+ // 非 Windows 确保可执行权限(npm 打包可能丢失)
40
+ if (process.platform !== "win32") {
186
41
  try {
187
- const markerDir = path.join(require("os").homedir(), ".erp-cli");
188
- if (!fs.existsSync(markerDir)) fs.mkdirSync(markerDir, { recursive: true });
189
- fs.writeFileSync(markerDir + "/.last-update-check", String(Date.now()));
42
+ fs.chmodSync(binary, 0o755);
190
43
  } catch {
191
- // 写入失败不影响
44
+ // 忽略权限错误
192
45
  }
193
- } catch {
194
- // 静默失败,不影响正常使用
195
- // 即使失败也写入检查时间,避免频繁重试
196
- try {
197
- const markerDir = path.join(require("os").homedir(), ".erp-cli");
198
- if (!fs.existsSync(markerDir)) fs.mkdirSync(markerDir, { recursive: true });
199
- fs.writeFileSync(markerDir + "/.last-update-check", String(Date.now()));
200
- } catch {}
201
46
  }
202
- }
203
47
 
204
- function main() {
205
- // 后台静默更新,不阻塞主流程
206
- silentAutoUpdate().catch(() => {});
207
-
208
- const binary = getBinaryPath();
209
48
  const args = process.argv.slice(2);
210
-
211
49
  try {
212
- execFileSync(binary, args, {
213
- stdio: "inherit",
214
- env: { ...process.env },
215
- });
50
+ execFileSync(binary, args, { stdio: "inherit" });
216
51
  } catch (err) {
217
52
  process.exitCode = err.status || 1;
218
53
  }
@@ -1,146 +0,0 @@
1
- const { execSync } = require("child_process");
2
- const path = require("path");
3
- const fs = require("fs");
4
-
5
- // ── 配置 ──
6
- const BINARY_NAME = "erp-cli";
7
- const VERSION = require("../package.json").version;
8
- const GITHUB_REPO = "windaka-erp/erp-cli";
9
- const GITEE_REPO = "xing-wenkai/erp-cli";
10
-
11
- // ── 平台映射 ──
12
- function getPlatformInfo() {
13
- const platform = process.platform;
14
- const arch = process.arch;
15
-
16
- const map = {
17
- win32_x64: { target: "windows-amd64", ext: ".exe" },
18
- win32_arm64: { target: "windows-arm64", ext: ".exe" },
19
- darwin_x64: { target: "darwin-amd64", ext: "" },
20
- darwin_arm64: { target: "darwin-arm64", ext: "" },
21
- linux_x64: { target: "linux-amd64", ext: "" },
22
- linux_arm64: { target: "linux-arm64", ext: "" },
23
- };
24
-
25
- const key = `${platform}_${arch}`;
26
- const info = map[key];
27
- if (!info) {
28
- console.error(`Unsupported platform: ${platform}-${arch}`);
29
- process.exit(1);
30
- }
31
- return info;
32
- }
33
-
34
- // ── 下载地址(GitHub 优先,Gitee 回退)──
35
- function getDownloadURLs(info) {
36
- const filename = `${BINARY_NAME}-${info.target}${info.ext}`;
37
- return [
38
- `https://github.com/${GITHUB_REPO}/releases/download/v${VERSION}/${filename}`,
39
- `https://gitee.com/${GITEE_REPO}/releases/download/v${VERSION}/${filename}`,
40
- ];
41
- }
42
-
43
- // ── 获取已安装二进制的版本号 ──
44
- function getInstalledVersion(binaryPath) {
45
- try {
46
- const output = execSync(`"${binaryPath}" --version`, {
47
- encoding: "utf8",
48
- timeout: 5000,
49
- });
50
- // 输出格式: "erp-cli version v0.2.0" 或 "erp-cli version 0.2.0"
51
- const match = output.match(/version\s+v?(\S+)/i);
52
- return match ? match[1] : null;
53
- } catch {
54
- return null;
55
- }
56
- }
57
-
58
- // ── 比较版本号 ──
59
- function isVersionOutdated(installed, expected) {
60
- if (!installed) return true;
61
- // 去掉 v 前缀统一比较
62
- const a = installed.replace(/^v/, "");
63
- const b = expected.replace(/^v/, "");
64
- return a !== b;
65
- }
66
-
67
- // ── 下载二进制 ──
68
- function downloadBinary(url, binaryPath) {
69
- if (process.platform === "win32") {
70
- execSync(
71
- `powershell -Command "Invoke-WebRequest -Uri '${url}' -OutFile '${binaryPath}'"`,
72
- { stdio: "inherit" }
73
- );
74
- } else {
75
- execSync(`curl -L -o "${binaryPath}" "${url}"`, {
76
- stdio: "inherit",
77
- });
78
- }
79
-
80
- // 设置可执行权限(非 Windows)
81
- if (process.platform !== "win32") {
82
- fs.chmodSync(binaryPath, 0o755);
83
- }
84
- }
85
-
86
- // ── 主流程 ──
87
- function install() {
88
- const pkgDir = path.join(__dirname, "..");
89
- const binDir = path.join(pkgDir, "bin");
90
- if (!fs.existsSync(binDir)) {
91
- fs.mkdirSync(binDir, { recursive: true });
92
- }
93
-
94
- const info = getPlatformInfo();
95
- const binaryPath = path.join(binDir, `${BINARY_NAME}${info.ext}`);
96
-
97
- // 1. 检查 bin/ 目录是否已有对应平台的二进制
98
- if (fs.existsSync(binaryPath)) {
99
- const installedVer = getInstalledVersion(binaryPath);
100
- if (!isVersionOutdated(installedVer, VERSION)) {
101
- console.log(`Found bundled binary: ${binaryPath} (v${installedVer})`);
102
- return;
103
- }
104
- // 版本不一致,需要更新
105
- console.log(
106
- `Binary version mismatch: installed v${installedVer}, expected v${VERSION}. Updating...`
107
- );
108
- }
109
-
110
- // 2. 检查项目根目录是否有本地编译产物(开发模式)
111
- const localBinary = path.join(pkgDir, `${BINARY_NAME}${info.ext}`);
112
- if (fs.existsSync(localBinary)) {
113
- console.log(`Found local binary, copying...`);
114
- fs.copyFileSync(localBinary, binaryPath);
115
- console.log(`Installed from local build: ${binaryPath}`);
116
- return;
117
- }
118
-
119
- // 3. 从 GitHub Releases 下载,失败回退 Gitee
120
- const urls = getDownloadURLs(info);
121
- console.log(`Downloading ${BINARY_NAME} v${VERSION} for ${info.target}...`);
122
-
123
- let downloaded = false;
124
- for (const url of urls) {
125
- console.log(` Trying: ${url}`);
126
- try {
127
- downloadBinary(url, binaryPath);
128
- downloaded = true;
129
- console.log(`Successfully installed ${BINARY_NAME} v${VERSION}`);
130
- break;
131
- } catch (err) {
132
- console.warn(` Failed: ${err.message}`);
133
- }
134
- }
135
-
136
- if (!downloaded) {
137
- console.error(`All download sources failed.`);
138
- console.error(`You can build from source:`);
139
- console.error(` git clone https://github.com/${GITHUB_REPO}.git`);
140
- console.error(` cd erp-cli && go build -o ${BINARY_NAME} .`);
141
- console.error(` Then copy the binary to: ${binaryPath}`);
142
- process.exit(1);
143
- }
144
- }
145
-
146
- install();