sp-rag 0.6.7 → 0.6.9
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 +301 -275
- package/dist/cli.js +70 -39
- package/dist/lib/doctor.js +275 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,275 +1,301 @@
|
|
|
1
|
-
# `sp-rag`
|
|
2
|
-
|
|
3
|
-
CLI để setup nhanh SP-RAG theo hướng dev-friendly:
|
|
4
|
-
|
|
5
|
-
- lưu cấu hình mặc định để dev không phải nhớ lại URL, client, alias
|
|
6
|
-
- cài MCP config đúng format cho từng client
|
|
7
|
-
- cài native skill / rule / custom agent cho từng IDE khi có convention ổn định
|
|
8
|
-
- kiểm tra nhanh health và observability của stack
|
|
9
|
-
- gọi sync codegraph/GitNexus theo branch hoặc `commit_sha`
|
|
10
|
-
- đọc docs đã render
|
|
11
|
-
- chạy evaluation/regression suite từ file JSON
|
|
12
|
-
|
|
13
|
-
## Yêu cầu
|
|
14
|
-
|
|
15
|
-
- Node.js `>= 20`
|
|
16
|
-
|
|
17
|
-
## Trạng thái package
|
|
18
|
-
|
|
19
|
-
- package npm public: `sp-rag`
|
|
20
|
-
- version đang publish: `0.6.7`
|
|
21
|
-
- binary public: `sp-rag`
|
|
22
|
-
|
|
23
|
-
## Cài từ source trong monorepo
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
cd apps/sp-rag-cli
|
|
27
|
-
npm install
|
|
28
|
-
npm test -- --run
|
|
29
|
-
npm run build
|
|
30
|
-
node dist/index.js doctor
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Cài nhanh qua `npx`
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npx sp-rag@latest install --client codex --mcp-token <grc_pat_...> --doctor
|
|
37
|
-
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
38
|
-
npx sp-rag@latest install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
39
|
-
npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
40
|
-
npx sp-rag@latest explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
41
|
-
npx sp-rag@latest token add --token <grc_pat_...>
|
|
42
|
-
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
43
|
-
npx sp-rag@latest mcp add antigravity
|
|
44
|
-
npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
45
|
-
npx sp-rag@latest skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
46
|
-
npx sp-rag@latest skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Tương đương bằng `npm`:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
|
|
53
|
-
npm exec --yes sp-rag@latest add -- --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
54
|
-
npm exec --yes sp-rag@latest explain -- --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
55
|
-
npm exec --yes sp-rag@latest token add -- --token <grc_pat_...>
|
|
56
|
-
npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Flow gọn cho dev
|
|
60
|
-
|
|
61
|
-
Flow khuyên dùng sau khi đã có `grc_pat_*`:
|
|
62
|
-
|
|
63
|
-
1. chạy `install` đúng một lần cho client đầu tiên, có kèm `--mcp-token`
|
|
64
|
-
2. từ lần sau, dùng `add --client ...` để cài thêm MCP + skill cho client khác mà không phải nhập lại token
|
|
65
|
-
3. khi chỉ muốn làm một nửa, dùng `mcp add` hoặc `skill install`
|
|
66
|
-
4. khi đổi token, chỉ cần `token add`
|
|
67
|
-
5. khi muốn kiểm tra máy đang được cấu hình ra sao, dùng `explain`
|
|
68
|
-
|
|
69
|
-
Ghi chú:
|
|
70
|
-
|
|
71
|
-
- với `cursor` và `vscode` ở `scope project`, nếu Sếpp đang đứng sẵn trong repo thì có thể bỏ `--cwd`
|
|
72
|
-
- CLI sẽ tự dùng thư mục hiện tại cho cả MCP lẫn skill
|
|
73
|
-
- rule/agent mới đã được tăng độ ưu tiên MCP-first, giảm khả năng model nhảy thẳng sang grep/read file local
|
|
74
|
-
- skill mới cũng dặn model tổng hợp từ `matched_passages`, `top_entities`, `top_relations`, `citations`; không bê nguyên `answer_brief`
|
|
75
|
-
|
|
76
|
-
Ví dụ:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
|
|
80
|
-
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
81
|
-
sp-rag mcp add antigravity
|
|
82
|
-
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
83
|
-
sp-rag token add --token <grc_pat_moi>
|
|
84
|
-
sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Lấy token ở đâu
|
|
88
|
-
|
|
89
|
-
`sp-rag` public bây giờ dùng token gắn với tài khoản thật trên server.
|
|
90
|
-
|
|
91
|
-
Luồng chuẩn:
|
|
92
|
-
|
|
93
|
-
1. đăng nhập dashboard hoặc API account của GraphRAG
|
|
94
|
-
2. tạo `personal access token` qua account hiện tại
|
|
95
|
-
3. dùng chính token `grc_pat_*` đó cho MCP và CLI
|
|
96
|
-
4. kiểm tra token bằng `sp-rag token verify`
|
|
97
|
-
|
|
98
|
-
Điểm quan trọng:
|
|
99
|
-
|
|
100
|
-
- token dev dùng cho MCP nên là `grc_pat_*` của account
|
|
101
|
-
- quyền nhìn thấy tool/resource trên MCP bám theo role của account đó
|
|
102
|
-
- `viewer`, `operator`, `approver`, `admin` sẽ thấy inventory khác nhau
|
|
103
|
-
- `MCP_SERVER_ACCESS_TOKENS_JSON` chỉ còn là fallback legacy/break-glass, không còn là luồng chính cho dev
|
|
104
|
-
- khi client đã có sẵn một alias trỏ cùng MCP endpoint, CLI sẽ ưu tiên cập nhật alias đó thay vì tạo alias mới song song
|
|
105
|
-
|
|
106
|
-
Tự tạo PAT bằng API hiện có:
|
|
107
|
-
|
|
108
|
-
1. lấy session token qua `POST /v1/account/login`
|
|
109
|
-
2. gọi `POST /v1/account/personal-access-tokens`
|
|
110
|
-
3. dùng trường `generated_token`
|
|
111
|
-
|
|
112
|
-
Ví dụ đầy đủ:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
curl -X POST https://sp-rag.secomapp.com/api/v1/account/login \
|
|
116
|
-
-H "Content-Type: application/json" \
|
|
117
|
-
-d '{"email":"dev@example.com","password":"<mật-khẩu>"}'
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Lấy `session_token`, rồi gọi tiếp:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
curl -X POST https://sp-rag.secomapp.com/api/v1/account/personal-access-tokens \
|
|
124
|
-
-H "Authorization: Bearer <grc_sess_...>" \
|
|
125
|
-
-H "Content-Type: application/json" \
|
|
126
|
-
-d '{"label":"SP-RAG CLI","expires_in_days":30}'
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Response sẽ trả `generated_token`, chính là token `grc_pat_*` để đưa vào `sp-rag`.
|
|
130
|
-
|
|
131
|
-
Ví dụ dev dùng token trực tiếp:
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
135
|
-
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
136
|
-
npx sp-rag@latest explain --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Hoặc dùng biến môi trường:
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
$env:GRAPHRAG_MCP_TOKEN="<grc_pat_...>"
|
|
143
|
-
npx sp-rag@latest mcp add vscode --scope project --cwd D:/Webs/seo-booster --auth-env-var GRAPHRAG_MCP_TOKEN
|
|
144
|
-
npx sp-rag@latest token verify --token $env:GRAPHRAG_MCP_TOKEN
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## MCP client được hỗ trợ
|
|
148
|
-
|
|
149
|
-
- `codex`
|
|
150
|
-
- `cursor`
|
|
151
|
-
- `claude-code`
|
|
152
|
-
- `antigravity`
|
|
153
|
-
- `vscode`
|
|
154
|
-
- `opencode`
|
|
155
|
-
|
|
156
|
-
## Skill client được hỗ trợ
|
|
157
|
-
|
|
158
|
-
- `codex` -> `SKILL.md`
|
|
159
|
-
- `claude-code` -> `SKILL.md`
|
|
160
|
-
- `antigravity` -> `SKILL.md`
|
|
161
|
-
- `opencode` -> `SKILL.md`
|
|
162
|
-
- `cursor` -> `.cursor/rules/sp-rag.mdc`
|
|
163
|
-
- `vscode` -> `.github/agents/sp-rag.agent.md` + `.github/copilot-instructions.md` hoặc `~/.copilot/agents/sp-rag.agent.md` + `~/.copilot/instructions/sp-rag.instructions.md`
|
|
164
|
-
|
|
165
|
-
Ghi chú:
|
|
166
|
-
|
|
167
|
-
- generated skill luôn được render bằng tiếng Anh
|
|
168
|
-
- `cursor` hiện nên dùng `scope project` cho rule `.mdc`
|
|
169
|
-
- `vscode` hỗ trợ cả `scope project` lẫn `scope global`
|
|
170
|
-
- nếu không muốn lưu token literal vào file config client, dùng `sp-rag mcp add --auth-env-var SP_RAG_MCP_TOKEN`
|
|
171
|
-
|
|
172
|
-
## Path mặc định quan trọng
|
|
173
|
-
|
|
174
|
-
### MCP
|
|
175
|
-
|
|
176
|
-
- `codex`: `~/.codex/config.toml`
|
|
177
|
-
- `cursor` project: `.cursor/mcp.json`
|
|
178
|
-
- `cursor` global: `~/.cursor/mcp.json`
|
|
179
|
-
- `claude-code`: `.mcp.json`
|
|
180
|
-
- `antigravity`: `~/.gemini/antigravity/mcp_config.json`
|
|
181
|
-
- `vscode` project: `.vscode/mcp.json`
|
|
182
|
-
- `vscode` global: file `mcp.json` trong user profile của VS Code
|
|
183
|
-
- `opencode` project: `opencode.json`
|
|
184
|
-
- `opencode` global: `~/.config/opencode/opencode.json`
|
|
185
|
-
|
|
186
|
-
### Skill / rule / custom agent
|
|
187
|
-
|
|
188
|
-
- `codex`: `~/.codex/skills/sp-rag/SKILL.md`
|
|
189
|
-
- `claude-code`: `~/.claude/skills/sp-rag/SKILL.md`
|
|
190
|
-
- `antigravity`: `~/.gemini/antigravity/skills/sp-rag/SKILL.md`
|
|
191
|
-
- `opencode`: `~/.config/opencode/skills/sp-rag/SKILL.md`
|
|
192
|
-
- `cursor` project: `.cursor/rules/sp-rag.mdc`
|
|
193
|
-
- `vscode` project: `.github/agents/sp-rag.agent.md` và `.github/copilot-instructions.md`
|
|
194
|
-
- `vscode` global: `~/.copilot/agents/sp-rag.agent.md` và `~/.copilot/instructions/sp-rag.instructions.md`
|
|
195
|
-
|
|
196
|
-
## Luồng khuyên dùng cho dev mới
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
200
|
-
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
201
|
-
sp-rag explain --client codex
|
|
202
|
-
sp-rag token add --token <grc_pat_...>
|
|
203
|
-
sp-rag token verify --token <grc_pat_...>
|
|
204
|
-
sp-rag config show
|
|
205
|
-
sp-rag codegraph status
|
|
206
|
-
sp-rag codegraph watch --interval-ms 2000
|
|
207
|
-
sp-rag codegraph runs --limit 5
|
|
208
|
-
sp-rag codegraph metrics
|
|
209
|
-
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
210
|
-
sp-rag mcp add antigravity
|
|
211
|
-
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
212
|
-
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
213
|
-
sp-rag skill install --client codex
|
|
214
|
-
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
215
|
-
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
216
|
-
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
## Lệnh chính
|
|
220
|
-
|
|
221
|
-
```bash
|
|
222
|
-
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
223
|
-
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
224
|
-
sp-rag explain --client codex
|
|
225
|
-
sp-rag token add --token <grc_pat_...>
|
|
226
|
-
sp-rag token verify --token <grc_pat_...>
|
|
227
|
-
sp-rag config show
|
|
228
|
-
sp-rag doctor
|
|
229
|
-
sp-rag codegraph status
|
|
230
|
-
sp-rag codegraph watch --interval-ms 2000
|
|
231
|
-
sp-rag codegraph runs --limit 10
|
|
232
|
-
sp-rag codegraph metrics
|
|
233
|
-
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
234
|
-
sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
|
|
235
|
-
sp-rag docs get public --format md
|
|
236
|
-
sp-rag mcp add codex
|
|
237
|
-
sp-rag mcp add antigravity
|
|
238
|
-
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
239
|
-
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
240
|
-
sp-rag skill install --client codex
|
|
241
|
-
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
242
|
-
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
243
|
-
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
244
|
-
sp-rag update setup --client codex
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
##
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
-
|
|
252
|
-
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
1
|
+
# `sp-rag`
|
|
2
|
+
|
|
3
|
+
CLI để setup nhanh SP-RAG theo hướng dev-friendly:
|
|
4
|
+
|
|
5
|
+
- lưu cấu hình mặc định để dev không phải nhớ lại URL, client, alias
|
|
6
|
+
- cài MCP config đúng format cho từng client
|
|
7
|
+
- cài native skill / rule / custom agent cho từng IDE khi có convention ổn định
|
|
8
|
+
- kiểm tra nhanh health và observability của stack
|
|
9
|
+
- gọi sync codegraph/GitNexus theo branch hoặc `commit_sha`
|
|
10
|
+
- đọc docs đã render
|
|
11
|
+
- chạy evaluation/regression suite từ file JSON
|
|
12
|
+
|
|
13
|
+
## Yêu cầu
|
|
14
|
+
|
|
15
|
+
- Node.js `>= 20`
|
|
16
|
+
|
|
17
|
+
## Trạng thái package
|
|
18
|
+
|
|
19
|
+
- package npm public: `sp-rag`
|
|
20
|
+
- version đang publish: `0.6.7`
|
|
21
|
+
- binary public: `sp-rag`
|
|
22
|
+
|
|
23
|
+
## Cài từ source trong monorepo
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd apps/sp-rag-cli
|
|
27
|
+
npm install
|
|
28
|
+
npm test -- --run
|
|
29
|
+
npm run build
|
|
30
|
+
node dist/index.js doctor
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Cài nhanh qua `npx`
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx sp-rag@latest install --client codex --mcp-token <grc_pat_...> --doctor
|
|
37
|
+
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
38
|
+
npx sp-rag@latest install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
39
|
+
npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
40
|
+
npx sp-rag@latest explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
41
|
+
npx sp-rag@latest token add --token <grc_pat_...>
|
|
42
|
+
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
43
|
+
npx sp-rag@latest mcp add antigravity
|
|
44
|
+
npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
45
|
+
npx sp-rag@latest skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
46
|
+
npx sp-rag@latest skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Tương đương bằng `npm`:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
|
|
53
|
+
npm exec --yes sp-rag@latest add -- --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
54
|
+
npm exec --yes sp-rag@latest explain -- --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
55
|
+
npm exec --yes sp-rag@latest token add -- --token <grc_pat_...>
|
|
56
|
+
npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Flow gọn cho dev
|
|
60
|
+
|
|
61
|
+
Flow khuyên dùng sau khi đã có `grc_pat_*`:
|
|
62
|
+
|
|
63
|
+
1. chạy `install` đúng một lần cho client đầu tiên, có kèm `--mcp-token`
|
|
64
|
+
2. từ lần sau, dùng `add --client ...` để cài thêm MCP + skill cho client khác mà không phải nhập lại token
|
|
65
|
+
3. khi chỉ muốn làm một nửa, dùng `mcp add` hoặc `skill install`
|
|
66
|
+
4. khi đổi token, chỉ cần `token add`
|
|
67
|
+
5. khi muốn kiểm tra máy đang được cấu hình ra sao, dùng `explain`
|
|
68
|
+
|
|
69
|
+
Ghi chú:
|
|
70
|
+
|
|
71
|
+
- với `cursor` và `vscode` ở `scope project`, nếu Sếpp đang đứng sẵn trong repo thì có thể bỏ `--cwd`
|
|
72
|
+
- CLI sẽ tự dùng thư mục hiện tại cho cả MCP lẫn skill
|
|
73
|
+
- rule/agent mới đã được tăng độ ưu tiên MCP-first, giảm khả năng model nhảy thẳng sang grep/read file local
|
|
74
|
+
- skill mới cũng dặn model tổng hợp từ `matched_passages`, `top_entities`, `top_relations`, `citations`; không bê nguyên `answer_brief`
|
|
75
|
+
|
|
76
|
+
Ví dụ:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
|
|
80
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
81
|
+
sp-rag mcp add antigravity
|
|
82
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
83
|
+
sp-rag token add --token <grc_pat_moi>
|
|
84
|
+
sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Lấy token ở đâu
|
|
88
|
+
|
|
89
|
+
`sp-rag` public bây giờ dùng token gắn với tài khoản thật trên server.
|
|
90
|
+
|
|
91
|
+
Luồng chuẩn:
|
|
92
|
+
|
|
93
|
+
1. đăng nhập dashboard hoặc API account của GraphRAG
|
|
94
|
+
2. tạo `personal access token` qua account hiện tại
|
|
95
|
+
3. dùng chính token `grc_pat_*` đó cho MCP và CLI
|
|
96
|
+
4. kiểm tra token bằng `sp-rag token verify`
|
|
97
|
+
|
|
98
|
+
Điểm quan trọng:
|
|
99
|
+
|
|
100
|
+
- token dev dùng cho MCP nên là `grc_pat_*` của account
|
|
101
|
+
- quyền nhìn thấy tool/resource trên MCP bám theo role của account đó
|
|
102
|
+
- `viewer`, `operator`, `approver`, `admin` sẽ thấy inventory khác nhau
|
|
103
|
+
- `MCP_SERVER_ACCESS_TOKENS_JSON` chỉ còn là fallback legacy/break-glass, không còn là luồng chính cho dev
|
|
104
|
+
- khi client đã có sẵn một alias trỏ cùng MCP endpoint, CLI sẽ ưu tiên cập nhật alias đó thay vì tạo alias mới song song
|
|
105
|
+
|
|
106
|
+
Tự tạo PAT bằng API hiện có:
|
|
107
|
+
|
|
108
|
+
1. lấy session token qua `POST /v1/account/login`
|
|
109
|
+
2. gọi `POST /v1/account/personal-access-tokens`
|
|
110
|
+
3. dùng trường `generated_token`
|
|
111
|
+
|
|
112
|
+
Ví dụ đầy đủ:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
curl -X POST https://sp-rag.secomapp.com/api/v1/account/login \
|
|
116
|
+
-H "Content-Type: application/json" \
|
|
117
|
+
-d '{"email":"dev@example.com","password":"<mật-khẩu>"}'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Lấy `session_token`, rồi gọi tiếp:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
curl -X POST https://sp-rag.secomapp.com/api/v1/account/personal-access-tokens \
|
|
124
|
+
-H "Authorization: Bearer <grc_sess_...>" \
|
|
125
|
+
-H "Content-Type: application/json" \
|
|
126
|
+
-d '{"label":"SP-RAG CLI","expires_in_days":30}'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Response sẽ trả `generated_token`, chính là token `grc_pat_*` để đưa vào `sp-rag`.
|
|
130
|
+
|
|
131
|
+
Ví dụ dev dùng token trực tiếp:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
135
|
+
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
136
|
+
npx sp-rag@latest explain --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Hoặc dùng biến môi trường:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
$env:GRAPHRAG_MCP_TOKEN="<grc_pat_...>"
|
|
143
|
+
npx sp-rag@latest mcp add vscode --scope project --cwd D:/Webs/seo-booster --auth-env-var GRAPHRAG_MCP_TOKEN
|
|
144
|
+
npx sp-rag@latest token verify --token $env:GRAPHRAG_MCP_TOKEN
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## MCP client được hỗ trợ
|
|
148
|
+
|
|
149
|
+
- `codex`
|
|
150
|
+
- `cursor`
|
|
151
|
+
- `claude-code`
|
|
152
|
+
- `antigravity`
|
|
153
|
+
- `vscode`
|
|
154
|
+
- `opencode`
|
|
155
|
+
|
|
156
|
+
## Skill client được hỗ trợ
|
|
157
|
+
|
|
158
|
+
- `codex` -> `SKILL.md`
|
|
159
|
+
- `claude-code` -> `SKILL.md`
|
|
160
|
+
- `antigravity` -> `SKILL.md`
|
|
161
|
+
- `opencode` -> `SKILL.md`
|
|
162
|
+
- `cursor` -> `.cursor/rules/sp-rag.mdc`
|
|
163
|
+
- `vscode` -> `.github/agents/sp-rag.agent.md` + `.github/copilot-instructions.md` hoặc `~/.copilot/agents/sp-rag.agent.md` + `~/.copilot/instructions/sp-rag.instructions.md`
|
|
164
|
+
|
|
165
|
+
Ghi chú:
|
|
166
|
+
|
|
167
|
+
- generated skill luôn được render bằng tiếng Anh
|
|
168
|
+
- `cursor` hiện nên dùng `scope project` cho rule `.mdc`
|
|
169
|
+
- `vscode` hỗ trợ cả `scope project` lẫn `scope global`
|
|
170
|
+
- nếu không muốn lưu token literal vào file config client, dùng `sp-rag mcp add --auth-env-var SP_RAG_MCP_TOKEN`
|
|
171
|
+
|
|
172
|
+
## Path mặc định quan trọng
|
|
173
|
+
|
|
174
|
+
### MCP
|
|
175
|
+
|
|
176
|
+
- `codex`: `~/.codex/config.toml`
|
|
177
|
+
- `cursor` project: `.cursor/mcp.json`
|
|
178
|
+
- `cursor` global: `~/.cursor/mcp.json`
|
|
179
|
+
- `claude-code`: `.mcp.json`
|
|
180
|
+
- `antigravity`: `~/.gemini/antigravity/mcp_config.json`
|
|
181
|
+
- `vscode` project: `.vscode/mcp.json`
|
|
182
|
+
- `vscode` global: file `mcp.json` trong user profile của VS Code
|
|
183
|
+
- `opencode` project: `opencode.json`
|
|
184
|
+
- `opencode` global: `~/.config/opencode/opencode.json`
|
|
185
|
+
|
|
186
|
+
### Skill / rule / custom agent
|
|
187
|
+
|
|
188
|
+
- `codex`: `~/.codex/skills/sp-rag/SKILL.md`
|
|
189
|
+
- `claude-code`: `~/.claude/skills/sp-rag/SKILL.md`
|
|
190
|
+
- `antigravity`: `~/.gemini/antigravity/skills/sp-rag/SKILL.md`
|
|
191
|
+
- `opencode`: `~/.config/opencode/skills/sp-rag/SKILL.md`
|
|
192
|
+
- `cursor` project: `.cursor/rules/sp-rag.mdc`
|
|
193
|
+
- `vscode` project: `.github/agents/sp-rag.agent.md` và `.github/copilot-instructions.md`
|
|
194
|
+
- `vscode` global: `~/.copilot/agents/sp-rag.agent.md` và `~/.copilot/instructions/sp-rag.instructions.md`
|
|
195
|
+
|
|
196
|
+
## Luồng khuyên dùng cho dev mới
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
200
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
201
|
+
sp-rag explain --client codex
|
|
202
|
+
sp-rag token add --token <grc_pat_...>
|
|
203
|
+
sp-rag token verify --token <grc_pat_...>
|
|
204
|
+
sp-rag config show
|
|
205
|
+
sp-rag codegraph status
|
|
206
|
+
sp-rag codegraph watch --interval-ms 2000
|
|
207
|
+
sp-rag codegraph runs --limit 5
|
|
208
|
+
sp-rag codegraph metrics
|
|
209
|
+
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
210
|
+
sp-rag mcp add antigravity
|
|
211
|
+
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
212
|
+
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
213
|
+
sp-rag skill install --client codex
|
|
214
|
+
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
215
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
216
|
+
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Lệnh chính
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
223
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
224
|
+
sp-rag explain --client codex
|
|
225
|
+
sp-rag token add --token <grc_pat_...>
|
|
226
|
+
sp-rag token verify --token <grc_pat_...>
|
|
227
|
+
sp-rag config show
|
|
228
|
+
sp-rag doctor
|
|
229
|
+
sp-rag codegraph status
|
|
230
|
+
sp-rag codegraph watch --interval-ms 2000
|
|
231
|
+
sp-rag codegraph runs --limit 10
|
|
232
|
+
sp-rag codegraph metrics
|
|
233
|
+
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
234
|
+
sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
|
|
235
|
+
sp-rag docs get public --format md
|
|
236
|
+
sp-rag mcp add codex
|
|
237
|
+
sp-rag mcp add antigravity
|
|
238
|
+
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
239
|
+
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
240
|
+
sp-rag skill install --client codex
|
|
241
|
+
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
242
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
243
|
+
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
244
|
+
sp-rag update setup --client codex
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## `doctor` kiểm gì
|
|
248
|
+
|
|
249
|
+
`sp-rag doctor` giờ kiểm 2 lớp:
|
|
250
|
+
|
|
251
|
+
- ping các endpoint `api`, `mcp`, `codegraph`
|
|
252
|
+
- đọc đúng file MCP của client để kiểm:
|
|
253
|
+
- IDE đang nhìn vào file nào
|
|
254
|
+
- server entry nào được match theo alias hoặc theo endpoint
|
|
255
|
+
- `Authorization` đang là token literal, env var hợp lệ, hay đang thiếu
|
|
256
|
+
- khả năng IDE sẽ bật link đăng nhập OAuth
|
|
257
|
+
|
|
258
|
+
Ví dụ:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
sp-rag doctor --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Nếu output có dòng:
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
- Authorization: thiếu hoàn toàn
|
|
268
|
+
- Khả năng IDE bật link đăng nhập: cao
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
thì vấn đề thường không nằm ở skill, mà ở chỗ IDE chưa nạp được bearer header từ file MCP thực tế.
|
|
272
|
+
|
|
273
|
+
## Cấu hình mặc định
|
|
274
|
+
|
|
275
|
+
CLI lưu cấu hình tại:
|
|
276
|
+
|
|
277
|
+
- `~/.sp-rag/config.json`
|
|
278
|
+
- có thể override home dir bằng `SP_RAG_HOME_DIR`
|
|
279
|
+
|
|
280
|
+
CLI có thể lưu thêm:
|
|
281
|
+
|
|
282
|
+
- `mcpToken` cho flow cài nhanh
|
|
283
|
+
- `authEnvVar` nếu muốn client đọc token từ biến môi trường thay vì lưu literal
|
|
284
|
+
- `defaultClient`, `defaultScope`, `skillClient`
|
|
285
|
+
|
|
286
|
+
Giá trị mặc định:
|
|
287
|
+
|
|
288
|
+
- base URL: `https://sp-rag.secomapp.com`
|
|
289
|
+
- MCP URL: `https://sp-rag.secomapp.com/mcp`
|
|
290
|
+
- alias MCP: `sp-rag`
|
|
291
|
+
|
|
292
|
+
## Evaluation mẫu
|
|
293
|
+
|
|
294
|
+
- file mẫu: [`examples/eval-suite.sample.json`](./examples/eval-suite.sample.json)
|
|
295
|
+
|
|
296
|
+
## Tài liệu thêm
|
|
297
|
+
|
|
298
|
+
- [Hướng dẫn dev sử dụng SP-RAG](../../docs/runbooks/dev-usage-guide.md)
|
|
299
|
+
- [Runbook CLI `sp-rag`](../../docs/runbooks/sp-rag-cli.md)
|
|
300
|
+
- [Runbook phát hành CLI `sp-rag`](../../docs/runbooks/sp-rag-cli-release.md)
|
|
301
|
+
- [Runbook MCP Public](../../docs/runbooks/mcp-public-clients.md)
|
package/dist/cli.js
CHANGED
|
@@ -2,9 +2,21 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { loadCliConfig, saveCliConfig, } from './lib/config-store.js';
|
|
3
3
|
import { runEvaluationSuite } from './lib/eval.js';
|
|
4
4
|
import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, resolveMcpConfigPath, } from './lib/mcp-config.js';
|
|
5
|
+
import { diagnoseMcpConfig, formatMcpDoctorResult } from './lib/doctor.js';
|
|
5
6
|
import { fetchJson, fetchText, runDoctor } from './lib/http.js';
|
|
6
7
|
import { installSkill, resolveSkillInstallTarget, } from './lib/skill.js';
|
|
7
|
-
|
|
8
|
+
let cliVersionPromise;
|
|
9
|
+
async function resolveCliVersion() {
|
|
10
|
+
if (!cliVersionPromise) {
|
|
11
|
+
cliVersionPromise = readFile(new URL('../package.json', import.meta.url), 'utf8')
|
|
12
|
+
.then((raw) => {
|
|
13
|
+
const packageJson = JSON.parse(raw);
|
|
14
|
+
return packageJson.version?.trim() || '0.0.0';
|
|
15
|
+
})
|
|
16
|
+
.catch(() => '0.0.0');
|
|
17
|
+
}
|
|
18
|
+
return cliVersionPromise;
|
|
19
|
+
}
|
|
8
20
|
function parseArgv(argv) {
|
|
9
21
|
const positionals = [];
|
|
10
22
|
const options = {};
|
|
@@ -177,28 +189,28 @@ function resolveAuthForMcp(parsed, defaults) {
|
|
|
177
189
|
};
|
|
178
190
|
}
|
|
179
191
|
function helpText() {
|
|
180
|
-
return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
|
|
181
|
-
|
|
182
|
-
Lệnh chính:
|
|
183
|
-
sp-rag install --client codex|cursor|claude-code|antigravity|vscode|opencode [--base-url URL] [--mcp-url URL] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH] [--doctor]
|
|
184
|
-
sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
|
|
185
|
-
sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
186
|
-
sp-rag token add --token TOKEN
|
|
187
|
-
sp-rag token verify [--token TOKEN] [--base-url URL]
|
|
188
|
-
sp-rag config show
|
|
189
|
-
sp-rag explain [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
|
|
190
|
-
sp-rag doctor [--base-url URL]
|
|
191
|
-
sp-rag codegraph status [--base-url URL]
|
|
192
|
-
sp-rag codegraph watch [--base-url URL] [--interval-ms N] [--max-polls N]
|
|
193
|
-
sp-rag codegraph runs [--base-url URL] [--limit N]
|
|
194
|
-
sp-rag codegraph metrics [--base-url URL]
|
|
195
|
-
sp-rag codegraph recover [--base-url URL] [--reason TEXT]
|
|
196
|
-
sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
|
|
197
|
-
sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
|
|
198
|
-
sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
|
|
199
|
-
sp-rag skill install [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
|
|
200
|
-
sp-rag eval run --file eval-suite.json [--base-url URL]
|
|
201
|
-
sp-rag update setup [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
192
|
+
return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
|
|
193
|
+
|
|
194
|
+
Lệnh chính:
|
|
195
|
+
sp-rag install --client codex|cursor|claude-code|antigravity|vscode|opencode [--base-url URL] [--mcp-url URL] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH] [--doctor]
|
|
196
|
+
sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
|
|
197
|
+
sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
198
|
+
sp-rag token add --token TOKEN
|
|
199
|
+
sp-rag token verify [--token TOKEN] [--base-url URL]
|
|
200
|
+
sp-rag config show
|
|
201
|
+
sp-rag explain [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
|
|
202
|
+
sp-rag doctor [--base-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
|
|
203
|
+
sp-rag codegraph status [--base-url URL]
|
|
204
|
+
sp-rag codegraph watch [--base-url URL] [--interval-ms N] [--max-polls N]
|
|
205
|
+
sp-rag codegraph runs [--base-url URL] [--limit N]
|
|
206
|
+
sp-rag codegraph metrics [--base-url URL]
|
|
207
|
+
sp-rag codegraph recover [--base-url URL] [--reason TEXT]
|
|
208
|
+
sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
|
|
209
|
+
sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
|
|
210
|
+
sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
|
|
211
|
+
sp-rag skill install [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
|
|
212
|
+
sp-rag eval run --file eval-suite.json [--base-url URL]
|
|
213
|
+
sp-rag update setup [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
202
214
|
`;
|
|
203
215
|
}
|
|
204
216
|
function buildCliConfig(defaults) {
|
|
@@ -375,6 +387,28 @@ async function runSkillInstall(parsed, defaults, explicitClient) {
|
|
|
375
387
|
async function saveResolvedConfig(defaults) {
|
|
376
388
|
return saveCliConfig(buildCliConfig(defaults), defaults.homeDir);
|
|
377
389
|
}
|
|
390
|
+
async function emitDoctorReport(parsed, defaults, explicitClient, checks) {
|
|
391
|
+
const results = checks ?? (await runDoctor({ baseUrl: defaults.baseUrl }));
|
|
392
|
+
for (const result of results) {
|
|
393
|
+
process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
|
|
394
|
+
}
|
|
395
|
+
const client = resolveSelectedClient(parsed, defaults, explicitClient);
|
|
396
|
+
if (!client) {
|
|
397
|
+
process.stdout.write('INFO doctor bỏ qua chẩn đoán file MCP vì chưa xác định được client.\n');
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
const mcpDiagnostic = await diagnoseMcpConfig({
|
|
401
|
+
client,
|
|
402
|
+
url: defaults.mcpUrl,
|
|
403
|
+
serverAlias: defaults.serverAlias,
|
|
404
|
+
scope: supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope,
|
|
405
|
+
cwd: optionString(parsed, 'cwd'),
|
|
406
|
+
});
|
|
407
|
+
for (const line of formatMcpDoctorResult(mcpDiagnostic)) {
|
|
408
|
+
process.stdout.write(`${line}\n`);
|
|
409
|
+
}
|
|
410
|
+
return mcpDiagnostic;
|
|
411
|
+
}
|
|
378
412
|
async function runClientSetup(parsed, defaults, client) {
|
|
379
413
|
const nextDefaults = deriveDefaultsForClient(parsed, defaults, client);
|
|
380
414
|
const configPath = await saveResolvedConfig(nextDefaults);
|
|
@@ -386,10 +420,7 @@ async function runClientSetup(parsed, defaults, client) {
|
|
|
386
420
|
await runSkillInstall(parsed, nextDefaults, client);
|
|
387
421
|
}
|
|
388
422
|
if (optionFlag(parsed, 'doctor')) {
|
|
389
|
-
|
|
390
|
-
for (const result of results) {
|
|
391
|
-
process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
|
|
392
|
-
}
|
|
423
|
+
await emitDoctorReport(parsed, nextDefaults, client);
|
|
393
424
|
}
|
|
394
425
|
}
|
|
395
426
|
async function runInit(parsed) {
|
|
@@ -405,10 +436,7 @@ async function runInit(parsed) {
|
|
|
405
436
|
await runSkillInstall(parsed, nextDefaults, client);
|
|
406
437
|
}
|
|
407
438
|
if (optionFlag(parsed, 'doctor')) {
|
|
408
|
-
|
|
409
|
-
for (const result of results) {
|
|
410
|
-
process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
|
|
411
|
-
}
|
|
439
|
+
await emitDoctorReport(parsed, nextDefaults, client);
|
|
412
440
|
}
|
|
413
441
|
}
|
|
414
442
|
async function runInstall(parsed) {
|
|
@@ -560,7 +588,7 @@ export async function runCli(argv) {
|
|
|
560
588
|
const [group, action] = parsed.positionals;
|
|
561
589
|
try {
|
|
562
590
|
if (optionFlag(parsed, 'version')) {
|
|
563
|
-
process.stdout.write(`sp-rag ${
|
|
591
|
+
process.stdout.write(`sp-rag ${await resolveCliVersion()}\n`);
|
|
564
592
|
return 0;
|
|
565
593
|
}
|
|
566
594
|
if (!group || group === 'help' || group === '--help') {
|
|
@@ -597,13 +625,16 @@ export async function runCli(argv) {
|
|
|
597
625
|
}
|
|
598
626
|
if (group === 'doctor') {
|
|
599
627
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
600
|
-
const results = await runDoctor({
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
|
|
628
|
+
const results = await runDoctor({ baseUrl: defaults.baseUrl });
|
|
629
|
+
const mcpDiagnostic = await emitDoctorReport(parsed, defaults, undefined, results);
|
|
630
|
+
if (!mcpDiagnostic) {
|
|
631
|
+
return results.every((result) => result.ok) ? 0 : 1;
|
|
605
632
|
}
|
|
606
|
-
return results.every((result) => result.ok)
|
|
633
|
+
return results.every((result) => result.ok) &&
|
|
634
|
+
mcpDiagnostic.entryFound &&
|
|
635
|
+
!mcpDiagnostic.likelyOAuthLogin
|
|
636
|
+
? 0
|
|
637
|
+
: 1;
|
|
607
638
|
}
|
|
608
639
|
if (group === 'codegraph' && action === 'status') {
|
|
609
640
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
@@ -655,7 +686,7 @@ export async function runCli(argv) {
|
|
|
655
686
|
return 0;
|
|
656
687
|
}
|
|
657
688
|
if (group === 'version') {
|
|
658
|
-
process.stdout.write(`sp-rag ${
|
|
689
|
+
process.stdout.write(`sp-rag ${await resolveCliVersion()}\n`);
|
|
659
690
|
return 0;
|
|
660
691
|
}
|
|
661
692
|
process.stdout.write(helpText());
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolveMcpConfigPath, } from './mcp-config.js';
|
|
3
|
+
function normalizeEndpoint(value) {
|
|
4
|
+
if (typeof value !== 'string') {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const trimmed = value.trim();
|
|
8
|
+
return trimmed ? trimmed.replace(/\/+$/, '') : undefined;
|
|
9
|
+
}
|
|
10
|
+
function parseJsonEntries(raw, client) {
|
|
11
|
+
const parsed = JSON.parse(raw);
|
|
12
|
+
let sectionKey = 'mcpServers';
|
|
13
|
+
let endpointKey = 'url';
|
|
14
|
+
if (client === 'vscode') {
|
|
15
|
+
sectionKey = 'servers';
|
|
16
|
+
}
|
|
17
|
+
else if (client === 'opencode') {
|
|
18
|
+
sectionKey = 'mcp';
|
|
19
|
+
}
|
|
20
|
+
else if (client === 'antigravity') {
|
|
21
|
+
endpointKey = 'serverUrl';
|
|
22
|
+
}
|
|
23
|
+
const section = parsed[sectionKey] && typeof parsed[sectionKey] === 'object'
|
|
24
|
+
? parsed[sectionKey]
|
|
25
|
+
: {};
|
|
26
|
+
return Object.entries(section).flatMap(([alias, value]) => {
|
|
27
|
+
if (!value || typeof value !== 'object') {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const entry = value;
|
|
31
|
+
const headers = parseStringHeaders(entry.headers);
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
alias,
|
|
35
|
+
endpoint: normalizeEndpoint(entry[endpointKey]),
|
|
36
|
+
headers,
|
|
37
|
+
oauth: entry.oauth,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function parseStringHeaders(value) {
|
|
43
|
+
if (!value || typeof value !== 'object') {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const headers = {};
|
|
47
|
+
for (const [key, headerValue] of Object.entries(value)) {
|
|
48
|
+
if (typeof headerValue === 'string') {
|
|
49
|
+
headers[key] = headerValue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
53
|
+
}
|
|
54
|
+
function parseTomlEntries(raw) {
|
|
55
|
+
const entries = new Map();
|
|
56
|
+
let currentAlias;
|
|
57
|
+
let currentSection;
|
|
58
|
+
for (const originalLine of raw.split(/\r?\n/)) {
|
|
59
|
+
const line = originalLine.trim();
|
|
60
|
+
if (!line || line.startsWith('#')) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const headersMatch = line.match(/^\[mcp_servers\.(?:"([^"]+)"|([^\].]+))\.headers\]$/);
|
|
64
|
+
if (headersMatch) {
|
|
65
|
+
currentAlias = headersMatch[1] || headersMatch[2];
|
|
66
|
+
currentSection = 'headers';
|
|
67
|
+
if (!entries.has(currentAlias)) {
|
|
68
|
+
entries.set(currentAlias, { alias: currentAlias });
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const serverMatch = line.match(/^\[mcp_servers\.(?:"([^"]+)"|([^\].]+))\]$/);
|
|
73
|
+
if (serverMatch) {
|
|
74
|
+
currentAlias = serverMatch[1] || serverMatch[2];
|
|
75
|
+
currentSection = 'server';
|
|
76
|
+
if (!entries.has(currentAlias)) {
|
|
77
|
+
entries.set(currentAlias, { alias: currentAlias });
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (!currentAlias || !currentSection) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const keyValueMatch = line.match(/^([A-Za-z0-9_-]+)\s*=\s*"(.+)"$/);
|
|
85
|
+
if (!keyValueMatch) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const [, key, value] = keyValueMatch;
|
|
89
|
+
const entry = entries.get(currentAlias) ?? { alias: currentAlias };
|
|
90
|
+
if (currentSection === 'server' && key === 'url') {
|
|
91
|
+
entry.endpoint = normalizeEndpoint(value);
|
|
92
|
+
}
|
|
93
|
+
if (currentSection === 'headers') {
|
|
94
|
+
entry.headers = {
|
|
95
|
+
...(entry.headers ?? {}),
|
|
96
|
+
[key]: value,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
entries.set(currentAlias, entry);
|
|
100
|
+
}
|
|
101
|
+
return [...entries.values()];
|
|
102
|
+
}
|
|
103
|
+
function findEntry(entries, serverAlias, targetUrl) {
|
|
104
|
+
const byAlias = entries.find((entry) => entry.alias === serverAlias);
|
|
105
|
+
if (byAlias) {
|
|
106
|
+
return {
|
|
107
|
+
entry: byAlias,
|
|
108
|
+
matchedBy: 'alias',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const normalizedTarget = normalizeEndpoint(targetUrl);
|
|
112
|
+
if (!normalizedTarget) {
|
|
113
|
+
return {};
|
|
114
|
+
}
|
|
115
|
+
const byEndpoint = entries.find((entry) => entry.endpoint === normalizedTarget);
|
|
116
|
+
if (byEndpoint) {
|
|
117
|
+
return {
|
|
118
|
+
entry: byEndpoint,
|
|
119
|
+
matchedBy: 'endpoint',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
function analyzeAuthorization(headerValue) {
|
|
125
|
+
if (!headerValue?.trim()) {
|
|
126
|
+
return {
|
|
127
|
+
authorizationState: 'missing',
|
|
128
|
+
likelyOAuthLogin: true,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const trimmed = headerValue.trim();
|
|
132
|
+
const envStylePatterns = [
|
|
133
|
+
/\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}/,
|
|
134
|
+
/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/,
|
|
135
|
+
/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/,
|
|
136
|
+
];
|
|
137
|
+
for (const pattern of envStylePatterns) {
|
|
138
|
+
const match = trimmed.match(pattern);
|
|
139
|
+
if (!match) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const envVar = match[1];
|
|
143
|
+
const envValue = process.env[envVar]?.trim();
|
|
144
|
+
return {
|
|
145
|
+
authorizationState: envValue ? 'env-present' : 'env-missing',
|
|
146
|
+
authorizationSource: 'env',
|
|
147
|
+
authorizationEnvVar: envVar,
|
|
148
|
+
likelyOAuthLogin: !envValue,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (/^Bearer\s+\S+/i.test(trimmed)) {
|
|
152
|
+
return {
|
|
153
|
+
authorizationState: 'present',
|
|
154
|
+
authorizationSource: 'literal',
|
|
155
|
+
likelyOAuthLogin: false,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
authorizationState: 'invalid',
|
|
160
|
+
likelyOAuthLogin: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function entriesForClient(raw, client) {
|
|
164
|
+
if (client === 'codex') {
|
|
165
|
+
return parseTomlEntries(raw);
|
|
166
|
+
}
|
|
167
|
+
return parseJsonEntries(raw, client);
|
|
168
|
+
}
|
|
169
|
+
export async function diagnoseMcpConfig(options) {
|
|
170
|
+
const resolved = resolveMcpConfigPath({
|
|
171
|
+
client: options.client,
|
|
172
|
+
url: options.url,
|
|
173
|
+
serverAlias: options.serverAlias,
|
|
174
|
+
scope: options.scope,
|
|
175
|
+
cwd: options.cwd,
|
|
176
|
+
});
|
|
177
|
+
const baseResult = {
|
|
178
|
+
client: options.client,
|
|
179
|
+
path: resolved.path,
|
|
180
|
+
scope: resolved.scope,
|
|
181
|
+
exists: false,
|
|
182
|
+
entryFound: false,
|
|
183
|
+
authorizationState: 'missing',
|
|
184
|
+
likelyOAuthLogin: true,
|
|
185
|
+
issues: [],
|
|
186
|
+
};
|
|
187
|
+
let raw = '';
|
|
188
|
+
try {
|
|
189
|
+
raw = await readFile(resolved.path, 'utf8');
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return {
|
|
193
|
+
...baseResult,
|
|
194
|
+
issues: [
|
|
195
|
+
`Không đọc được file MCP tại ${resolved.path}: ${error instanceof Error ? error.message : String(error)}`,
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const entries = entriesForClient(raw, options.client);
|
|
200
|
+
const { entry, matchedBy } = findEntry(entries, options.serverAlias, options.url);
|
|
201
|
+
if (!entry) {
|
|
202
|
+
return {
|
|
203
|
+
...baseResult,
|
|
204
|
+
exists: true,
|
|
205
|
+
issues: [
|
|
206
|
+
`Không tìm thấy server MCP alias ${options.serverAlias} hoặc endpoint ${options.url} trong file config.`,
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const authorization = analyzeAuthorization(entry.headers?.Authorization ?? entry.headers?.authorization);
|
|
211
|
+
const issues = [...baseResult.issues];
|
|
212
|
+
if (authorization.authorizationState === 'missing') {
|
|
213
|
+
issues.push('Thiếu Authorization header. IDE rất dễ rơi sang luồng đăng nhập OAuth.');
|
|
214
|
+
}
|
|
215
|
+
else if (authorization.authorizationState === 'env-missing') {
|
|
216
|
+
issues.push(`Authorization đang dùng biến môi trường ${authorization.authorizationEnvVar} nhưng biến này chưa có giá trị trong phiên hiện tại.`);
|
|
217
|
+
}
|
|
218
|
+
else if (authorization.authorizationState === 'invalid') {
|
|
219
|
+
issues.push('Authorization header có định dạng lạ, IDE có thể bỏ qua và bật OAuth.');
|
|
220
|
+
}
|
|
221
|
+
const oauthDisabled = options.client === 'opencode' && typeof entry.oauth === 'boolean'
|
|
222
|
+
? entry.oauth === false
|
|
223
|
+
: undefined;
|
|
224
|
+
if (options.client === 'opencode' && authorization.authorizationState !== 'missing' && oauthDisabled !== true) {
|
|
225
|
+
issues.push('OpenCode chưa có oauth=false dù đã cấu hình Authorization header.');
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
...baseResult,
|
|
229
|
+
exists: true,
|
|
230
|
+
entryFound: true,
|
|
231
|
+
matchedAlias: entry.alias,
|
|
232
|
+
matchedBy,
|
|
233
|
+
endpoint: entry.endpoint,
|
|
234
|
+
oauthDisabled,
|
|
235
|
+
issues,
|
|
236
|
+
...authorization,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
export function formatMcpDoctorResult(result) {
|
|
240
|
+
const lines = [
|
|
241
|
+
`MCP config cho ${result.client}: ${result.path} (${result.scope})`,
|
|
242
|
+
];
|
|
243
|
+
if (!result.exists) {
|
|
244
|
+
lines.push(`- Trạng thái file: thiếu hoặc không đọc được`);
|
|
245
|
+
for (const issue of result.issues) {
|
|
246
|
+
lines.push(`- Cảnh báo: ${issue}`);
|
|
247
|
+
}
|
|
248
|
+
return lines;
|
|
249
|
+
}
|
|
250
|
+
lines.push(`- Trạng thái file: đã đọc được`);
|
|
251
|
+
lines.push(`- Server entry: ${result.entryFound
|
|
252
|
+
? `${result.matchedAlias} (${result.matchedBy === 'endpoint' ? 'khớp theo endpoint' : 'khớp theo alias'})`
|
|
253
|
+
: 'không tìm thấy'}`);
|
|
254
|
+
if (result.endpoint) {
|
|
255
|
+
lines.push(`- Endpoint thực tế: ${result.endpoint}`);
|
|
256
|
+
}
|
|
257
|
+
const authText = result.authorizationState === 'present'
|
|
258
|
+
? 'đã có bearer token literal'
|
|
259
|
+
: result.authorizationState === 'env-present'
|
|
260
|
+
? `dùng biến môi trường ${result.authorizationEnvVar} và đang có giá trị`
|
|
261
|
+
: result.authorizationState === 'env-missing'
|
|
262
|
+
? `dùng biến môi trường ${result.authorizationEnvVar} nhưng hiện chưa có giá trị`
|
|
263
|
+
: result.authorizationState === 'invalid'
|
|
264
|
+
? 'có header nhưng định dạng không hợp lệ'
|
|
265
|
+
: 'thiếu hoàn toàn';
|
|
266
|
+
lines.push(`- Authorization: ${authText}`);
|
|
267
|
+
if (typeof result.oauthDisabled === 'boolean') {
|
|
268
|
+
lines.push(`- oauth=false: ${result.oauthDisabled ? 'có' : 'không'}`);
|
|
269
|
+
}
|
|
270
|
+
lines.push(`- Khả năng IDE bật link đăng nhập: ${result.likelyOAuthLogin ? 'cao' : 'thấp'}`);
|
|
271
|
+
for (const issue of result.issues) {
|
|
272
|
+
lines.push(`- Cảnh báo: ${issue}`);
|
|
273
|
+
}
|
|
274
|
+
return lines;
|
|
275
|
+
}
|