@dalehkx/quote-cli 0.3.4 → 0.3.6
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/bin/quote.js +2 -0
- package/package.json +5 -2
- package/scripts/prepack.cjs +40 -0
- package/skill/SKILL.md +222 -0
- package/skill/agents/openai.yaml +7 -0
- package/skill/references/api-mapping.md +596 -0
- package/skill/references/data-schema.md +111 -0
- package/skill/references/inquiry-api-guide.md +749 -0
- package/skill/references/inquiry-flow.md +191 -0
- package/skill/references/workflow.md +78 -0
- package/src/adapter/api.mjs +529 -43
- package/src/auth.mjs +32 -6
- package/src/commands/inquiry.mjs +37 -8
- package/src/commands/install.mjs +162 -0
- package/src/commands/login.mjs +4 -14
- package/src/commands/order.mjs +224 -16
- package/src/constants.mjs +3 -2
package/bin/quote.js
CHANGED
|
@@ -8,6 +8,7 @@ import { registerCompareCommand } from '../src/commands/compare.mjs';
|
|
|
8
8
|
import { registerOrderCommands } from '../src/commands/order.mjs';
|
|
9
9
|
import { registerConfigCommands } from '../src/commands/config.mjs';
|
|
10
10
|
import { registerLoginCommands } from '../src/commands/login.mjs';
|
|
11
|
+
import { registerInstallCommand } from '../src/commands/install.mjs';
|
|
11
12
|
|
|
12
13
|
const { version } = createRequire(import.meta.url)('../package.json');
|
|
13
14
|
|
|
@@ -18,6 +19,7 @@ program
|
|
|
18
19
|
.description('通用询报价 CLI 工具')
|
|
19
20
|
.version(version);
|
|
20
21
|
|
|
22
|
+
registerInstallCommand(program);
|
|
21
23
|
registerInquiryCommands(program);
|
|
22
24
|
registerReplyCommands(program);
|
|
23
25
|
registerCompareCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dalehkx/quote-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "通用询报价 CLI 工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
|
-
"src/"
|
|
11
|
+
"src/",
|
|
12
|
+
"scripts/",
|
|
13
|
+
"skill/"
|
|
12
14
|
],
|
|
13
15
|
"scripts": {
|
|
14
16
|
"start": "node bin/quote.js",
|
|
17
|
+
"prepack": "node scripts/prepack.cjs",
|
|
15
18
|
"test": "vitest run",
|
|
16
19
|
"test:watch": "vitest"
|
|
17
20
|
},
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// prepack: sync packages/skill/ → packages/cli/skill/
|
|
3
|
+
// Runs from the monorepo root via `npm -w @dalehkx/quote-cli run prepack`
|
|
4
|
+
// or from packages/cli/ directly during `npm pack` / `npm publish`.
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// packages/cli/ → find packages/skill/ relative to monorepo root
|
|
10
|
+
const cliDir = path.join(__dirname, '..');
|
|
11
|
+
const repoRoot = path.join(cliDir, '..', '..');
|
|
12
|
+
const skillSrc = path.join(repoRoot, 'packages', 'skill');
|
|
13
|
+
const skillDest = path.join(cliDir, 'skill');
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(skillSrc)) {
|
|
16
|
+
console.error(`[prepack] skill source not found: ${skillSrc}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function copyDir(src, dest) {
|
|
21
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
22
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
23
|
+
// 불필요한 파일 제외
|
|
24
|
+
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
|
25
|
+
const srcPath = path.join(src, entry.name);
|
|
26
|
+
const destPath = path.join(dest, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
copyDir(srcPath, destPath);
|
|
29
|
+
} else {
|
|
30
|
+
fs.copyFileSync(srcPath, destPath);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 기존 skill 디렉토리 초기화 후 복사
|
|
36
|
+
if (fs.existsSync(skillDest)) {
|
|
37
|
+
fs.rmSync(skillDest, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
copyDir(skillSrc, skillDest);
|
|
40
|
+
console.log(`[prepack] synced packages/skill/ → packages/cli/skill/`);
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cass-quote
|
|
3
|
+
description:
|
|
4
|
+
汽配询报价管理技能
|
|
5
|
+
支持账号注册/登录、创建询价单(多配件/VIN解析)、监听报价、比价分析。
|
|
6
|
+
Use when the user wants to create an inquiry (RFQ), manage quotations,
|
|
7
|
+
compare prices from suppliers, or confirm purchase orders.
|
|
8
|
+
Also use when the user mentions 询价、报价、比价、下单、刹车片、配件.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Quote Skill
|
|
12
|
+
|
|
13
|
+
对接 casstime 平台的询报价 CLI 工具,实现完整的注册 → 询价 → 报价 → 比价 → 下单流程。
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @dalehkx/quote-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## AI 操作说明
|
|
22
|
+
|
|
23
|
+
### 首次运行权限配置(仅第一次调用时执行)
|
|
24
|
+
|
|
25
|
+
读取 `~/.claude/settings.json`,检查 `permissions` 是否已包含 `"allow": ["Bash(quote *)"]`。
|
|
26
|
+
如果没有,在开始任何操作前,先询问用户(建议同意):
|
|
27
|
+
|
|
28
|
+
> 为了让后续 `quote` 命令自动执行、无需每次确认,是否将其加入白名单?(推荐,只需重启一次 Claude Code 即可永久生效)
|
|
29
|
+
|
|
30
|
+
- 用户**同意**:写入 `~/.claude/settings.json`,然后告知「已写入,**请重启 Claude Code** 后重新调用 skill,之后所有操作无需再确认。」写入后停止本次流程。
|
|
31
|
+
- 用户**拒绝**:直接继续,后续每条 `quote` 命令需要用户手动确认。
|
|
32
|
+
|
|
33
|
+
### 登录
|
|
34
|
+
|
|
35
|
+
**先问用户偏好密码登录还是短信验证码登录。**
|
|
36
|
+
|
|
37
|
+
- **密码登录**:先向用户收集账号和密码,然后一次执行 `quote login -u <账号> -p <密码>`
|
|
38
|
+
- **短信验证码登录**:先执行 `quote login --sms --send-code -u <手机号>` 触发发码,等用户告知验证码后执行 `quote login --sms -u <手机号> --code <验证码>`
|
|
39
|
+
|
|
40
|
+
### 创建询价单
|
|
41
|
+
|
|
42
|
+
**执行前先向用户确认车辆品牌和车型**,再带参数执行避免进入交互式品牌选择:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
quote inquiry create -p "刹车片" --brand VW --brand-name 大众 -m 朗逸
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
如果用户有 VIN 码,优先用 VIN 自动识别品牌车型:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
quote inquiry create -p "刹车片" --vin LSVXZ25N0F2113381
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 展示询价列表
|
|
55
|
+
|
|
56
|
+
列表时**始终使用 `quote inquiry list -v`**,可一次返回车型、VIN、创建时间等完整字段,无需再逐条调 `detail`。
|
|
57
|
+
|
|
58
|
+
展示格式示例:
|
|
59
|
+
|
|
60
|
+
| 配件 | 车型 | 报价 | 单号 |
|
|
61
|
+
|------|------|------|------|
|
|
62
|
+
| 火花塞 | 华晨宝马 520Li | 0条 | B26061705034 |
|
|
63
|
+
| 火花塞 | 华晨宝马 520Li | 0条 | B26061704095 |
|
|
64
|
+
|
|
65
|
+
**当用户说「查某配件的报价」而存在多条同名询价单时**,不要让用户猜单号,主动问:
|
|
66
|
+
> 「你有 N 条火花塞询价单,车型都是 XXX,分别是 HH:MM 和 HH:MM 创建的,查哪一条?」
|
|
67
|
+
|
|
68
|
+
### 监听报价
|
|
69
|
+
|
|
70
|
+
用 CronCreate 每分钟轮询一次,有实价报价就停。不要用 `watch`——它是持续进程,agent 只能在退出后拿到输出,实时 stdout 不可见。
|
|
71
|
+
|
|
72
|
+
#### 标准流程
|
|
73
|
+
|
|
74
|
+
**第一步:立即检查一次**
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
quote inquiry detail <单号>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
输出中出现 `关联报价 (N 条)` → 有实价报价(`listReplies` 已过滤 price=0 的占坑槽位),直接展示,不需要轮询。
|
|
81
|
+
|
|
82
|
+
**第二步:无报价,注册每分钟轮询**
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
CronCreate:
|
|
86
|
+
cron: "*/1 * * * *"
|
|
87
|
+
recurring: true
|
|
88
|
+
prompt: "检查询价单 <单号> 报价(第 N 次,最多 30 次):运行 quote inquiry detail <单号>,
|
|
89
|
+
若出现"关联报价"则展示报价并 CronDelete <jobId> 停止轮询;
|
|
90
|
+
若 N >= 30 则告知用户等待超时并 CronDelete <jobId>;
|
|
91
|
+
否则等下次触发。"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- `*/1 * * * *` 每分钟触发,30 次 = 30 分钟上限
|
|
95
|
+
- prompt 里带入计数 N 和 jobId,agent 每次唤醒自己维护计数
|
|
96
|
+
- 发现报价或到达上限后 CronDelete 结束
|
|
97
|
+
|
|
98
|
+
**人工终端场景**(用户自己盯着终端):
|
|
99
|
+
```bash
|
|
100
|
+
quote inquiry watch <单号> --timeout 300
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 下单
|
|
104
|
+
|
|
105
|
+
**先查地址和物流 code,再带参数执行,避免交互式等待:**
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# 第一步:查收货地址(记录要用的 addressId)
|
|
109
|
+
quote order addresses
|
|
110
|
+
|
|
111
|
+
# 第二步:查报价的可用物流(记录 logisticsCode,★推荐 那条优先)
|
|
112
|
+
quote order logistics -i <单号> -r <报价ID>
|
|
113
|
+
|
|
114
|
+
# 第三步:带参下单
|
|
115
|
+
quote order confirm -i <单号> -r <报价ID> -a <addressId> -l <logisticsCode>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `-l default` 或不传 `-l` 时自动使用推荐物流(`defaultLogisticsDTO`);只有都没有推荐物流时才需要手动选
|
|
119
|
+
- 返回成功后提示用户到 casstime App 完成付款
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 命令参考
|
|
124
|
+
|
|
125
|
+
### 注册 / 登录
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
quote register # 新用户注册
|
|
129
|
+
quote login # 密码登录(交互式,见上方 AI 操作说明)
|
|
130
|
+
quote login --sms # 短信验证码登录(交互式,见上方 AI 操作说明)
|
|
131
|
+
quote whoami # 查看登录状态
|
|
132
|
+
quote logout # 登出
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 创建询价单
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# 指定品牌车型(推荐,避免交互)
|
|
139
|
+
quote inquiry create -p "刹车片" --brand VW --brand-name 大众 -m 朗逸
|
|
140
|
+
|
|
141
|
+
# 多配件一张单
|
|
142
|
+
quote inquiry create -p "刹车片" "机油滤芯" "雨刷" --brand VW --brand-name 大众 -m 朗逸
|
|
143
|
+
|
|
144
|
+
# 通过 VIN 自动识别品牌车型
|
|
145
|
+
quote inquiry create -p "刹车片" --vin LSVXZ25N0F2113381
|
|
146
|
+
|
|
147
|
+
# 附带 OE 号和数量
|
|
148
|
+
quote inquiry create -p "刹车片" --brand VW --brand-name 大众 -o 1K0615301 -q 2
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 查看询价单
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
quote inquiry list # 所有询价单
|
|
155
|
+
quote inquiry list --status pending # 待报价
|
|
156
|
+
quote inquiry list --status quoted # 已报价
|
|
157
|
+
quote inquiry list --status ordered # 已下单
|
|
158
|
+
quote inquiry detail <单号> # 详情
|
|
159
|
+
quote inquiry watch <单号> --json --timeout 300 # AI 调用:有报价即退出
|
|
160
|
+
quote inquiry watch <单号> -i 10 --timeout 300 # 每10秒检查,最多等5分钟
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 查看报价 / 比价
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
quote reply list -i <单号> # 查看所有报价
|
|
167
|
+
quote compare -i <单号> # 比价(按价格排序)
|
|
168
|
+
quote compare -i <单号> --sort delivery # 按货期排序
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 下单
|
|
172
|
+
|
|
173
|
+
**先查物流选项,再带参数执行,避免进入交互式选择:**
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# 1. 查看收货地址(获取 addressId)
|
|
177
|
+
quote order addresses
|
|
178
|
+
|
|
179
|
+
# 2. 查看该报价的可用物流(获取 logisticsCode)
|
|
180
|
+
quote order logistics -i <单号> -r <报价ID>
|
|
181
|
+
|
|
182
|
+
# 3. 带参下单(非交互)
|
|
183
|
+
quote order confirm -i <单号> -r <报价ID> -a <addressId> -l <logisticsCode>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
如果用户想交互式选择,也可以直接运行(会依次提示选地址、选物流):
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
quote order confirm -i <单号> -r <报价ID>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
quote order list # 查看订单列表(维修厂所有人的订单)
|
|
194
|
+
quote order list --mine # 只查看当前登录账号下的订单
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 账号说明
|
|
198
|
+
|
|
199
|
+
| 状态 | 说明 |
|
|
200
|
+
|------|------|
|
|
201
|
+
| 未注册 | 执行 `quote register` 完成注册 + 公司信息填写 |
|
|
202
|
+
| 已注册未认证 | 有 5 次免费询价额度,超出报错"询价次数已用完" |
|
|
203
|
+
| 已认证 | 无限询价,支持简易询价接口 |
|
|
204
|
+
|
|
205
|
+
注册后需在 casstime APP 或平台完成企业认证,解锁无限询价:https://ec-hwbeta.casstime.com
|
|
206
|
+
|
|
207
|
+
## Token 自动续签
|
|
208
|
+
|
|
209
|
+
CLI 内部已处理 token 续签,**无需手动干预**:
|
|
210
|
+
|
|
211
|
+
| 场景 | 处理方式 |
|
|
212
|
+
|------|----------|
|
|
213
|
+
| token 距过期不足 5 分钟 | 发请求前同步刷新,取到新 token 再发 |
|
|
214
|
+
| token 本地记录已过期 | 阻塞刷新后重试 |
|
|
215
|
+
| 服务端返回 401 / 652 / 653 / 654 | 自动 refresh + 重试一次 |
|
|
216
|
+
| refresh token 也失效 | 清空凭证,报错提示 `quote login` |
|
|
217
|
+
|
|
218
|
+
code agent 遇到 `"请重新登录"` 错误时,说明 refresh token 也已失效(如长时间未使用、在其他设备退出),需执行一次 `quote login` 重新授权。
|
|
219
|
+
|
|
220
|
+
## 数据存储
|
|
221
|
+
|
|
222
|
+
配置和 Token 存储在 `~/.quote/config.json`,全局唯一,不受工作目录影响。
|